Adds binary code lib for coding various symbols

Adds a variable length binary code library for
coding various symbols for typical use in headers.

The main codes implemented are:
1. Coding a symbol from an n-ary alphabet using a
quasi-uniform code.
2. A bilevel code for coding symbols from an n-ary
alphabet based on a reference value for the symbol
also taken from the same alphabet.
The code has two steps. If the symbol is close to
the reference a shorter code is used, while if it is
farther away a longer code is used.
3. A finite (terminated) subexponential code that codes
a symbol from an n-ary alphabet using subexp parameter k.
4. A finite (terminated) subexponential code that codes
a symbol from an n-ary alphabet using subexp parameter k,
based on a given reference also taken from the same
alphabet. This code essentially reorders the values
before using the same code as 3.

Also adds corresponding encoder side functions to count
the number of bits used.

These codes will be subsequently used for more efficient
encoding of loop-restoration parameters and global motion
parameters.

Change-Id: I28c82b611925c1ab17f544c48c4b1287930764b7
diff --git a/aom_dsp/aom_dsp.mk b/aom_dsp/aom_dsp.mk
index 8ae9e80..dedead4 100644
--- a/aom_dsp/aom_dsp.mk
+++ b/aom_dsp/aom_dsp.mk
@@ -39,6 +39,8 @@
 DSP_SRCS-yes += bitwriter.h
 DSP_SRCS-yes += bitwriter_buffer.c
 DSP_SRCS-yes += bitwriter_buffer.h
+DSP_SRCS-yes += binary_codes_writer.c
+DSP_SRCS-yes += binary_codes_writer.h
 DSP_SRCS-yes += psnr.c
 DSP_SRCS-yes += psnr.h
 DSP_SRCS-$(CONFIG_INTERNAL_STATS) += ssim.c
@@ -62,6 +64,8 @@
 DSP_SRCS-yes += bitreader.h
 DSP_SRCS-yes += bitreader_buffer.c
 DSP_SRCS-yes += bitreader_buffer.h
+DSP_SRCS-yes += binary_codes_reader.c
+DSP_SRCS-yes += binary_codes_reader.h
 endif
 
 # intra predictions
