merged from master
diff --git a/Makefile b/Makefile
index 5056a09..6cc3a54 100644
--- a/Makefile
+++ b/Makefile
@@ -4,17 +4,18 @@
 OBJS = src/base64.o src/common.o \
   src/kll_float_sketch_pg_functions.o src/kll_float_sketch_c_adapter.o \
   src/cpc_sketch_pg_functions.o src/cpc_sketch_c_adapter.o \
+  src/theta_sketch_pg_functions.o src/theta_sketch_c_adapter.o \
   src/frequent_strings_sketch_pg_functions.o src/frequent_strings_sketch_c_adapter.o
 
-# assume a copy or link sketches-core-cpp in the current dir
-CORE = sketches-core-cpp
+# assume a copy or link datasketches-cpp in the current dir
+CORE = datasketches-cpp
 CPC = $(CORE)/cpc/src
 OBJS += $(CPC)/cpc_sketch.o $(CPC)/fm85.o $(CPC)/fm85Compression.o $(CPC)/fm85Confidence.o $(CPC)/fm85Merging.o $(CPC)/fm85Util.o $(CPC)/iconEstimator.o $(CPC)/u32Table.o
 
-DATA = sql/datasketches_cpc_sketch.sql sql/datasketches_kll_float_sketch.sql
+DATA = sql/datasketches_cpc_sketch.sql sql/datasketches_kll_float_sketch.sql sql/datasketches_theta_sketch.sql sql/datasketches_frequent_strings_sketch.sql
 
 CXX = g++-8
-PG_CPPFLAGS = -I/usr/local/include -I$(CORE)/kll/include -I$(CORE)/common/include -I$(CORE)/cpc/include -I$(CORE)/fi/include
+PG_CPPFLAGS = -I/usr/local/include -I$(CORE)/kll/include -I$(CORE)/common/include -I$(CORE)/cpc/include -I$(CORE)/theta/include -I$(CORE)/fi/include
 SHLIB_LINK = -lstdc++ -L/usr/local/lib
 
 PG_CONFIG = pg_config
