This is an automated email from the ASF dual-hosted git repository.

leerho pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datasketches-cuda.git


The following commit(s) were added to refs/heads/main by this push:
     new c910680  Implement HLL finalizer and update CCCL (#13)
c910680 is described below

commit c910680d34427b31bda674e3a8b4c70fde51a3ee
Author: Daniel Jünger <[email protected]>
AuthorDate: Thu Aug 20 18:57:46 2026 +0200

    Implement HLL finalizer and update CCCL (#13)
    
    Implement the HLL Composite policy finalizer locally for host and device
    code, removing production use of datasketches-cpp estimator helpers.
    Also update the synthetic CCCL pin to 3.5.2 and correct installed
    dependency documentation.
    
    Tests: CUDA 13.3/GCC 15 (48/48), pre-commit.
    
    Closes #9
---
 CMakeLists.txt                                     |   6 +-
 README.md                                          |  15 +-
 cmake/thirdparty/get_cccl.cmake                    |   4 +-
 cmake/thirdparty/get_datasketches_cpp.cmake        |  13 +-
 .../cuda/detail/hll/composite_finalizer.cuh        | 202 +++++++
 .../cuda/detail/hll/composite_finalizer.hpp        | 115 +---
 .../detail/hll/composite_interpolation_table.cuh   |  69 +++
 .../hll/composite_interpolation_table_data.inl     | 656 +++++++++++++++++++++
 include/datasketches/cuda/detail/hll/policy.cuh    |  24 +-
 test/hll/CMakeLists.txt                            |   1 +
 test/hll/composite_finalizer_device_test.cu        | 109 ++++
 test/hll/composite_finalizer_test.cpp              |  83 ++-
 test/hll/policy_compile_test.cu                    |   2 +
 13 files changed, 1146 insertions(+), 153 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1e2aff0..4b6b2d2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -42,9 +42,9 @@ find_package(CUDAToolkit REQUIRED)
 include(cmake/thirdparty/get_cccl.cmake)
 
 # datasketches-cpp is a *public* dependency: include/datasketches/cuda/hll.hpp,
-# detail/hll/preamble.hpp, detail/hll/composite_finalizer.hpp, and
-# detail/hll/reduction_state.hpp all use upstream symbols (target_hll_type,
-# HllUtil, CompositeInterpolationXTable, HarmonicNumbers, INVERSE_POWERS_OF_2).
+# detail/hll/preamble.hpp, detail/hll/sketch_impl.hpp, and
+# detail/hll/reduction_state.hpp use upstream symbols (target_hll_type,
+# HllUtil, and INVERSE_POWERS_OF_2).
 # Configure it at top level so consumers via add_subdirectory/CPM inherit
 # the include dirs and the `datasketches` target automatically.
 include(cmake/thirdparty/get_datasketches_cpp.cmake)
diff --git a/README.md b/README.md
index a563271..c7d2197 100644
--- a/README.md
+++ b/README.md
@@ -81,7 +81,7 @@ Required:
 
 Fetched automatically via CPM at configure time (no manual install required):
 
-- [NVIDIA/cccl](https://github.com/NVIDIA/cccl) — pinned to commit 
`c95f99757cf95044ce82b905eec88ff40c851f7b` as synthetic version `3.5.1` while 
this library develops against unreleased cudax HLL APIs. This should move to a 
real CCCL release once the required APIs are tagged.
+- [NVIDIA/cccl](https://github.com/NVIDIA/cccl) — pinned to commit 
`cba1df5786a2ffabc85887a9bfb1b7febee6232d` as synthetic version `3.5.2` while 
this library develops against unreleased cudax HLL APIs. This should move to a 
real CCCL release once the required APIs are tagged.
 - [apache/datasketches-cpp](https://github.com/apache/datasketches-cpp) 
`5.2.0` (fall-back if `find_package(DataSketches 5.0.0 CONFIG)` does not locate 
a system install)
 - [Catch2](https://github.com/catchorg/Catch2) `3.5.3` (test-only)
 
@@ -127,16 +127,17 @@ find_package(datasketches_cuda CONFIG REQUIRED)
 target_link_libraries(my_target PRIVATE datasketches::cuda)
 ```
 
-Note: an installed `datasketches_cuda` does not propagate CCCL or
-`datasketches-cpp` to consumers (both are CPM-fetched into the build tree).
-Downstream `find_package` consumers must provide both on their
-`CMAKE_PREFIX_PATH`. Consumption via `add_subdirectory` or CPM works without
-any extra setup.
+The installed package config propagates CUDAToolkit, CCCL, and
+datasketches-cpp through `find_dependency`. These dependencies are not bundled
+or installed by `datasketches_cuda`, so downstream `find_package` consumers
+must make them discoverable through `CMAKE_PREFIX_PATH` or the normal CMake
+package search paths. Consumption via `add_subdirectory` or CPM works without
+additional dependency setup.
 
 ## Known Issues
 
 - **HLL_8 only.** `HLL_4` and `HLL_6` packing are not yet implemented; 
constructing with those throws `std::invalid_argument`. `AuxHashMap` (the HLL_4 
exception table) is also pending.
 - **No LIST / SET deserialization.** The wire format's small-cardinality modes 
are rejected at parse. Sketches must already be in HLL mode.
 - **Round-trip diverges on `FLAGS` (oooFlag) and `hipAccum`.** GPU output 
always sets `oooFlag=1` (pins CPU side to the Composite estimator) and 
`hipAccum=0` (no HIP tracking on parallel atomic update). All other bytes 
round-trip exactly.
-- **CCCL uses a synthetic development version.** Until upstream tags a CCCL 
release containing the required cudax HLL policy and explicit stream / 
memory-resource APIs, `cmake/thirdparty/get_cccl.cmake` uses `CPMFindPackage` 
with synthetic version `3.5.1` and a pinned CCCL main commit. This prevents 
automatically accepting older CCCL installs from disk while keeping an explicit 
`CPM_CCCL_SOURCE` override available for development.
+- **CCCL uses a synthetic development version.** Until upstream tags a CCCL 
release containing the required cudax HLL policy and explicit stream / 
memory-resource APIs, `cmake/thirdparty/get_cccl.cmake` uses `CPMFindPackage` 
with synthetic version `3.5.2` and a pinned CCCL main commit. This prevents 
automatically accepting older CCCL installs from disk while keeping an explicit 
`CPM_CCCL_SOURCE` override available for development.
 - **No driver on some dev hosts.** CI gates the runtime parity test 
(`parity_test.cu`); host-only tests (preamble, reduction state, normalizing 
hasher, composite finalizer, policy compile) pass without a GPU.
diff --git a/cmake/thirdparty/get_cccl.cmake b/cmake/thirdparty/get_cccl.cmake
index 3fcd202..295e987 100644
--- a/cmake/thirdparty/get_cccl.cmake
+++ b/cmake/thirdparty/get_cccl.cmake
@@ -40,8 +40,8 @@ if(NOT COMMAND CPMFindPackage)
 endif()
 
 function(find_and_configure_cccl)
-  set(_cccl_version 3.5.1)
-  set(_cccl_tag c95f99757cf95044ce82b905eec88ff40c851f7b)
+  set(_cccl_version 3.5.2)
+  set(_cccl_tag cba1df5786a2ffabc85887a9bfb1b7febee6232d)
   message(WARNING
     "datasketches_cuda: using CCCL@${_cccl_tag} as synthetic version 
${_cccl_version} "
     "(TODO switch to a released CCCL version once required cudax HLL APIs are 
tagged)")
diff --git a/cmake/thirdparty/get_datasketches_cpp.cmake 
b/cmake/thirdparty/get_datasketches_cpp.cmake
index 340b18c..ab0643b 100644
--- a/cmake/thirdparty/get_datasketches_cpp.cmake
+++ b/cmake/thirdparty/get_datasketches_cpp.cmake
@@ -17,15 +17,12 @@
 
 # apache/datasketches-cpp: header-only library used as:
 #   - parity reference (CPU sketch) in tests
-#   - delegation target for the Composite estimator helpers
-#     (HarmonicNumbers, CubicInterpolation, CompositeInterpolationXTable)
-#   - public dependency: hll/include/hll_sketch.hpp, detail/preamble.hpp,
-#     and detail/composite_finalizer.hpp all include upstream headers directly,
-#     so any consumer of datasketches::cuda also needs datasketches on the 
path.
+#   - public dependency for target_hll_type, HllUtil, wire-format constants,
+#     and inverse-power tables used by installed headers
 #
-# Minimum version: 5.0.0. The helpers we use (HllUtil, HarmonicNumbers, etc.)
-# have been in the public surface since at least v3.x; 5.0.0 is a conservative
-# floor aligned with current stable releases (latest: 5.2.0).
+# Minimum version: 5.0.0. The required HLL types and helpers have been in the
+# public surface since at least v3.x; 5.0.0 is a conservative floor aligned
+# with the pinned CPM fallback release, 5.2.0.
 #
 # Developer override: -DCPM_datasketches_SOURCE=/path/to/local/checkout 
(CPM-native).
 # Note: if find_package succeeds, the CPM_datasketches_SOURCE override is 
ignored
diff --git a/include/datasketches/cuda/detail/hll/composite_finalizer.cuh 
b/include/datasketches/cuda/detail/hll/composite_finalizer.cuh
new file mode 100644
index 0000000..15ce7e7
--- /dev/null
+++ b/include/datasketches/cuda/detail/hll/composite_finalizer.cuh
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <cuda/std/cmath>
+
+#include <cuda_runtime.h>
+
+#include <datasketches/cuda/detail/hll/composite_interpolation_table.cuh>
+
+namespace datasketches::cuda::detail::hll {
+
+//! @brief Returns the HLL raw harmonic-mean estimate.
+[[nodiscard]] __host__ __device__ inline double raw_estimate(double z, 
std::uint8_t lg_k) noexcept
+{
+  const std::uint32_t config_k = 1u << lg_k;
+  double correction_factor;
+  if (lg_k == 4) {
+    correction_factor = 0.673;
+  } else if (lg_k == 5) {
+    correction_factor = 0.697;
+  } else if (lg_k == 6) {
+    correction_factor = 0.709;
+  } else {
+    correction_factor = 0.7213 / (1.0 + (1.079 / config_k));
+  }
+  return (correction_factor * config_k * config_k) / z;
+}
+
+[[nodiscard]] __host__ __device__ inline double harmonic_number(std::uint32_t 
value) noexcept
+{
+  switch (value) {
+    case 0: return 0.0;
+    case 1: return 1.0;
+    case 2: return 1.5;
+    case 3: return 11.0 / 6.0;
+    case 4: return 25.0 / 12.0;
+    case 5: return 137.0 / 60.0;
+    case 6: return 49.0 / 20.0;
+    case 7: return 363.0 / 140.0;
+    case 8: return 761.0 / 280.0;
+    case 9: return 7129.0 / 2520.0;
+    case 10: return 7381.0 / 2520.0;
+    case 11: return 83711.0 / 27720.0;
+    case 12: return 86021.0 / 27720.0;
+    case 13: return 1145993.0 / 360360.0;
+    case 14: return 1171733.0 / 360360.0;
+    case 15: return 1195757.0 / 360360.0;
+    case 16: return 2436559.0 / 720720.0;
+    case 17: return 42142223.0 / 12252240.0;
+    case 18: return 14274301.0 / 4084080.0;
+    case 19: return 275295799.0 / 77597520.0;
+    case 20: return 55835135.0 / 15519504.0;
+    case 21: return 18858053.0 / 5173168.0;
+    case 22: return 19093197.0 / 5173168.0;
+    case 23: return 444316699.0 / 118982864.0;
+    case 24: return 1347822955.0 / 356948592.0;
+    default: break;
+  }
+
+  constexpr double euler_mascheroni = 0.577215664901532860606512090082;
+  const double x                    = static_cast<double>(value);
+  const double inv_sq               = 1.0 / (x * x);
+  double sum                        = ::cuda::std::log(x) + euler_mascheroni + 
(1.0 / (2.0 * x));
+  double power                      = inv_sq;
+  sum -= power * (1.0 / 12.0);
+  power *= inv_sq;
+  sum += power * (1.0 / 120.0);
+  power *= inv_sq;
+  sum -= power * (1.0 / 252.0);
+  power *= inv_sq;
+  sum += power * (1.0 / 240.0);
+  return sum;
+}
+
+//! @brief Returns the low-cardinality bitmap estimate.
+[[nodiscard]] __host__ __device__ inline double bitmap_estimate(std::uint32_t 
num_zeroes,
+                                                                std::uint8_t 
lg_k) noexcept
+{
+  const std::uint32_t config_k = 1u << lg_k;
+  if (num_zeroes == 0) { return config_k * ::cuda::std::log(config_k / 0.5); }
+  return config_k * (harmonic_number(config_k) - harmonic_number(num_zeroes));
+}
+
+[[nodiscard]] __host__ __device__ inline int find_straddle(const double* 
x_values,
+                                                           int length,
+                                                           double x) noexcept
+{
+  int left  = 0;
+  int right = length - 1;
+  while (left + 1 < right) {
+    const int middle = left + ((right - left) / 2);
+    if (x_values[middle] <= x) {
+      left = middle;
+    } else {
+      right = middle;
+    }
+  }
+  return left;
+}
+
+[[nodiscard]] __host__ __device__ inline double cubic_interpolate(double x0,
+                                                                  double y0,
+                                                                  double x1,
+                                                                  double y1,
+                                                                  double x2,
+                                                                  double y2,
+                                                                  double x3,
+                                                                  double y3,
+                                                                  double x) 
noexcept
+{
+  const double l0_numer = (x - x1) * (x - x2) * (x - x3);
+  const double l1_numer = (x - x0) * (x - x2) * (x - x3);
+  const double l2_numer = (x - x0) * (x - x1) * (x - x3);
+  const double l3_numer = (x - x0) * (x - x1) * (x - x2);
+
+  const double l0_denom = (x0 - x1) * (x0 - x2) * (x0 - x3);
+  const double l1_denom = (x1 - x0) * (x1 - x2) * (x1 - x3);
+  const double l2_denom = (x2 - x0) * (x2 - x1) * (x2 - x3);
+  const double l3_denom = (x3 - x0) * (x3 - x1) * (x3 - x2);
+
+  const double term0 = y0 * l0_numer / l0_denom;
+  const double term1 = y1 * l1_numer / l1_denom;
+  const double term2 = y2 * l2_numer / l2_denom;
+  const double term3 = y3 * l3_numer / l3_denom;
+  return term0 + term1 + term2 + term3;
+}
+
+[[nodiscard]] __host__ __device__ inline double interpolate_composite(const 
double* x_values,
+                                                                      
std::uint32_t y_stride,
+                                                                      double 
x) noexcept
+{
+  constexpr int length = 
static_cast<int>(composite_interpolation::num_x_values);
+  if (x == x_values[length - 1]) { return static_cast<double>(y_stride) * 
(length - 1); }
+
+  const int offset = find_straddle(x_values, length, x);
+  const int base   = offset == 0 ? 0 : (offset == length - 2 ? offset - 2 : 
offset - 1);
+
+  return cubic_interpolate(x_values[base + 0],
+                           static_cast<double>(y_stride) * (base + 0),
+                           x_values[base + 1],
+                           static_cast<double>(y_stride) * (base + 1),
+                           x_values[base + 2],
+                           static_cast<double>(y_stride) * (base + 2),
+                           x_values[base + 3],
+                           static_cast<double>(y_stride) * (base + 3),
+                           x);
+}
+
+//! @brief DataSketches Composite cardinality estimate for an HLL_8 register
+//! array represented by its harmonic sum and zero-register count.
+[[nodiscard]] __host__ __device__ inline double composite_estimate(double z,
+                                                                   
std::uint32_t num_zeroes,
+                                                                   
std::uint8_t lg_k) noexcept
+{
+  const double raw_est         = raw_estimate(z, lg_k);
+  const double* x_values       = composite_interpolation::x_values_for(lg_k);
+  const std::uint32_t y_stride = composite_interpolation::y_stride_for(lg_k);
+
+  if (raw_est < x_values[0]) { return 0.0; }
+
+  constexpr std::uint32_t last = composite_interpolation::last_x_offset;
+  if (raw_est > x_values[last]) {
+    const double final_y = static_cast<double>(y_stride) * last;
+    return raw_est * (final_y / x_values[last]);
+  }
+
+  const double adjusted = interpolate_composite(x_values, y_stride, raw_est);
+  if (adjusted > static_cast<double>(3u << lg_k)) { return adjusted; }
+
+  const double linear  = bitmap_estimate(num_zeroes, lg_k);
+  const double average = (adjusted + linear) / 2.0;
+
+  double crossover = 0.64;
+  if (lg_k == 4) {
+    crossover = 0.718;
+  } else if (lg_k == 5) {
+    crossover = 0.672;
+  }
+
+  return average > (crossover * static_cast<double>(1u << lg_k)) ? adjusted : 
linear;
+}
+
+}  // namespace datasketches::cuda::detail::hll
diff --git a/include/datasketches/cuda/detail/hll/composite_finalizer.hpp 
b/include/datasketches/cuda/detail/hll/composite_finalizer.hpp
index 4c4460d..5305bf2 100644
--- a/include/datasketches/cuda/detail/hll/composite_finalizer.hpp
+++ b/include/datasketches/cuda/detail/hll/composite_finalizer.hpp
@@ -19,115 +19,24 @@
 
 #pragma once
 
-#include <cmath>
-#include <cstdint>
-
-#include <CompositeInterpolationXTable.hpp>
-#include <CubicInterpolation.hpp>
-#include <HarmonicNumbers.hpp>
+#include <datasketches/cuda/detail/hll/composite_finalizer.cuh>
 
 namespace datasketches::cuda::detail::hll {
 
-//! @brief Returns the HLL "raw" estimator (HyperLogLog harmonic mean form with
-//! the small-k correction factor).
-//!
-//! Mirrors `HllArray::getHllRawEstimate` (`HllArray-internal.hpp:577-587`).
-//!
-//! @param[in] kxq0_plus_kxq1 The sum of `kxq0_` and `kxq1_` from the wider
-//!   register-array reduction (which is `sum_i 2^{-r_i}` split for precision).
-//! @param[in] lgK The HLL precision parameter.
-//! @return The bias-uncorrected HLL estimate.
-inline double raw_estimate(double kxq0_plus_kxq1, uint8_t lgK) noexcept
-{
-  const uint32_t configK = 1u << lgK;
-  double correctionFactor;
-  if (lgK == 4) {
-    correctionFactor = 0.673;
-  } else if (lgK == 5) {
-    correctionFactor = 0.697;
-  } else if (lgK == 6) {
-    correctionFactor = 0.709;
-  } else {
-    correctionFactor = 0.7213 / (1.0 + (1.079 / configK));
-  }
-  return (correctionFactor * configK * configK) / kxq0_plus_kxq1;
-}
-
-//! @brief Returns the linear-counting (bit-map) estimate used by Composite at
-//! low cardinalities.
-//!
-//! Mirrors `HllArray::getHllBitMapEstimate` (`HllArray-internal.hpp:563-574`).
-//! Delegates to `datasketches::HarmonicNumbers::getBitMapEstimate` for the
-//! actual harmonic-number formula.
-//!
-//! @param[in] curMin The minimum register value (always 0 for HLL_8 with at
-//!   least one zero register).
-//! @param[in] numAtCurMin Count of registers equal to `curMin`. When
-//!   `curMin == 0`, this is the count of zero (unhit) registers.
-//! @param[in] lgK The HLL precision parameter.
-//! @return The linear-counting estimate.
-inline double bitmap_estimate(uint8_t curMin, uint32_t numAtCurMin, uint8_t 
lgK)
-{
-  const uint32_t configK         = 1u << lgK;
-  const uint32_t numUnhitBuckets = (curMin == 0) ? numAtCurMin : 0u;
-  if (numUnhitBuckets == 0) { return configK * std::log(configK / 0.5); }
-  const uint32_t numHitBuckets = configK - numUnhitBuckets;
-  return 
::datasketches::HarmonicNumbers<>::getBitMapEstimate(static_cast<int>(configK),
-                                                              
static_cast<int>(numHitBuckets));
-}
-
-//! @brief Composite ("non-HIP") cardinality estimator.
+//! @brief DataSketches Composite estimate from the serialized HLL reduction 
state.
 //!
-//! Mirrors `HllArray::getCompositeEstimate` (`HllArray-internal.hpp:367-409`)
-//! verbatim, but takes the wider reduction state as parameters instead of
-//! reading them from an `HllArray` instance. The interpolation and harmonic
-//! helpers are reached via nested-namespace lookup into `datasketches::`.
-//!
-//! @param[in] kxq0_plus_kxq1 `kxq0 + kxq1` from the wider reduction.
-//! @param[in] curMin Minimum register value across the array.
-//! @param[in] numAtCurMin Count of registers equal to `curMin`.
-//! @param[in] lgK The HLL precision parameter (4..21).
+//! @param[in] z Sum of `2^-register[i]` across all registers.
+//! @param[in] cur_min Minimum register value.
+//! @param[in] num_at_cur_min Number of registers equal to `cur_min`.
+//! @param[in] lg_k HLL precision parameter.
 //! @return The Composite cardinality estimate.
-inline double composite_finalizer(double kxq0_plus_kxq1,
-                                  uint8_t curMin,
-                                  uint32_t numAtCurMin,
-                                  uint8_t lgK)
+[[nodiscard]] inline double composite_finalizer(double z,
+                                                std::uint8_t cur_min,
+                                                std::uint32_t num_at_cur_min,
+                                                std::uint8_t lg_k) noexcept
 {
-  const double rawEst = raw_estimate(kxq0_plus_kxq1, lgK);
-
-  const double* xArr     = 
::datasketches::CompositeInterpolationXTable<>::get_x_arr(lgK);
-  const uint32_t xArrLen = 
::datasketches::CompositeInterpolationXTable<>::get_x_arr_length();
-  const double yStride   = 
::datasketches::CompositeInterpolationXTable<>::get_y_stride(lgK);
-
-  if (rawEst < xArr[0]) { return 0.0; }
-
-  const uint32_t xArrLenM1 = xArrLen - 1;
-
-  if (rawEst > xArr[xArrLenM1]) {
-    const double finalY = yStride * xArrLenM1;
-    const double factor = finalY / xArr[xArrLenM1];
-    return rawEst * factor;
-  }
-
-  const double adjEst = 
::datasketches::CubicInterpolation<>::usingXArrAndYStride(
-    xArr, static_cast<int>(xArrLen), yStride, rawEst);
-
-  // Empirical: avoid the linear-counting estimator if it might have a crazy
-  // value. Threshold 3*k is safe for 2^4 <= k <= 2^21.
-  if (adjEst > static_cast<double>(3u << lgK)) { return adjEst; }
-
-  const double linEst = bitmap_estimate(curMin, numAtCurMin, lgK);
-  const double avgEst = (adjEst + linEst) / 2.0;
-
-  // Empirical crossover constants (HllArray-internal.hpp:404-406).
-  double crossOver = 0.64;
-  if (lgK == 4) {
-    crossOver = 0.718;
-  } else if (lgK == 5) {
-    crossOver = 0.672;
-  }
-
-  return (avgEst > (crossOver * static_cast<double>(1u << lgK))) ? adjEst : 
linEst;
+  const std::uint32_t num_zeroes = cur_min == 0 ? num_at_cur_min : 0;
+  return composite_estimate(z, num_zeroes, lg_k);
 }
 
 }  // namespace datasketches::cuda::detail::hll
diff --git 
a/include/datasketches/cuda/detail/hll/composite_interpolation_table.cuh 
b/include/datasketches/cuda/detail/hll/composite_interpolation_table.cuh
new file mode 100644
index 0000000..62853d5
--- /dev/null
+++ b/include/datasketches/cuda/detail/hll/composite_interpolation_table.cuh
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include <cuda_runtime.h>
+
+namespace datasketches::cuda::detail::hll::composite_interpolation {
+
+inline constexpr std::uint8_t min_lg_k       = 4;
+inline constexpr std::uint8_t max_lg_k       = 18;
+inline constexpr std::uint32_t num_x_values  = 257;
+inline constexpr std::uint32_t last_x_offset = num_x_values - 1;
+
+// DataSketches C++ 5.2.0, commit de8553ba372e618382c2e7b44b0ffc9422b9458c:
+// hll/include/CompositeInterpolationXTable-internal.hpp.
+//
+// The source table contains lgK 4..21. CCCL currently supports lgK 4..18, so
+// only those rows are checked in here. Keep host and device storage under the
+// same name so host/device estimator code uses exactly the same values.
+#if defined(__CUDA_ARCH__)
+#define DATASKETCHES_CUDA_HLL_TABLE_DECL static __device__ constexpr
+#else
+#define DATASKETCHES_CUDA_HLL_TABLE_DECL inline constexpr
+#endif
+
+DATASKETCHES_CUDA_HLL_TABLE_DECL std::uint32_t y_strides[] = {
+  1, 2, 3, 5, 10, 20, 40, 80, 160, 320, 640, 1280, 2560, 5120, 10240};
+
+DATASKETCHES_CUDA_HLL_TABLE_DECL double x_values[15][num_x_values] = {
+#include <datasketches/cuda/detail/hll/composite_interpolation_table_data.inl>
+};
+
+#undef DATASKETCHES_CUDA_HLL_TABLE_DECL
+
+[[nodiscard]] __host__ __device__ constexpr bool valid_lg_k(std::uint8_t lg_k) 
noexcept
+{
+  return lg_k >= min_lg_k && lg_k <= max_lg_k;
+}
+
+[[nodiscard]] __host__ __device__ inline const double* 
x_values_for(std::uint8_t lg_k) noexcept
+{
+  return x_values[lg_k - min_lg_k];
+}
+
+[[nodiscard]] __host__ __device__ inline std::uint32_t 
y_stride_for(std::uint8_t lg_k) noexcept
+{
+  return y_strides[lg_k - min_lg_k];
+}
+
+}  // namespace datasketches::cuda::detail::hll::composite_interpolation
diff --git 
a/include/datasketches/cuda/detail/hll/composite_interpolation_table_data.inl 
b/include/datasketches/cuda/detail/hll/composite_interpolation_table_data.inl
new file mode 100644
index 0000000..0c7760a
--- /dev/null
+++ 
b/include/datasketches/cuda/detail/hll/composite_interpolation_table_data.inl
@@ -0,0 +1,656 @@
+/*
+ * 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.
+ */
+
+// clang-format off
+{
+  10.767999803534, 11.237701481774, 11.722738717438, 12.223246391222,
+  12.739366773787, 13.271184824495, 13.818759686650, 14.382159835785,
+  14.961390904922, 15.556414447178, 16.167227058768, 16.793705840034,
+  17.435831011559, 18.093368824077, 18.766214022468, 19.454114555153,
+  20.156877376380, 20.874309118151, 21.606085061388, 22.351926834624,
+  23.111406651437, 23.884222842419, 24.669953053285, 25.468324695415,
+  26.278740385032, 27.100807701976, 27.934264623663, 28.778498012717,
+  29.633064579479, 30.497559091830, 31.371415928956, 32.254248998304,
+  33.145649456017, 34.045084140394, 34.952069032314, 35.866210502243,
+  36.787128772113, 37.714316067007, 38.647432342061, 39.586020708848,
+  40.529738186256, 41.478377527525, 42.431435504179, 43.388619067416,
+  44.349650659292, 45.314111448463, 46.281805382722, 47.252472240229,
+  48.225969481651, 49.202042512440, 50.180350422249, 51.160761480664,
+  52.143032445587, 53.127233235412, 54.112986437616, 55.100182392694,
+  56.088558030701, 57.078156023607, 58.068788167275, 59.060493030644,
+  60.053033784417, 61.046284072738, 62.040286486621, 63.034929032992,
+  64.030027371399, 65.025588917110, 66.021614945599, 67.018177418440,
+  68.015186786528, 69.012467972526, 70.010014322660, 71.007638150468,
+  72.005472777573, 73.003558069130, 74.001925061314, 75.000323809106,
+  75.998875046818, 76.997735309722, 77.996356820987, 78.995236661037,
+  79.994185446564, 80.993179953574, 81.991936172868, 82.991277726952,
+  83.990564675011, 84.990016215330, 85.989354175963, 86.988664031183,
+  87.988038383374, 88.987487931153, 89.987011275632, 90.986376635228,
+  91.985802667485, 92.985125933965, 93.984894049744, 94.984597185523,
+  95.984374633793, 96.983987641198, 97.983728001025, 98.983151114601,
+  99.982917278162, 100.982752268263, 101.982610588914, 102.982425129077,
+  103.982425883214, 104.982193814993, 105.982188025268, 106.982005912713,
+  107.981949640254, 108.981777381902, 109.981767708570, 110.981785341504,
+  111.981686149156, 112.981635012054, 113.981557303839, 114.981412817204,
+  115.981636136290, 116.981451847734, 117.981619278961, 118.981656205677,
+  119.981462628717, 120.981648197593, 121.981672839888, 122.981536601882,
+  123.981424780957, 124.981245233627, 125.981297203177, 126.981469882453,
+  127.981420558510, 128.981470958251, 129.981803124940, 130.981454077005,
+  131.981307772677, 132.981136552712, 133.981060440468, 134.980764897921,
+  135.980250123519, 136.979947889981, 137.979778713731, 138.979651075854,
+  139.979521006237, 140.979619490143, 141.979418214464, 142.979378742690,
+  143.979006714929, 144.978789085341, 145.978940618746, 146.978699584620,
+  147.978509489139, 148.978375065557, 149.978382007245, 150.978320879037,
+  151.978431501891, 152.978357310224, 153.978079953131, 154.977867908315,
+  155.977897282267, 156.977732141540, 157.977285841428, 158.977381755450,
+  159.977107808858, 160.976898092463, 161.976945065052, 162.976415653211,
+  163.976063254486, 164.976168718176, 165.976009634854, 166.975746910144,
+  167.975365498336, 168.975302031315, 169.975145815395, 170.975048401084,
+  171.974904933990, 172.974962856090, 173.974307743427, 174.974505556696,
+  175.974404122880, 176.974154794798, 177.973936891006, 178.973909768514,
+  179.973629325330, 180.973396428571, 181.973041798012, 182.972922327014,
+  183.972453839631, 184.972389341638, 185.971978290400, 186.971721489488,
+  187.971610992921, 188.971432541974, 189.970886522529, 190.970636748690,
+  191.970543849249, 192.970222044828, 193.969945219163, 194.969816174441,
+  195.969660267113, 196.969306217626, 197.969283141670, 198.968952697752,
+  199.968650726389, 200.968771498143, 201.969063388798, 202.968624157712,
+  203.968385122471, 204.968459873029, 205.968253426841, 206.968128284823,
+  207.968002441937, 208.967981887571, 209.967853843352, 210.967653026858,
+  211.967601511385, 212.967479077297, 213.967293002672, 214.967265475369,
+  215.967098197208, 216.966915139758, 217.966680984700, 218.966156974161,
+  219.965788928685, 220.965578199561, 221.965550145047, 222.965055562297,
+  223.965041228123, 224.965088576052, 225.964951649635, 226.964837079061,
+  227.964949815142, 228.964753278386, 229.964358666956, 230.964158288467,
+  231.964118659643, 232.964105599719, 233.964013041408, 234.963810178150,
+  235.963847138572, 236.963634735645, 237.963596910032, 238.963659218725,
+  239.963498440155, 240.963445597658, 241.963141291971, 242.962684055746,
+  243.962664757583, 244.962762174588, 245.962862664373, 246.962595237647,
+  247.962493432533, 248.962292518484, 249.962622934132, 250.962187495200,
+  251.962038306920, 252.961926735684, 253.961867248643, 254.961699581530,
+  255.961371028199
+},
+// log K = 5
+{
+  22.304000309384, 23.261989079230, 24.249656493833, 25.267226901899,
+  26.314781734430, 27.392414068382, 28.500177845081, 29.638044883897,
+  30.805888087516, 32.003714838418, 33.231360682863, 34.488477823483,
+  35.774924122967, 37.090293268866, 38.434292452116, 39.806594197334,
+  41.206691028159, 42.634002763091, 44.088065403269, 45.568286128520,
+  47.074066947685, 48.604745105475, 50.159729872619, 51.738339282229,
+  53.339805618020, 54.963435280656, 56.608464119077, 58.274038434156,
+  59.959403900602, 61.663926660982, 63.386737477574, 65.127093833107,
+  66.884091606686, 68.656959698762, 70.445052178435, 72.247581860591,
+  74.063607663774, 75.892531705810, 77.733879994604, 79.586788509372,
+  81.450593478752, 83.324645303244, 85.208482496209, 87.101116685537,
+  89.002152917959, 90.911090999940, 92.827275073114, 94.750224301260,
+  96.679773715696, 98.615214511699, 100.556211548644, 102.502148428334,
+  104.452899214344, 106.407767504404, 108.366625832228, 110.329564692634,
+  112.295481742874, 114.264715056639, 116.236769512562, 118.211214593179,
+  120.188376370012, 122.167746134232, 124.148833002551, 126.131612799989,
+  128.116072530208, 130.102155735854, 132.089449244974, 134.077816832908,
+  136.067404409248, 138.057703527432, 140.049350564796, 142.041756984355,
+  144.034780624078, 146.028584459531, 148.022894428003, 150.017952482368,
+  152.013358245376, 154.009157360372, 156.005076955043, 158.001501688538,
+  159.998043891849, 161.995141096448, 163.992507770330, 165.990019741406,
+  167.987379363144, 169.985323168231, 171.983371903769, 173.981134136378,
+  175.979306888599, 177.977458741010, 179.975994045101, 181.974478508488,
+  183.973329438036, 185.972061903815, 187.971014174496, 189.969582278901,
+  191.968450510061, 193.967607022497, 195.966726931677, 197.965915341472,
+  199.964910588293, 201.964427852336, 203.964308576556, 205.963778587196,
+  207.963244179225, 209.962423156859, 211.961859295409, 213.961171237132,
+  215.960624048009, 217.959980906604, 219.959156277574, 221.958692298143,
+  223.958335010302, 225.958329061602, 227.957722024027, 229.957134954397,
+  231.956897929205, 233.956901572571, 235.956650770311, 237.956593290745,
+  239.956700470895, 241.956192761863, 243.956003327600, 245.955794901574,
+  247.955501305446, 249.954960233132, 251.954510187803, 253.954252376340,
+  255.954011943050, 257.953388008439, 259.953139524693, 261.952918466928,
+  263.952432910213, 265.952485387126, 267.952763908808, 269.952510701682,
+  271.952383356830, 273.951921008979, 275.951766755515, 277.951697578478,
+  279.951700548755, 281.951805228218, 283.951934023417, 285.952140722678,
+  287.952063941231, 289.951717207670, 291.951294241955, 293.950937111819,
+  295.950900506385, 297.950320403388, 299.949546547128, 301.949474819234,
+  303.949155377395, 305.948725010033, 307.948408553357, 309.948120512731,
+  311.947897212148, 313.947738181662, 315.947255247579, 317.946631669691,
+  319.946302193308, 321.945855881155, 323.945603079048, 325.944930145876,
+  327.945048538923, 329.944795565500, 331.944227346318, 333.943705422222,
+  335.943231918970, 337.942333069602, 339.942354997227, 341.941890952026,
+  343.941346276351, 345.940653575739, 347.940019001109, 349.940026817951,
+  351.938944534275, 353.938172407151, 355.937584534690, 357.937041224148,
+  359.936673631279, 361.936179855216, 363.936105840596, 365.935367434940,
+  367.935295315770, 369.934793960370, 371.934314961518, 373.933949277377,
+  375.933420587284, 377.933148188654, 379.932379363314, 381.931900192237,
+  383.930951130857, 385.930667541762, 387.930025971711, 389.929529183966,
+  391.929116798157, 393.928361940635, 395.927673611921, 397.927111110912,
+  399.926483516685, 401.925799510661, 403.924918492428, 405.924540208915,
+  407.924014467935, 409.923779779206, 411.922979548363, 413.922515268061,
+  415.922148908645, 417.921643066153, 419.921126587639, 421.920766647684,
+  423.920642824255, 425.920411492484, 427.920603679205, 429.920445437023,
+  431.920286509746, 433.919789058392, 435.919408587813, 437.918873200300,
+  439.918417416574, 441.918192491069, 443.918376503567, 445.917692006023,
+  447.917132642691, 449.917217997363, 451.916924115487, 453.916662151474,
+  455.916485288107, 457.915879851633, 459.915842501592, 461.915547354915,
+  463.914995141359, 465.914585759187, 467.914144928428, 469.913956069337,
+  471.913046082896, 473.912468624617, 475.912322983182, 477.912118100240,
+  479.911923664773, 481.911518944025, 483.911023761757, 485.910386539830,
+  487.910092345284, 489.910153786770, 491.909575245360, 493.908973823502,
+  495.908577061168, 497.908550221055, 499.908127020659, 501.907710605296,
+  503.907806972566, 505.907336571662, 507.907081330259, 509.906648846180,
+  511.906201574892
+},
+// log K = 6
+{
+  45.375999854524, 46.820948326168, 48.298703712049, 49.809402685314,
+  51.353165756275, 52.929933710602, 54.539893242468, 56.183033892089,
+  57.859217068495, 59.568519998303, 61.310801009963, 63.085886926328,
+  64.893798324934, 66.734230191052, 68.607173306372, 70.512210309984,
+  72.449265953981, 74.418007054647, 76.417910710559, 78.448929349152,
+  80.510585932256, 82.602514696500, 84.724061528721, 86.875166381330,
+  89.055217833675, 91.263852803721, 93.500342620773, 95.764451518395,
+  98.055566319691, 100.373234086043, 102.716794393386, 105.085599217566,
+  107.479363461004, 109.897282858060, 112.338907676111, 114.803401103124,
+  117.290259931022, 119.799265623507, 122.329323908482, 124.879681624600,
+  127.450299022993, 130.040239267142, 132.648756733593, 135.275366525219,
+  137.919720233989, 140.580778017562, 143.258128898839, 145.951552244700,
+  148.660145472460, 151.383257444852, 154.120434694643, 156.871202914301,
+  159.634634288797, 162.410377522877, 165.198249005575, 167.997420145534,
+  170.807579918337, 173.628020587905, 176.458633641294, 179.298282098581,
+  182.147449731726, 185.005094444186, 187.870899328937, 190.744709255561,
+  193.625572419776, 196.513740460772, 199.408599560293, 202.309940032246,
+  205.217132295894, 208.129828601313, 211.047995735211, 213.970919426104,
+  216.899109829571, 219.832024990852, 222.769136305806, 225.710258431247,
+  228.655217514772, 231.603984417526, 234.555642012353, 237.510635882749,
+  240.468820458907, 243.429903849174, 246.393192036307, 249.359251612075,
+  252.327416912482, 255.297598046767, 258.269777722144, 261.244337060733,
+  264.219958654591, 267.197643910088, 270.177202282129, 273.157339433786,
+  276.139287129066, 279.122276848731, 282.107226383752, 285.092136000673,
+  288.078230550129, 291.065256815999, 294.053160971001, 297.042401030971,
+  300.031784889467, 303.022502412860, 306.013581138287, 309.005124942334,
+  311.996910583050, 314.989635544162, 317.982189300799, 320.975762085573,
+  323.969719809182, 326.963672522149, 329.958419674913, 332.953046205989,
+  335.948338966110, 338.943201657934, 341.938798787180, 344.935321901459,
+  347.931089897905, 350.927355153871, 353.923731760523, 356.920383399325,
+  359.916863372939, 362.914245241722, 365.911216314347, 368.908636150615,
+  371.905028432797, 374.901981729273, 377.899718967915, 380.897391911925,
+  383.894945428721, 386.892974193389, 389.890517424427, 392.889050804732,
+  395.886893560179, 398.884852345719, 401.883235933616, 404.881805247652,
+  407.880049402262, 410.878410789472, 413.876755787854, 416.875140105536,
+  419.873758096660, 422.872739431833, 425.871627898739, 428.870663138631,
+  431.869475472357, 434.868378059774, 437.867537661532, 440.866274620049,
+  443.865086570094, 446.864282236320, 449.863630871851, 452.862772062678,
+  455.861877807413, 458.860680461949, 461.859728874700, 464.858478829514,
+  467.857314559298, 470.856707830759, 473.855641079729, 476.854880576501,
+  479.853812351976, 482.852666825246, 485.851471230749, 488.850997731659,
+  491.850420535006, 494.849118717584, 497.848645341646, 500.848230362352,
+  503.847546143645, 506.847285358235, 509.846275998715, 512.845599444922,
+  515.845441684852, 518.844522015985, 521.843923976006, 524.843281639782,
+  527.842399994830, 530.841152548296, 533.840788827465, 536.840058715879,
+  539.839578267778, 542.839076801367, 545.838441853502, 548.837925005355,
+  551.837456112430, 554.837041819817, 557.836916936797, 560.836116071833,
+  563.834832988710, 566.834107840572, 569.832763471368, 572.831700689757,
+  575.832050680783, 578.831190216307, 581.831249402778, 584.830325442080,
+  587.828993643112, 590.827962761832, 593.827454067745, 596.825833598259,
+  599.824193205007, 602.822806340644, 605.822276069352, 608.821423024191,
+  611.820006110523, 614.819016304854, 617.818220240160, 620.817311827644,
+  623.816286470744, 626.815211383752, 629.815077535810, 632.813925717940,
+  635.813392704099, 638.812424935595, 641.811289306882, 644.809932622788,
+  647.808609610738, 650.807771758015, 653.806851303878, 656.805452367508,
+  659.804133900742, 662.803103749985, 665.801910649522, 668.800969589926,
+  671.798796938405, 674.798110719805, 677.797843657187, 680.797711918663,
+  683.796449689643, 686.794998006462, 689.794205385109, 692.792836542916,
+  695.792195299304, 698.790778974184, 701.790704644168, 704.790168225574,
+  707.789261680438, 710.788674453657, 713.787207421297, 716.786043303658,
+  719.784740017888, 722.783523691482, 725.782739693893, 728.781695938105,
+  731.780295310459, 734.778719103047, 737.778056368816, 740.777014963309,
+  743.776470087777, 746.774710218241, 749.773897180146, 752.772555359445,
+  755.771799417720, 758.770328749569, 761.768940347768, 764.768630836408,
+  767.767399121425
+},
+// log K = 7
+{
+  91.554623, 93.970317, 96.431113, 98.937264, 101.488909, 104.086010, 
106.728780, 109.417062,
+  112.150914, 114.930279, 117.755065, 120.625143, 123.540431, 126.500927, 
129.506371, 132.556551,
+  135.651450, 138.790477, 141.973357, 145.200176, 148.470504, 151.783478, 
155.139376, 158.537455,
+  161.977357, 165.458477, 168.980630, 172.543258, 176.145951, 179.788203, 
183.469266, 187.188166,
+  190.945533, 194.739618, 198.570460, 202.437447, 206.339379, 210.276276, 
214.247092, 218.251243,
+  222.288169, 226.357728, 230.458810, 234.590263, 238.751826, 242.942859, 
247.162518, 251.410098,
+  255.685517, 259.987202, 264.314569, 268.667458, 273.044873, 277.445418, 
281.869296, 286.316796,
+  290.785471, 295.276055, 299.788143, 304.319743, 308.869691, 313.438842, 
318.026761, 322.632499,
+  327.254588, 331.892954, 336.547483, 341.217702, 345.901880, 350.601214, 
355.313731, 360.038868,
+  364.777348, 369.526476, 374.288795, 379.061811, 383.846122, 388.640850, 
393.446218, 398.260691,
+  403.084626, 407.916053, 412.755659, 417.604951, 422.461448, 427.324128, 
432.195028, 437.072269,
+  441.956190, 446.846167, 451.741463, 456.641868, 461.549478, 466.461280, 
471.377098, 476.297446,
+  481.222717, 486.151860, 491.085033, 496.022133, 500.962856, 505.906586, 
510.853868, 515.802981,
+  520.755460, 525.711123, 530.668833, 535.629910, 540.592334, 545.557764, 
550.525455, 555.494082,
+  560.463996, 565.436164, 570.410847, 575.385933, 580.361996, 585.339352, 
590.318151, 595.300216,
+  600.281543, 605.264482, 610.250114, 615.236608, 620.222025, 625.208433, 
630.195367, 635.183997,
+  640.172271, 645.162589, 650.153647, 655.144276, 660.133923, 665.123818, 
670.115789, 675.109169,
+  680.103270, 685.097349, 690.091960, 695.086028, 700.080326, 705.075670, 
710.069697, 715.065265,
+  720.062426, 725.058529, 730.054916, 735.050560, 740.048508, 745.044790, 
750.041067, 755.037833,
+  760.035090, 765.033371, 770.029865, 775.025946, 780.022408, 785.021534, 
790.018699, 795.016742,
+  800.013947, 805.012680, 810.009483, 815.007921, 820.008589, 825.007658, 
830.007411, 835.006892,
+  840.006917, 845.006362, 850.004606, 855.003367, 860.001219, 864.999491, 
869.999131, 874.998190,
+  879.998460, 884.997482, 889.995952, 894.995130, 899.994555, 904.993474, 
909.991434, 914.990519,
+  919.991564, 924.992234, 929.993901, 934.992310, 939.992013, 944.993341, 
949.993576, 954.992981,
+  959.993548, 964.993781, 969.993129, 974.994050, 979.994706, 984.994372, 
989.994395, 994.995339,
+  999.995544, 1004.995498, 1009.996864, 1014.997829, 1019.996531, 1024.996724, 
1029.997574, 1034.998591,
+  1039.997807, 1044.998954, 1050.000113, 1055.000633, 1060.000845, 
1065.000760, 1070.000907, 1075.001248,
+  1080.001187, 1084.999775, 1089.999105, 1094.999647, 1100.001983, 
1105.003468, 1110.004542, 1115.005170,
+  1120.006517, 1125.006493, 1130.006837, 1135.008369, 1140.006966, 
1145.009225, 1150.008497, 1155.009589,
+  1160.009558, 1165.011395, 1170.009739, 1175.011131, 1180.013303, 
1185.012525, 1190.014190, 1195.016399,
+  1200.016087, 1205.016715, 1210.018008, 1215.018108, 1220.019677, 
1225.020269, 1230.019468, 1235.020515,
+  1240.021850, 1245.020961, 1250.019785, 1255.020253, 1260.020623, 
1265.020156, 1270.020625, 1275.019028,
+  1280.019972
+},
+// log K = 8
+{
+  183.877784, 188.720097, 193.652407, 198.675131, 203.788397, 208.992238, 
214.286620, 219.671580,
+  225.147277, 230.713559, 236.370208, 242.117078, 247.953569, 253.880078, 
259.895869, 266.000405,
+  272.193694, 278.474457, 284.843102, 291.298228, 297.839692, 304.466434, 
311.177471, 317.972674,
+  324.850719, 331.811076, 338.852902, 345.974709, 353.175880, 360.455237, 
367.811613, 375.244484,
+  382.752472, 390.334218, 397.988777, 405.714596, 413.510671, 421.376147, 
429.308973, 437.308197,
+  445.372302, 453.500121, 461.690796, 469.942249, 478.253652, 486.623402, 
495.049662, 503.532193,
+  512.068670, 520.658371, 529.299186, 537.990886, 546.732097, 555.520093, 
564.355935, 573.235689,
+  582.159345, 591.126063, 600.133321, 609.181348, 618.267848, 627.392384, 
636.552822, 645.748152,
+  654.977601, 664.239902, 673.534497, 682.859448, 692.213983, 701.598606, 
711.009376, 720.446781,
+  729.910995, 739.397888, 748.908546, 758.443812, 768.000572, 777.577584, 
787.174815, 796.791250,
+  806.427107, 816.079660, 825.748493, 835.435992, 845.136828, 854.854141, 
864.583886, 874.328396,
+  884.085720, 893.855568, 903.637568, 913.431080, 923.234553, 933.049830, 
942.873895, 952.707793,
+  962.551743, 972.402421, 982.263001, 992.131464, 1002.006941, 1011.888460, 
1021.777462, 1031.671376,
+  1041.569600, 1051.476632, 1061.386996, 1071.302834, 1081.224251, 
1091.148847, 1101.080297, 1111.013589,
+  1120.951962, 1130.893360, 1140.838011, 1150.786112, 1160.737100, 
1170.689817, 1180.646908, 1190.604979,
+  1200.565150, 1210.527894, 1220.492169, 1230.460128, 1240.429764, 
1250.401749, 1260.373274, 1270.349608,
+  1280.325544, 1290.302087, 1300.279074, 1310.257850, 1320.238948, 
1330.221273, 1340.203554, 1350.186669,
+  1360.171345, 1370.156233, 1380.142561, 1390.129535, 1400.119177, 
1410.107938, 1420.097171, 1430.087586,
+  1440.077831, 1450.067996, 1460.060299, 1470.050059, 1480.042995, 
1490.035101, 1500.026878, 1510.021128,
+  1520.014262, 1530.008487, 1540.002895, 1549.996342, 1559.991075, 
1569.986665, 1579.981202, 1589.976021,
+  1599.969512, 1609.964051, 1619.959210, 1629.957092, 1639.953516, 
1649.948113, 1659.946715, 1669.945253,
+  1679.941736, 1689.938651, 1699.937600, 1709.936396, 1719.933608, 
1729.931040, 1739.930316, 1749.927141,
+  1759.922600, 1769.920847, 1779.918614, 1789.915707, 1799.913000, 
1809.910958, 1819.908532, 1829.906905,
+  1839.903410, 1849.904217, 1859.901705, 1869.898815, 1879.898869, 
1889.899032, 1899.899113, 1909.896164,
+  1919.893199, 1929.894515, 1939.894965, 1949.893184, 1959.892164, 
1969.890794, 1979.889035, 1989.888005,
+  1999.888538, 2009.888252, 2019.888976, 2029.890242, 2039.890655, 
2049.891065, 2059.891068, 2069.889204,
+  2079.891120, 2089.892462, 2099.892479, 2109.891814, 2119.893049, 
2129.893705, 2139.893471, 2149.894162,
+  2159.894213, 2169.895041, 2179.895987, 2189.894721, 2199.894028, 
2209.896005, 2219.898683, 2229.898673,
+  2239.898012, 2249.898018, 2259.898215, 2269.899380, 2279.902590, 
2289.903144, 2299.902047, 2309.904214,
+  2319.903812, 2329.904180, 2339.902528, 2349.903057, 2359.903089, 
2369.904512, 2379.902563, 2389.905522,
+  2399.903653, 2409.902313, 2419.904452, 2429.905949, 2439.905059, 
2449.904954, 2459.904977, 2469.905111,
+  2479.904049, 2489.903762, 2499.902022, 2509.904352, 2519.903272, 
2529.901643, 2539.900789, 2549.903045,
+  2559.904334
+},
+// log K = 9
+{
+  368.528954, 378.224619, 388.100156, 398.155846, 408.391912, 418.808631, 
429.406377, 440.185048,
+  451.144613, 462.284693, 473.604932, 485.105306, 496.784844, 508.643147, 
520.679581, 532.893098,
+  545.282616, 557.848295, 570.587645, 583.500268, 596.584345, 609.838697, 
623.261582, 636.851681,
+  650.607666, 664.527320, 678.608336, 692.849674, 707.248088, 721.803089, 
736.511331, 751.372034,
+  766.381798, 781.538178, 796.839808, 812.283881, 827.867995, 843.588520, 
859.444318, 875.432109,
+  891.550599, 907.795630, 924.165943, 940.657393, 957.268601, 973.995685, 
990.837235, 1007.789667,
+  1024.850025, 1042.016602, 1059.286426, 1076.658199, 1094.126523, 
1111.689346, 1129.345589, 1147.092119,
+  1164.926795, 1182.844471, 1200.846880, 1218.929006, 1237.088281, 
1255.322930, 1273.630393, 1292.007269,
+  1310.452948, 1328.963430, 1347.538768, 1366.176566, 1384.871299, 
1403.626249, 1422.434094, 1441.295238,
+  1460.208340, 1479.169853, 1498.179265, 1517.234855, 1536.335403, 
1555.477305, 1574.659180, 1593.881592,
+  1613.139378, 1632.433516, 1651.764587, 1671.127255, 1690.519577, 
1709.942250, 1729.393153, 1748.871689,
+  1768.377142, 1787.907209, 1807.464755, 1827.043146, 1846.641976, 
1866.263022, 1885.903553, 1905.564908,
+  1925.240581, 1944.936723, 1964.648221, 1984.375489, 2004.117246, 
2023.875053, 2043.644075, 2063.423514,
+  2083.218689, 2103.026148, 2122.840475, 2142.669046, 2162.505619, 
2182.351758, 2202.205963, 2222.068658,
+  2241.939010, 2261.814547, 2281.701803, 2301.591894, 2321.490728, 
2341.393208, 2361.298412, 2381.211528,
+  2401.132345, 2421.057721, 2440.985510, 2460.916691, 2480.851468, 
2500.792645, 2520.738446, 2540.683395,
+  2560.634603, 2580.586307, 2600.540530, 2620.497170, 2640.458137, 
2660.421783, 2680.386186, 2700.352664,
+  2720.321411, 2740.291944, 2760.264068, 2780.238910, 2800.212546, 
2820.185829, 2840.163952, 2860.142145,
+  2880.120568, 2900.102466, 2920.081981, 2940.062964, 2960.046262, 
2980.029101, 3000.012591, 3019.998424,
+  3039.983637, 3059.969573, 3079.955927, 3099.942114, 3119.930348, 
3139.919614, 3159.909130, 3179.899816,
+  3199.892231, 3219.885316, 3239.878917, 3259.871675, 3279.865195, 
3299.856210, 3319.848871, 3339.840385,
+  3359.834158, 3379.830660, 3399.826361, 3419.822855, 3439.814714, 
3459.813765, 3479.808673, 3499.804339,
+  3519.797785, 3539.791012, 3559.789885, 3579.785152, 3599.780237, 
3619.775480, 3639.772724, 3659.768920,
+  3679.765735, 3699.766361, 3719.765048, 3739.762845, 3759.761526, 
3779.757598, 3799.755719, 3819.751149,
+  3839.750182, 3859.749673, 3879.749344, 3899.749939, 3919.749060, 
3939.748385, 3959.750520, 3979.750055,
+  3999.750104, 4019.749963, 4039.748453, 4059.746737, 4079.744522, 
4099.744430, 4119.741804, 4139.740704,
+  4159.742056, 4179.740063, 4199.739155, 4219.741718, 4239.743433, 
4259.743115, 4279.743265, 4299.745518,
+  4319.746682, 4339.750491, 4359.748549, 4379.747226, 4399.750319, 
4419.750379, 4439.752339, 4459.750561,
+  4479.750612, 4499.747862, 4519.750693, 4539.747476, 4559.749051, 
4579.750603, 4599.750089, 4619.754781,
+  4639.755536, 4659.758879, 4679.762145, 4699.762199, 4719.763886, 
4739.765081, 4759.765681, 4779.765427,
+  4799.767582, 4819.769915, 4839.770107, 4859.769398, 4879.768976, 
4899.768288, 4919.768786, 4939.768607,
+  4959.772822, 4979.772833, 4999.775808, 5019.773468, 5039.776181, 
5059.770860, 5079.773006, 5099.773136,
+  5119.772474
+},
+// log K = 10
+{
+  737.833738, 757.236338, 776.997875, 797.119465, 817.602058, 838.445349, 
859.650320, 881.216437,
+  903.143414, 925.430957, 948.079220, 971.086231, 994.451449, 1018.173846, 
1042.251928, 1066.683804,
+  1091.467648, 1116.601416, 1142.083017, 1167.909918, 1194.079097, 
1220.588887, 1247.435099, 1274.614860,
+  1302.125360, 1329.962942, 1358.123177, 1386.601609, 1415.396299, 
1444.501692, 1473.913256, 1503.628515,
+  1533.642054, 1563.948888, 1594.544067, 1625.424305, 1656.582169, 
1688.016543, 1719.718553, 1751.685990,
+  1783.912130, 1816.393610, 1849.122123, 1882.095124, 1915.305647, 
1948.748605, 1982.419843, 2016.313904,
+  2050.422277, 2084.742618, 2119.270601, 2153.998538, 2188.920843, 
2224.034736, 2259.333960, 2294.812594,
+  2330.467059, 2366.291844, 2402.281356, 2438.428954, 2474.734330, 
2511.187839, 2547.786319, 2584.527356,
+  2621.406814, 2658.418551, 2695.554270, 2732.812564, 2770.190157, 
2807.682744, 2845.287138, 2882.995251,
+  2920.807982, 2958.719996, 2996.728771, 3034.826885, 3073.014082, 
3111.287116, 3149.642093, 3188.073159,
+  3226.577352, 3265.156836, 3303.803738, 3342.515111, 3381.291514, 
3420.130671, 3459.024315, 3497.974631,
+  3536.978221, 3576.034737, 3615.138610, 3654.285859, 3693.480455, 
3732.715296, 3771.991792, 3811.305300,
+  3850.654870, 3890.041152, 3929.457167, 3968.905021, 4008.381466, 
4047.886976, 4087.419414, 4126.978997,
+  4166.562624, 4206.166726, 4245.793284, 4285.445151, 4325.113012, 
4364.802795, 4404.506274, 4444.228475,
+  4483.965191, 4523.719569, 4563.487708, 4603.267460, 4643.060387, 
4682.865430, 4722.680718, 4762.503248,
+  4802.341326, 4842.185529, 4882.039088, 4921.900432, 4961.772625, 
5001.649566, 5041.534380, 5081.425809,
+  5121.323013, 5161.224908, 5201.130560, 5241.044625, 5280.962456, 
5320.882373, 5360.810007, 5400.740058,
+  5440.673843, 5480.606748, 5520.547394, 5560.493520, 5600.438100, 
5640.383853, 5680.335386, 5720.292400,
+  5760.250576, 5800.212705, 5840.172984, 5880.134991, 5920.097923, 
5960.060294, 6000.031177, 6040.003398,
+  6079.981696, 6119.954349, 6159.929074, 6199.908569, 6239.886163, 
6279.863659, 6319.838261, 6359.815405,
+  6399.795170, 6439.777225, 6479.755885, 6519.738857, 6559.723169, 
6599.708549, 6639.695186, 6679.679788,
+  6719.663289, 6759.651052, 6799.634129, 6839.624763, 6879.615013, 
6919.605584, 6959.591310, 6999.586006,
+  7039.577399, 7079.565236, 7119.557222, 7159.548481, 7199.533025, 
7239.519648, 7279.511781, 7319.507639,
+  7359.506105, 7399.497488, 7439.494944, 7479.491545, 7519.488415, 
7559.480534, 7599.475646, 7639.470315,
+  7679.468730, 7719.460928, 7759.455834, 7799.455686, 7839.451758, 
7879.446841, 7919.443884, 7959.444138,
+  7999.443141, 8039.446339, 8079.445285, 8119.444120, 8159.443157, 
8199.446480, 8239.438893, 8279.437597,
+  8319.439714, 8359.441566, 8399.440754, 8439.439156, 8479.440112, 
8519.442365, 8559.444795, 8599.444497,
+  8639.446714, 8679.449177, 8719.447620, 8759.443423, 8799.445995, 
8839.446170, 8879.449622, 8919.450486,
+  8959.450372, 8999.449731, 9039.448449, 9079.452579, 9119.451596, 
9159.454020, 9199.456651, 9239.460902,
+  9279.460419, 9319.461322, 9359.464611, 9399.467790, 9439.471448, 
9479.468426, 9519.462982, 9559.464881,
+  9599.462983, 9639.461706, 9679.457284, 9719.457023, 9759.455648, 
9799.458786, 9839.456980, 9879.457411,
+  9919.454971, 9959.451735, 9999.451916, 10039.443939, 10079.435325, 
10119.432142, 10159.431313, 10199.431306,
+  10239.429671
+},
+// log K = 11
+{
+  1476.444530, 1515.260638, 1554.794859, 1595.048761, 1636.023652, 
1677.720065, 1720.138513, 1763.279158,
+  1807.141823, 1851.724811, 1897.027530, 1943.048153, 1989.784818, 
2037.234835, 2085.395149, 2134.263580,
+  2183.834818, 2234.105548, 2285.071077, 2336.726064, 2389.065940, 
2442.084276, 2495.776521, 2550.134177,
+  2605.153465, 2660.825522, 2717.142881, 2774.098631, 2831.683790, 
2889.889911, 2948.711233, 3008.137464,
+  3068.158605, 3128.766926, 3189.951984, 3251.704129, 3314.013826, 
3376.873018, 3440.270569, 3504.194851,
+  3568.636295, 3633.586039, 3699.031630, 3764.962925, 3831.371336, 
3898.245744, 3965.574384, 4033.345701,
+  4101.550888, 4170.180686, 4239.221393, 4308.664143, 4378.497100, 
4448.712735, 4519.296212, 4590.243535,
+  4661.538747, 4733.174738, 4805.142002, 4877.427081, 4950.019513, 
5022.915001, 5096.098561, 5169.567185,
+  5243.312012, 5317.314578, 5391.572253, 5466.077564, 5540.821377, 
5615.792025, 5690.985894, 5766.392174,
+  5842.007403, 5917.816734, 5993.815607, 6069.999957, 6146.362662, 
6222.895187, 6299.592875, 6376.440778,
+  6453.443173, 6530.588182, 6607.874560, 6685.288536, 6762.829840, 
6840.490840, 6918.272948, 6996.164556,
+  7074.161694, 7152.259404, 7230.451098, 7308.737035, 7387.113244, 
7465.575350, 7544.120230, 7622.733447,
+  7701.427750, 7780.188044, 7859.016102, 7937.904850, 8016.853624, 
8095.860379, 8174.919884, 8254.034842,
+  8333.197794, 8412.405307, 8491.657429, 8570.946804, 8650.278289, 
8729.648657, 8809.056098, 8888.496764,
+  8967.966663, 9047.466051, 9126.993636, 9206.547289, 9286.127523, 
9365.733732, 9445.361201, 9525.008810,
+  9604.680107, 9684.363532, 9764.066435, 9843.787987, 9923.523567, 
10003.272886, 10083.037524, 10162.811803,
+  10242.597755, 10322.402415, 10402.216522, 10482.042584, 10561.877077, 
10641.722187, 10721.570740, 10801.435848,
+  10881.301249, 10961.178474, 11041.059765, 11120.951444, 11200.845806, 
11280.746075, 11360.652145, 11440.567398,
+  11520.476709, 11600.395391, 11680.316855, 11760.241900, 11840.170134, 
11920.103548, 12000.038206, 12079.975425,
+  12159.912459, 12239.857191, 12319.802744, 12399.754385, 12479.707055, 
12559.658981, 12639.614004, 12719.566502,
+  12799.526877, 12879.488833, 12959.451231, 13039.416480, 13119.388158, 
13199.360789, 13279.334923, 13359.298927,
+  13439.261641, 13519.235871, 13599.204919, 13679.181082, 13759.157391, 
13839.137425, 13919.112545, 13999.096522,
+  14079.082789, 14159.060487, 14239.040581, 14319.025236, 14399.006995, 
14478.994078, 14558.980576, 14638.962774,
+  14718.952192, 14798.938522, 14878.931501, 14958.919062, 15038.907891, 
15118.897247, 15198.889129, 15278.881057,
+  15358.873272, 15438.867627, 15518.861931, 15598.858206, 15678.850014, 
15758.849382, 15838.847872, 15918.837658,
+  15998.837920, 16078.831305, 16158.827345, 16238.815546, 16318.817302, 
16398.814389, 16478.815820, 16558.811634,
+  16638.808126, 16718.807170, 16798.805652, 16878.813389, 16958.806543, 
17038.807885, 17118.813146, 17198.813752,
+  17278.817574, 17358.820511, 17438.819443, 17518.814262, 17598.815511, 
17678.814160, 17758.816726, 17838.817584,
+  17918.814197, 17998.821276, 18078.822342, 18158.828474, 18238.822064, 
18318.826732, 18398.827758, 18478.827319,
+  18558.834153, 18638.835651, 18718.839105, 18798.840903, 18878.836463, 
18958.836015, 19038.836351, 19118.832583,
+  19198.829856, 19278.822953, 19358.824866, 19438.823697, 19518.826538, 
19598.826954, 19678.831393, 19758.822279,
+  19838.818757, 19918.808247, 19998.804516, 20078.809201, 20158.808900, 
20238.806398, 20318.798516, 20398.802224,
+  20478.803179
+},
+// log K = 12
+{
+  2953.666722, 3031.310137, 3110.389526, 3190.908699, 3272.865410, 
3356.267166, 3441.114376, 3527.404284,
+  3615.136846, 3704.310976, 3794.924283, 3886.973846, 3980.451903, 
4075.355844, 4171.681168, 4269.423939,
+  4368.569738, 4469.111760, 4571.043603, 4674.356370, 4779.038117, 
4885.076002, 4992.460659, 5101.176875,
+  5211.214098, 5322.553631, 5435.184633, 5549.088369, 5664.258175, 
5780.673987, 5898.312682, 6017.158593,
+  6137.195815, 6258.397951, 6380.761679, 6504.261630, 6628.870413, 
6754.585867, 6881.374325, 7009.206926,
+  7138.080376, 7267.979175, 7398.866805, 7530.722851, 7663.531580, 
7797.269872, 7931.907340, 8067.451667,
+  8203.855692, 8341.091306, 8479.155031, 8618.026003, 8757.682272, 
8898.095612, 9039.253787, 9181.124777,
+  9323.694329, 9466.944202, 9610.861899, 9755.403639, 9900.578346, 
10046.356477, 10192.718770, 10339.635914,
+  10487.081762, 10635.066232, 10783.560130, 10932.549371, 11082.024055, 
11231.949071, 11382.332885, 11533.122831,
+  11684.327555, 11835.935533, 11987.926999, 12140.281619, 12292.994375, 
12446.046449, 12599.411083, 12753.088314,
+  12907.079745, 13061.355923, 13215.895621, 13370.722085, 13525.795736, 
13681.095971, 13836.645692, 13992.405906,
+  14148.380716, 14304.566860, 14460.959382, 14617.539754, 14774.296125, 
14931.214508, 15088.271748, 15245.493870,
+  15402.867309, 15560.380994, 15718.035423, 15875.801492, 16033.687891, 
16191.701231, 16349.808763, 16508.026001,
+  16666.336068, 16824.745049, 16983.259302, 17141.848044, 17300.504517, 
17459.240272, 17618.060296, 17776.914688,
+  17935.848010, 18094.852645, 18253.914288, 18413.023567, 18572.168358, 
18731.376016, 18890.631505, 19049.948196,
+  19209.279260, 19368.638871, 19528.043860, 19687.498901, 19846.941765, 
20006.443084, 20165.973535, 20325.525957,
+  20485.109520, 20644.729917, 20804.363876, 20964.021778, 21123.698687, 
21283.379344, 21443.080536, 21602.821439,
+  21762.568135, 21922.305338, 22082.065878, 22241.856145, 22401.629436, 
22561.435266, 22721.262644, 22881.080478,
+  23040.916036, 23200.734053, 23360.583255, 23520.433188, 23680.275065, 
23840.132678, 24000.012847, 24159.886135,
+  24319.788338, 24479.654024, 24639.540316, 24799.466449, 24959.369335, 
25119.276072, 25279.186502, 25439.091982,
+  25599.020083, 25758.930671, 25918.847522, 26078.773060, 26238.704021, 
26398.647566, 26558.603366, 26718.539855,
+  26878.464425, 27038.402892, 27198.344989, 27358.270906, 27518.203448, 
27678.169545, 27838.133625, 27998.111630,
+  28158.064264, 28318.032491, 28477.994153, 28637.950838, 28797.910362, 
28957.884994, 29117.861910, 29277.814384,
+  29437.774223, 29597.762256, 29757.729819, 29917.688497, 30077.675371, 
30237.667941, 30397.639431, 30557.644527,
+  30717.622991, 30877.622641, 31037.598941, 31197.601527, 31357.599118, 
31517.585221, 31677.569983, 31837.556138,
+  31997.527880, 32157.500622, 32317.491709, 32477.504936, 32637.501123, 
32797.516330, 32957.547336, 33117.541765,
+  33277.561207, 33437.554332, 33597.564988, 33757.565321, 33917.561180, 
34077.580598, 34237.597132, 34397.598246,
+  34557.617536, 34717.623318, 34877.649722, 35037.645029, 35197.681143, 
35357.700478, 35517.701804, 35677.734383,
+  35837.736494, 35997.737103, 36157.732045, 36317.741539, 36477.735602, 
36637.761491, 36797.742508, 36957.734137,
+  37117.742952, 37277.789218, 37437.793798, 37597.779066, 37757.796449, 
37917.794116, 38077.804399, 38237.797507,
+  38397.788049, 38557.768583, 38717.768279, 38877.752553, 39037.747349, 
39197.737646, 39357.774395, 39517.752643,
+  39677.753347, 39837.740038, 39997.719486, 40157.709578, 40317.708657, 
40477.719261, 40637.726978, 40797.711139,
+  40957.707784
+},
+// log K = 13
+{
+  5908.111420, 6063.410343, 6221.578483, 6382.623803, 6546.553966, 
6713.369171, 6883.070010, 7055.656719,
+  7231.129758, 7409.486672, 7590.720089, 7774.822139, 7961.786850, 
8151.601206, 8344.261218, 8539.750086,
+  8738.048786, 8939.141702, 9143.011205, 9349.645220, 9559.003557, 
9771.078383, 9985.846791, 10203.277103,
+  10423.349219, 10646.031323, 10871.293001, 11099.111735, 11329.434411, 
11562.255804, 11797.522109, 12035.204106,
+  12275.263649, 12517.670237, 12762.388671, 13009.375491, 13258.587519, 
13510.006825, 13763.564399, 14019.227089,
+  14276.975295, 14536.745792, 14798.511796, 15062.215057, 15327.806593, 
15595.257499, 15864.529774, 16135.586770,
+  16408.362074, 16682.849583, 16958.977019, 17236.708221, 17515.990972, 
17796.798914, 18079.096585, 18362.834517,
+  18647.959152, 18934.452073, 19222.251998, 19511.366053, 19801.717928, 
20093.269020, 20385.960319, 20679.772736,
+  20974.698498, 21270.684814, 21567.671010, 21865.649193, 22164.581341, 
22464.423293, 22765.172894, 23066.785452,
+  23369.201089, 23672.387494, 23976.354000, 24281.072155, 24586.465328, 
24892.557323, 25199.303712, 25506.654130,
+  25814.626835, 26123.148009, 26432.247046, 26741.889610, 27052.042135, 
27362.685181, 27673.761784, 27985.292246,
+  28297.219809, 28609.572368, 28922.317633, 29235.448741, 29548.944253, 
29862.766175, 30176.910610, 30491.363107,
+  30806.103787, 31121.126024, 31436.412153, 31751.975134, 32067.737444, 
32383.768560, 32700.017044, 33016.458300,
+  33333.101934, 33649.904492, 33966.923435, 34284.105041, 34601.429152, 
34918.876076, 35236.481998, 35554.235511,
+  35872.093847, 36190.101572, 36508.190892, 36826.420800, 37144.739624, 
37463.138566, 37781.598984, 38100.191087,
+  38418.846511, 38737.587005, 39056.391321, 39375.255819, 39694.182864, 
40013.173503, 40332.221146, 40651.311460,
+  40970.465534, 41289.697002, 41608.913381, 41928.242461, 42247.585089, 
42566.988678, 42886.409594, 43205.849396,
+  43525.322840, 43844.784209, 44164.327769, 44483.869861, 44803.383648, 
45122.975593, 45442.593765, 45762.258110,
+  46081.937012, 46401.605265, 46721.295022, 47041.004034, 47360.695978, 
47680.435451, 48000.192231, 48319.953839,
+  48639.726508, 48959.506239, 49279.318744, 49599.117846, 49918.951926, 
50238.772730, 50558.620076, 50878.455501,
+  51198.285377, 51518.135316, 51837.976134, 52157.809260, 52477.635805, 
52797.492063, 53117.368983, 53437.262930,
+  53757.142921, 54077.026859, 54396.907345, 54716.835955, 55036.713062, 
55356.640827, 55676.537369, 55996.488865,
+  56316.420754, 56636.355217, 56956.329593, 57276.245282, 57596.202473, 
57916.159021, 58236.077854, 58556.010303,
+  58875.938110, 59195.924442, 59515.871947, 59835.838035, 60155.809948, 
60475.808167, 60795.765545, 61115.766398,
+  61435.725825, 61755.677959, 62075.688445, 62395.647263, 62715.609853, 
63035.591787, 63355.588060, 63675.579841,
+  63995.560980, 64315.552298, 64635.582956, 64955.538779, 65275.512154, 
65595.490993, 65915.477332, 66235.470693,
+  66555.483426, 66875.455452, 67195.455039, 67515.435090, 67835.418300, 
68155.401870, 68475.404599, 68795.452423,
+  69115.474466, 69435.450344, 69755.439668, 70075.479966, 70395.468204, 
70715.475957, 71035.472807, 71355.513734,
+  71675.486047, 71995.450475, 72315.443410, 72635.425138, 72955.481520, 
73275.482426, 73595.476688, 73915.469540,
+  74235.426693, 74555.381683, 74875.368981, 75195.429157, 75515.480428, 
75835.517265, 76155.557945, 76475.556134,
+  76795.579451, 77115.605924, 77435.604178, 77755.572770, 78075.576305, 
78395.562526, 78715.547599, 79035.551674,
+  79355.540615, 79675.575726, 79995.547586, 80315.544278, 80635.546227, 
80955.555617, 81275.540591, 81595.549538,
+  81915.550079
+},
+// log K = 14
+{
+  11817.000969, 12127.609164, 12443.958867, 12766.059919, 13093.926347, 
13427.563687, 13766.972021, 14112.158775,
+  14463.118632, 14819.833221, 15182.310838, 15550.514164, 15924.452624, 
16304.097682, 16689.414687, 17080.390156,
+  17476.985675, 17879.171048, 18286.913146, 18700.171223, 19118.909859, 
19543.074108, 19972.606221, 20407.466793,
+  20847.600290, 21292.965548, 21743.489087, 22199.109276, 22659.760749, 
23125.379711, 23595.914325, 24071.277989,
+  24551.407611, 25036.233071, 25525.671155, 26019.641791, 26518.074136, 
27020.885189, 27528.004081, 28039.347478,
+  28554.818908, 29074.343878, 29597.869471, 30125.254970, 30656.449891, 
31191.351228, 31729.908970, 32272.003587,
+  32817.586623, 33366.531359, 33918.769390, 34474.232727, 35032.798601, 
35594.423192, 36159.024745, 36726.469786,
+  37296.712851, 37869.686035, 38445.278953, 39023.442285, 39604.084062, 
40187.147209, 40772.503600, 41360.148758,
+  41949.978377, 42541.929077, 43135.892496, 43731.836459, 44329.664552, 
44929.334224, 45530.772542, 46133.919152,
+  46738.746702, 47345.120215, 47953.051656, 48562.469011, 49173.281212, 
49785.453501, 50398.908717, 51013.645932,
+  51629.563526, 52246.635759, 52864.820434, 53484.057447, 54104.303719, 
54725.504675, 55347.680019, 55970.744363,
+  56594.688890, 57219.458967, 57844.949821, 58471.197998, 59098.180259, 
59725.774793, 60354.058215, 60982.962558,
+  61612.427469, 62242.448368, 62873.013426, 63504.106743, 64135.672742, 
64767.665213, 65400.082326, 66032.949331,
+  66666.219558, 67299.817447, 67933.801373, 68568.092593, 69202.717636, 
69837.643507, 70472.852133, 71108.305774,
+  71744.023189, 72380.016332, 73016.168118, 73652.560876, 74289.210741, 
74926.041959, 75563.048434, 76200.207724,
+  76837.505542, 77474.999283, 78112.608284, 78750.375633, 79388.254423, 
80026.267074, 80664.419197, 81302.602941,
+  81940.909776, 82579.321050, 83217.801329, 83856.420605, 84495.098999, 
85133.832545, 85772.588001, 86411.448347,
+  87050.399262, 87689.358100, 88328.463714, 88967.516990, 89606.618839, 
90245.788934, 90884.986443, 91524.243962,
+  92163.523370, 92802.835394, 93442.198790, 94081.633840, 94721.043838, 
95360.482304, 96000.002974, 96639.518339,
+  97279.069377, 97918.584802, 98558.189888, 99197.773242, 99837.390001, 
100476.994827, 101116.655652, 101756.293703,
+  102395.977437, 103035.662337, 103675.360919, 104315.063845, 104954.783621, 
105594.504004, 106234.229823, 106874.056079,
+  107513.846129, 108153.562857, 108793.393622, 109433.202056, 110072.966808, 
110712.777026, 111352.577741, 111992.441538,
+  112632.256702, 113272.083743, 113911.943484, 114551.818432, 115191.687411, 
115831.584194, 116471.522453, 117111.338979,
+  117751.192777, 118391.070556, 119030.991702, 119670.888488, 120310.801509, 
120950.761000, 121590.691826, 122230.618394,
+  122870.618097, 123510.616056, 124150.552363, 124790.488946, 125430.445483, 
126070.428297, 126710.409643, 127350.398562,
+  127990.397846, 128630.413222, 129270.354937, 129910.365912, 130550.332174, 
131190.259646, 131830.280970, 132470.304278,
+  133110.295205, 133750.312426, 134390.364847, 135030.321000, 135670.319926, 
136310.335570, 136950.324790, 137590.338289,
+  138230.368657, 138870.374084, 139510.447896, 140150.468353, 140790.441338, 
141430.438424, 142070.440787, 142710.431348,
+  143350.433770, 143990.493350, 144630.548409, 145270.549935, 145910.530486, 
146550.488808, 147190.510290, 147830.509426,
+  148470.575641, 149110.541845, 149750.592427, 150390.634921, 151030.706500, 
151670.646574, 152310.624482, 152950.686127,
+  153590.659153, 154230.646236, 154870.720079, 155510.614028, 156150.596142, 
156790.659641, 157430.633386, 158070.649047,
+  158710.591350, 159350.585980, 159990.557711, 160630.530183, 161270.499427, 
161910.521323, 162550.516603, 163190.482792,
+  163830.422631
+},
+// log K = 15
+{
+  23634.780143, 24256.008404, 24888.717596, 25532.934303, 26188.680791, 
26855.965949, 27534.798395, 28225.179199,
+  28927.099457, 29640.545847, 30365.501013, 31101.936533, 31849.825685, 
32609.111850, 33379.755958, 34161.706474,
+  34954.912131, 35759.291030, 36574.778310, 37401.280667, 38238.742234, 
39087.060449, 39946.133936, 40815.861824,
+  41696.151280, 42586.877221, 43487.927203, 44399.169293, 45320.460825, 
46251.693279, 47192.741847, 48143.464216,
+  49103.716679, 50073.349113, 51052.208336, 52040.138165, 53036.993192, 
54042.604241, 55056.838449, 56079.501709,
+  57110.434199, 58149.484623, 59196.455710, 60251.226284, 61313.593108, 
62383.385277, 63460.484960, 64544.666337,
+  65635.763495, 66733.629361, 67838.096489, 68948.987037, 70066.117363, 
71189.338133, 72318.464573, 73453.423815,
+  74593.923438, 75739.864335, 76891.058098, 78047.391508, 79208.654009, 
80374.753680, 81545.514022, 82720.817799,
+  83900.405263, 85084.273208, 86272.263471, 87464.134340, 88659.829437, 
89859.204804, 91062.115479, 92268.425863,
+  93478.041907, 94690.804589, 95906.647693, 97125.398400, 98347.018145, 
99571.305992, 100798.274503, 102027.737913,
+  103259.574749, 104493.727918, 105730.035415, 106968.465700, 108208.988306, 
109451.445144, 110695.748927, 111941.827269,
+  113189.666448, 114439.093665, 115690.116530, 116942.608958, 118196.501294, 
119451.722652, 120708.276299, 121966.046247,
+  123225.027480, 124485.082349, 125746.201609, 127008.357433, 128271.416054, 
129535.417336, 130800.297278, 132066.008820,
+  133332.483576, 134599.733640, 135867.681429, 137136.303792, 138405.538969, 
139675.413251, 140945.839375, 142216.787412,
+  143488.232199, 144760.234976, 146032.650196, 147305.459198, 148578.703708, 
149852.356585, 151126.347770, 152400.641475,
+  153675.275168, 154950.179904, 156225.390849, 157500.864126, 158776.585926, 
160052.504602, 161328.716398, 162605.171897,
+  163881.825931, 165158.677102, 166435.646121, 167712.814846, 168990.136047, 
170267.619890, 171545.238277, 172823.011460,
+  174100.890250, 175378.821293, 176656.920467, 177935.093547, 179213.318173, 
180491.692642, 181770.130424, 183048.727206,
+  184327.217835, 185605.921891, 186884.657773, 188163.418059, 189442.221838, 
190721.122201, 192000.068271, 193279.087882,
+  194558.139554, 195837.195523, 197116.400538, 198395.612565, 199674.831001, 
200954.101970, 202233.365695, 203512.639968,
+  204792.003319, 206071.379608, 207350.730914, 208630.080321, 209909.544216, 
211189.014497, 212468.508049, 213748.055445,
+  215027.584964, 216307.216078, 217586.896296, 218866.477232, 220146.109506, 
221425.767458, 222705.465633, 223985.129542,
+  225264.797531, 226544.544335, 227824.304232, 229104.031191, 230383.739676, 
231663.424684, 232943.198829, 234222.906969,
+  235502.669185, 236782.475696, 238062.262723, 239342.090234, 240622.002668, 
241901.911022, 243181.742902, 244461.690551,
+  245741.531359, 247021.432644, 248301.277604, 249581.168404, 250861.094078, 
252141.038818, 253421.065594, 254701.020169,
+  255980.887518, 257260.848328, 258540.873565, 259820.905666, 261100.908594, 
262380.854763, 263660.862699, 264940.834842,
+  266220.775656, 267500.745402, 268780.761926, 270060.823540, 271340.798715, 
272620.823081, 273900.881190, 275180.928531,
+  276460.914606, 277740.919081, 279020.967358, 280301.002010, 281581.054314, 
282861.097638, 284141.109728, 285421.181335,
+  286701.182750, 287981.231437, 289261.342952, 290541.328315, 291821.320901, 
293101.395969, 294381.402286, 295661.374812,
+  296941.357112, 298221.462446, 299501.443884, 300781.429735, 302061.512800, 
303341.459364, 304621.530042, 305901.507763,
+  307181.541613, 308461.506346, 309741.474814, 311021.458944, 312301.373851, 
313581.258984, 314861.222655, 316141.192042,
+  317421.179698, 318701.162346, 319981.143104, 321261.185706, 322541.120379, 
323821.065555, 325100.961639, 326380.920540,
+  327660.786948
+},
+// log K = 16
+{
+  47270.338530, 48512.804588, 49778.235006, 51066.679846, 52378.178287, 
53712.756179, 55070.430016, 56451.198432,
+  57855.044557, 59281.941569, 60731.855114, 62204.727445, 63700.498656, 
65219.087806, 66760.381754, 68324.278827,
+  69910.681844, 71519.442760, 73150.426782, 74803.469853, 76478.400456, 
78175.038414, 79893.184924, 81632.639705,
+  83393.214587, 85174.662642, 86976.739912, 88799.210643, 90641.829434, 
92504.355605, 94386.478165, 96287.925119,
+  98208.422967, 100147.694840, 102105.387259, 104081.226364, 106074.916897, 
108086.136683, 110114.560800, 112159.857579,
+  114221.724675, 116299.785348, 118393.731853, 120503.258946, 122627.958417, 
124767.540962, 126921.668848, 129090.003274,
+  131272.204811, 133467.918004, 135676.870426, 137898.595565, 140132.829694, 
142379.269230, 144637.536478, 146907.321805,
+  149188.299797, 151480.151490, 153782.571459, 156095.292246, 158417.834407, 
160749.983694, 163091.465145, 165441.984077,
+  167801.236994, 170168.903670, 172544.767987, 174928.494171, 177319.918928, 
179718.624717, 182124.426670, 184537.056818,
+  186956.260737, 189381.869001, 191813.551321, 194251.107949, 196694.280093, 
199142.910671, 201596.663557, 204055.453662,
+  206519.090402, 208987.322132, 211460.079506, 213936.962995, 216417.945039, 
218902.907481, 221391.594168, 223883.857806,
+  226379.519239, 228878.478995, 231380.515508, 233885.422831, 236393.251620, 
238903.734661, 241416.885791, 243932.462444,
+  246450.361381, 248970.497360, 251492.768859, 254017.059221, 256543.244066, 
259071.366198, 261601.098326, 264132.472390,
+  266665.477130, 269200.003838, 271735.898262, 274273.128446, 276811.699617, 
279351.334667, 281892.176977, 284434.126935,
+  286977.141855, 289520.993664, 292065.816286, 294611.452586, 297157.986206, 
299705.252956, 302253.226506, 304801.859484,
+  307351.170990, 309901.148395, 312451.594894, 315002.530644, 317553.961670, 
320105.951895, 322658.553440, 325211.299379,
+  327764.626147, 330318.311300, 332872.331048, 335426.696106, 337981.358498, 
340536.275072, 343091.446562, 345646.972942,
+  348202.757347, 350758.693296, 353314.808579, 355871.203754, 358427.702337, 
360984.491079, 363541.449032, 366098.493281,
+  368655.687887, 371213.030472, 373770.561193, 376328.233445, 378886.001706, 
381443.937538, 384001.856504, 386559.857503,
+  389117.926075, 391676.219835, 394234.530250, 396792.812601, 399351.291660, 
401909.794770, 404468.330377, 407026.964781,
+  409585.614496, 412144.407471, 414703.175858, 417261.980659, 419820.958264, 
422379.863083, 424938.751186, 427497.927620,
+  430056.993719, 432616.066348, 435175.266190, 437734.382772, 440293.531277, 
442852.711332, 445412.016423, 447971.502143,
+  450530.798765, 453090.167199, 455649.502729, 458208.951251, 460768.459694, 
463328.053373, 465887.570627, 468447.239271,
+  471006.792818, 473566.544068, 476126.194227, 478685.887605, 481245.577573, 
483805.348711, 486365.028302, 488924.844568,
+  491484.612666, 494044.321666, 496604.011864, 499163.799789, 501723.629793, 
504283.493711, 506843.290195, 509403.157856,
+  511962.990320, 514522.941350, 517082.791953, 519642.848656, 522202.833761, 
524762.686522, 527322.632758, 529882.529037,
+  532442.420851, 535002.422317, 537562.386309, 540122.357425, 542682.434171, 
545242.468829, 547802.466694, 550362.492718,
+  552922.461624, 555482.539495, 558042.531431, 560602.467437, 563162.648952, 
565722.750610, 568282.702877, 570842.810092,
+  573402.917604, 575962.999235, 578523.081937, 581083.162584, 583643.265455, 
586203.287883, 588763.260931, 591323.292027,
+  593883.252456, 596443.273935, 599003.353442, 601563.492606, 604123.421135, 
606683.438234, 609243.605174, 611803.638038,
+  614363.628593, 616923.690580, 619483.675256, 622043.744171, 624603.673246, 
627163.577236, 629723.677960, 632283.670157,
+  634843.692958, 637403.576097, 639963.511752, 642523.482942, 645083.419327, 
647643.330711, 650203.272467, 652763.290376,
+  655323.267522
+},
+// log K = 17
+{
+  94541.455324, 97026.403245, 99557.280165, 102134.178452, 104757.187033, 
107426.362674, 110141.726029, 112903.278005,
+  115710.975458, 118564.785344, 121464.629896, 124410.377345, 127401.910279, 
130439.087070, 133521.683723, 136649.499031,
+  139822.321627, 143039.848320, 146301.785984, 149607.844773, 152957.695333, 
156350.945326, 159787.233040, 163266.157476,
+  166787.301371, 170350.201319, 173954.353438, 177599.302692, 181284.526367, 
185009.507089, 188773.757457, 192576.638207,
+  196417.619925, 200296.089540, 204211.494530, 208163.221220, 212150.613086, 
216173.056150, 220229.879308, 224320.462264,
+  228444.146459, 232600.284426, 236788.187347, 241007.264352, 245256.725247, 
249535.938618, 253844.217048, 258180.876528,
+  262545.282090, 266936.686787, 271354.453768, 275797.958112, 280266.429404, 
284759.298770, 289275.861948, 293815.386243,
+  298377.390708, 302961.087179, 307565.895452, 312191.173759, 316836.362522, 
321500.721532, 326183.652758, 330884.723656,
+  335603.197219, 340338.617624, 345090.316855, 349857.860719, 354640.538362, 
359438.001710, 364249.638745, 369074.940533,
+  373913.351331, 378764.438864, 383627.767868, 388502.854215, 393389.259584, 
398286.483436, 403194.238263, 408111.948151,
+  413039.292303, 417975.909674, 422921.304084, 427875.195485, 432837.200959, 
437807.062429, 442784.399966, 447768.726662,
+  452760.032525, 457757.794185, 462761.810279, 467771.737196, 472787.226777, 
477808.283249, 482834.411114, 487865.521166,
+  492901.275392, 497941.616150, 502986.164281, 508034.749030, 513087.210697, 
518143.298172, 523202.692981, 528265.454984,
+  533331.384435, 538400.437943, 543472.278711, 548546.790172, 553623.737338, 
558703.047575, 563784.666463, 568868.524932,
+  573954.251207, 579041.935016, 584131.557779, 589222.910016, 594315.786547, 
599410.314112, 604506.227555, 609603.610432,
+  614702.124262, 619801.918486, 624902.903258, 630005.085347, 635107.990499, 
640211.847431, 645316.755403, 650422.424613,
+  655529.009211, 660636.166872, 665744.170495, 670852.806947, 675962.099161, 
681072.042796, 686182.458370, 691293.269596,
+  696404.763335, 701516.635253, 706628.965299, 711741.575313, 716854.769083, 
721968.220605, 727082.027784, 732196.114131,
+  737310.594653, 742425.339665, 747540.295793, 752655.505647, 757770.968129, 
762886.636891, 768002.527720, 773118.559142,
+  778234.871441, 783351.273881, 788467.944809, 793584.599211, 798701.497594, 
803818.484285, 808935.619316, 814053.067243,
+  819170.390229, 824287.946609, 829405.450988, 834523.054071, 839640.856519, 
844758.800102, 849876.719924, 854994.831607,
+  860112.896788, 865231.116455, 870349.453898, 875467.910756, 880586.444976, 
885705.102070, 890823.822978, 895942.513436,
+  901061.120048, 906179.716020, 911298.587548, 916417.583647, 921536.507373, 
926655.570709, 931774.690027, 936893.879961,
+  942012.956398, 947132.142141, 952251.503479, 957370.815448, 962490.012685, 
967609.521437, 972729.002837, 977848.339983,
+  982967.824448, 988087.392216, 993206.992691, 998326.635354, 1003446.304388, 
1008565.985024, 1013685.706678, 1018805.581548,
+  1023925.239073, 1029045.157502, 1034165.035124, 1039284.843524, 
1044404.676526, 1049524.620186, 1054644.441170, 1059764.409794,
+  1064884.318434, 1070004.338968, 1075124.268749, 1080244.471055, 
1085364.528193, 1090484.511927, 1095604.595114, 1100724.598478,
+  1105844.666500, 1110964.804168, 1116084.864159, 1121204.874577, 
1126325.062599, 1131445.152321, 1136565.301592, 1141685.638897,
+  1146805.806800, 1151925.790859, 1157045.932640, 1162166.112443, 
1167286.221698, 1172406.273611, 1177526.581875, 1182646.832239,
+  1187766.882661, 1192886.884388, 1198006.962839, 1203127.084377, 
1208246.959060, 1213366.976076, 1218486.763464, 1223606.758568,
+  1228726.721761, 1233846.593404, 1238966.815228, 1244086.865449, 
1249206.854850, 1254326.996667, 1259447.092239, 1264567.198081,
+  1269687.499091, 1274807.474194, 1279927.244161, 1285047.273317, 
1290167.090035, 1295287.035620, 1300406.888764, 1305526.667946,
+  1310646.540340
+},
+// log K = 18
+{
+  189083.688921, 194053.597973, 199115.342781, 204269.157017, 209515.195566, 
214853.553887, 220284.295855, 225807.399998,
+  231422.826900, 237130.469824, 242930.139690, 248821.634191, 254804.718978, 
260879.073467, 267044.289594, 273299.936993,
+  279645.549742, 286080.586199, 292604.521050, 299216.651567, 305916.376160, 
312702.908374, 319575.536730, 326533.420485,
+  333575.639430, 340701.388339, 347909.713977, 355199.642852, 362570.108280, 
370020.154080, 377548.652175, 385154.413997,
+  392836.374125, 400593.371525, 408424.172521, 416327.671123, 424302.406749, 
432347.292674, 440461.014310, 448642.241683,
+  456889.603709, 465201.946081, 473577.749277, 482015.802141, 490514.770444, 
499073.243760, 507689.794308, 516363.149230,
+  525091.902823, 533874.850510, 542710.434311, 551597.433198, 560534.241273, 
569520.019389, 578553.034265, 587632.180111,
+  596756.084542, 605923.440522, 615133.037840, 624383.626242, 633673.841133, 
643002.674316, 652368.578283, 661770.556501,
+  671207.605795, 680678.417768, 690181.818697, 699716.780471, 709282.084973, 
718876.864175, 728499.951143, 738150.280049,
+  747827.053968, 757529.236597, 767255.784062, 777006.100829, 786779.065927, 
796573.507429, 806388.828153, 816223.976258,
+  826078.608350, 835951.751956, 845842.493689, 855750.225870, 865674.190527, 
875613.809349, 885568.425527, 895537.272219,
+  905519.763307, 915515.310346, 925523.353103, 935543.172648, 945574.292664, 
955616.280042, 965668.428231, 975730.535479,
+  985802.055447, 995882.596628, 1005971.583108, 1016068.664992, 
1026173.649961, 1036285.774891, 1046404.955770, 1056530.588186,
+  1066662.648243, 1076800.535542, 1086944.269881, 1097093.058652, 
1107247.076183, 1117405.990557, 1127569.310917, 1137737.011345,
+  1147908.717700, 1158084.362020, 1168263.501029, 1178446.221907, 
1188632.206842, 1198821.131431, 1209013.020373, 1219207.570786,
+  1229404.854893, 1239604.423801, 1249806.433263, 1260010.619993, 
1270216.819018, 1280424.774173, 1290634.560600, 1300846.097203,
+  1311059.128630, 1321273.628402, 1331489.832488, 1341707.300774, 
1351925.947953, 1362145.802925, 1372366.802414, 1382588.547896,
+  1392811.264776, 1403035.227449, 1413259.922280, 1423485.322819, 
1433711.916106, 1443938.981360, 1454166.596984, 1464394.761679,
+  1474623.684212, 1484853.156422, 1495083.020146, 1505313.735890, 
1515544.731764, 1525776.086634, 1536007.742073, 1546239.679012,
+  1556472.212370, 1566704.988763, 1576937.965495, 1587171.446007, 
1597405.292186, 1607639.395274, 1617873.660775, 1628108.037273,
+  1638342.741799, 1648577.832828, 1658813.325573, 1669048.803784, 
1679284.679963, 1689520.927615, 1699757.050010, 1709993.263665,
+  1720229.557663, 1730466.102090, 1740702.604009, 1750939.618504, 
1761176.930182, 1771414.210528, 1781651.713742, 1791889.218503,
+  1802126.805234, 1812364.257333, 1822602.054007, 1832839.687792, 
1843077.871973, 1853315.944762, 1863554.202066, 1873792.633125,
+  1884030.883830, 1894269.182711, 1904507.712050, 1914746.469507, 
1924985.484753, 1935224.245956, 1945463.225215, 1955702.268883,
+  1965941.321722, 1976180.037370, 1986419.052784, 1996658.429579, 
2006897.450283, 2017136.904971, 2027376.240039, 2037615.549295,
+  2047855.197425, 2058094.765906, 2068334.279787, 2078573.820075, 
2088813.469730, 2099053.348620, 2109293.174176, 2119533.012704,
+  2129772.820132, 2140012.744709, 2150252.607739, 2160492.533017, 
2170732.406422, 2180972.400110, 2191212.436319, 2201452.637894,
+  2211692.339292, 2221932.460057, 2232172.746036, 2242412.892705, 
2252653.174824, 2262893.432271, 2273134.002270, 2283374.311008,
+  2293614.537881, 2303854.703732, 2314095.191374, 2324335.236186, 
2334575.624526, 2344816.313269, 2355056.224171, 2365296.361285,
+  2375537.048184, 2385777.280284, 2396017.222502, 2406257.145546, 
2416497.272190, 2426737.212210, 2436977.500856, 2447217.897426,
+  2457458.423605, 2467698.536697, 2477938.448096, 2488178.544726, 
2498418.640441, 2508658.160374, 2518898.274852, 2529138.509345,
+  2539378.310538, 2549617.989497, 2559857.504886, 2570097.363128, 
2580337.195261, 2590576.878833, 2600816.572159, 2611055.930087,
+  2621295.625004
+},
+  // clang-format on
diff --git a/include/datasketches/cuda/detail/hll/policy.cuh 
b/include/datasketches/cuda/detail/hll/policy.cuh
index 9efb8aa..506e6bd 100644
--- a/include/datasketches/cuda/detail/hll/policy.cuh
+++ b/include/datasketches/cuda/detail/hll/policy.cuh
@@ -29,6 +29,7 @@
 
 #include <cuda/experimental/__cuco/hash_functions.cuh>
 
+#include <datasketches/cuda/detail/hll/composite_finalizer.cuh>
 #include <datasketches/cuda/detail/hll/normalizing_hasher.cuh>
 
 namespace datasketches::cuda::detail::hll {
@@ -51,10 +52,7 @@ namespace datasketches::cuda::detail::hll {
 //! `slotNo = HllUtil::getLow26(coupon) & configKmask = h1 & ((1<<lgK)-1)` 
since
 //! lgK <= 21 < 26. The 62-cap matches `HllUtil::coupon` line 144.
 //!
-//! `finalize` is a no-op stub satisfying the cudax policy concept. The 
downstream
-//! `hll_sketch::get_estimate` bypasses cudax's `estimate()` path entirely (it
-//! D2H-copies the registers, runs a wider reduction, and applies the Composite
-//! estimator on host).
+//! The policy finalizer uses the DataSketches Composite estimator.
 template <class _Key>
 struct policy {
   // Per-key normalization matching datasketches-cpp's hll_sketch::update(...)
@@ -124,18 +122,14 @@ struct policy {
     return static_cast<::cuda::std::uint8_t>(__cap + 1);
   }
 
-  //! @brief No-op finalizer satisfying the cudax policy concept.
-  //!
-  //! @note Never invoked by `datasketches::cuda::hll_sketch`. Compute the
-  //! estimate via the Composite estimator on host-side after a D2H copy of the
-  //! register array. See `composite_finalizer.hpp`.
-  //!
-  //! @return 0 unconditionally.
-  [[nodiscard]] __host__ __device__ static constexpr ::cuda::std::size_t 
finalize(double,
-                                                                               
   int,
-                                                                               
   int) noexcept
+  //! @brief Applies the DataSketches Composite estimator.
+  [[nodiscard]] __host__ __device__ static double finalize(double z,
+                                                           int num_zeroes,
+                                                           int precision) 
noexcept
   {
-    return 0;
+    return composite_estimate(z,
+                              static_cast<::cuda::std::uint32_t>(num_zeroes),
+                              static_cast<::cuda::std::uint8_t>(precision));
   }
 };
 
diff --git a/test/hll/CMakeLists.txt b/test/hll/CMakeLists.txt
index 9e42e7e..b255b3e 100644
--- a/test/hll/CMakeLists.txt
+++ b/test/hll/CMakeLists.txt
@@ -46,6 +46,7 @@ endfunction()
 ConfigureTest(NORMALIZING_HASHER_TEST normalizing_hasher_test.cpp)
 ConfigureTest(POLICY_COMPILE_TEST policy_compile_test.cu)
 ConfigureTest(COMPOSITE_FINALIZER_TEST composite_finalizer_test.cpp)
+ConfigureTest(COMPOSITE_FINALIZER_DEVICE_TEST 
composite_finalizer_device_test.cu)
 ConfigureTest(REDUCTION_STATE_TEST reduction_state_test.cpp)
 ConfigureTest(PREAMBLE_TEST preamble_test.cpp)
 ConfigureTest(BASIC_SKETCH_TEST basic_sketch_test.cu)
diff --git a/test/hll/composite_finalizer_device_test.cu 
b/test/hll/composite_finalizer_device_test.cu
new file mode 100644
index 0000000..4adfbd2
--- /dev/null
+++ b/test/hll/composite_finalizer_device_test.cu
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <vector>
+
+#include <cuda_runtime.h>
+
+#include <thrust/copy.h>
+#include <thrust/device_vector.h>
+
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <datasketches/cuda/detail/hll/composite_finalizer.cuh>
+#include <datasketches/cuda/detail/hll/composite_interpolation_table.cuh>
+
+namespace {
+
+struct finalizer_case {
+  double z;
+  std::uint32_t zeroes;
+  std::uint8_t lg_k;
+};
+
+__global__ void finalize_kernel(const finalizer_case* cases, double* results, 
std::size_t size)
+{
+  const std::size_t index = blockIdx.x * blockDim.x + threadIdx.x;
+  if (index < size) {
+    const auto c   = cases[index];
+    results[index] = datasketches::cuda::detail::hll::composite_estimate(c.z, 
c.zeroes, c.lg_k);
+  }
+}
+
+double z_for_raw(double raw, std::uint8_t lg_k)
+{
+  const std::uint32_t config_k = 1u << lg_k;
+  double correction_factor;
+  if (lg_k == 4) {
+    correction_factor = 0.673;
+  } else if (lg_k == 5) {
+    correction_factor = 0.697;
+  } else if (lg_k == 6) {
+    correction_factor = 0.709;
+  } else {
+    correction_factor = 0.7213 / (1.0 + (1.079 / config_k));
+  }
+  return (correction_factor * config_k * config_k) / raw;
+}
+
+}  // namespace
+
+TEST_CASE("Composite finalizer host and device results agree", 
"[composite_finalizer][device]")
+{
+  namespace table = datasketches::cuda::detail::hll::composite_interpolation;
+  std::vector<finalizer_case> cases;
+
+  for (std::uint8_t lg_k = table::min_lg_k; lg_k <= table::max_lg_k; ++lg_k) {
+    const auto* x_values = table::x_values_for(lg_k);
+    const auto config_k  = std::uint32_t{1} << lg_k;
+    cases.push_back({static_cast<double>(config_k), config_k, lg_k});
+    cases.push_back({z_for_raw(x_values[0] * 0.5, lg_k), config_k, lg_k});
+    cases.push_back({z_for_raw(x_values[table::last_x_offset] * 2.0, lg_k), 0, 
lg_k});
+
+    for (std::uint32_t i = 0; i + 1 < table::num_x_values; ++i) {
+      const double midpoint = (x_values[i] + x_values[i + 1]) / 2.0;
+      cases.push_back({z_for_raw(midpoint, lg_k), config_k / 2, lg_k});
+    }
+  }
+
+  std::vector<double> expected(cases.size());
+  std::transform(cases.begin(), cases.end(), expected.begin(), [](const 
finalizer_case& c) {
+    return datasketches::cuda::detail::hll::composite_estimate(c.z, c.zeroes, 
c.lg_k);
+  });
+
+  thrust::device_vector<finalizer_case> device_cases = cases;
+  thrust::device_vector<double> device_results(cases.size());
+  constexpr int block_size = 256;
+  const int grid_size      = static_cast<int>((cases.size() + block_size - 1) 
/ block_size);
+  finalize_kernel<<<grid_size, 
block_size>>>(thrust::raw_pointer_cast(device_cases.data()),
+                                             
thrust::raw_pointer_cast(device_results.data()),
+                                             cases.size());
+  REQUIRE(cudaGetLastError() == cudaSuccess);
+
+  std::vector<double> actual(device_results.size());
+  thrust::copy(device_results.begin(), device_results.end(), actual.begin());
+  for (std::size_t i = 0; i < cases.size(); ++i) {
+    CAPTURE(i, cases[i].lg_k, cases[i].zeroes, cases[i].z, expected[i], 
actual[i]);
+    REQUIRE(actual[i] == 
Catch::Approx(expected[i]).epsilon(1e-12).margin(1e-9));
+  }
+}
diff --git a/test/hll/composite_finalizer_test.cpp 
b/test/hll/composite_finalizer_test.cpp
index c3381f1..badc7f5 100644
--- a/test/hll/composite_finalizer_test.cpp
+++ b/test/hll/composite_finalizer_test.cpp
@@ -17,15 +17,16 @@
  * under the License.
  */
 
-// Validates that `datasketches::cuda::detail::hll::composite_finalizer` 
produces the
+// Validates that `datasketches::cuda::detail::hll::composite_estimate` 
produces the
 // same result as `datasketches::hll_sketch::get_estimate()` when the CPU 
sketch
 // is forced into Composite mode (oooFlag=true).
 //
-// The test pulls (kxq0, kxq1, curMin, numAtCurMin) out of the CPU sketch's
-// serialized wire format and feeds them to our finalizer. To make the CPU side
-// also use Composite (instead of HIP), the FLAGS byte is patched to set the
-// OOO bit before deserialize.
+// The test pulls (kxq0, kxq1, curMin, numAtCurMin) out of the CPU sketch's 
serialized
+// wire format, derives the zero-register count, and feeds that state to our 
finalizer.
+// To make the CPU side also use Composite (instead of HIP), the FLAGS byte is 
patched
+// to set the OOO bit before deserialize.
 
+#include <cmath>
 #include <cstdint>
 #include <cstring>
 #include <random>
@@ -36,7 +37,12 @@
 
 #include <hll.hpp>
 
-#include <datasketches/cuda/detail/hll/composite_finalizer.hpp>
+#include <datasketches/cuda/detail/hll/composite_finalizer.cuh>
+#include <datasketches/cuda/detail/hll/composite_interpolation_table.cuh>
+
+#include <CompositeInterpolationXTable.hpp>
+#include <CubicInterpolation.hpp>
+#include <HarmonicNumbers.hpp>
 
 namespace {
 
@@ -67,7 +73,7 @@ cpu_state extract_state(const std::vector<uint8_t>& bytes)
 
 // Build an HLL_8 CPU sketch promoted into HLL mode by inserting `n` distinct
 // keys via `start_full_size=true`, then return:
-//   1. our composite_finalizer result, and
+//   1. our composite_estimate result, and
 //   2. CPU `get_estimate()` after patching the FLAGS byte to set OOO so that
 //      the CPU also returns Composite (not HIP).
 struct test_result {
@@ -92,8 +98,8 @@ test_result run(uint8_t lgK, uint64_t n, uint64_t seed)
   auto cpu_composite = ::datasketches::hll_sketch::deserialize(bytes.data(), 
bytes.size());
 
   return {
-    datasketches::cuda::detail::hll::composite_finalizer(
-      state.kxq0 + state.kxq1, state.cur_min, state.num_at_cur_min, lgK),
+    datasketches::cuda::detail::hll::composite_estimate(
+      state.kxq0 + state.kxq1, state.cur_min == 0 ? state.num_at_cur_min : 0u, 
lgK),
     cpu_composite.get_estimate(),
   };
 }
@@ -103,10 +109,10 @@ test_result run(uint8_t lgK, uint64_t n, uint64_t seed)
 TEST_CASE("composite_finalizer matches CPU getCompositeEstimate", 
"[composite_finalizer]")
 {
   using Catch::Approx;
-  // Cover lgK across the typical operating range and a few cardinality regimes
-  // to exercise the 0/cubic/linear/asymptote branches of the Composite 
blender.
-  for (uint8_t lgK : {uint8_t{8}, uint8_t{12}, uint8_t{16}}) {
-    for (uint64_t n : {uint64_t{50}, uint64_t{1'000}, uint64_t{100'000}, 
uint64_t{2'000'000}}) {
+  for (std::uint8_t lgK = 4; lgK <= 18; ++lgK) {
+    const std::uint64_t config_k = std::uint64_t{1} << lgK;
+    const std::uint64_t high_n   = config_k * 32 < 200'000 ? config_k * 32 : 
200'000;
+    for (std::uint64_t n : {config_k / 4, config_k * 2, high_n}) {
       auto r = run(lgK, n, /*seed=*/0xC0FFEE0042 ^ (uint64_t(lgK) << 40) ^ n);
       INFO("lgK=" << int(lgK) << " n=" << n);
       REQUIRE(r.our_estimate == 
Approx(r.cpu_composite_estimate).epsilon(1e-12));
@@ -124,7 +130,54 @@ TEST_CASE("composite_finalizer empty sketch yields 0", 
"[composite_finalizer]")
   // For all-zero registers: kxq0+kxq1 = configK, raw = 
correctionFactor*configK, and
   // raw is well below xArr[0] for typical lgK.
   const double kxq_sum = static_cast<double>(configK);
-  const double our     = datasketches::cuda::detail::hll::composite_finalizer(
-    kxq_sum, /*curMin=*/0u, /*numAtCurMin=*/configK, lgK);
+  const double our =
+    datasketches::cuda::detail::hll::composite_estimate(kxq_sum, 
/*num_zeroes=*/configK, lgK);
   REQUIRE(our == Approx(0.0));
 }
+
+TEST_CASE("host interpolation tables match DataSketches C++", 
"[composite_finalizer][table]")
+{
+  using Catch::Approx;
+  namespace local = datasketches::cuda::detail::hll::composite_interpolation;
+
+  for (std::uint8_t lg_k = local::min_lg_k; lg_k <= local::max_lg_k; ++lg_k) {
+    const double* expected = 
::datasketches::CompositeInterpolationXTable<>::get_x_arr(lg_k);
+    const double* actual   = local::x_values_for(lg_k);
+    REQUIRE(local::y_stride_for(lg_k) ==
+            
::datasketches::CompositeInterpolationXTable<>::get_y_stride(lg_k));
+
+    for (std::uint32_t i = 0; i < local::num_x_values; ++i) {
+      CAPTURE(lg_k, i);
+      REQUIRE(actual[i] == expected[i]);
+    }
+
+    const auto y_stride = local::y_stride_for(lg_k);
+    for (std::uint32_t i = 0; i + 1 < local::num_x_values; ++i) {
+      const double midpoint  = (actual[i] + actual[i + 1]) / 2.0;
+      const double reference = 
::datasketches::CubicInterpolation<>::usingXArrAndYStride(
+        expected, static_cast<int>(local::num_x_values), y_stride, midpoint);
+      const double result =
+        datasketches::cuda::detail::hll::interpolate_composite(actual, 
y_stride, midpoint);
+      CAPTURE(lg_k, i, midpoint);
+      REQUIRE(result == Approx(reference).epsilon(1e-12));
+    }
+  }
+}
+
+TEST_CASE("host harmonic and bitmap estimators match DataSketches C++",
+          "[composite_finalizer][bitmap]")
+{
+  using Catch::Approx;
+  for (std::uint8_t lg_k = 4; lg_k <= 18; ++lg_k) {
+    const std::uint32_t config_k = 1u << lg_k;
+    for (std::uint32_t zeroes : {config_k, config_k / 2, 1u}) {
+      const auto hits        = config_k - zeroes;
+      const double reference = 
::datasketches::HarmonicNumbers<>::getBitMapEstimate(config_k, hits);
+      CAPTURE(lg_k, zeroes);
+      REQUIRE(datasketches::cuda::detail::hll::bitmap_estimate(zeroes, lg_k) ==
+              Approx(reference).epsilon(1e-15));
+    }
+    REQUIRE(datasketches::cuda::detail::hll::bitmap_estimate(0, lg_k) ==
+            Approx(config_k * std::log(config_k / 0.5)).epsilon(1e-15));
+  }
+}
diff --git a/test/hll/policy_compile_test.cu b/test/hll/policy_compile_test.cu
index 1405146..ea240aa 100644
--- a/test/hll/policy_compile_test.cu
+++ b/test/hll/policy_compile_test.cu
@@ -49,6 +49,8 @@ static_assert(std::is_same_v<policy_t::hash_result_type, 
__uint128_t>,
               "policy::hash_result_type must be __uint128_t");
 static_assert(std::is_same_v<policy_t::register_type, ::std::int32_t>,
               "policy::register_type must be int32_t");
+static_assert(std::is_same_v<decltype(policy_t::finalize(1.0, 1, 4)), double>,
+              "policy::finalize must preserve fractional estimates");
 static_assert(
   std::is_same_v<datasketches::cuda::detail::hll::register_type, 
policy_t::register_type>,
   "detail::hll::register_type must match policy::register_type");


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to