diff --git a/aom_dsp/binary_codes_reader.c b/aom_dsp/binary_codes_reader.c
new file mode 100644
index 0000000..0bbcffa
--- /dev/null
+++ b/aom_dsp/binary_codes_reader.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2017, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include "aom_dsp/bitreader.h"
+
+#include "av1/common/common.h"
+
+// Inverse recenters a non-negative literal v around a reference r
+static uint16_t inv_recenter_nonneg(uint16_t r, uint16_t v) {
+  if (v > (r << 1))
+    return v;
+  else if ((v & 1) == 0)
+    return (v >> 1) + r;
+  else
+    return r - ((v + 1) >> 1);
+}
+
+// Inverse recenters a non-negative literal v in [0, n-1] around a
+// reference r also in [0, n-1]
+static uint16_t inv_recenter_finite_nonneg(uint16_t n, uint16_t r, uint16_t v) {
+  if ((r << 1) <= n) {
+    return inv_recenter_nonneg(r, v);
+  } else {
+    return n - 1 - inv_recenter_nonneg(n - 1 - r, v);
+  }
+}
+
+int16_t aom_read_primitive_symmetric(aom_reader *r, unsigned int mag_bits) {
+  if (aom_read_bit(r, NULL)) {
+    int s = aom_read_bit(r, NULL);
+    int16_t x = aom_read_literal(r, mag_bits, NULL) + 1;
+    return (s > 0 ? -x : x);
+  } else {
+    return 0;
+  }
+}
+
+uint16_t aom_read_primitive_quniform(aom_reader *r, uint16_t n) {
+  if (n <= 1) return 0;
+  const int l = get_msb(n - 1) + 1;
+  const int m = (1 << l) - n;
+  const int v = aom_read_literal(r, l - 1, NULL);
+  return v < m ? v : (v << 1) - m + aom_read_bit(r, NULL);
+}
+
+uint16_t aom_read_primitive_refbilevel(aom_reader *r, uint16_t n, uint16_t p,
+                                       uint16_t ref) {
+  if (n <= 1) return 0;
+  assert(p > 0 && p <= n);
+  assert(ref < n);
+  int lolimit = ref - p / 2;
+  int hilimit = lolimit + p - 1;
+  if (lolimit < 0) {
+    lolimit = 0;
+    hilimit = p - 1;
+  } else if (hilimit >= n) {
+    hilimit = n - 1;
+    lolimit = n - p;
+  }
+  int v;
+  if (aom_read_bit(r, NULL)) {
+    v = aom_read_primitive_quniform(r, p) + lolimit;
+  } else {
+    v = aom_read_primitive_quniform(r, n - p);
+    if (v >= lolimit) v += p;
+  }
+  return v;
+}
+
+// Decode finite subexponential code that for a symbol v in [0, n-1] with
+// parameter k
+uint16_t aom_read_primitive_subexpfin(aom_reader *r, uint16_t n, uint16_t k) {
+  int i = 0;
+  int mk = 0;
+  uint16_t v;
+  while (1) {
+    int b = (i ? k + i - 1 : k);
+    int a = (1 << b);
+    if (n <= mk + 3 * a) {
+      v = aom_read_primitive_quniform(r, n - mk) + mk;
+      break;
+    } else {
+      if (aom_read_bit(r, NULL)) {
+        i = i + 1;
+        mk += a;
+      } else {
+        v = aom_read_literal(r, b, NULL) + mk;
+        break;
+      }
+    }
+  }
+  return v;
+}
+
+// Decode finite subexponential code that for a symbol v in [0, n-1] with
+// parameter k
+// based on a reference ref also in [0, n-1].
+uint16_t aom_read_primitive_refsubexpfin(aom_reader *r, uint16_t n, uint16_t k,
+                                         uint16_t ref) {
+  return inv_recenter_finite_nonneg(n, ref,
+                                    aom_read_primitive_subexpfin(r, n, k));
+}
diff --git a/aom_dsp/binary_codes_reader.h b/aom_dsp/binary_codes_reader.h
new file mode 100644
index 0000000..d1c4d4c
--- /dev/null
+++ b/aom_dsp/binary_codes_reader.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2017, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#ifndef AOM_DSP_BINARY_CODES_READER_H_
+#define AOM_DSP_BINARY_CODES_READER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <assert.h>
+#include "./aom_config.h"
+#include "aom/aom_integer.h"
+#include "aom_dsp/bitreader.h"
+
+int16_t aom_read_primitive_symmetric(aom_reader *r, unsigned int mag_bits);
+
+uint16_t aom_read_primitive_quniform(aom_reader *r, uint16_t n);
+uint16_t aom_read_primitive_refbilevel(aom_reader *r, uint16_t n, uint16_t p,
+                                       uint16_t ref);
+uint16_t aom_read_primitive_subexpfin(aom_reader *r, uint16_t n, uint16_t k);
+uint16_t aom_read_primitive_refsubexpfin(aom_reader *r, uint16_t n, uint16_t k,
+                                         uint16_t ref);
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // AOM_DSP_BINARY_CODES_READER_H_
diff --git a/aom_dsp/binary_codes_writer.c b/aom_dsp/binary_codes_writer.c
new file mode 100644
index 0000000..9edb018
--- /dev/null
+++ b/aom_dsp/binary_codes_writer.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2017, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include "aom_dsp/bitwriter.h"
+
+#include "av1/common/common.h"
+
+// Recenters a non-negative literal v around a reference r
+static uint16_t recenter_nonneg(uint16_t r, uint16_t v) {
+  if (v > (r << 1))
+    return v;
+  else if (v >= r)
+    return ((v - r) << 1);
+  else
+    return ((r - v) << 1) - 1;
+}
+
+// Recenters a non-negative literal v in [0, n-1] around a
+// reference r also in [0, n-1]
+static uint16_t recenter_finite_nonneg(uint16_t n, uint16_t r, uint16_t v) {
+  if ((r << 1) <= n) {
+    return recenter_nonneg(r, v);
+  } else {
+    return recenter_nonneg(n - 1 - r, n - 1 - v);
+  }
+}
+
+// Codes a symbol v in [-2^mag_bits, 2^mag_bits].
+// mag_bits is number of bits for magnitude. The alphabet is of size
+// 2 * 2^mag_bits + 1, symmetric around 0, where one bit is used to
+// indicate 0 or non-zero, mag_bits bits are used to indicate magnitide
+// and 1 more bit for the sign if non-zero.
+void aom_write_primitive_symmetric(aom_writer *w, int16_t v,
+                                   unsigned int abs_bits) {
+  if (v == 0) {
+    aom_write_bit(w, 0);
+  } else {
+    const int x = abs(v);
+    const int s = v < 0;
+    aom_write_bit(w, 1);
+    aom_write_bit(w, s);
+    aom_write_literal(w, x - 1, abs_bits);
+  }
+}
+
+int aom_count_primitive_symmetric(int16_t v, unsigned int abs_bits) {
+  return (v == 0 ? 1 : abs_bits + 2);
+}
+
+// Encodes a value v in [0, n-1] quasi-uniformly
+void aom_write_primitive_quniform(aom_writer *w, uint16_t n, uint16_t v) {
+  if (n <= 1) return;
+  const int l = get_msb(n - 1) + 1;
+  const int m = (1 << l) - n;
+  if (v < m) {
+    aom_write_literal(w, v, l - 1);
+  } else {
+    aom_write_literal(w, m + ((v - m) >> 1), l - 1);
+    aom_write_bit(w, (v - m) & 1);
+  }
+}
+
+int aom_count_primitive_quniform(uint16_t n, uint16_t v) {
+  if (n <= 1) return 0;
+  const int l = get_msb(n - 1) + 1;
+  const int m = (1 << l) - n;
+  return v < m ? l - 1 : l;
+}
+
+// Encodes a value v in [0, n-1] based on a reference ref also in [0, n-1]
+// The closest p values of v from ref are coded using a p-ary quasi-unoform
+// short code while the remaining n-p values are coded with a longer code.
+void aom_write_primitive_refbilevel(aom_writer *w, uint16_t n, uint16_t p,
+                                    uint16_t ref, uint16_t v) {
+  if (n <= 1) return;
+  assert(p > 0 && p <= n);
+  assert(ref < n);
+  int lolimit = ref - p / 2;
+  int hilimit = lolimit + p - 1;
+  if (lolimit < 0) {
+    lolimit = 0;
+    hilimit = p - 1;
+  } else if (hilimit >= n) {
+    hilimit = n - 1;
+    lolimit = n - p;
+  }
+  if (v >= lolimit && v <= hilimit) {
+    aom_write_bit(w, 1);
+    v = v - lolimit;
+    aom_write_primitive_quniform(w, p, v);
+  } else {
+    aom_write_bit(w, 0);
+    if (v > hilimit) v -= p;
+    aom_write_primitive_quniform(w, n - p, v);
+  }
+}
+
+int aom_count_primitive_refbilevel(uint16_t n, uint16_t p, uint16_t ref,
+                                   uint16_t v) {
+  if (n <= 1) return 0;
+  assert(p > 0 && p <= n);
+  assert(ref < n);
+  int lolimit = ref - p / 2;
+  int hilimit = lolimit + p - 1;
+  if (lolimit < 0) {
+    lolimit = 0;
+    hilimit = p - 1;
+  } else if (hilimit >= n) {
+    hilimit = n - 1;
+    lolimit = n - p;
+  }
+  int count = 0;
+  if (v >= lolimit && v <= hilimit) {
+    count++;
+    v = v - lolimit;
+    count += aom_count_primitive_quniform(p, v);
+  } else {
+    count++;
+    if (v > hilimit) v -= p;
+    count += aom_count_primitive_quniform(n - p, v);
+  }
+  return count;
+}
+
+// Finite subexponential code that codes a symbol v in [0, n-1] with parameter k
+void aom_write_primitive_subexpfin(aom_writer *w, uint16_t n, uint16_t k,
+                                   uint16_t v) {
+  int i = 0;
+  int mk = 0;
+  while (1) {
+    int b = (i ? k + i - 1 : k);
+    int a = (1 << b);
+    if (n <= mk + 3 * a) {
+      aom_write_primitive_quniform(w, n - mk, v - mk);
+      break;
+    } else {
+      int t = (v >= mk + a);
+      aom_write_bit(w, t);
+      if (t) {
+        i = i + 1;
+        mk += a;
+      } else {
+        aom_write_literal(w, v - mk, b);
+        break;
+      }
+    }
+  }
+}
+
+int aom_count_primitive_subexpfin(uint16_t n, uint16_t k, uint16_t v) {
+  int count = 0;
+  int i = 0;
+  int mk = 0;
+  while (1) {
+    int b = (i ? k + i - 1 : k);
+    int a = (1 << b);
+    if (n <= mk + 3 * a) {
+      count += aom_count_primitive_quniform(n - mk, v - mk);
+      break;
+    } else {
+      int t = (v >= mk + a);
+      count++;
+      if (t) {
+        i = i + 1;
+        mk += a;
+      } else {
+        count += b;
+        break;
+      }
+    }
+  }
+  return count;
+}
+
+// Finite subexponential code that codes a symbol v in [0, n-1] with parameter k
+// based on a reference ref also in [0, n-1].
+// Recenters symbol around r first and then uses a finite subexponential code.
+void aom_write_primitive_refsubexpfin(aom_writer *w, uint16_t n, uint16_t k,
+                                      uint16_t ref, uint16_t v) {
+  aom_write_primitive_subexpfin(w, n, k, recenter_finite_nonneg(n, ref, v));
+}
+
+int aom_count_primitive_refsubexpfin(uint16_t n, uint16_t k, uint16_t ref,
+                                     uint16_t v) {
+  return aom_count_primitive_subexpfin(n, k, recenter_finite_nonneg(n, ref, v));
+}
diff --git a/aom_dsp/binary_codes_writer.h b/aom_dsp/binary_codes_writer.h
new file mode 100644
index 0000000..d80de9e
--- /dev/null
+++ b/aom_dsp/binary_codes_writer.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#ifndef AOM_DSP_BINARY_CODES_WRITER_H_
+#define AOM_DSP_BINARY_CODES_WRITER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <assert.h>
+#include "./aom_config.h"
+#include "aom/aom_integer.h"
+#include "aom_dsp/bitwriter.h"
+
+// Codes a symbol v in [-2^mag_bits, 2^mag_bits]
+// mag_bits is number of bits for magnitude. The alphabet is of size
+// 2 * 2^mag_bits + 1, symmetric around 0, where one bit is used to
+// indicate 0 or non-zero, mag_bits bits are used to indicate magnitide
+// and 1 more bit for the sign if non-zero.
+void aom_write_primitive_symmetric(aom_writer *w, int16_t v,
+                                   unsigned int mag_bits);
+
+// Encodes a value v in [0, n-1] quasi-uniformly
+void aom_write_primitive_quniform(aom_writer *w, uint16_t n, uint16_t v);
+
+// Encodes a value v in [0, n-1] based on a reference ref also in [0, n-1]
+// The closest p values of v from ref are coded using a p-ary quasi-unoform
+// short code while the remaining n-p values are coded with a longer code.
+void aom_write_primitive_refbilevel(aom_writer *w, uint16_t n, uint16_t p,
+                                    uint16_t ref, uint16_t v);
+
+// Finite subexponential code that codes a symbol v in [0, n-1] with parameter k
+void aom_write_primitive_subexpfin(aom_writer *w, uint16_t n, uint16_t k,
+                                   uint16_t v);
+
+// Finite subexponential code that codes a symbol v in [0, n-1] with parameter k
+// based on a reference ref also in [0, n-1].
+void aom_write_primitive_refsubexpfin(aom_writer *w, uint16_t n, uint16_t k,
+                                      uint16_t ref, uint16_t v);
+
+// Functions that counts bits for the above primitives
+int aom_count_primitive_symmetric(int16_t v, unsigned int mag_bits);
+int aom_count_primitive_quniform(uint16_t n, uint16_t v);
+int aom_count_primitive_refbilevel(uint16_t n, uint16_t p, uint16_t ref,
+                                   uint16_t v);
+int aom_count_primitive_subexpfin(uint16_t n, uint16_t k, uint16_t v);
+int aom_count_primitive_refsubexpfin(uint16_t n, uint16_t k, uint16_t ref,
+                                     uint16_t v);
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // AOM_DSP_BINARY_CODES_WRITER_H_
diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c
index 428cfa5..fae9715 100644
--- a/av1/decoder/decodeframe.c
+++ b/av1/decoder/decodeframe.c
@@ -21,6 +21,7 @@
 #include "aom_dsp/aom_dsp_common.h"
 #include "aom_dsp/bitreader.h"
 #include "aom_dsp/bitreader_buffer.h"