diff --git a/sql/datasketches_theta_sketch.sql b/sql/datasketches_theta_sketch.sql
new file mode 100644
index 0000000..2d1f7f0
--- /dev/null
+++ b/sql/datasketches_theta_sketch.sql
@@ -0,0 +1,127 @@
+-- Copyright 2019, Verizon Media.
+-- Licensed under the terms of the Apache License 2.0. See LICENSE file at the project root for terms.
+
+CREATE TYPE theta_sketch;
+
+CREATE OR REPLACE FUNCTION theta_sketch_in(cstring) RETURNS theta_sketch
+     AS '$libdir/datasketches', 'pg_sketch_in'
+     LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION theta_sketch_out(theta_sketch) RETURNS cstring
+     AS '$libdir/datasketches', 'pg_sketch_out'
+     LANGUAGE C STRICT IMMUTABLE;
+
+CREATE TYPE theta_sketch (
+    INPUT = theta_sketch_in,
+    OUTPUT = theta_sketch_out,
+    STORAGE = EXTERNAL
+);
+
+CREATE CAST (bytea as theta_sketch) WITHOUT FUNCTION AS ASSIGNMENT;
+CREATE CAST (theta_sketch as bytea) WITHOUT FUNCTION AS ASSIGNMENT;
+
+CREATE OR REPLACE FUNCTION theta_sketch_add_item(internal, anyelement) RETURNS internal
+    AS '$libdir/datasketches', 'pg_theta_sketch_add_item'
+    LANGUAGE C IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION theta_sketch_add_item(internal, anyelement, int) RETURNS internal
+    AS '$libdir/datasketches', 'pg_theta_sketch_add_item'
+    LANGUAGE C IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION theta_sketch_add_item(internal, anyelement, int, real) RETURNS internal
+    AS '$libdir/datasketches', 'pg_theta_sketch_add_item'
+    LANGUAGE C IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION theta_sketch_get_estimate(theta_sketch) RETURNS double precision
+    AS '$libdir/datasketches', 'pg_theta_sketch_get_estimate'
+    LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION theta_sketch_get_estimate_and_bounds(theta_sketch) RETURNS double precision[]
+    AS '$libdir/datasketches', 'pg_theta_sketch_get_estimate_and_bounds'
+    LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION theta_sketch_get_estimate_and_bounds(theta_sketch, int) RETURNS double precision[]
+    AS '$libdir/datasketches', 'pg_theta_sketch_get_estimate_and_bounds'
+    LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION theta_sketch_from_internal(internal) RETURNS theta_sketch
+    AS '$libdir/datasketches', 'pg_theta_sketch_from_internal'
+    LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION theta_sketch_get_estimate_from_internal(internal) RETURNS double precision
+    AS '$libdir/datasketches', 'pg_theta_sketch_get_estimate_from_internal'
+    LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION theta_sketch_to_string(theta_sketch) RETURNS TEXT
+    AS '$libdir/datasketches', 'pg_theta_sketch_to_string'
+    LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION theta_sketch_union_agg(internal, theta_sketch) RETURNS internal
+    AS '$libdir/datasketches', 'pg_theta_sketch_union_agg'
+    LANGUAGE C IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION theta_sketch_union_agg(internal, theta_sketch, int) RETURNS internal
+    AS '$libdir/datasketches', 'pg_theta_sketch_union_agg'
+    LANGUAGE C IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION theta_union_get_result(internal) RETURNS theta_sketch
+    AS '$libdir/datasketches', 'pg_theta_union_get_result'
+    LANGUAGE C STRICT IMMUTABLE;
+
+CREATE AGGREGATE theta_sketch_distinct(anyelement) (
+    sfunc = theta_sketch_add_item,
+    stype = internal,
+    finalfunc = theta_sketch_get_estimate_from_internal
+);
+
+CREATE AGGREGATE theta_sketch_distinct(anyelement, int) (
+    sfunc = theta_sketch_add_item,
+    stype = internal,
+    finalfunc = theta_sketch_get_estimate_from_internal
+);
+
+CREATE AGGREGATE theta_sketch_build(anyelement) (
+    sfunc = theta_sketch_add_item,
+    stype = internal,
+    finalfunc = theta_sketch_from_internal
+);
+
+CREATE AGGREGATE theta_sketch_build(anyelement, int) (
+    sfunc = theta_sketch_add_item,
+    stype = internal,
+    finalfunc = theta_sketch_from_internal
+);
+
+CREATE AGGREGATE theta_sketch_build(anyelement, int, real) (
+    sfunc = theta_sketch_add_item,
+    stype = internal,
+    finalfunc = theta_sketch_from_internal
+);
+
+CREATE AGGREGATE theta_sketch_union(theta_sketch) (
+    sfunc = theta_sketch_union_agg,
+    stype = internal,
+    finalfunc = theta_union_get_result
+);
+
+CREATE AGGREGATE theta_sketch_union(theta_sketch, int) (
+    sfunc = theta_sketch_union_agg,
+    stype = internal,
+    finalfunc = theta_union_get_result
+);
+
+CREATE OR REPLACE FUNCTION theta_sketch_union(theta_sketch, theta_sketch) RETURNS theta_sketch
+    AS '$libdir/datasketches', 'pg_theta_sketch_union'
+    LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION theta_sketch_union(theta_sketch, theta_sketch, int) RETURNS theta_sketch
+    AS '$libdir/datasketches', 'pg_theta_sketch_union'
+    LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION theta_sketch_intersection(theta_sketch, theta_sketch) RETURNS theta_sketch
+    AS '$libdir/datasketches', 'pg_theta_sketch_intersection'
+    LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION theta_sketch_a_not_b(theta_sketch, theta_sketch) RETURNS theta_sketch
+    AS '$libdir/datasketches', 'pg_theta_sketch_a_not_b'
+    LANGUAGE C STRICT IMMUTABLE;
diff --git a/src/allocator.h b/src/allocator.h
index 2388f98..2a1f1ef 100644
--- a/src/allocator.h
+++ b/src/allocator.h
@@ -40,13 +40,13 @@
     return static_cast<pointer>(p);
   }
 
-  void deallocate(pointer p, size_type) { pfree(p); }
+  void deallocate(pointer p, size_type) { if (p) pfree(p); }
 
   size_type max_size() const {
     return static_cast<size_type>(-1) / sizeof(T);
   }
 
