You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datasketches.apache.org by ti...@apache.org on 2026/05/08 07:39:47 UTC
(datasketches-rust) branch main updated: refactor: introduce hash_value for compatible hashing (#118)
This is an automated email from the ASF dual-hosted git repository.
tisonkun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datasketches-rust.git
The following commit(s) were added to refs/heads/main by this push:
new 3a33bdd refactor: introduce hash_value for compatible hashing (#118)
3a33bdd is described below
commit 3a33bddd0bc5f8448d50411dde9d276a838e5fa0
Author: tison <wa...@gmail.com>
AuthorDate: Fri May 8 15:39:42 2026 +0800
refactor: introduce hash_value for compatible hashing (#118)
Signed-off-by: tison <wa...@gmail.com>
---
CHANGELOG.md | 3 +
Cargo.lock | 431 ++++++++++++++++++-------
datasketches/src/common/mod.rs | 13 -
datasketches/src/cpc/sketch.rs | 33 +-
datasketches/src/hash_value/canonical_float.rs | 118 +++++++
datasketches/src/hash_value/mod.rs | 91 ++++++
datasketches/src/hash_value/natural_extend.rs | 144 +++++++++
datasketches/src/hash_value/raw_bytes.rs | 136 ++++++++
datasketches/src/hash_value/sign_extend.rs | 165 ++++++++++
datasketches/src/hash_value/value.rs | 116 +++++++
datasketches/src/hll/mod.rs | 5 +-
datasketches/src/hll/sketch.rs | 10 +-
datasketches/src/lib.rs | 1 +
datasketches/src/theta/sketch.rs | 46 +--
datasketches/tests/theta_sketch_test.rs | 25 +-
15 files changed, 1145 insertions(+), 192 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 972f86c..305be78 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,14 +8,17 @@ All significant changes to this project will be documented in this file.
* `CountMinSketch` now has a type parameter for the count type. Possible values are `u8` to `u64` and `i8` to `i64`.
* `HllUnion::get_result` is renamed to `HllUnion::to_sketch`.
+* `update_f32` and `update_f64` are removed from `ThetaSketch`. Use `hash_value`'s wrapper instead.
### New features
+* New module `hash_value` provides several value wrappers for matching concrete hashing strategies.
* `CountMinSketch` with unsigned values now supports `halve` and `decay` operations.
* `CpcSketch` and `CpcUnion` are now available for cardinality estimation.
* `CpcWrapper` is now available for reading estimation from a serialized CpcSketch without full deserialization.
* `FrequentItemsSketch` now supports serde for any value implement `FrequentItemValue` (builtin supports for `i64`, `u64`, and `String`).
* Expose `codec::SketchBytes`, `codec::SketchSlice`, and `FrequentItemValue` as public API.
+* `hll::Coupon` is now public. You can calculate the coupon and reuse it multiple times avoiding the overhead of hashing multiple times.
## v0.2.0 (2026-01-14)
diff --git a/Cargo.lock b/Cargo.lock
index e857def..3f3c50a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -13,9 +13,9 @@ dependencies = [
[[package]]
name = "anstream"
-version = "0.6.21"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
+checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -28,15 +28,15 @@ dependencies = [
[[package]]
name = "anstyle"
-version = "1.0.13"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
+checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
[[package]]
name = "anstyle-parse"
-version = "0.2.7"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
+checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
dependencies = [
"utf8parse",
]
@@ -47,7 +47,7 @@ version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
- "windows-sys 0.61.2",
+ "windows-sys",
]
[[package]]
@@ -58,9 +58,15 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
- "windows-sys 0.61.2",
+ "windows-sys",
]
+[[package]]
+name = "anyhow"
+version = "1.0.102"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
+
[[package]]
name = "autocfg"
version = "1.5.0"
@@ -69,9 +75,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bitflags"
-version = "2.10.0"
+version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
+checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
[[package]]
name = "cfg-if"
@@ -81,9 +87,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "clap"
-version = "4.5.55"
+version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e34525d5bbbd55da2bb745d34b36121baac88d07619a9a09cfcf4a6c0832785"
+checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
dependencies = [
"clap_builder",
"clap_derive",
@@ -91,9 +97,9 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.5.55"
+version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59a20016a20a3da95bef50ec7238dbd09baeef4311dcdd38ec15aba69812fb61"
+checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
dependencies = [
"anstream",
"anstyle",
@@ -103,9 +109,9 @@ dependencies = [
[[package]]
name = "clap_derive"
-version = "4.5.55"
+version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5"
+checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
dependencies = [
"heck",
"proc-macro2",
@@ -115,26 +121,25 @@ dependencies = [
[[package]]
name = "clap_lex"
-version = "0.7.7"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
+checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]]
name = "colorchoice"
-version = "1.0.4"
+version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
+checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]]
name = "console"
-version = "0.15.11"
+version = "0.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
+checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87"
dependencies = [
"encode_unicode",
"libc",
- "once_cell",
- "windows-sys 0.59.0",
+ "windows-sys",
]
[[package]]
@@ -152,10 +157,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]]
-name = "env_home"
-version = "0.1.0"
+name = "equivalent"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
@@ -164,25 +169,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
- "windows-sys 0.61.2",
+ "windows-sys",
]
[[package]]
name = "fastrand"
-version = "2.3.0"
+version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
+
+[[package]]
+name = "foldhash"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "getrandom"
-version = "0.3.4"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
+checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasip2",
+ "wasip3",
]
[[package]]
@@ -208,17 +220,50 @@ dependencies = [
"syn",
]
+[[package]]
+name = "hashbrown"
+version = "0.15.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
+dependencies = [
+ "foldhash",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
+
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+[[package]]
+name = "id-arena"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
+
+[[package]]
+name = "indexmap"
+version = "2.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.17.0",
+ "serde",
+ "serde_core",
+]
+
[[package]]
name = "insta"
-version = "1.46.1"
+version = "1.47.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "248b42847813a1550dafd15296fd9748c651d0c32194559dbc05d804d54b21e8"
+checksum = "7b4a6248eb93a4401ed2f37dfe8ea592d3cf05b7cf4f8efa867b6895af7e094e"
dependencies = [
"console",
"once_cell",
@@ -232,23 +277,41 @@ version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
+[[package]]
+name = "itoa"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
+
+[[package]]
+name = "leb128fmt"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
+
[[package]]
name = "libc"
-version = "0.2.180"
+version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
+checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "linux-raw-sys"
-version = "0.11.0"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
+
+[[package]]
+name = "log"
+version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "memchr"
-version = "2.7.6"
+version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
+checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "num-traits"
@@ -261,9 +324,9 @@ dependencies = [
[[package]]
name = "once_cell"
-version = "1.21.3"
+version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "once_cell_polyfill"
@@ -271,6 +334,16 @@ version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
+[[package]]
+name = "prettyplease"
+version = "0.2.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
+dependencies = [
+ "proc-macro2",
+ "syn",
+]
+
[[package]]
name = "proc-macro2"
version = "1.0.106"
@@ -282,24 +355,24 @@ dependencies = [
[[package]]
name = "quote"
-version = "1.0.44"
+version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
+checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
-version = "5.3.0"
+version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "regex"
-version = "1.12.2"
+version = "1.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
+checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
dependencies = [
"aho-corasick",
"memchr",
@@ -309,9 +382,9 @@ dependencies = [
[[package]]
name = "regex-automata"
-version = "0.4.13"
+version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
+checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
dependencies = [
"aho-corasick",
"memchr",
@@ -320,21 +393,21 @@ dependencies = [
[[package]]
name = "regex-syntax"
-version = "0.8.8"
+version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
+checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "rustix"
-version = "1.1.3"
+version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
+checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
- "windows-sys 0.61.2",
+ "windows-sys",
]
[[package]]
@@ -343,6 +416,54 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+[[package]]
+name = "semver"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
+
+[[package]]
+name = "serde"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.149"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
+dependencies = [
+ "itoa",
+ "memchr",
+ "serde",
+ "serde_core",
+ "zmij",
+]
+
[[package]]
name = "similar"
version = "2.7.0"
@@ -357,9 +478,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
-version = "2.0.114"
+version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
+checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
@@ -368,22 +489,28 @@ dependencies = [
[[package]]
name = "tempfile"
-version = "3.24.0"
+version = "3.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
+checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand",
"getrandom",
"once_cell",
"rustix",
- "windows-sys 0.61.2",
+ "windows-sys",
]
[[package]]
name = "unicode-ident"
-version = "1.0.22"
+version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "utf8parse"
@@ -397,119 +524,169 @@ version = "1.0.1+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
dependencies = [
- "wit-bindgen",
+ "wit-bindgen 0.46.0",
]
[[package]]
-name = "which"
-version = "8.0.0"
+name = "wasip3"
+version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d"
+checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
dependencies = [
- "env_home",
- "rustix",
- "winsafe",
+ "wit-bindgen 0.51.0",
]
[[package]]
-name = "windows-link"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
-
-[[package]]
-name = "windows-sys"
-version = "0.59.0"
+name = "wasm-encoder"
+version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
dependencies = [
- "windows-targets",
+ "leb128fmt",
+ "wasmparser",
]
[[package]]
-name = "windows-sys"
-version = "0.61.2"
+name = "wasm-metadata"
+version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
dependencies = [
- "windows-link",
+ "anyhow",
+ "indexmap",
+ "wasm-encoder",
+ "wasmparser",
]
[[package]]
-name = "windows-targets"
-version = "0.52.6"
+name = "wasmparser"
+version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_gnullvm",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "bitflags",
+ "hashbrown 0.15.5",
+ "indexmap",
+ "semver",
]
[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.52.6"
+name = "which"
+version = "8.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+checksum = "81995fafaaaf6ae47a7d0cc83c67caf92aeb7e5331650ae6ff856f7c0c60c459"
+dependencies = [
+ "libc",
+]
[[package]]
-name = "windows_aarch64_msvc"
-version = "0.52.6"
+name = "windows-link"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
-name = "windows_i686_gnu"
-version = "0.52.6"
+name = "windows-sys"
+version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
[[package]]
-name = "windows_i686_gnullvm"
-version = "0.52.6"
+name = "wit-bindgen"
+version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]]
-name = "windows_i686_msvc"
-version = "0.52.6"
+name = "wit-bindgen"
+version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
+dependencies = [
+ "wit-bindgen-rust-macro",
+]
[[package]]
-name = "windows_x86_64_gnu"
-version = "0.52.6"
+name = "wit-bindgen-core"
+version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
+dependencies = [
+ "anyhow",
+ "heck",
+ "wit-parser",
+]
[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.52.6"
+name = "wit-bindgen-rust"
+version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
+dependencies = [
+ "anyhow",
+ "heck",
+ "indexmap",
+ "prettyplease",
+ "syn",
+ "wasm-metadata",
+ "wit-bindgen-core",
+ "wit-component",
+]
[[package]]
-name = "windows_x86_64_msvc"
-version = "0.52.6"
+name = "wit-bindgen-rust-macro"
+version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
+dependencies = [
+ "anyhow",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wit-bindgen-core",
+ "wit-bindgen-rust",
+]
[[package]]
-name = "winsafe"
-version = "0.0.19"
+name = "wit-component"
+version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
+checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
+dependencies = [
+ "anyhow",
+ "bitflags",
+ "indexmap",
+ "log",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "wasm-encoder",
+ "wasm-metadata",
+ "wasmparser",
+ "wit-parser",
+]
[[package]]
-name = "wit-bindgen"
-version = "0.46.0"
+name = "wit-parser"
+version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
+checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
+dependencies = [
+ "anyhow",
+ "id-arena",
+ "indexmap",
+ "log",
+ "semver",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "unicode-xid",
+ "wasmparser",
+]
[[package]]
name = "x"
@@ -518,3 +695,9 @@ dependencies = [
"clap",
"which",
]
+
+[[package]]
+name = "zmij"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
diff --git a/datasketches/src/common/mod.rs b/datasketches/src/common/mod.rs
index a43373a..36b734f 100644
--- a/datasketches/src/common/mod.rs
+++ b/datasketches/src/common/mod.rs
@@ -26,16 +26,3 @@ pub use self::resize::ResizeFactor;
// private to datasketches crate
pub(crate) mod binomial_bounds;
pub(crate) mod inv_pow2_table;
-
-/// Canonicalize double value for compatibility with Java
-pub(crate) fn canonical_double(value: f64) -> u64 {
- if value.is_nan() {
- // Java's Double.doubleToLongBits() NaN value
- 0x7ff8000000000000u64
- } else {
- // -0.0 + 0.0 == +0.0 under IEEE754 roundTiesToEven rounding mode,
- // which Rust guarantees. Thus, by adding a positive zero we
- // canonicalize signed zero without any branches in one instruction.
- (value + 0.0).to_bits()
- }
-}
diff --git a/datasketches/src/cpc/sketch.rs b/datasketches/src/cpc/sketch.rs
index 534f0a0..5064e45 100644
--- a/datasketches/src/cpc/sketch.rs
+++ b/datasketches/src/cpc/sketch.rs
@@ -24,7 +24,6 @@ use crate::codec::assert::ensure_serial_version_is;
use crate::codec::assert::insufficient_data;
use crate::codec::family::Family;
use crate::common::NumStdDev;
-use crate::common::canonical_double;
use crate::common::inv_pow2_table::INVERSE_POWERS_OF_2;
use crate::cpc::DEFAULT_LG_K;
use crate::cpc::Flavor;
@@ -172,7 +171,25 @@ impl CpcSketch {
/// Update the sketch with a hashable value.
///
- /// For `f32`/`f64` values, use `update_f32`/`update_f64` instead.
+ /// You may use [`hash_value`](crate::hash_value) wrappers when matching other datasketches
+ /// implementations require a specific value hashing strategy.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use datasketches::cpc::CpcSketch;
+ /// use datasketches::hash_value;
+ ///
+ /// let mut sketch = CpcSketch::with_seed(11, 123);
+ /// sketch.update(1);
+ /// sketch.update(2);
+ /// sketch.update(3);
+ ///
+ /// let mut sketch = CpcSketch::with_seed(11, 123);
+ /// sketch.update(hash_value::canonical_float::from_f64(1.5));
+ /// sketch.update(hash_value::canonical_float::from_f64(2.5));
+ /// sketch.update(hash_value::canonical_float::from_f64(3.5));
+ /// ```
pub fn update<T: Hash>(&mut self, value: T) {
let mut hasher = MurmurHash3X64128::with_seed(self.seed);
value.hash(&mut hasher);
@@ -191,18 +208,6 @@ impl CpcSketch {
self.row_col_update(row_col);
}
- /// Update the sketch with a f64 value.
- pub fn update_f64(&mut self, value: f64) {
- // Canonicalize double for compatibility with Java
- let canonical = canonical_double(value);
- self.update(canonical);
- }
-
- /// Update the sketch with a f32 value.
- pub fn update_f32(&mut self, value: f32) {
- self.update_f64(value as f64);
- }
-
pub(super) fn flavor(&self) -> Flavor {
determine_flavor(self.lg_k, self.num_coupons)
}
diff --git a/datasketches/src/hash_value/canonical_float.rs b/datasketches/src/hash_value/canonical_float.rs
new file mode 100644
index 0000000..354cffd
--- /dev/null
+++ b/datasketches/src/hash_value/canonical_float.rs
@@ -0,0 +1,118 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//! Canonical floating-point hash value wrappers.
+//!
+//! [`CanonicalFloat`] maps `f32` and `f64` values through a canonical `f64` bit pattern before
+//! hashing. Signed zero values hash the same, all NaN values use one canonical NaN bit pattern,
+//! and equal `f32`/`f64` values hash the same.
+//!
+//! This strategy is compatible with how other datasketches hashes floating-point numbers.
+
+use std::hash::Hash;
+use std::hash::Hasher;
+
+use super::value::HashStrategy;
+use super::value::Value;
+
+/// A floating-point value wrapper that uses canonical floating-point hashing.
+///
+/// See the [module level documentation](super) for more.
+pub type CanonicalFloat<T> = Value<T, CanonicalFloatStrategy>;
+
+/// Hashing strategy for [`CanonicalFloat`].
+#[doc(hidden)]
+pub struct CanonicalFloatStrategy;
+
+/// Create a canonical hashable value from a `f32` value.
+///
+/// `f32` values are converted to `f64` before hashing. Values that are not exactly representable
+/// in `f32` may hash differently from the corresponding `f64` value. Signed zero values hash the
+/// same, and all NaN values use one canonical NaN bit pattern.
+///
+/// # Examples
+///
+/// ```
+/// # use datasketches::hash_value::calculate_hash;
+/// # use datasketches::hash_value::canonical_float;
+/// assert_eq!(
+/// calculate_hash(canonical_float::from_f32(0.0)),
+/// calculate_hash(canonical_float::from_f32(-0.0))
+/// );
+/// assert_eq!(
+/// calculate_hash(canonical_float::from_f32(5.0)),
+/// calculate_hash(canonical_float::from_f64(5.0))
+/// );
+/// assert_ne!(
+/// calculate_hash(canonical_float::from_f32(3.15)),
+/// calculate_hash(canonical_float::from_f64(3.15))
+/// );
+/// ```
+#[inline(always)]
+pub fn from_f32(v: f32) -> CanonicalFloat<f32> {
+ CanonicalFloat::new(v)
+}
+
+/// Create a canonical hashable value from a `f64` value.
+///
+/// Signed zero values hash the same, and all NaN values use one canonical NaN bit pattern.
+///
+/// # Examples
+///
+/// ```
+/// # use datasketches::hash_value::calculate_hash;
+/// # use datasketches::hash_value::canonical_float;
+/// assert_eq!(
+/// calculate_hash(canonical_float::from_f64(0.0)),
+/// calculate_hash(canonical_float::from_f64(-0.0))
+/// );
+/// assert_eq!(
+/// calculate_hash(canonical_float::from_f32(5.0)),
+/// calculate_hash(canonical_float::from_f64(5.0))
+/// );
+/// assert_ne!(
+/// calculate_hash(canonical_float::from_f32(3.15)),
+/// calculate_hash(canonical_float::from_f64(3.15))
+/// );
+/// ```
+#[inline(always)]
+pub fn from_f64(v: f64) -> CanonicalFloat<f64> {
+ CanonicalFloat::new(v)
+}
+
+impl HashStrategy<f32> for CanonicalFloatStrategy {
+ fn hash<H: Hasher>(value: &f32, state: &mut H) {
+ let value = *value as f64;
+ let canonical_value = from_f64(value);
+ canonical_value.hash(state);
+ }
+}
+
+impl HashStrategy<f64> for CanonicalFloatStrategy {
+ fn hash<H: Hasher>(value: &f64, state: &mut H) {
+ let canonical = if value.is_nan() {
+ // Java's Double.doubleToLongBits() NaN value.
+ 0x7ff8000000000000u64
+ } else {
+ // -0.0 + 0.0 == +0.0 under IEEE754 roundTiesToEven rounding mode,
+ // which Rust guarantees. Thus, by adding a positive zero we
+ // canonicalize signed zero without any branches in one instruction.
+ (value + 0.0).to_bits()
+ };
+ canonical.hash(state);
+ }
+}
diff --git a/datasketches/src/hash_value/mod.rs b/datasketches/src/hash_value/mod.rs
new file mode 100644
index 0000000..61e6024
--- /dev/null
+++ b/datasketches/src/hash_value/mod.rs
@@ -0,0 +1,91 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//! Hashable value wrappers for sketches.
+//!
+//! Sketch update APIs accept any value that implements [`Hash`]. For most Rust values,
+//! passing the value directly is sufficient. This module provides value wrappers for
+//! cases where the default implementation does not match a sketch's compatibility rules.
+//!
+//! ## Floating-point Numbers
+//!
+//! [`canonical_float::CanonicalFloat`] maps `f32` and `f64` values through a canonical `f64` bit
+//! pattern before hashing. Signed zero values hash the same, all NaN values use one canonical NaN
+//! bit pattern, and equal `f32`/`f64` values hash the same.
+//!
+//! This strategy is the same as how other datasketches implementations hash floating-point numbers.
+//!
+//! Read the docs of concrete value wrapper for more details and examples.
+//!
+//! * [`canonical_float::from_f32`]
+//! * [`canonical_float::from_f64`]
+//!
+//! ## Integers
+//!
+//! [`sign_extend::SignExtend`] first sign-extends values to 64-bits, and then hashes the resulting
+//! integers. This strategy is the same as how datasketches-cpp hashes short integers for
+//! `HllSketch` and `CpcSketch`.
+//!
+//! Read the docs of concrete value wrapper for more details and examples.
+//!
+//! * [`sign_extend::from_i8`], [`sign_extend::from_u8`]
+//! * [`sign_extend::from_i16`], [`sign_extend::from_u16`]
+//! * [`sign_extend::from_i32`], [`sign_extend::from_u32`]
+//!
+//! [`natural_extend::NaturalExtend`] first widens signed values to `i64` and unsigned values to
+//! `u64`, and then hashes the resulting integers. This strategy is the same as how datasketches-cpp
+//! hashes short integers for `BloomFilter`.
+//!
+//! Read the docs of concrete value wrapper for more details and examples.
+//!
+//! * [`natural_extend::from_i8`], [`natural_extend::from_u8`]
+//! * [`natural_extend::from_i16`], [`natural_extend::from_u16`]
+//! * [`natural_extend::from_i32`], [`natural_extend::from_u32`]
+//!
+//! ## Raw Bytes
+//!
+//! [`raw_bytes::RawBytes`] hashes byte and string inputs as raw bytes without Rust's slice or
+//! string length prefix.
+//!
+//! Empty byte and string inputs have zero bytes to hash. Other datasketches implementations skip
+//! empty strings before hashing, so check `is_empty` before updating a sketch when that behavior
+//! matters.
+//!
+//! Read the docs of concrete value wrapper for more details and examples.
+//!
+//! * [`raw_bytes::from_vec`]
+//! * [`raw_bytes::from_string`]
+//! * [`raw_bytes::from_slice`]
+//! * [`raw_bytes::from_str`]
+
+pub mod canonical_float;
+pub mod natural_extend;
+pub mod raw_bytes;
+pub mod sign_extend;
+pub mod value;
+
+use std::hash::Hash;
+use std::hash::Hasher;
+
+#[doc(hidden)] // for doctest
+pub fn calculate_hash<T: Hash>(t: T) -> u64 {
+ use std::hash::DefaultHasher;
+
+ let mut s = DefaultHasher::new();
+ t.hash(&mut s);
+ s.finish()
+}
diff --git a/datasketches/src/hash_value/natural_extend.rs b/datasketches/src/hash_value/natural_extend.rs
new file mode 100644
index 0000000..0717b50
--- /dev/null
+++ b/datasketches/src/hash_value/natural_extend.rs
@@ -0,0 +1,144 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//! Naturally extended integer hash value wrappers.
+//!
+//! [`NaturalExtend`] first widens signed values to `i64` and unsigned values to `u64`, and then
+//! hashes the resulting integers.
+//!
+//! This strategy is the same as how datasketches-cpp hashes short integers for `BloomFilter`.
+
+use std::hash::Hash;
+use std::hash::Hasher;
+
+use super::value::HashStrategy;
+use super::value::Value;
+
+/// An integer value wrapper that uses Rust's natural integer widening before hashing.
+///
+/// See the [module level documentation](super) for more.
+pub type NaturalExtend<T> = Value<T, NaturalExtendStrategy>;
+
+/// Hashing strategy for [`NaturalExtend`].
+#[doc(hidden)]
+pub struct NaturalExtendStrategy;
+
+/// Create a naturally extended hashable value from an `i8` value.
+///
+/// # Examples
+///
+/// ```
+/// # use datasketches::hash_value::calculate_hash;
+/// # use datasketches::hash_value::natural_extend::from_i8;
+/// assert_eq!(calculate_hash(from_i8(-1)), calculate_hash(-1i64));
+/// assert_eq!(calculate_hash(from_i8(42)), calculate_hash(42i64));
+/// ```
+pub fn from_i8(v: i8) -> NaturalExtend<i8> {
+ NaturalExtend::new(v)
+}
+
+/// Create a naturally extended hashable value from a `u8` value.
+///
+/// # Examples
+///
+/// ```
+/// # use datasketches::hash_value::calculate_hash;
+/// # use datasketches::hash_value::natural_extend::from_u8;
+/// assert_eq!(calculate_hash(from_u8(255)), calculate_hash(255u64));
+/// assert_eq!(calculate_hash(from_u8(42)), calculate_hash(42u64));
+/// ```
+pub fn from_u8(v: u8) -> NaturalExtend<u8> {
+ NaturalExtend::new(v)
+}
+
+/// Create a naturally extended hashable value from an `i16` value.
+///
+/// # Examples
+///
+/// ```
+/// # use datasketches::hash_value::calculate_hash;
+/// # use datasketches::hash_value::natural_extend::from_i16;
+/// assert_eq!(calculate_hash(from_i16(-1)), calculate_hash(-1i64));
+/// assert_eq!(calculate_hash(from_i16(42)), calculate_hash(42i64));
+/// ```
+pub fn from_i16(v: i16) -> NaturalExtend<i16> {
+ NaturalExtend::new(v)
+}
+
+/// Create a naturally extended hashable value from a `u16` value.
+///
+/// # Examples
+///
+/// ```
+/// # use datasketches::hash_value::calculate_hash;
+/// # use datasketches::hash_value::natural_extend::from_u16;
+/// assert_eq!(calculate_hash(from_u16(65535)), calculate_hash(65535u64));
+/// assert_eq!(calculate_hash(from_u16(42)), calculate_hash(42u64));
+/// ```
+pub fn from_u16(v: u16) -> NaturalExtend<u16> {
+ NaturalExtend::new(v)
+}
+
+/// Create a naturally extended hashable value from an `i32` value.
+///
+/// # Examples
+///
+/// ```
+/// # use datasketches::hash_value::calculate_hash;
+/// # use datasketches::hash_value::natural_extend::from_i32;
+/// assert_eq!(calculate_hash(from_i32(-1)), calculate_hash(-1i64));
+/// assert_eq!(calculate_hash(from_i32(42)), calculate_hash(42i64));
+/// ```
+pub fn from_i32(v: i32) -> NaturalExtend<i32> {
+ NaturalExtend::new(v)
+}
+
+/// Create a naturally extended hashable value from a `u32` value.
+///
+/// # Examples
+///
+/// ```
+/// # use datasketches::hash_value::calculate_hash;
+/// # use datasketches::hash_value::natural_extend::from_u32;
+/// assert_eq!(
+/// calculate_hash(from_u32(4294967295)),
+/// calculate_hash(4294967295u64)
+/// );
+/// assert_eq!(calculate_hash(from_u32(42)), calculate_hash(42u64));
+/// ```
+pub fn from_u32(v: u32) -> NaturalExtend<u32> {
+ NaturalExtend::new(v)
+}
+
+macro_rules! impl_natural_extend {
+ ($t:ty, |$v:ident| $extended:expr) => {
+ impl HashStrategy<$t> for NaturalExtendStrategy {
+ fn hash<H: Hasher>(value: &$t, state: &mut H) {
+ let $v = *value;
+ let extended = $extended;
+ extended.hash(state);
+ }
+ }
+ };
+}
+
+impl_natural_extend!(i8, |v| v as i64);
+impl_natural_extend!(u8, |v| v as u64);
+impl_natural_extend!(i16, |v| v as i64);
+impl_natural_extend!(u16, |v| v as u64);
+impl_natural_extend!(i32, |v| v as i64);
+impl_natural_extend!(u32, |v| v as u64);
diff --git a/datasketches/src/hash_value/raw_bytes.rs b/datasketches/src/hash_value/raw_bytes.rs
new file mode 100644
index 0000000..4d9cfc1
--- /dev/null
+++ b/datasketches/src/hash_value/raw_bytes.rs
@@ -0,0 +1,136 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//! Raw byte and string hash value wrappers.
+//!
+//! [`RawBytes`] hashes byte and string inputs as raw bytes without Rust's slice or string length
+//! prefix.
+//!
+//! Empty byte and string inputs have zero bytes to hash. Other datasketches implementations skip
+//! empty strings before hashing, so check `is_empty` before updating a sketch when that behavior
+//! matters.
+
+use std::hash::Hasher;
+
+use super::value::HashStrategy;
+use super::value::Value;
+
+/// A byte or string value wrapper that hashes raw bytes.
+///
+/// See the [module level documentation](super) for more.
+pub type RawBytes<T> = Value<T, RawBytesStrategy>;
+
+/// Hashing strategy for [`RawBytes`].
+#[doc(hidden)]
+pub struct RawBytesStrategy;
+
+/// Create a raw-byte hashable value from a byte vector.
+///
+/// This hashes the vector contents without Rust's slice length prefix.
+///
+/// # Examples
+///
+/// ```
+/// # use datasketches::hash_value::calculate_hash;
+/// # use datasketches::hash_value::raw_bytes::{from_slice, from_vec};
+/// assert_eq!(
+/// calculate_hash(from_vec(b"abc".to_vec())),
+/// calculate_hash(from_slice(b"abc"))
+/// );
+/// assert!(from_vec(vec![]).is_empty());
+/// assert!(!from_vec(b"abc".to_vec()).is_empty());
+/// ```
+pub fn from_vec(v: Vec<u8>) -> RawBytes<Vec<u8>> {
+ RawBytes::new(v)
+}
+
+/// Create a raw-byte hashable value from a string.
+///
+/// This hashes the UTF-8 bytes of the string without Rust's string length prefix.
+///
+/// # Examples
+///
+/// ```
+/// # use datasketches::hash_value::calculate_hash;
+/// # use datasketches::hash_value::raw_bytes::{from_str, from_string};
+/// assert_eq!(
+/// calculate_hash(from_string("abc".to_owned())),
+/// calculate_hash(from_str("abc"))
+/// );
+/// assert!(from_string(String::new()).is_empty());
+/// assert!(!from_string("abc".to_string()).is_empty());
+/// ```
+pub fn from_string(v: String) -> RawBytes<String> {
+ RawBytes::new(v)
+}
+
+/// Create a raw-byte hashable value from a byte slice.
+///
+/// This hashes the slice contents without Rust's slice length prefix.
+///
+/// # Examples
+///
+/// ```
+/// # use datasketches::hash_value::calculate_hash;
+/// # use datasketches::hash_value::raw_bytes::{from_slice, from_vec};
+/// assert_eq!(
+/// calculate_hash(from_slice(b"abc")),
+/// calculate_hash(from_vec(b"abc".to_vec()))
+/// );
+/// assert!(from_slice(&[]).is_empty());
+/// assert!(!from_slice(b"abc").is_empty());
+/// ```
+pub fn from_slice(v: &[u8]) -> RawBytes<&[u8]> {
+ RawBytes::new(v)
+}
+
+/// Create a raw-byte hashable value from a string slice.
+///
+/// This hashes the UTF-8 bytes of the string slice without Rust's string length prefix.
+///
+/// # Examples
+///
+/// ```
+/// # use datasketches::hash_value::calculate_hash;
+/// # use datasketches::hash_value::raw_bytes::{from_str, from_string};
+/// assert_eq!(
+/// calculate_hash(from_str("abc")),
+/// calculate_hash(from_string("abc".to_owned()))
+/// );
+/// assert!(from_str("").is_empty());
+/// assert!(!from_str("abc").is_empty());
+/// ```
+pub fn from_str(v: &str) -> RawBytes<&str> {
+ RawBytes::new(v)
+}
+
+macro_rules! impl_raw_bytes {
+ ($t:ty, |$v:ident| $as_slice:expr) => {
+ impl HashStrategy<$t> for RawBytesStrategy {
+ fn hash<H: Hasher>(value: &$t, state: &mut H) {
+ let $v = value;
+ let slice = $as_slice;
+ state.write(slice);
+ }
+ }
+ };
+}
+
+impl_raw_bytes!(Vec<u8>, |v| v.as_slice());
+impl_raw_bytes!(&[u8], |v| v);
+impl_raw_bytes!(String, |v| v.as_bytes());
+impl_raw_bytes!(&str, |v| v.as_bytes());
diff --git a/datasketches/src/hash_value/sign_extend.rs b/datasketches/src/hash_value/sign_extend.rs
new file mode 100644
index 0000000..c1ec518
--- /dev/null
+++ b/datasketches/src/hash_value/sign_extend.rs
@@ -0,0 +1,165 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//! Sign-extended integer hash value wrappers.
+//!
+//! [`SignExtend`] first sign-extends values to 64-bits and then hashes the resulting integers.
+//!
+//! This strategy is the same as how datasketches-cpp hashes short integers for `HllSketch` and
+//! `CpcSketch`.
+
+use std::hash::Hash;
+use std::hash::Hasher;
+
+use super::value::HashStrategy;
+use super::value::Value;
+
+/// An integer value wrapper that sign-extends the value before hashing.
+///
+/// See the [module level documentation](super) for more.
+pub type SignExtend<T> = Value<T, SignExtendStrategy>;
+
+/// Hashing strategy for [`SignExtend`].
+#[doc(hidden)]
+pub struct SignExtendStrategy;
+
+/// Create a sign-extended hashable value from an `i8` value.
+///
+/// # Examples
+///
+/// ```
+/// # use datasketches::hash_value::calculate_hash;
+/// # use datasketches::hash_value::sign_extend::{from_i8, from_u8};
+/// assert_eq!(calculate_hash(from_i8(-1)), calculate_hash(from_u8(255)));
+/// assert_eq!(calculate_hash(from_i8(-1)), calculate_hash(-1i64));
+/// assert_eq!(calculate_hash(from_i8(42)), calculate_hash(42i64));
+/// ```
+pub fn from_i8(v: i8) -> SignExtend<i8> {
+ SignExtend::new(v)
+}
+
+/// Create a sign-extended hashable value from a `u8` value.
+///
+/// `255u8` sign-extends like `-1i8`, not like `255u64`.
+///
+/// # Examples
+///
+/// ```
+/// # use datasketches::hash_value::calculate_hash;
+/// # use datasketches::hash_value::sign_extend::{from_i8, from_u8};
+/// assert_eq!(calculate_hash(from_u8(255)), calculate_hash(from_i8(-1)));
+/// assert_eq!(calculate_hash(from_u8(255)), calculate_hash(-1i64));
+/// assert_eq!(calculate_hash(from_u8(1)), calculate_hash(1i64));
+/// ```
+pub fn from_u8(v: u8) -> SignExtend<u8> {
+ SignExtend::new(v)
+}
+
+/// Create a sign-extended hashable value from an `i16` value.
+///
+/// # Examples
+///
+/// ```
+/// # use datasketches::hash_value::calculate_hash;
+/// # use datasketches::hash_value::sign_extend::{from_i16, from_u16};
+/// assert_eq!(
+/// calculate_hash(from_i16(-1)),
+/// calculate_hash(from_u16(65535))
+/// );
+/// assert_eq!(calculate_hash(from_i16(-1)), calculate_hash(-1i64));
+/// assert_eq!(calculate_hash(from_i16(42)), calculate_hash(42i64));
+/// ```
+pub fn from_i16(v: i16) -> SignExtend<i16> {
+ SignExtend::new(v)
+}
+
+/// Create a sign-extended hashable value from a `u16` value.
+///
+/// `65535u16` sign-extends like `-1i16`, not like `65535u64`.
+///
+/// # Examples
+///
+/// ```
+/// # use datasketches::hash_value::calculate_hash;
+/// # use datasketches::hash_value::sign_extend::{from_i16, from_u16};
+/// assert_eq!(
+/// calculate_hash(from_u16(65535)),
+/// calculate_hash(from_i16(-1))
+/// );
+/// assert_eq!(calculate_hash(from_u16(65535)), calculate_hash(-1i64));
+/// assert_eq!(calculate_hash(from_u16(42)), calculate_hash(42i64));
+/// ```
+pub fn from_u16(v: u16) -> SignExtend<u16> {
+ SignExtend::new(v)
+}
+
+/// Create a sign-extended hashable value from an `i32` value.
+///
+/// # Examples
+///
+/// ```
+/// # use datasketches::hash_value::calculate_hash;
+/// # use datasketches::hash_value::sign_extend::{from_i32, from_u32};
+/// assert_eq!(
+/// calculate_hash(from_i32(-1)),
+/// calculate_hash(from_u32(4294967295))
+/// );
+/// assert_eq!(calculate_hash(from_i32(-1)), calculate_hash(-1i64));
+/// assert_eq!(calculate_hash(from_i32(42)), calculate_hash(42i64));
+/// ```
+pub fn from_i32(v: i32) -> SignExtend<i32> {
+ SignExtend::new(v)
+}
+
+/// Create a sign-extended hashable value from a `u32` value.
+///
+/// `4294967295u32` sign-extends like `-1i32`, not like `4294967295u64`.
+///
+/// # Examples
+///
+/// ```
+/// # use datasketches::hash_value::calculate_hash;
+/// # use datasketches::hash_value::sign_extend::{from_i32, from_u32};
+/// assert_eq!(
+/// calculate_hash(from_u32(4294967295)),
+/// calculate_hash(from_i32(-1))
+/// );
+/// assert_eq!(calculate_hash(from_u32(4294967295)), calculate_hash(-1i64));
+/// assert_eq!(calculate_hash(from_u32(42)), calculate_hash(42i64));
+/// ```
+pub fn from_u32(v: u32) -> SignExtend<u32> {
+ SignExtend::new(v)
+}
+
+macro_rules! impl_sign_extend {
+ ($t:ty, |$v:ident| $extended:expr) => {
+ impl HashStrategy<$t> for SignExtendStrategy {
+ fn hash<H: Hasher>(value: &$t, state: &mut H) {
+ let $v = *value;
+ let extended = $extended as u64;
+ extended.hash(state);
+ }
+ }
+ };
+}
+
+impl_sign_extend!(i8, |v| v as i64);
+impl_sign_extend!(u8, |v| (v as i8) as i64);
+impl_sign_extend!(i16, |v| v as i64);
+impl_sign_extend!(u16, |v| (v as i16) as i64);
+impl_sign_extend!(i32, |v| v as i64);
+impl_sign_extend!(u32, |v| (v as i32) as i64);
diff --git a/datasketches/src/hash_value/value.rs b/datasketches/src/hash_value/value.rs
new file mode 100644
index 0000000..be857b0
--- /dev/null
+++ b/datasketches/src/hash_value/value.rs
@@ -0,0 +1,116 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//! Shared value wrapper and hashing strategy support.
+
+use std::cmp::Ordering;
+use std::fmt;
+use std::hash::Hash;
+use std::hash::Hasher;
+use std::marker::PhantomData;
+use std::ops::Deref;
+use std::ops::DerefMut;
+
+/// A value wrapper that hashes its inner value with strategy `S`.
+///
+/// Most users should prefer the strategy-specific constructors.
+pub struct Value<T, S> {
+ value: T,
+ strategy: PhantomData<fn() -> S>,
+}
+
+impl<T, S> Value<T, S> {
+ /// Create a value wrapper.
+ #[inline(always)]
+ pub fn new(value: T) -> Self {
+ Self {
+ value,
+ strategy: PhantomData,
+ }
+ }
+
+ /// Get the value out.
+ #[inline(always)]
+ pub fn into_inner(self) -> T {
+ self.value
+ }
+}
+
+impl<T, S> Deref for Value<T, S> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ &self.value
+ }
+}
+
+impl<T, S> DerefMut for Value<T, S> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.value
+ }
+}
+
+impl<T: Clone, S> Clone for Value<T, S> {
+ fn clone(&self) -> Self {
+ Self::new(self.value.clone())
+ }
+}
+
+impl<T: Copy, S> Copy for Value<T, S> {}
+
+impl<T: PartialEq, S> PartialEq for Value<T, S> {
+ fn eq(&self, other: &Self) -> bool {
+ self.value == other.value
+ }
+}
+
+impl<T: Eq, S> Eq for Value<T, S> {}
+
+impl<T: PartialOrd, S> PartialOrd for Value<T, S> {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ self.value.partial_cmp(&other.value)
+ }
+}
+
+impl<T: Ord, S> Ord for Value<T, S> {
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.value.cmp(&other.value)
+ }
+}
+
+impl<T: fmt::Debug, S> fmt::Debug for Value<T, S> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(&self.value, f)
+ }
+}
+
+impl<T: fmt::Display, S> fmt::Display for Value<T, S> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(&self.value, f)
+ }
+}
+
+impl<T, S: HashStrategy<T>> Hash for Value<T, S> {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ S::hash(&self.value, state);
+ }
+}
+
+#[doc(hidden)]
+pub trait HashStrategy<T> {
+ fn hash<H: Hasher>(value: &T, state: &mut H);
+}
diff --git a/datasketches/src/hll/mod.rs b/datasketches/src/hll/mod.rs
index 1a3c2e3..626a4e1 100644
--- a/datasketches/src/hll/mod.rs
+++ b/datasketches/src/hll/mod.rs
@@ -206,12 +206,15 @@ impl Coupon {
/// Compute the HLL coupon for a hashable value.
///
+ /// You may use [`hash_value`](crate::hash_value) wrappers when matching other datasketches
+ /// implementations require a specific value hashing strategy.
+ ///
/// Hashes `v` using MurmurHash3 128-bit and packs the result into a coupon:
/// the low 26 bits of the low hash word become the slot index, and the
/// leading-zero count of the high hash word (capped at 62, then plus one)
/// becomes the 6-bit register value.
#[inline(always)]
- pub fn from_hash(v: impl Hash) -> Self {
+ pub fn from_hash<T: Hash>(v: T) -> Self {
let mut hasher = MurmurHash3X64128::default();
v.hash(&mut hasher);
let (lo, hi) = hasher.finish128();
diff --git a/datasketches/src/hll/sketch.rs b/datasketches/src/hll/sketch.rs
index 5214d46..eaa10ae 100644
--- a/datasketches/src/hll/sketch.rs
+++ b/datasketches/src/hll/sketch.rs
@@ -158,9 +158,12 @@ impl HllSketch {
/// Update the sketch with a value.
///
- /// Accepts any type that implements [`Hash`]. The value is hashed and converted to
+ /// Accepts any type that implements [`Hash`]. The value is hashed and converted to
/// an internal coupon, which is then inserted into the sketch.
///
+ /// You may use [`hash_value`](crate::hash_value) wrappers when matching other datasketches
+ /// implementations require a specific value hashing strategy.
+ ///
/// If you need to insert the same logical value into multiple sketches, consider
/// pre-computing the coupon with [`Coupon::from_hash`] and calling
/// [`update_with_coupon`](Self::update_with_coupon) on each sketch to avoid
@@ -171,9 +174,14 @@ impl HllSketch {
/// ```
/// # use datasketches::hll::HllSketch;
/// # use datasketches::hll::HllType;
+ /// # use datasketches::hash_value;
/// let mut sketch = HllSketch::new(10, HllType::Hll8);
/// sketch.update("apple");
/// assert!(sketch.estimate() >= 1.0);
+ ///
+ /// let mut sketch = HllSketch::new(10, HllType::Hll8);
+ /// sketch.update(hash_value::raw_bytes::from_str("apple"));
+ /// assert!(sketch.estimate() >= 1.0);
/// ```
pub fn update<T: Hash>(&mut self, value: T) {
self.update_with_coupon(Coupon::from_hash(value));
diff --git a/datasketches/src/lib.rs b/datasketches/src/lib.rs
index 02dc692..33ea2f8 100644
--- a/datasketches/src/lib.rs
+++ b/datasketches/src/lib.rs
@@ -37,6 +37,7 @@ pub mod countmin;
pub mod cpc;
pub mod error;
pub mod frequencies;
+pub mod hash_value;
pub mod hll;
pub mod tdigest;
pub mod theta;
diff --git a/datasketches/src/theta/sketch.rs b/datasketches/src/theta/sketch.rs
index fdba5fe..1313675 100644
--- a/datasketches/src/theta/sketch.rs
+++ b/datasketches/src/theta/sketch.rs
@@ -30,7 +30,6 @@ use crate::codec::family::Family;
use crate::common::NumStdDev;
use crate::common::ResizeFactor;
use crate::common::binomial_bounds;
-use crate::common::canonical_double;
use crate::error::Error;
use crate::hash::DEFAULT_UPDATE_SEED;
use crate::hash::compute_seed_hash;
@@ -74,7 +73,7 @@ pub trait ThetaSketchView: private::Sealed {
fn is_empty(&self) -> bool;
/// Returns an iterator over retained hash values.
- fn iter<'a>(&'a self) -> impl Iterator<Item = u64> + 'a;
+ fn iter(&self) -> impl Iterator<Item = u64> + '_;
/// Returns number of retained hash values.
fn num_retained(&self) -> usize;
@@ -107,48 +106,25 @@ impl ThetaSketch {
/// Update the sketch with a hashable value.
///
- /// For `f32`/`f64` values, use `update_f32`/`update_f64` instead.
+ /// You may use [`hash_value`](crate::hash_value) wrappers when matching other datasketches
+ /// implementations require a specific value hashing strategy.
///
/// # Examples
///
/// ```
- /// # use datasketches::theta::ThetaSketch;
- /// let mut sketch = ThetaSketch::builder().build();
- /// sketch.update("apple");
- /// assert!(sketch.estimate() >= 1.0);
- /// ```
- pub fn update<T: Hash>(&mut self, value: T) {
- self.table.try_insert(value);
- }
-
- /// Update the sketch with a f64 value.
- ///
- /// # Examples
+ /// use datasketches::hash_value;
+ /// use datasketches::theta::ThetaSketch;
///
- /// ```
- /// # use datasketches::theta::ThetaSketch;
/// let mut sketch = ThetaSketch::builder().build();
- /// sketch.update_f64(1.0);
+ /// sketch.update("apple");
/// assert!(sketch.estimate() >= 1.0);
- /// ```
- pub fn update_f64(&mut self, value: f64) {
- // Canonicalize double for compatibility with Java
- let canonical = canonical_double(value);
- self.update(canonical);
- }
-
- /// Update the sketch with a f32 value.
///
- /// # Examples
- ///
- /// ```
- /// # use datasketches::theta::ThetaSketch;
/// let mut sketch = ThetaSketch::builder().build();
- /// sketch.update_f32(1.0);
+ /// sketch.update(hash_value::raw_bytes::from_str("apple"));
/// assert!(sketch.estimate() >= 1.0);
/// ```
- pub fn update_f32(&mut self, value: f32) {
- self.update_f64(value as f64);
+ pub fn update<T: Hash>(&mut self, value: T) {
+ self.table.try_insert(value);
}
/// Return cardinality estimate
@@ -352,7 +328,7 @@ impl ThetaSketchView for ThetaSketch {
ThetaSketch::is_empty(self)
}
- fn iter<'a>(&'a self) -> impl Iterator<Item = u64> + 'a {
+ fn iter(&self) -> impl Iterator<Item = u64> + '_ {
ThetaSketch::iter(self)
}
@@ -927,7 +903,7 @@ impl ThetaSketchView for CompactThetaSketch {
CompactThetaSketch::is_empty(self)
}
- fn iter<'a>(&'a self) -> impl Iterator<Item = u64> + 'a {
+ fn iter(&self) -> impl Iterator<Item = u64> + '_ {
CompactThetaSketch::iter(self)
}
diff --git a/datasketches/tests/theta_sketch_test.rs b/datasketches/tests/theta_sketch_test.rs
index 73045cd..28448c2 100644
--- a/datasketches/tests/theta_sketch_test.rs
+++ b/datasketches/tests/theta_sketch_test.rs
@@ -16,6 +16,7 @@
// under the License.
use datasketches::common::NumStdDev;
+use datasketches::hash_value;
use datasketches::theta::ThetaSketch;
#[test]
@@ -39,14 +40,30 @@ fn test_update_various_types() {
sketch.update("string");
sketch.update(42i64);
sketch.update(42u64);
- sketch.update_f64(3.15);
- sketch.update_f64(3.15);
- sketch.update_f32(3.15);
- sketch.update_f32(3.15);
+ // where floating-point numbers have different representations
+ sketch.update(hash_value::canonical_float::from_f64(3.15));
+ sketch.update(hash_value::canonical_float::from_f64(3.15));
+ sketch.update(hash_value::canonical_float::from_f32(3.15));
+ sketch.update(hash_value::canonical_float::from_f32(3.15));
sketch.update([1u8, 2, 3]);
assert!(!sketch.is_empty());
assert_eq!(sketch.estimate(), 5.0);
+
+ let mut sketch = ThetaSketch::builder().lg_k(12).build();
+
+ sketch.update("string");
+ sketch.update(42i64);
+ sketch.update(42u64);
+ // where floating-point numbers have the same representation
+ sketch.update(hash_value::canonical_float::from_f64(5.0));
+ sketch.update(hash_value::canonical_float::from_f64(5.0));
+ sketch.update(hash_value::canonical_float::from_f32(5.0));
+ sketch.update(hash_value::canonical_float::from_f32(5.0));
+ sketch.update([1u8, 2, 3]);
+
+ assert!(!sketch.is_empty());
+ assert_eq!(sketch.estimate(), 4.0);
}
#[test]
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datasketches.apache.org
For additional commands, e-mail: commits-help@datasketches.apache.org