+#include "aom_dsp/binary_codes_reader.h"
 #include "aom_mem/aom_mem.h"
 #include "aom_ports/mem.h"
 #include "aom_ports/mem_ops.h"
diff --git a/av1/decoder/dsubexp.c b/av1/decoder/dsubexp.c
index ee6a295..5171f11 100644
--- a/av1/decoder/dsubexp.c
+++ b/av1/decoder/dsubexp.c
@@ -80,15 +80,3 @@
     *p = (aom_prob)inv_remap_prob(delp, *p);
   }
 }
-
-#if CONFIG_GLOBAL_MOTION
-int aom_read_primitive_symmetric(aom_reader *r, unsigned int mag_bits) {
-  if (aom_read_bit(r, ACCT_STR_NAME)) {
-    int s = aom_read_bit(r, ACCT_STR_NAME);
-    int x = aom_read_literal(r, mag_bits, ACCT_STR_NAME) + 1;
-    return (s > 0 ? -x : x);
-  } else {
-    return 0;
-  }
-}
-#endif  // CONFIG_GLOBAL_MOTION
\ No newline at end of file
diff --git a/av1/decoder/dsubexp.h b/av1/decoder/dsubexp.h
index 60aa7df..4bc3857 100644
--- a/av1/decoder/dsubexp.h
+++ b/av1/decoder/dsubexp.h
@@ -29,11 +29,4 @@
 #ifdef __cplusplus
 }  // extern "C"
 #endif