-  void construct(pointer p, const value_type&& x) {
+  void construct(pointer p, const value_type& x) {
     new(p) value_type(std::forward<const value_type>(x));
   }
   void destroy(pointer p) { p->~value_type(); }
diff --git a/src/kll_float_sketch_c_adapter.cpp b/src/kll_float_sketch_c_adapter.cpp
index 12accc7..a73ba23 100644
--- a/src/kll_float_sketch_c_adapter.cpp
+++ b/src/kll_float_sketch_c_adapter.cpp
@@ -10,7 +10,7 @@
 
 #include <kll_sketch.hpp>
 
-typedef datasketches::kll_sketch<float, palloc_allocator<void>> kll_float_sketch;
+typedef datasketches::kll_sketch<float, std::less<float>, datasketches::serde<float>, palloc_allocator<float>> kll_float_sketch;
 
 void* kll_float_sketch_new(unsigned k) {
   try {
@@ -72,7 +72,7 @@
 void kll_float_sketch_to_string(const void* sketchptr, char* buffer, unsigned length) {
   try {
     std::stringstream s;
-    s << *(static_cast<const kll_float_sketch*>(sketchptr));
+    static_cast<const kll_float_sketch*>(sketchptr)->to_stream(s);
     snprintf(buffer, length, s.str().c_str());
   } catch (std::exception& e) {
     elog(ERROR, e.what());
@@ -93,8 +93,7 @@
 
 void* kll_float_sketch_deserialize(const char* buffer, unsigned length) {
   try {
-    auto ptr = kll_float_sketch::deserialize(buffer, length);
-    return ptr.release();
+    return new (palloc(sizeof(kll_float_sketch))) kll_float_sketch(kll_float_sketch::deserialize(buffer, length));
   } catch (std::exception& e) {
     elog(ERROR, e.what());
   }
diff --git a/src/theta_sketch_c_adapter.cpp b/src/theta_sketch_c_adapter.cpp
new file mode 100644
index 0000000..b99485c
--- /dev/null
+++ b/src/theta_sketch_c_adapter.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2019, Verizon Media.
+ * Licensed under the terms of the Apache License 2.0. See LICENSE file at the project root for terms.
+ */
+
+#include "theta_sketch_c_adapter.h"
+#include "allocator.h"
+
+extern "C" {
+#include <postgres.h>
+}
+
+#include <sstream>
+
+#include <theta_sketch.hpp>
+#include <theta_union.hpp>
+#include <theta_intersection.hpp>
+#include <theta_a_not_b.hpp>
+
+typedef datasketches::theta_sketch_alloc<palloc_allocator<void>> theta_sketch_pg;
+typedef datasketches::update_theta_sketch_alloc<palloc_allocator<void>> update_theta_sketch_pg;
+typedef datasketches::compact_theta_sketch_alloc<palloc_allocator<void>> compact_theta_sketch_pg;
+typedef datasketches::theta_union_alloc<palloc_allocator<void>> theta_union_pg;
+typedef datasketches::theta_intersection_alloc<palloc_allocator<void>> theta_intersection_pg;
+typedef datasketches::theta_a_not_b_alloc<palloc_allocator<void>> theta_a_not_b_pg;
+
+void* theta_sketch_new_default() {
+  try {
+    return new (palloc(sizeof(update_theta_sketch_pg))) update_theta_sketch_pg(update_theta_sketch_pg::builder().build());
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+void* theta_sketch_new_lgk(unsigned lg_k) {
+  try {
+    return new (palloc(sizeof(update_theta_sketch_pg))) update_theta_sketch_pg(update_theta_sketch_pg::builder().set_lg_k(lg_k).build());
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+void* theta_sketch_new_lgk_p(unsigned lg_k, float p) {
+  try {
+    return new (palloc(sizeof(update_theta_sketch_pg))) update_theta_sketch_pg(update_theta_sketch_pg::builder().set_lg_k(lg_k).set_p(p).build());
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+void theta_sketch_delete(void* sketchptr) {
+  try {
+    static_cast<theta_sketch_pg*>(sketchptr)->~theta_sketch_pg();
+    pfree(sketchptr);
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+void theta_sketch_update(void* sketchptr, const void* data, unsigned length) {
+  try {
+    static_cast<update_theta_sketch_pg*>(sketchptr)->update(data, length);
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+void* theta_sketch_compact(void* sketchptr) {
+  try {
+    auto newptr = new (palloc(sizeof(compact_theta_sketch_pg))) compact_theta_sketch_pg(static_cast<update_theta_sketch_pg*>(sketchptr)->compact());
+    static_cast<update_theta_sketch_pg*>(sketchptr)->~update_theta_sketch_pg();
+    pfree(sketchptr);
+    return newptr;
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+double theta_sketch_get_estimate(const void* sketchptr) {
+  try {
+    return static_cast<const theta_sketch_pg*>(sketchptr)->get_estimate();
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+Datum* theta_sketch_get_estimate_and_bounds(const void* sketchptr, unsigned num_std_devs) {
+  try {
+    Datum* est_and_bounds = (Datum*) palloc(sizeof(Datum) * 3);
+    est_and_bounds[0] = Float8GetDatum(static_cast<const theta_sketch_pg*>(sketchptr)->get_estimate());
+    est_and_bounds[1] = Float8GetDatum(static_cast<const theta_sketch_pg*>(sketchptr)->get_lower_bound(num_std_devs));
+    est_and_bounds[2] = Float8GetDatum(static_cast<const theta_sketch_pg*>(sketchptr)->get_upper_bound(num_std_devs));
+    return est_and_bounds;
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+void theta_sketch_to_string(const void* sketchptr, char* buffer, unsigned length) {
+  try {
+    std::stringstream s;
+    static_cast<const theta_sketch_pg*>(sketchptr)->to_stream(s);
+    snprintf(buffer, length, s.str().c_str());
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+void* theta_sketch_serialize(const void* sketchptr) {
+  try {
+    auto data = static_cast<const theta_sketch_pg*>(sketchptr)->serialize(VARHDRSZ);
+    bytea* buffer = (bytea*) data.first.release();
+    const size_t length = data.second;
+    SET_VARSIZE(buffer, length);
+    return buffer;
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+void* theta_sketch_deserialize(const char* buffer, unsigned length) {
+  try {
+    auto ptr = theta_sketch_pg::deserialize(buffer, length);
+    return ptr.release();
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+void* theta_union_new_default() {
+  try {
+    return new (palloc(sizeof(theta_union_pg))) theta_union_pg(theta_union_pg::builder().build());
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+void* theta_union_new(unsigned lg_k) {
+  try {
+    return new (palloc(sizeof(theta_union_pg))) theta_union_pg(theta_union_pg::builder().set_lg_k(lg_k).build());
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+void theta_union_delete(void* unionptr) {
+  try {
+    static_cast<theta_union_pg*>(unionptr)->~theta_union_pg();
+    pfree(unionptr);
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+void theta_union_update(void* unionptr, const void* sketchptr) {
+  try {
+    static_cast<theta_union_pg*>(unionptr)->update(*static_cast<const theta_sketch_pg*>(sketchptr));
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+void* theta_union_get_result(const void* unionptr) {
+  try {
+    return new (palloc(sizeof(compact_theta_sketch_pg))) compact_theta_sketch_pg(static_cast<const theta_union_pg*>(unionptr)->get_result());
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+void* theta_intersection_new_default() {
+  try {
+    return new (palloc(sizeof(theta_intersection_pg))) theta_intersection_pg;
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+void theta_intersection_delete(void* interptr) {
+  try {
+    static_cast<theta_intersection_pg*>(interptr)->~theta_intersection_pg();
+    pfree(interptr);
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+void theta_intersection_update(void* interptr, const void* sketchptr) {
+  try {
+    static_cast<theta_intersection_pg*>(interptr)->update(*static_cast<const theta_sketch_pg*>(sketchptr));
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+void* theta_intersection_get_result(const void* interptr) {
+  try {
+    return new (palloc(sizeof(compact_theta_sketch_pg))) compact_theta_sketch_pg(static_cast<const theta_intersection_pg*>(interptr)->get_result());
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
+
+void* theta_a_not_b(const void* sketchptr1, const void* sketchptr2) {
+  try {
+    theta_a_not_b_pg a_not_b;
+    return new (palloc(sizeof(compact_theta_sketch_pg))) compact_theta_sketch_pg(a_not_b.compute(
+      *static_cast<const theta_sketch_pg*>(sketchptr1),
+      *static_cast<const theta_sketch_pg*>(sketchptr2)
+    ));
+  } catch (std::exception& e) {
+    elog(ERROR, e.what());
+  }
+}
diff --git a/src/theta_sketch_c_adapter.h b/src/theta_sketch_c_adapter.h
new file mode 100644
index 0000000..75c7502
--- /dev/null
+++ b/src/theta_sketch_c_adapter.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019, Verizon Media.
+ * Licensed under the terms of the Apache License 2.0. See LICENSE file at the project root for terms.
+ */
+
+#ifndef THETA_SKETCH_C_ADAPTER_H
+#define THETA_SKETCH_C_ADAPTER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <postgres.h>
+
+void* theta_sketch_new_default();
+void* theta_sketch_new_lgk(unsigned lg_k);
+void* theta_sketch_new_lgk_p(unsigned lg_k, float p);
+void theta_sketch_delete(void* sketchptr);
+
+void theta_sketch_update(void* sketchptr, const void* data, unsigned length);
+void* theta_sketch_compact(void* sketchptr);
+void theta_sketch_union(void* sketchptr1, const void* sketchptr2);
+double theta_sketch_get_estimate(const void* sketchptr);
+Datum* theta_sketch_get_estimate_and_bounds(const void* sketchptr, unsigned num_std_devs);
+void theta_sketch_to_string(const void* sketchptr, char* buffer, unsigned length);
+
+void* theta_sketch_serialize(const void* sketchptr);
+void* theta_sketch_deserialize(const char* buffer, unsigned length);
+
+void* theta_union_new_default();
+void* theta_union_new(unsigned lg_k);
+void theta_union_delete(void* unionptr);
+void theta_union_update(void* unionptr, const void* sketchptr);
+void* theta_union_get_result(const void* unionptr);
+
+void* theta_intersection_new_default();
+void theta_intersection_delete(void* interptr);
+void theta_intersection_update(void* interptr, const void* sketchptr);
+void* theta_intersection_get_result(const void* interptr);
+
+void* theta_a_not_b(const void* sketchptr1, const void* sketchptr2);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/theta_sketch_pg_functions.c b/src/theta_sketch_pg_functions.c
new file mode 100644
index 0000000..899817a
--- /dev/null
+++ b/src/theta_sketch_pg_functions.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright 2019, Verizon Media.
+ * Licensed under the terms of the Apache License 2.0. See LICENSE file at the project root for terms.
+ */
+
+#include <postgres.h>
+#include <fmgr.h>
+#include <utils/lsyscache.h>
+#include <utils/builtins.h>
+#include <utils/array.h>
+#include <catalog/pg_type.h>
+
+#include "theta_sketch_c_adapter.h"
+#include "base64.h"
+
+/* PG_FUNCTION_INFO_V1 macro to pass functions to postgres */
+PG_FUNCTION_INFO_V1(pg_theta_sketch_add_item);
+PG_FUNCTION_INFO_V1(pg_theta_sketch_get_estimate);
+PG_FUNCTION_INFO_V1(pg_theta_sketch_get_estimate_and_bounds);
+PG_FUNCTION_INFO_V1(pg_theta_sketch_to_string);
+PG_FUNCTION_INFO_V1(pg_theta_sketch_union_agg);
+PG_FUNCTION_INFO_V1(pg_theta_sketch_from_internal);
+PG_FUNCTION_INFO_V1(pg_theta_sketch_get_estimate_from_internal);
+PG_FUNCTION_INFO_V1(pg_theta_union_get_result);
+PG_FUNCTION_INFO_V1(pg_theta_sketch_union);
+PG_FUNCTION_INFO_V1(pg_theta_sketch_intersection);
+PG_FUNCTION_INFO_V1(pg_theta_sketch_a_not_b);
+
+/* function declarations */
+Datum pg_theta_sketch_recv(PG_FUNCTION_ARGS);
+Datum pg_theta_sketch_send(PG_FUNCTION_ARGS);
+Datum pg_theta_sketch_add_item(PG_FUNCTION_ARGS);
+Datum pg_theta_sketch_get_estimate(PG_FUNCTION_ARGS);
+Datum pg_theta_sketch_get_estimate_and_bounds(PG_FUNCTION_ARGS);
+Datum pg_theta_sketch_to_string(PG_FUNCTION_ARGS);
+Datum pg_theta_sketch_union_agg(PG_FUNCTION_ARGS);
+Datum pg_theta_sketch_from_internal(PG_FUNCTION_ARGS);
+Datum pg_theta_sketch_get_estimate_from_internal(PG_FUNCTION_ARGS);
+Datum pg_theta_union_get_result(PG_FUNCTION_ARGS);
+Datum pg_theta_sketch_union(PG_FUNCTION_ARGS);
+Datum pg_theta_sketch_intersection(PG_FUNCTION_ARGS);
+Datum pg_theta_sketch_a_not_b(PG_FUNCTION_ARGS);
+
+Datum pg_theta_sketch_add_item(PG_FUNCTION_ARGS) {
+  void* sketchptr;
+  int lg_k;
+  float p;
+
+  // anyelement
+  Oid   element_type;
+  Datum element;
+  int16 typlen;
+  bool  typbyval;
+  char  typalign;
+
+  MemoryContext oldcontext;
+  MemoryContext aggcontext;
+
+  if (PG_ARGISNULL(0) && PG_ARGISNULL(1)) {
+    PG_RETURN_NULL();
+  } else if (PG_ARGISNULL(1)) {
+    PG_RETURN_POINTER(PG_GETARG_POINTER(0)); // no update value. return unmodified state
+  }
+
+  if (!AggCheckCallContext(fcinfo, &aggcontext)) {
+    elog(ERROR, "theta_sketch_add_item called in non-aggregate context");
+  }
+  oldcontext = MemoryContextSwitchTo(aggcontext);
+
+  if (PG_ARGISNULL(0)) {
+    lg_k = PG_GETARG_INT32(2);
+    p = PG_GETARG_FLOAT4(3);
+    if (lg_k) {
+      sketchptr = p ? theta_sketch_new_lgk_p(lg_k, p) : theta_sketch_new_lgk(lg_k);
+    } else {
+      sketchptr = theta_sketch_new_default();
+    }
+  } else {
+    sketchptr = PG_GETARG_POINTER(0);
+  }
+
+  element_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
+  element = PG_GETARG_DATUM(1);
+  get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
+  if (typlen == -1) {
+    // varlena
+    theta_sketch_update(sketchptr, VARDATA_ANY(element), VARSIZE_ANY_EXHDR(element));
+  } else if (typbyval) {
+    // fixed-length passed by value
+    theta_sketch_update(sketchptr, &element, typlen);
+  } else {
+    // fixed-length passed by reference
+    theta_sketch_update(sketchptr, (void*)element, typlen);
+  }
+
+  MemoryContextSwitchTo(oldcontext);
+
+  PG_RETURN_POINTER(sketchptr);
+}
+
+Datum pg_theta_sketch_get_estimate(PG_FUNCTION_ARGS) {
+  const bytea* bytes_in;
+  void* sketchptr;
+  double estimate;
+  bytes_in = PG_GETARG_BYTEA_P(0);
+  sketchptr = theta_sketch_deserialize(VARDATA(bytes_in), VARSIZE(bytes_in) - VARHDRSZ);
+  estimate = theta_sketch_get_estimate(sketchptr);
+  theta_sketch_delete(sketchptr);
+  PG_RETURN_FLOAT8(estimate);
+}
+
+Datum pg_theta_sketch_get_estimate_and_bounds(PG_FUNCTION_ARGS) {
+  const bytea* bytes_in;
+  void* sketchptr;
+  int num_std_devs;
+
+  // output array
+  Datum* est_and_bounds;
+  ArrayType* arr_out;
+  int16 elmlen_out;
+  bool elmbyval_out;
+  char elmalign_out;
+
+  bytes_in = PG_GETARG_BYTEA_P(0);
+  sketchptr = theta_sketch_deserialize(VARDATA(bytes_in), VARSIZE(bytes_in) - VARHDRSZ);
+  num_std_devs = PG_GETARG_INT32(1);
+  if (num_std_devs == 0) num_std_devs = 1; // default
+  est_and_bounds = theta_sketch_get_estimate_and_bounds(sketchptr, num_std_devs);
+  theta_sketch_delete(sketchptr);
+
+  // construct output array
+  get_typlenbyvalalign(FLOAT8OID, &elmlen_out, &elmbyval_out, &elmalign_out);
+  arr_out = construct_array(est_and_bounds, 3, FLOAT8OID, elmlen_out, elmbyval_out, elmalign_out);
+  PG_RETURN_ARRAYTYPE_P(arr_out);
+}
+
+Datum pg_theta_sketch_to_string(PG_FUNCTION_ARGS) {
+  const bytea* bytes_in;
+  void* sketchptr;
+  char str[1024];
+  bytes_in = PG_GETARG_BYTEA_P(0);
+  sketchptr = theta_sketch_deserialize(VARDATA(bytes_in), VARSIZE(bytes_in) - VARHDRSZ);
+  theta_sketch_to_string(sketchptr, str, 1024);
+  theta_sketch_delete(sketchptr);
+  PG_RETURN_TEXT_P(cstring_to_text(str));
+}
+
+Datum pg_theta_sketch_union_agg(PG_FUNCTION_ARGS) {
+  void* unionptr;
+  bytea* sketch_bytes;
+  void* sketchptr;
+  int lg_k;
+
+  MemoryContext oldcontext;
+  MemoryContext aggcontext;
+
+  if (PG_ARGISNULL(0) && PG_ARGISNULL(1)) {
+    PG_RETURN_NULL();
+  } else if (PG_ARGISNULL(1)) {
+    PG_RETURN_POINTER(PG_GETARG_POINTER(0)); // no update value. return unmodified state
+  }
+
+  if (!AggCheckCallContext(fcinfo, &aggcontext)) {
+    elog(ERROR, "theta_sketch_merge called in non-aggregate context");
+  }
+  oldcontext = MemoryContextSwitchTo(aggcontext);
+
+  if (PG_ARGISNULL(0)) {
+    lg_k = PG_GETARG_INT32(2);
+    unionptr = lg_k ? theta_union_new(lg_k) : theta_union_new_default();
+  } else {
+    unionptr = PG_GETARG_POINTER(0);
+  }
+
+  sketch_bytes = PG_GETARG_BYTEA_P(1);
+  sketchptr = theta_sketch_deserialize(VARDATA(sketch_bytes), VARSIZE(sketch_bytes) - VARHDRSZ);
+  theta_union_update(unionptr, sketchptr);
+  theta_sketch_delete(sketchptr);
+
+  MemoryContextSwitchTo(oldcontext);
+
+  PG_RETURN_POINTER(unionptr);
+}
+
+Datum pg_theta_sketch_from_internal(PG_FUNCTION_ARGS) {
+  void* sketchptr;
+  bytea* bytes_out;
+
+  MemoryContext oldcontext;
+  MemoryContext aggcontext;
+
+  if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+
+  if (!AggCheckCallContext(fcinfo, &aggcontext)) {
+    elog(ERROR, "theta_sketch_from_internal called in non-aggregate context");
+  }
+  oldcontext = MemoryContextSwitchTo(aggcontext);
+
+  sketchptr = PG_GETARG_POINTER(0);
+  sketchptr = theta_sketch_compact(sketchptr);
+  bytes_out = theta_sketch_serialize(sketchptr);
+  theta_sketch_delete(sketchptr);
+
+  MemoryContextSwitchTo(oldcontext);
+
+  PG_RETURN_BYTEA_P(bytes_out);
+}
+
+Datum pg_theta_sketch_get_estimate_from_internal(PG_FUNCTION_ARGS) {
+  void* sketchptr;
+  double estimate;
+
+  MemoryContext oldcontext;
+  MemoryContext aggcontext;
+
+  if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+
+  if (!AggCheckCallContext(fcinfo, &aggcontext)) {
+    elog(ERROR, "theta_sketch_from_internal called in non-aggregate context");
+  }
+  oldcontext = MemoryContextSwitchTo(aggcontext);
+
+  sketchptr = PG_GETARG_POINTER(0);
+  estimate = theta_sketch_get_estimate(sketchptr);
+  theta_sketch_delete(sketchptr);
+
+  MemoryContextSwitchTo(oldcontext);
+
+  PG_RETURN_FLOAT8(estimate);
+}
+
+Datum pg_theta_union_get_result(PG_FUNCTION_ARGS) {
+  void* unionptr;
+  void* sketchptr;
+  bytea* bytes_out;
+
+  MemoryContext oldcontext;
+  MemoryContext aggcontext;
+
+  if (PG_ARGISNULL(0)) PG_RETURN_NULL();
+
+  if (!AggCheckCallContext(fcinfo, &aggcontext)) {
+    elog(ERROR, "theta_union_get_result called in non-aggregate context");
+  }
+  oldcontext = MemoryContextSwitchTo(aggcontext);
+
+  unionptr = PG_GETARG_POINTER(0);
+  sketchptr = theta_union_get_result(unionptr);
+  bytes_out = theta_sketch_serialize(sketchptr);
+  theta_sketch_delete(sketchptr);
+  theta_union_delete(unionptr);
+
+  MemoryContextSwitchTo(oldcontext);
+
+  PG_RETURN_BYTEA_P(bytes_out);
+}
+
+Datum pg_theta_sketch_union(PG_FUNCTION_ARGS) {
+  const bytea* bytes_in1;
+  const bytea* bytes_in2;
+  void* sketchptr1;
+  void* sketchptr2;
+  void* unionptr;
+  void* sketchptr;
+  bytea* bytes_out;
+  int lg_k;
+  
+  lg_k = PG_GETARG_INT32(2);
+  unionptr = lg_k ? theta_union_new(lg_k) : theta_union_new_default();
+  if (!PG_ARGISNULL(0)) {
+    bytes_in1 = PG_GETARG_BYTEA_P(0);
+    sketchptr1 = theta_sketch_deserialize(VARDATA(bytes_in1), VARSIZE(bytes_in1) - VARHDRSZ);
+    theta_union_update(unionptr, sketchptr1);
+    theta_sketch_delete(sketchptr1);
+  }
+  if (!PG_ARGISNULL(1)) {
+    bytes_in2 = PG_GETARG_BYTEA_P(1);
+    sketchptr2 = theta_sketch_deserialize(VARDATA(bytes_in2), VARSIZE(bytes_in2) - VARHDRSZ);
+    theta_union_update(unionptr, sketchptr2);
+    theta_sketch_delete(sketchptr2);
+  }
+  sketchptr = theta_union_get_result(unionptr);
+  theta_union_delete(unionptr);
+  bytes_out = theta_sketch_serialize(sketchptr);
+  theta_sketch_delete(sketchptr);
+  PG_RETURN_BYTEA_P(bytes_out);
+}
+
+Datum pg_theta_sketch_intersection(PG_FUNCTION_ARGS) {
+  const bytea* bytes_in1;
+  const bytea* bytes_in2;
+  void* sketchptr1;
+  void* sketchptr2;
+  void* interptr;
+  void* sketchptr;
+  bytea* bytes_out;
+  
+  interptr = theta_intersection_new_default();
+  if (!PG_ARGISNULL(0)) {
+    bytes_in1 = PG_GETARG_BYTEA_P(0);
+    sketchptr1 = theta_sketch_deserialize(VARDATA(bytes_in1), VARSIZE(bytes_in1) - VARHDRSZ);
+    theta_intersection_update(interptr, sketchptr1);
+    theta_sketch_delete(sketchptr1);
+  }
+  if (!PG_ARGISNULL(1)) {
+    bytes_in2 = PG_GETARG_BYTEA_P(1);
+    sketchptr2 = theta_sketch_deserialize(VARDATA(bytes_in2), VARSIZE(bytes_in2) - VARHDRSZ);
+    theta_intersection_update(interptr, sketchptr2);
+    theta_sketch_delete(sketchptr2);
+  }
+  sketchptr = theta_intersection_get_result(interptr);
+  theta_intersection_delete(interptr);
+  bytes_out = theta_sketch_serialize(sketchptr);
+  theta_sketch_delete(sketchptr);
+  PG_RETURN_BYTEA_P(bytes_out);
+}
+
+Datum pg_theta_sketch_a_not_b(PG_FUNCTION_ARGS) {
+  const bytea* bytes_in1;
+  const bytea* bytes_in2;
+  void* sketchptr1;
+  void* sketchptr2;
+  void* sketchptr;
+  bytea* bytes_out;
+
+  if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) {
+    elog(ERROR, "theta_a_not_b expects two valid theta sketches");
+  }
+
+  bytes_in1 = PG_GETARG_BYTEA_P(0);
+  sketchptr1 = theta_sketch_deserialize(VARDATA(bytes_in1), VARSIZE(bytes_in1) - VARHDRSZ);
+  bytes_in2 = PG_GETARG_BYTEA_P(1);
+  sketchptr2 = theta_sketch_deserialize(VARDATA(bytes_in2), VARSIZE(bytes_in2) - VARHDRSZ);
+  sketchptr = theta_a_not_b(sketchptr1, sketchptr2);
+  theta_sketch_delete(sketchptr1);
+  theta_sketch_delete(sketchptr2);
+  bytes_out = theta_sketch_serialize(sketchptr);
+  theta_sketch_delete(sketchptr);
+  PG_RETURN_BYTEA_P(bytes_out);
+}