##! Functions to create and manipulate probabilistic cardinality counters.

%%{
#include "zeek/probabilistic/CardinalityCounter.h"
#include "zeek/OpaqueVal.h"

using namespace zeek::probabilistic;
%%}

module GLOBAL;

## Initializes a probabilistic cardinality counter that uses the HyperLogLog
## algorithm.
##
## err: the desired error rate (e.g. 0.01).
##
## confidence: the desired confidence for the error rate (e.g., 0.95).
##
## Returns: a HLL cardinality handle.
##
## .. zeek:see:: hll_cardinality_estimate hll_cardinality_merge_into hll_cardinality_add
##    hll_cardinality_copy
function hll_cardinality_init%(err: double, confidence: double%): opaque of cardinality
	%{
	auto* c = new zeek::probabilistic::detail::CardinalityCounter(err, confidence);
	auto cv = zeek::make_intrusive<zeek::CardinalityVal>(c);

	return cv;
	%}

## Adds an element to a HyperLogLog cardinality counter.
##
## handle: the HLL handle.
##
## elem: the element to add.
##
## Returns: true on success.
##
## .. zeek:see:: hll_cardinality_estimate hll_cardinality_merge_into
##    hll_cardinality_init hll_cardinality_copy
function hll_cardinality_add%(handle: opaque of cardinality, elem: any%): bool
	%{
	auto* cv = static_cast<CardinalityVal*>(handle);

	if ( ! cv->Type() && ! cv->Typify(elem->GetType()) )
		{
		reporter->Error("failed to set HLL type");
		return zeek::val_mgr->False();
		}

	else if ( ! same_type(cv->Type(), elem->GetType()) )
		{
		reporter->Error("incompatible HLL data type");
		return zeek::val_mgr->False();
		}

	cv->Add(elem);
	return zeek::val_mgr->True();
	%}

## Merges a HLL cardinality counter into another.
##
## .. note:: The same restrictions as for Bloom filter merging apply,
##    see :zeek:id:`bloomfilter_merge`.
##
## handle1: the first HLL handle, which will contain the merged result.
##
## handle2: the second HLL handle, which will be merged into the first.
##
## Returns: true on success.
##
## .. zeek:see:: hll_cardinality_estimate  hll_cardinality_add
##    hll_cardinality_init hll_cardinality_copy
function hll_cardinality_merge_into%(handle1: opaque of cardinality, handle2: opaque of cardinality%): bool
	%{
	auto* v1 = static_cast<CardinalityVal*>(handle1);
	auto* v2 = static_cast<CardinalityVal*>(handle2);

	if ( (v1->Type() != v2->Type()) && // both 0 is ok
	     (v1->Type() != nullptr) && // any one 0 also is ok
	     (v2->Type() != nullptr) &&
	     ! same_type(v1->Type(), v2->Type()) )
		{
		reporter->Error("incompatible HLL types");
		return zeek::val_mgr->False();
		}

	zeek::probabilistic::detail::CardinalityCounter* h1 = v1->Get();
	zeek::probabilistic::detail::CardinalityCounter* h2 = v2->Get();

	bool res = h1->Merge(h2);
	if ( ! res )
		{
		reporter->Error("Cardinality counters with different parameters cannot be merged");
		return zeek::val_mgr->False();
		}

	return zeek::val_mgr->True();
	%}

## Estimate the current cardinality of an HLL cardinality counter.
##
## handle: the HLL handle.
##
## Returns: the cardinality estimate. Returns -1.0 if the counter is empty.
##
## .. zeek:see:: hll_cardinality_merge_into hll_cardinality_add
##    hll_cardinality_init hll_cardinality_copy
function hll_cardinality_estimate%(handle: opaque of cardinality%): double
	%{
	auto* cv = static_cast<CardinalityVal*>(handle);
	zeek::probabilistic::detail::CardinalityCounter* h = cv->Get();

	double estimate = h->Size();

	return zeek::make_intrusive<zeek::DoubleVal>(estimate);
	%}

## Copy a HLL cardinality counter.
##
## handle: cardinality counter to copy.
##
## Returns: copy of handle.
##
## .. zeek:see:: hll_cardinality_estimate hll_cardinality_merge_into hll_cardinality_add
##    hll_cardinality_init
function hll_cardinality_copy%(handle: opaque of cardinality%): opaque of cardinality
	%{
	auto* cv = static_cast<CardinalityVal*>(handle);
	zeek::probabilistic::detail::CardinalityCounter* h = cv->Get();
	auto* h2 = new zeek::probabilistic::detail::CardinalityCounter(*h);
	auto out = zeek::make_intrusive<zeek::CardinalityVal>(h2);

	return out;
	%}