-#if CONFIG_GLOBAL_MOTION
-// mag_bits is number of bits for magnitude. The alphabet is of size
-// 2 * 2^mag_bits + 1, symmetric around 0, where one bit is used to
-// indicate 0 or non-zero, mag_bits bits are used to indicate magnitide
-// and 1 more bit for the sign if non-zero.
-int aom_read_primitive_symmetric(aom_reader *r, unsigned int mag_bits);
-#endif  // CONFIG_GLOBAL_MOTION
 #endif  // AV1_DECODER_DSUBEXP_H_
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c
index 8f9e991..a164173 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -16,6 +16,7 @@
 #include "aom/aom_encoder.h"
 #include "aom_dsp/bitwriter_buffer.h"
 #include "aom_dsp/aom_dsp_common.h"
+#include "aom_dsp/binary_codes_writer.h"
 #include "aom_mem/aom_mem.h"
 #include "aom_ports/mem_ops.h"
 #include "aom_ports/system_state.h"
diff --git a/av1/encoder/subexp.c b/av1/encoder/subexp.c
index 98c1d27..8960d33 100644
--- a/av1/encoder/subexp.c
+++ b/av1/encoder/subexp.c
@@ -280,16 +280,3 @@
       av1_prob_diff_update_savings_search(ct, *oldp, &newp, upd, probwt);
   return savings;
 }
-
-void aom_write_primitive_symmetric(aom_writer *w, int word,
-                                   unsigned int abs_bits) {
-  if (word == 0) {
-    aom_write_bit(w, 0);
-  } else {
-    const int x = abs(word);
-    const int s = word < 0;
-    aom_write_bit(w, 1);
-    aom_write_bit(w, s);
-    aom_write_literal(w, x - 1, abs_bits);
-  }
-}
diff --git a/av1/encoder/subexp.h b/av1/encoder/subexp.h
index 4dc51f6..049265c 100644
--- a/av1/encoder/subexp.h
+++ b/av1/encoder/subexp.h
@@ -42,14 +42,6 @@
     unsigned int ct[ENTROPY_NODES][COEF_PROBS_BUFS][2], const aom_prob *oldp,
     aom_prob *bestp, aom_prob upd, int stepsize, int n);
 #endif  // CONFIG_SUBFRAME_PROB_UPDATE
-
-//
-// mag_bits is number of bits for magnitude. The alphabet is of size
-// 2 * 2^mag_bits + 1, symmetric around 0, where one bit is used to
-// indicate 0 or non-zero, mag_bits bits are used to indicate magnitide
-// and 1 more bit for the sign if non-zero.
-void aom_write_primitive_symmetric(aom_writer *w, int word,
-                                   unsigned int mag_bits);
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/test/binary_codes_test.cc b/test/binary_codes_test.cc
new file mode 100644
index 0000000..a149fcc
--- /dev/null
+++ b/test/binary_codes_test.cc
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+*/
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
+
+#include "test/acm_random.h"
+#include "aom/aom_integer.h"
+#include "aom_dsp/bitreader.h"
+#include "aom_dsp/bitwriter.h"
+#include "aom_dsp/binary_codes_reader.h"
+#include "aom_dsp/binary_codes_writer.h"
+
+using libaom_test::ACMRandom;
+
+namespace {
+
+// Test for Bilevel code with reference
+TEST(AV1, TestPrimitiveRefbilivel) {
+  ACMRandom rnd(ACMRandom::DeterministicSeed());
+  const int kBufferSize = 65536;
+  aom_writer bw;
+  uint8_t bw_buffer[kBufferSize];
+  const uint16_t kRanges = 8;
+  const uint16_t kNearRanges = 8;
+  const uint16_t kReferences = 8;
+  const uint16_t kValues = 16;
+  const uint16_t range_vals[kRanges] = { 1, 13, 64, 120, 230, 420, 1100, 8000 };
+  uint16_t enc_values[kRanges][kNearRanges][kReferences][kValues][4];
+  aom_start_encode(&bw, bw_buffer);
+  for (int n = 0; n < kRanges; ++n) {
+    const uint16_t range = range_vals[n];
+    for (int p = 0; p < kNearRanges; ++p) {
+      const uint16_t near_range = 1 + rnd(range);
+      for (int r = 0; r < kReferences; ++r) {
+        const uint16_t ref = rnd(range);
+        for (int v = 0; v < kValues; ++v) {
+          const uint16_t value = rnd(range);
+          enc_values[n][p][r][v][0] = range;
+          enc_values[n][p][r][v][1] = near_range;
+          enc_values[n][p][r][v][2] = ref;
+          enc_values[n][p][r][v][3] = value;
+          aom_write_primitive_refbilevel(&bw, range, near_range, ref, value);
+        }
+      }
+    }
+  }
+  aom_stop_encode(&bw);
+  aom_reader br;
+  aom_reader_init(&br, bw_buffer, kBufferSize, NULL, NULL);
+  GTEST_ASSERT_GE(aom_reader_tell(&br), 0u);
+  GTEST_ASSERT_LE(aom_reader_tell(&br), 1u);
+  for (int n = 0; n < kRanges; ++n) {
+    for (int p = 0; p < kNearRanges; ++p) {
+      for (int r = 0; r < kReferences; ++r) {
+        for (int v = 0; v < kValues; ++v) {
+          const uint16_t range = enc_values[n][p][r][v][0];
+          const uint16_t near_range = enc_values[n][p][r][v][1];
+          const uint16_t ref = enc_values[n][p][r][v][2];
+          const uint16_t value =
+              aom_read_primitive_refbilevel(&br, range, near_range, ref);
+          GTEST_ASSERT_EQ(value, enc_values[n][p][r][v][3]);
+        }
+      }
+    }
+  }
+}
+
+// Test for Finite subexponential code with reference
+TEST(AV1, TestPrimitiveRefsubexpfin) {
+  ACMRandom rnd(ACMRandom::DeterministicSeed());
+  const int kBufferSize = 65536;
+  aom_writer bw;
+  uint8_t bw_buffer[kBufferSize];
+  const uint16_t kRanges = 8;
+  const uint16_t kSubexpParams = 6;
+  const uint16_t kReferences = 8;
+  const uint16_t kValues = 16;
+  uint16_t enc_values[kRanges][kSubexpParams][kReferences][kValues][4];
+  const uint16_t range_vals[kRanges] = { 1, 13, 64, 120, 230, 420, 1100, 8000 };
+  aom_start_encode(&bw, bw_buffer);
+  for (int n = 0; n < kRanges; ++n) {
+    const uint16_t range = range_vals[n];
+    for (int k = 0; k < kSubexpParams; ++k) {
+      for (int r = 0; r < kReferences; ++r) {
+        const uint16_t ref = rnd(range);
+        for (int v = 0; v < kValues; ++v) {
+          const uint16_t value = rnd(range);
+          enc_values[n][k][r][v][0] = range;
+          enc_values[n][k][r][v][1] = k;
+          enc_values[n][k][r][v][2] = ref;
+          enc_values[n][k][r][v][3] = value;
+          aom_write_primitive_refsubexpfin(&bw, range, k, ref, value);
+        }
+      }
+    }
+  }
+  aom_stop_encode(&bw);
+  aom_reader br;
+  aom_reader_init(&br, bw_buffer, kBufferSize, NULL, NULL);
+  GTEST_ASSERT_GE(aom_reader_tell(&br), 0u);
+  GTEST_ASSERT_LE(aom_reader_tell(&br), 1u);
+  for (int n = 0; n < kRanges; ++n) {
+    for (int k = 0; k < kSubexpParams; ++k) {
+      for (int r = 0; r < kReferences; ++r) {
+        for (int v = 0; v < kValues; ++v) {
+          const uint16_t range = enc_values[n][k][r][v][0];
+          assert(k == enc_values[n][k][r][v][1]);
+          const uint16_t ref = enc_values[n][k][r][v][2];
+          const uint16_t value =
+              aom_read_primitive_refsubexpfin(&br, range, k, ref);
+          GTEST_ASSERT_EQ(value, enc_values[n][k][r][v][3]);
+        }
+      }
+    }
+  }
+}
+// TODO(debargha): Adds tests for other primitives
+}  // namespace
diff --git a/test/test.cmake b/test/test.cmake
index 1ce3314..4e9ef54 100644
--- a/test/test.cmake
+++ b/test/test.cmake
@@ -185,6 +185,7 @@
       "${AOM_ROOT}/test/idct8x8_test.cc"
       "${AOM_ROOT}/test/partial_idct_test.cc"
       "${AOM_ROOT}/test/superframe_test.cc"
+      "${AOM_ROOT}/test/binary_codes_test.cc"
       "${AOM_ROOT}/test/tile_independence_test.cc")
 
   if (CONFIG_ANS)
diff --git a/test/test.mk b/test/test.mk
index 8ffa87a..343d7e9 100644
--- a/test/test.mk
+++ b/test/test.mk
@@ -101,6 +101,7 @@
 LIBAOM_TEST_SRCS-yes                   += superframe_test.cc
 LIBAOM_TEST_SRCS-yes                   += tile_independence_test.cc
 LIBAOM_TEST_SRCS-yes                   += ethread_test.cc
+LIBAOM_TEST_SRCS-yes                   += binary_codes_test.cc
 ifeq ($(CONFIG_EXT_TILE),yes)
 LIBAOM_TEST_SRCS-yes                   += av1_ext_tile_test.cc
 endif