From 9365ebfba03d4fc98e1279b9ce64be1fbc775c97 Mon Sep 17 00:00:00 2001
From: Nik <njunger@uwaterloo.ca>
Date: Mon, 2 Feb 2015 20:00:08 -0500
Subject: [PATCH] Interfaces -> exposed structs

---
 doc_test.go          |   9 +-
 element.go           | 212 ++++++------------------
 element_arith.go     | 372 +++++++++++++++++++++++++++++++++++++++++
 element_checked.go   | 384 ------------------------------------------
 element_fmt.go       |  44 ++---
 element_io.go        | 111 +++++++++++++
 element_misc.go      |  67 ++++++++
 element_unchecked.go | 386 -------------------------------------------
 generation.go        |  24 +--
 pairing.go           | 108 ++++++------
 params.go            |  32 ++--
 11 files changed, 705 insertions(+), 1044 deletions(-)
 create mode 100644 element_arith.go
 delete mode 100644 element_checked.go
 create mode 100644 element_io.go
 create mode 100644 element_misc.go
 delete mode 100644 element_unchecked.go

diff --git a/doc_test.go b/doc_test.go
index 121f8cd..3bbe420 100644
--- a/doc_test.go
+++ b/doc_test.go
@@ -1,10 +1,12 @@
 package pbc
 
+import "fmt"
+
 // This example generates a pairing and some random group elements, then applies
 // the pairing operation.
 func Example() {
 	// In a real application, generate this once and publish it
-	params := pbc.GenerateA(160, 512)
+	params := GenerateA(160, 512)
 
 	pairing := params.NewPairing()
 
@@ -16,7 +18,8 @@ func Example() {
 	// Generate random group elements and pair them
 	g.Rand()
 	h.Rand()
-	fmt.Printf("g = %v\nh = %v\n", g, h)
+	fmt.Printf("g = %s\n", g)
+	fmt.Printf("h = %s\n", h)
 	x.Pair(g, h)
-	fmt.Printf("e(g,h) = %v\n", x)
+	fmt.Printf("e(g,h) = %s\n", x)
 }
diff --git a/element.go b/element.go
index 92febd6..c20e9ff 100644
--- a/element.go
+++ b/element.go
@@ -4,182 +4,78 @@ package pbc
 #include <pbc/pbc.h>
 */
 import "C"
+import "runtime"
 
-import (
-	"fmt"
-	"hash"
-	"math/big"
-	"runtime"
-)
+type Element struct {
+	pairing *Pairing // Prevents garbage collection
+	cptr    *C.struct_element_s
 
-type Element interface {
-	// NewFieldElement initializes a new element in the same group as this one.
-	// The returned element will be unchecked if and only if this element is.
-	NewFieldElement() Element
-
-	// Methods to directly set the value of the element:
-	Set0() Element
-	Set1() Element
-	SetInt32(int32) Element
-	SetBig(*big.Int) Element
-	Set(Element) Element
-
-	// Methods to hash a value into a group element:
-	SetFromHash([]byte) Element
-	SetFromStringHash(s string, h hash.Hash) Element
-
-	// SetString sets the value from an exported string representation.
-	SetString(s string, base int) (Element, bool)
-
-	// Methods to support the fmt package's Print and Scan functions:
-	Format(fmt.State, rune)
-	Scan(fmt.ScanState, rune) error
-
-	// Methods to export the element in human-readable format:
-	BigInt() *big.Int
-	String() string
-
-	// Methods to export the element as a sequence of bytes:
-	BytesLen() int
-	Bytes() []byte
-	XBytesLen() int
-	XBytes() []byte
-	CompressedBytesLen() int
-	CompressedBytes() []byte
-
-	// Methods to import an element from a sequence of bytes:
-	SetBytes([]byte) Element
-	SetXBytes([]byte) Element
-	SetCompressedBytes([]byte) Element
-
-	// Methods to retrieve sub-elements (coordinates for points, coefficients
-	// for polynomials):
-	Len() int
-	Item(int) Element
-	X() *big.Int
-	Y() *big.Int
-
-	// Methods to determine the mathematical properties of the element:
-	Is0() bool
-	Is1() bool
-	IsSquare() bool
-	Sign() int
-
-	// Methods to compare elements:
-	Cmp(x Element) int
-	Equals(x Element) bool
-
-	// Methods to perform arithmetic operations. Not all operations are valid
-	// for all groups.
-	Add(x, y Element) Element
-	Sub(x, y Element) Element
-	Mul(x, y Element) Element
-	MulBig(x Element, i *big.Int) Element
-	MulInt32(x Element, i int32) Element
-	MulZn(x, y Element) Element
-	Div(x, y Element) Element
-	Double(x Element) Element
-	Halve(x Element) Element
-	Square(x Element) Element
-	Neg(x Element) Element
-	Invert(x Element) Element
-
-	// Methods to exponentiate elements:
-	PowBig(x Element, i *big.Int) Element
-	PowZn(x, i Element) Element
-	Pow2Big(x Element, i *big.Int, y Element, j *big.Int) Element
-	Pow2Zn(x, i, y, j Element) Element
-	Pow3Big(x Element, i *big.Int, y Element, j *big.Int, z Element, k *big.Int) Element
-	Pow3Zn(x, i, y, j, z, k Element) Element
-
-	// Methods to perform pre-processed exponentiation:
-	PreparePower() Power
-	PowerBig(Power, *big.Int) Element
-	PowerZn(Power, Element) Element
-
-	// Methods to brute-force discrete logarithms in the group:
-	BruteForceDL(g, h Element) Element
-	PollardRhoDL(g, h Element) Element
-
-	// Rand sets the element to a random group element.
-	Rand() Element
-
-	// Pairing operations:
-	Pair(x, y Element) Element
-	ProdPair(elements ...Element) Element
-	ProdPairSlice(x, y []Element) Element
-
-	// Methods to perform pre-processed pairing operations:
-	PreparePairer() Pairer
-	PairerPair(Pairer, Element) Element
-
-	// Pairing returns the pairing associated with this element.
-	Pairing() Pairing
-
-	data() *C.struct_element_s
-}
-
-// Power stores pre-processed information to quickly exponentiate an element.
-// A Power can be generated for Element x by calling x.PreparePower(). When
-// PowBig or PowZn is called with Element target and integer i, the result of
-// x^i will be stored in target.
-type Power interface {
-	PowBig(target Element, i *big.Int) Element
-	PowZn(target Element, i Element) Element
-}
-
-type powerImpl struct {
-	data *C.struct_element_pp_s
+	checked   bool
+	fieldPtr  *C.struct_field_s
+	isInteger bool
 }
 
-func (power *powerImpl) PowBig(target Element, i *big.Int) Element {
-	return target.PowerBig(power, i)
+func clearElement(element *Element) {
+	C.element_clear(element.cptr)
 }
 
-func (power *powerImpl) PowZn(target Element, i Element) Element {
-	return target.PowerZn(power, i)
+func makeUncheckedElement(pairing *Pairing, initialize bool, field Field) *Element {
+	element := &Element{
+		cptr:    &C.struct_element_s{},
+		pairing: pairing,
+	}
+	if initialize {
+		switch field {
+		case G1:
+			C.element_init_G1(element.cptr, pairing.cptr)
+		case G2:
+			C.element_init_G2(element.cptr, pairing.cptr)
+		case GT:
+			C.element_init_GT(element.cptr, pairing.cptr)
+		case Zr:
+			C.element_init_Zr(element.cptr, pairing.cptr)
+		default:
+			panic(ErrUnknownField)
+		}
+	}
+	runtime.SetFinalizer(element, clearElement)
+	return element
 }
 
-func clearPower(power *powerImpl) {
-	C.element_pp_clear(power.data)
+func makeCheckedElement(pairing *Pairing, field Field, fieldPtr *C.struct_field_s) *Element {
+	element := makeUncheckedElement(pairing, true, field)
+	element.checked = true
+	element.fieldPtr = fieldPtr
+	element.isInteger = (field == Zr)
+	return element
 }
 
-func initPower(source Element) Power {
-	power := &powerImpl{
-		data: &C.struct_element_pp_s{},
+func checkFieldsMatch(f1, f2 *C.struct_field_s) {
+	if f1 != f2 {
+		panic(ErrIncompatible)
 	}
-	C.element_pp_init(power.data, source.data())
-	runtime.SetFinalizer(power, clearPower)
-	return power
 }
 
-// Pairer stores pre-processed information to quickly pair an element. A Pairer
-// can be generated for Element x by calling x.PreparePairer(). When Pair is
-// called with Elements target and y, the result of e(x,y) will be stored in
-// target.
-type Pairer interface {
-	Pair(target Element, y Element) Element
-}
-
-type pairerImpl struct {
-	source Element
-	data   *C.struct_pairing_pp_s
+func (el *Element) ensureChecked() {
+	if !el.checked {
+		panic(ErrUncheckedOp)
+	}
 }
 
-func (pairer *pairerImpl) Pair(target Element, y Element) Element {
-	return target.PairerPair(pairer, y)
+func (el *Element) checkCompatible(other *Element) {
+	other.ensureChecked()
+	checkFieldsMatch(el.fieldPtr, other.fieldPtr)
 }
 
-func clearPairer(pairer *pairerImpl) {
-	C.pairing_pp_clear(pairer.data)
+func (el *Element) checkAllCompatible(elements ...*Element) {
+	for _, other := range elements {
+		el.checkCompatible(other)
+	}
 }
 
-func initPairer(source Element) Pairer {
-	pairer := &pairerImpl{
-		source: source,
-		data:   &C.struct_pairing_pp_s{},
+func (el *Element) checkInteger() {
+	el.ensureChecked()
+	if !el.isInteger {
+		panic(ErrIllegalOp)
 	}
-	C.pairing_pp_init(pairer.data, source.data(), source.Pairing().(*pairingImpl).data)
-	runtime.SetFinalizer(pairer, clearPairer)
-	return pairer
 }
diff --git a/element_arith.go b/element_arith.go
new file mode 100644
index 0000000..b242017
--- /dev/null
+++ b/element_arith.go
@@ -0,0 +1,372 @@
+package pbc
+
+/*
+#include <pbc/pbc.h>
+*/
+import "C"
+import (
+	"math/big"
+	"runtime"
+	"unsafe"
+)
+
+func (el *Element) Set0() *Element {
+	C.element_set0(el.cptr)
+	return el
+}
+
+func (el *Element) Set1() *Element {
+	C.element_set1(el.cptr)
+	return el
+}
+
+func (el *Element) Rand() *Element {
+	C.element_random(el.cptr)
+	return el
+}
+
+func normalizeSign(sign int64) int {
+	if sign > 0 {
+		return 1
+	}
+	if sign < 0 {
+		return -1
+	}
+	return 0
+}
+
+func (el *Element) Cmp(x *Element) int {
+	if el.checked {
+		el.checkCompatible(x)
+	}
+	return normalizeSign(int64(C.element_cmp(el.cptr, x.cptr)))
+}
+
+func (el *Element) Equals(x *Element) bool { return el.Cmp(x) == 0 }
+
+func (el *Element) Is0() bool {
+	return C.element_is0(el.cptr) != 0
+}
+
+func (el *Element) Is1() bool {
+	return C.element_is1(el.cptr) != 0
+}
+
+func (el *Element) IsSquare() bool {
+	return C.element_is_sqr(el.cptr) != 0
+}
+
+func (el *Element) Sign() int {
+	return normalizeSign(int64(C.element_sign(el.cptr)))
+}
+
+func (el *Element) Add(x, y *Element) *Element {
+	if el.checked {
+		el.checkAllCompatible(x, y)
+	}
+	C.element_add(el.cptr, x.cptr, y.cptr)
+	return el
+}
+
+func (el *Element) Sub(x, y *Element) *Element {
+	if el.checked {
+		el.checkAllCompatible(x, y)
+	}
+	C.element_sub(el.cptr, x.cptr, y.cptr)
+	return el
+}
+
+func (el *Element) Mul(x, y *Element) *Element {
+	if el.checked {
+		el.checkAllCompatible(x, y)
+	}
+	C.element_mul(el.cptr, x.cptr, y.cptr)
+	return el
+}
+
+func (el *Element) MulBig(x *Element, i *big.Int) *Element {
+	if el.checked {
+		el.checkCompatible(x)
+	}
+	C.element_mul_mpz(el.cptr, x.cptr, &big2mpz(i)[0])
+	return el
+}
+
+func (el *Element) MulInt32(x *Element, i int32) *Element {
+	if el.checked {
+		el.checkCompatible(x)
+	}
+	C.element_mul_si(el.cptr, x.cptr, C.long(i))
+	return el
+}
+
+func (el *Element) MulZn(x, y *Element) *Element {
+	if el.checked {
+		el.checkCompatible(x)
+		y.checkInteger()
+	}
+	C.element_mul_zn(el.cptr, x.cptr, y.cptr)
+	return el
+}
+
+func (el *Element) Div(x, y *Element) *Element {
+	if el.checked {
+		el.checkAllCompatible(x, y)
+	}
+	C.element_div(el.cptr, x.cptr, y.cptr)
+	return el
+}
+
+func (el *Element) Double(x *Element) *Element {
+	if el.checked {
+		el.checkCompatible(x)
+	}
+	C.element_double(el.cptr, x.cptr)
+	return el
+}
+
+func (el *Element) Halve(x *Element) *Element {
+	if el.checked {
+		el.checkCompatible(x)
+	}
+	C.element_halve(el.cptr, x.cptr)
+	return el
+}
+
+func (el *Element) Square(x *Element) *Element {
+	if el.checked {
+		el.checkCompatible(x)
+	}
+	C.element_square(el.cptr, x.cptr)
+	return el
+}
+
+func (el *Element) Neg(x *Element) *Element {
+	if el.checked {
+		el.checkCompatible(x)
+	}
+	C.element_neg(el.cptr, x.cptr)
+	return el
+}
+
+func (el *Element) Invert(x *Element) *Element {
+	if el.checked {
+		el.checkCompatible(x)
+	}
+	C.element_invert(el.cptr, x.cptr)
+	return el
+}
+
+func (el *Element) PowBig(x *Element, i *big.Int) *Element {
+	if el.checked {
+		el.checkCompatible(x)
+	}
+	C.element_pow_mpz(el.cptr, x.cptr, &big2mpz(i)[0])
+	return el
+}
+
+func (el *Element) PowZn(x, i *Element) *Element {
+	if el.checked {
+		el.checkCompatible(x)
+		i.checkInteger()
+	}
+	C.element_pow_zn(el.cptr, x.cptr, i.cptr)
+	return el
+}
+
+func (el *Element) Pow2Big(x *Element, i *big.Int, y *Element, j *big.Int) *Element {
+	if el.checked {
+		el.checkAllCompatible(x, y)
+	}
+	C.element_pow2_mpz(el.cptr, x.cptr, &big2mpz(i)[0], y.cptr, &big2mpz(j)[0])
+	return el
+}
+
+func (el *Element) Pow2Zn(x, i, y, j *Element) *Element {
+	if el.checked {
+		el.checkAllCompatible(x, y)
+		i.checkInteger()
+		j.checkInteger()
+	}
+	C.element_pow2_zn(el.cptr, x.cptr, i.cptr, y.cptr, j.cptr)
+	return el
+}
+
+func (el *Element) Pow3Big(x *Element, i *big.Int, y *Element, j *big.Int, z *Element, k *big.Int) *Element {
+	if el.checked {
+		el.checkAllCompatible(x, y, z)
+	}
+	C.element_pow3_mpz(el.cptr, x.cptr, &big2mpz(i)[0], y.cptr, &big2mpz(j)[0], z.cptr, &big2mpz(k)[0])
+	return el
+}
+
+func (el *Element) Pow3Zn(x, i, y, j, z, k *Element) *Element {
+	if el.checked {
+		el.checkAllCompatible(x, y, z)
+		i.checkInteger()
+		j.checkInteger()
+		k.checkInteger()
+	}
+	C.element_pow3_zn(el.cptr, x.cptr, i.cptr, y.cptr, j.cptr, z.cptr, k.cptr)
+	return el
+}
+
+// Power stores pre-processed information to quickly exponentiate an element.
+// A Power can be generated for Element x by calling x.PreparePower(). When
+// PowBig or PowZn is called with Element target and integer i, the result of
+// x^i will be stored in target.
+type Power struct {
+	source *Element // Prevents garbage collection
+	pp     *C.struct_element_pp_s
+}
+
+func (power *Power) Source() *Element { return power.source }
+
+func (power *Power) PowBig(target *Element, i *big.Int) *Element {
+	return target.PowerBig(power, i)
+}
+
+func (power *Power) PowZn(target *Element, i *Element) *Element {
+	return target.PowerZn(power, i)
+}
+
+func clearPower(power *Power) {
+	C.element_pp_clear(power.pp)
+}
+
+func (el *Element) PreparePower() *Power {
+	power := &Power{
+		source: el,
+		pp:     &C.struct_element_pp_s{},
+	}
+	C.element_pp_init(power.pp, el.cptr)
+	runtime.SetFinalizer(power, clearPower)
+	return power
+}
+
+func (el *Element) PowerBig(power *Power, i *big.Int) *Element {
+	C.element_pp_pow(el.cptr, &big2mpz(i)[0], power.pp)
+	return el
+}
+
+func (el *Element) PowerZn(power *Power, i *Element) *Element {
+	if el.checked {
+		i.checkInteger()
+	}
+	C.element_pp_pow_zn(el.cptr, i.cptr, power.pp)
+	return el
+}
+
+func (el *Element) Pair(x, y *Element) *Element {
+	if el.checked {
+		x.ensureChecked()
+		y.ensureChecked()
+		pairing := el.pairing.cptr
+		checkFieldsMatch(el.fieldPtr, &pairing.GT[0])
+		checkFieldsMatch(x.fieldPtr, pairing.G1)
+		checkFieldsMatch(y.fieldPtr, pairing.G2)
+	}
+	C.pairing_apply(el.cptr, x.cptr, y.cptr, el.pairing.cptr)
+	return el
+}
+
+func (el *Element) doProdPair(in1, in2 []C.struct_element_s) *Element {
+	x := (*C.element_t)(unsafe.Pointer(&in1[0]))
+	y := (*C.element_t)(unsafe.Pointer(&in2[0]))
+	C.element_prod_pairing(el.cptr, x, y, C.int(len(in1)))
+	return el
+}
+
+func (el *Element) ProdPair(elements ...*Element) *Element {
+	n := len(elements)
+	if n%2 != 0 {
+		panic(ErrBadPairList)
+	}
+	if el.checked {
+		pairing := el.pairing.cptr
+		checkFieldsMatch(el.fieldPtr, &pairing.GT[0])
+		for i := 1; i < n; i += 2 {
+			elements[i-1].ensureChecked()
+			elements[i].ensureChecked()
+			checkFieldsMatch(elements[i-1].fieldPtr, pairing.G1)
+			checkFieldsMatch(elements[i].fieldPtr, pairing.G2)
+		}
+	}
+	half := n / 2
+	in1 := make([]C.struct_element_s, half)
+	in2 := make([]C.struct_element_s, half)
+	for i, j := 0, 0; j < n; i, j = i+1, j+2 {
+		in1[i] = *elements[j].cptr
+		in2[i] = *elements[j+1].cptr
+	}
+	return el.doProdPair(in1, in2)
+}
+
+func (el *Element) ProdPairSlice(x, y []*Element) *Element {
+	n := len(x)
+	if n != len(y) {
+		panic(ErrBadPairList)
+	}
+	if el.checked {
+		pairing := el.pairing.cptr
+		checkFieldsMatch(el.fieldPtr, &pairing.GT[0])
+		for i := 1; i < n; i++ {
+			x[i].ensureChecked()
+			checkFieldsMatch(x[i].fieldPtr, pairing.G1)
+		}
+		n = len(y)
+		for i := 1; i < n; i++ {
+			y[i].ensureChecked()
+			checkFieldsMatch(y[i].fieldPtr, pairing.G2)
+		}
+	}
+	in1 := make([]C.struct_element_s, n)
+	in2 := make([]C.struct_element_s, n)
+	for i := 0; i < n; i++ {
+		in1[i] = *x[i].cptr
+		in2[i] = *y[i].cptr
+	}
+	return el.doProdPair(in1, in2)
+}
+
+// Pairer stores pre-processed information to quickly pair an element. A Pairer
+// can be generated for Element x by calling x.PreparePairer(). When Pair is
+// called with Elements target and y, the result of e(x,y) will be stored in
+// target.
+type Pairer struct {
+	source *Element // Prevents garbage collection
+	pp     *C.struct_pairing_pp_s
+}
+
+func (pairer *Pairer) Source() *Element { return pairer.source }
+
+func (pairer *Pairer) Pair(target *Element, y *Element) *Element {
+	return target.PairerPair(pairer, y)
+}
+
+func clearPairer(pairer *Pairer) {
+	C.pairing_pp_clear(pairer.pp)
+}
+
+func (el *Element) PreparePairer() *Pairer {
+	pairer := &Pairer{
+		source: el,
+		pp:     &C.struct_pairing_pp_s{},
+	}
+	C.pairing_pp_init(pairer.pp, el.cptr, el.pairing.cptr)
+	runtime.SetFinalizer(pairer, clearPairer)
+	return pairer
+}
+
+func (el *Element) PairerPair(pairer *Pairer, y *Element) *Element {
+	if el.checked {
+		pairer.source.ensureChecked()
+		y.ensureChecked()
+		pairing := el.pairing.cptr
+		checkFieldsMatch(pairer.source.fieldPtr, pairing.G1)
+		checkFieldsMatch(y.fieldPtr, pairing.G2)
+		checkFieldsMatch(el.fieldPtr, &pairing.GT[0])
+	}
+	C.pairing_pp_apply(el.cptr, y.cptr, pairer.pp)
+	return el
+}
diff --git a/element_checked.go b/element_checked.go
deleted file mode 100644
index 07d2009..0000000
--- a/element_checked.go
+++ /dev/null
@@ -1,384 +0,0 @@
-package pbc
-
-/*
-#include <pbc/pbc.h>
-*/
-import "C"
-
-import (
-	"hash"
-	"math/big"
-)
-
-type checkedElement struct {
-	unchecked uncheckedElement
-	fieldPtr  *C.struct_field_s
-	isInteger bool
-}
-
-func makeChecked(pairing *pairingImpl, field Field, fieldPtr *C.struct_field_s) *checkedElement {
-	element := &checkedElement{
-		fieldPtr:  fieldPtr,
-		isInteger: field == Zr,
-	}
-	initUnchecked(&element.unchecked, pairing, true, field)
-	return element
-}
-
-func (el *checkedElement) data() *C.struct_element_s { return el.unchecked.d }
-
-func (el *checkedElement) Pairing() Pairing { return el.unchecked.pairing }
-
-func element2Checked(x Element) *checkedElement {
-	checked, ok := x.(*checkedElement)
-	if !ok {
-		panic(ErrUncheckedOp)
-	}
-	return checked
-}
-
-func checkFieldsMatch(f1, f2 *C.struct_field_s) {
-	if f1 != f2 {
-		panic(ErrIncompatible)
-	}
-}
-
-func (el *checkedElement) checkCompatible(other Element) {
-	otherChecked := element2Checked(other)
-	checkFieldsMatch(el.fieldPtr, otherChecked.fieldPtr)
-}
-
-func (el *checkedElement) checkAllCompatible(elements ...Element) {
-	for _, other := range elements {
-		el.checkCompatible(other)
-	}
-}
-
-func (el *checkedElement) checkInteger() {
-	if !el.isInteger {
-		panic(ErrIllegalOp)
-	}
-}
-
-func (el *checkedElement) NewFieldElement() Element {
-	newElement := &checkedElement{}
-	*newElement = *el
-	initUnchecked(&newElement.unchecked, el.unchecked.pairing, false, G1)
-	C.element_init_same_as(newElement.unchecked.d, el.unchecked.d)
-	return newElement
-}
-
-func (el *checkedElement) Set0() Element {
-	el.unchecked.Set0()
-	return el
-}
-
-func (el *checkedElement) Set1() Element {
-	el.unchecked.Set1()
-	return el
-}
-
-func (el *checkedElement) SetInt32(i int32) Element {
-	el.checkInteger()
-	el.unchecked.SetInt32(i)
-	return el
-}
-
-func (el *checkedElement) SetBig(i *big.Int) Element {
-	el.checkInteger()
-	el.unchecked.SetBig(i)
-	return el
-}
-
-func (el *checkedElement) Set(src Element) Element {
-	el.checkCompatible(src)
-	el.unchecked.Set(src)
-	return el
-}
-
-func (el *checkedElement) SetFromHash(hash []byte) Element {
-	el.unchecked.SetFromHash(hash)
-	return el
-}
-
-func (el *checkedElement) SetFromStringHash(s string, h hash.Hash) Element {
-	el.unchecked.SetFromStringHash(s, h)
-	return el
-}
-
-func (el *checkedElement) SetBytes(buf []byte) Element {
-	el.unchecked.SetBytes(buf)
-	return el
-}
-
-func (el *checkedElement) SetXBytes(buf []byte) Element {
-	el.unchecked.SetXBytes(buf)
-	return el
-}
-
-func (el *checkedElement) SetCompressedBytes(buf []byte) Element {
-	el.unchecked.SetCompressedBytes(buf)
-	return el
-}
-
-func (el *checkedElement) SetString(s string, base int) (Element, bool) {
-	_, ok := el.unchecked.SetString(s, base)
-	return el, ok
-}
-
-func (el *checkedElement) BigInt() *big.Int {
-	el.checkInteger()
-	return el.unchecked.BigInt()
-}
-
-func (el *checkedElement) BytesLen() int           { return el.unchecked.BytesLen() }
-func (el *checkedElement) XBytesLen() int          { return el.unchecked.XBytesLen() }
-func (el *checkedElement) CompressedBytesLen() int { return el.unchecked.CompressedBytesLen() }
-
-func checkedWrite(bytesWritten C.int, buffer []byte) []byte {
-	if int64(bytesWritten) > int64(len(buffer)) {
-		panic(ErrInternal)
-	}
-	return buffer
-}
-
-func (el *checkedElement) Bytes() []byte {
-	buf := make([]byte, el.BytesLen())
-	return checkedWrite(el.unchecked.writeBytes(buf), buf)
-}
-
-func (el *checkedElement) XBytes() []byte {
-	buf := make([]byte, el.XBytesLen())
-	return checkedWrite(el.unchecked.writeXBytes(buf), buf)
-}
-
-func (el *checkedElement) CompressedBytes() []byte {
-	buf := make([]byte, el.CompressedBytesLen())
-	return checkedWrite(el.unchecked.writeCompressedBytes(buf), buf)
-}
-
-func (el *checkedElement) Len() int { return el.unchecked.Len() }
-
-func (el *checkedElement) Item(i int) Element {
-	if i >= el.Len() {
-		panic(ErrOutOfRange)
-	}
-	uncheckedData := el.unchecked.Item(i).(*uncheckedElement)
-	item := &checkedElement{
-		fieldPtr:  uncheckedData.d.field,
-		isInteger: uncheckedData.Len() == 0,
-	}
-	item.unchecked = *uncheckedData
-	return item
-}
-
-func (el *checkedElement) X() *big.Int    { return el.unchecked.X() }
-func (el *checkedElement) Y() *big.Int    { return el.unchecked.Y() }
-func (el *checkedElement) Is0() bool      { return el.unchecked.Is0() }
-func (el *checkedElement) Is1() bool      { return el.unchecked.Is1() }
-func (el *checkedElement) IsSquare() bool { return el.unchecked.IsSquare() }
-func (el *checkedElement) Sign() int      { return el.unchecked.Sign() }
-
-func (el *checkedElement) Cmp(x Element) int {
-	el.checkCompatible(x)
-	return el.unchecked.Cmp(x)
-}
-
-func (el *checkedElement) Equals(x Element) bool { return el.unchecked.Equals(x) }
-
-func (el *checkedElement) Add(x Element, y Element) Element {
-	el.checkAllCompatible(x, y)
-	el.unchecked.Add(x, y)
-	return el
-}
-
-func (el *checkedElement) Sub(x, y Element) Element {
-	el.checkAllCompatible(x, y)
-	el.unchecked.Sub(x, y)
-	return el
-}
-
-func (el *checkedElement) Mul(x, y Element) Element {
-	el.checkAllCompatible(x, y)
-	el.unchecked.Mul(x, y)
-	return el
-}
-
-func (el *checkedElement) MulBig(x Element, i *big.Int) Element {
-	el.checkCompatible(x)
-	el.unchecked.MulBig(x, i)
-	return el
-}
-
-func (el *checkedElement) MulInt32(x Element, i int32) Element {
-	el.checkCompatible(x)
-	el.unchecked.MulInt32(x, i)
-	return el
-}
-
-func (el *checkedElement) MulZn(x, y Element) Element {
-	el.checkCompatible(x)
-	element2Checked(y).checkInteger()
-	el.unchecked.MulZn(x, y)
-	return el
-}
-
-func (el *checkedElement) Div(x, y Element) Element {
-	el.checkAllCompatible(x, y)
-	el.unchecked.Div(x, y)
-	return el
-}
-
-func (el *checkedElement) Double(x Element) Element {
-	el.checkCompatible(x)
-	el.unchecked.Double(x)
-	return el
-}
-
-func (el *checkedElement) Halve(x Element) Element {
-	el.checkCompatible(x)
-	el.unchecked.Halve(x)
-	return el
-}
-
-func (el *checkedElement) Square(x Element) Element {
-	el.checkCompatible(x)
-	el.unchecked.Square(x)
-	return el
-}
-
-func (el *checkedElement) Neg(x Element) Element {
-	el.checkCompatible(x)
-	el.unchecked.Neg(x)
-	return el
-}
-
-func (el *checkedElement) Invert(x Element) Element {
-	el.checkCompatible(x)
-	el.unchecked.Invert(x)
-	return el
-}
-
-func (el *checkedElement) PowBig(x Element, i *big.Int) Element {
-	el.checkCompatible(x)
-	el.unchecked.PowBig(x, i)
-	return el
-}
-
-func (el *checkedElement) PowZn(x, i Element) Element {
-	el.checkCompatible(x)
-	element2Checked(i).checkInteger()
-	el.unchecked.PowZn(x, i)
-	return el
-}
-
-func (el *checkedElement) Pow2Big(x Element, i *big.Int, y Element, j *big.Int) Element {
-	el.checkAllCompatible(x, y)
-	el.unchecked.Pow2Big(x, i, y, j)
-	return el
-}
-
-func (el *checkedElement) Pow2Zn(x, i, y, j Element) Element {
-	el.checkAllCompatible(x, y)
-	element2Checked(i).checkInteger()
-	element2Checked(j).checkInteger()
-	el.unchecked.Pow2Zn(x, i, y, j)
-	return el
-}
-
-func (el *checkedElement) Pow3Big(x Element, i *big.Int, y Element, j *big.Int, z Element, k *big.Int) Element {
-	el.checkAllCompatible(x, y, z)
-	el.unchecked.Pow3Big(x, i, y, j, z, k)
-	return el
-}
-
-func (el *checkedElement) Pow3Zn(x, i, y, j, z, k Element) Element {
-	el.checkAllCompatible(x, y, z)
-	element2Checked(i).checkInteger()
-	element2Checked(j).checkInteger()
-	element2Checked(k).checkInteger()
-	el.unchecked.Pow3Zn(x, i, y, j, z, k)
-	return el
-}
-
-func (el *checkedElement) PreparePower() Power { return initPower(el) }
-
-func (el *checkedElement) PowerBig(power Power, i *big.Int) Element {
-	el.unchecked.PowerBig(power, i)
-	return el
-}
-
-func (el *checkedElement) PowerZn(power Power, i Element) Element {
-	element2Checked(i).checkInteger()
-	el.unchecked.PowerZn(power, i)
-	return el
-}
-
-func (el *checkedElement) Pair(x, y Element) Element {
-	pairing := el.unchecked.pairing.data
-	checkFieldsMatch(el.fieldPtr, &pairing.GT[0])
-	checkFieldsMatch(element2Checked(x).fieldPtr, pairing.G1)
-	checkFieldsMatch(element2Checked(y).fieldPtr, pairing.G2)
-	el.unchecked.Pair(x, y)
-	return el
-}
-
-func (el *checkedElement) ProdPair(elements ...Element) Element {
-	pairing := el.unchecked.pairing.data
-	checkFieldsMatch(el.fieldPtr, &pairing.GT[0])
-	n := len(elements)
-	for i := 1; i < n; i += 2 {
-		checkFieldsMatch(element2Checked(elements[i-1]).fieldPtr, pairing.G1)
-		checkFieldsMatch(element2Checked(elements[i]).fieldPtr, pairing.G2)
-	}
-	el.unchecked.ProdPair(elements...)
-	return el
-}
-
-func (el *checkedElement) ProdPairSlice(x, y []Element) Element {
-	pairing := el.unchecked.pairing.data
-	checkFieldsMatch(el.fieldPtr, &pairing.GT[0])
-	n := len(x)
-	for i := 1; i < n; i++ {
-		checkFieldsMatch(element2Checked(x[i]).fieldPtr, pairing.G1)
-	}
-	n = len(y)
-	for i := 1; i < n; i++ {
-		checkFieldsMatch(element2Checked(y[i]).fieldPtr, pairing.G2)
-
-	}
-	el.unchecked.ProdPairSlice(x, y)
-	return el
-}
-
-func (el *checkedElement) PreparePairer() Pairer { return initPairer(el) }
-
-func (el *checkedElement) PairerPair(pairer Pairer, y Element) Element {
-	in1 := element2Checked(pairer.(*pairerImpl).source)
-	in2 := element2Checked(y)
-	pairing := el.unchecked.pairing.data
-	checkFieldsMatch(in1.fieldPtr, pairing.G1)
-	checkFieldsMatch(in2.fieldPtr, pairing.G2)
-	checkFieldsMatch(el.fieldPtr, &pairing.GT[0])
-	el.unchecked.PairerPair(pairer, y)
-	return el
-}
-
-func (el *checkedElement) BruteForceDL(g, h Element) Element {
-	el.checkInteger()
-	element2Checked(g).checkCompatible(h)
-	el.unchecked.BruteForceDL(g, h)
-	return el
-}
-
-func (el *checkedElement) PollardRhoDL(g, h Element) Element {
-	el.checkInteger()
-	element2Checked(g).checkCompatible(h)
-	el.unchecked.PollardRhoDL(g, h)
-	return el
-}
-
-func (el *checkedElement) Rand() Element {
-	el.unchecked.Rand()
-	return el
-}
diff --git a/element_fmt.go b/element_fmt.go
index 3c528cf..410ad1e 100644
--- a/element_fmt.go
+++ b/element_fmt.go
@@ -20,11 +20,11 @@ import (
 	"unsafe"
 )
 
-func (el *uncheckedElement) errorFormat(f fmt.State, c rune, err string) {
+func (el *Element) errorFormat(f fmt.State, c rune, err string) {
 	fmt.Fprintf(f, "%%!%c(%s pbc.Element)", c, err)
 }
 
-func (el *uncheckedElement) nativeFormat(f fmt.State, c rune) {
+func (el *Element) nativeFormat(f fmt.State, c rune) {
 	base := 10
 	if width, ok := f.Width(); ok {
 		if width < 2 || width > 36 {
@@ -35,7 +35,7 @@ func (el *uncheckedElement) nativeFormat(f fmt.State, c rune) {
 	}
 	var buf *C.char
 	var bufLen C.size_t
-	if C.element_out_str_wrapper(&buf, &bufLen, C.int(base), el.d) == 0 {
+	if C.element_out_str_wrapper(&buf, &bufLen, C.int(base), el.cptr) == 0 {
 		el.errorFormat(f, c, "INTERNALERROR")
 		return
 	}
@@ -44,14 +44,14 @@ func (el *uncheckedElement) nativeFormat(f fmt.State, c rune) {
 	fmt.Fprintf(f, "%s", str)
 }
 
-func (el *uncheckedElement) customFormat(f fmt.State, c rune) {
+func (el *Element) customFormat(f fmt.State, c rune) {
 	count := el.Len()
 	if count == 0 {
 		el.BigInt().Format(f, c)
 	} else {
 		fmt.Fprintf(f, "[")
 		for i := 0; i < count; i++ {
-			el.Item(i).(*uncheckedElement).customFormat(f, c)
+			el.Item(i).customFormat(f, c)
 			if i+1 < count {
 				fmt.Fprintf(f, ", ")
 			}
@@ -60,11 +60,15 @@ func (el *uncheckedElement) customFormat(f fmt.State, c rune) {
 	}
 }
 
-func (el *uncheckedElement) Format(f fmt.State, c rune) {
+func (el *Element) Format(f fmt.State, c rune) {
 	switch c {
 	case 'v':
 		if f.Flag('#') {
-			fmt.Fprintf(f, "pbc.Element{Checked: false, Pairing: %p, Addr: %p}", el.pairing, el)
+			if el.checked {
+				fmt.Fprintf(f, "pbc.Element{Checked: true, Integer: %t, Field: %p, Pairing: %p, Addr: %p}", el.isInteger, el.fieldPtr, el.pairing, el)
+			} else {
+				fmt.Fprintf(f, "pbc.Element{Checked: false, Pairing: %p, Addr: %p}", el.pairing, el)
+			}
 			break
 		}
 		fallthrough
@@ -77,21 +81,21 @@ func (el *uncheckedElement) Format(f fmt.State, c rune) {
 	}
 }
 
-func (el *checkedElement) Format(f fmt.State, c rune) {
-	if c == 'v' && f.Flag('#') {
-		fmt.Fprintf(f, "pbc.Element{Checked: true, Integer: %t, Field: %p, Pairing: %p, Addr: %p}", el.isInteger, el.fieldPtr, el.unchecked.pairing, el)
-	} else {
-		el.unchecked.Format(f, c)
-	}
-}
-
-func (el *uncheckedElement) String() string {
+func (el *Element) String() string {
 	return fmt.Sprintf("%s", el)
 }
 
-func (el *checkedElement) String() string { return el.unchecked.String() }
+func (el *Element) SetString(s string, base int) (*Element, bool) {
+	cstr := C.CString(s)
+	defer C.free(unsafe.Pointer(cstr))
 
-func (el *uncheckedElement) Scan(state fmt.ScanState, verb rune) error {
+	if ok := C.element_set_str(el.cptr, cstr, C.int(base)); ok == 0 {
+		return nil, false
+	}
+	return el, true
+}
+
+func (el *Element) Scan(state fmt.ScanState, verb rune) error {
 	if verb != 's' && verb != 'v' {
 		return ErrBadVerb
 	}
@@ -177,7 +181,3 @@ ReadLoop:
 	}
 	return nil
 }
-
-func (el *checkedElement) Scan(state fmt.ScanState, verb rune) error {
-	return el.unchecked.Scan(state, verb)
-}
diff --git a/element_io.go b/element_io.go
new file mode 100644
index 0000000..739a509
--- /dev/null
+++ b/element_io.go
@@ -0,0 +1,111 @@
+package pbc
+
+/*
+#include <pbc/pbc.h>
+*/
+import "C"
+import (
+	"hash"
+	"math/big"
+	"unsafe"
+)
+
+func (el *Element) BigInt() *big.Int {
+	if el.checked {
+		el.checkInteger()
+	}
+	mpz := newMpz()
+	C.element_to_mpz(&mpz[0], el.cptr)
+	return mpz2big(mpz)
+}
+
+func (el *Element) Set(src *Element) *Element {
+	if el.checked {
+		el.checkCompatible(src)
+	}
+	C.element_set(el.cptr, src.cptr)
+	return el
+}
+
+func (el *Element) SetInt32(i int32) *Element {
+	if el.checked {
+		el.checkInteger()
+	}
+	C.element_set_si(el.cptr, C.long(i))
+	return el
+}
+
+func (el *Element) SetBig(i *big.Int) *Element {
+	if el.checked {
+		el.checkInteger()
+	}
+	C.element_set_mpz(el.cptr, &big2mpz(i)[0])
+	return el
+}
+
+func (el *Element) SetFromHash(hash []byte) *Element {
+	C.element_from_hash(el.cptr, unsafe.Pointer(&hash[0]), C.int(len(hash)))
+	return el
+}
+
+func (el *Element) SetFromStringHash(s string, h hash.Hash) *Element {
+	h.Reset()
+	if _, err := h.Write([]byte(s)); err != nil {
+		panic(ErrHashFailure)
+	}
+	return el.SetFromHash(h.Sum([]byte{}))
+}
+
+func (el *Element) BytesLen() int {
+	return int(C.element_length_in_bytes(el.cptr))
+}
+
+func (el *Element) Bytes() []byte {
+	buf := make([]byte, el.BytesLen())
+	written := C.element_to_bytes((*C.uchar)(unsafe.Pointer(&buf[0])), el.cptr)
+	if int64(written) > int64(len(buf)) {
+		panic(ErrInternal)
+	}
+	return buf
+}
+
+func (el *Element) SetBytes(buf []byte) *Element {
+	C.element_from_bytes(el.cptr, (*C.uchar)(unsafe.Pointer(&buf[0])))
+	return el
+}
+
+func (el *Element) XBytesLen() int {
+	return int(C.element_length_in_bytes_x_only(el.cptr))
+}
+
+func (el *Element) XBytes() []byte {
+	buf := make([]byte, el.XBytesLen())
+	written := C.element_to_bytes_x_only((*C.uchar)(unsafe.Pointer(&buf[0])), el.cptr)
+	if int64(written) > int64(len(buf)) {
+		panic(ErrInternal)
+	}
+	return buf
+}
+
+func (el *Element) SetXBytes(buf []byte) *Element {
+	C.element_from_bytes_x_only(el.cptr, (*C.uchar)(unsafe.Pointer(&buf[0])))
+	return el
+}
+
+func (el *Element) CompressedBytesLen() int {
+	return int(C.element_length_in_bytes_compressed(el.cptr))
+}
+
+func (el *Element) CompressedBytes() []byte {
+	buf := make([]byte, el.CompressedBytesLen())
+	written := C.element_to_bytes_compressed((*C.uchar)(unsafe.Pointer(&buf[0])), el.cptr)
+	if int64(written) > int64(len(buf)) {
+		panic(ErrInternal)
+	}
+	return buf
+}
+
+func (el *Element) SetCompressedBytes(buf []byte) *Element {
+	C.element_from_bytes_compressed(el.cptr, (*C.uchar)(unsafe.Pointer(&buf[0])))
+	return el
+}
diff --git a/element_misc.go b/element_misc.go
new file mode 100644
index 0000000..2b6fdae
--- /dev/null
+++ b/element_misc.go
@@ -0,0 +1,67 @@
+package pbc
+
+/*
+#include <pbc/pbc.h>
+*/
+import "C"
+import "math/big"
+
+func (el *Element) Pairing() *Pairing { return el.pairing }
+
+func (el *Element) NewFieldElement() *Element {
+	newElement := makeUncheckedElement(el.pairing, false, G1)
+	C.element_init_same_as(newElement.cptr, el.cptr)
+	if el.checked {
+		newElement.checked = true
+		newElement.fieldPtr = el.fieldPtr
+		newElement.isInteger = el.isInteger
+	}
+	return newElement
+}
+
+func (el *Element) Len() int {
+	return int(C.element_item_count(el.cptr))
+}
+
+func (el *Element) Item(i int) *Element {
+	if el.checked && i >= el.Len() {
+		panic(ErrOutOfRange)
+	}
+	newElement := &Element{
+		pairing: el.pairing,
+		cptr:    C.element_item(el.cptr, C.int(i)),
+	}
+	if el.checked {
+		newElement.fieldPtr = newElement.cptr.field
+		newElement.isInteger = (newElement.Len() == 0)
+	}
+	return newElement
+}
+
+func (el *Element) X() *big.Int {
+	return el.Item(0).BigInt()
+}
+
+func (el *Element) Y() *big.Int {
+	return el.Item(1).BigInt()
+}
+
+func (el *Element) BruteForceDL(g, h *Element) *Element {
+	if el.checked {
+		el.checkInteger()
+		g.ensureChecked()
+		g.checkCompatible(h)
+	}
+	C.element_dlog_brute_force(el.cptr, g.cptr, h.cptr)
+	return el
+}
+
+func (el *Element) PollardRhoDL(g, h *Element) *Element {
+	if el.checked {
+		el.checkInteger()
+		g.ensureChecked()
+		g.checkCompatible(h)
+	}
+	C.element_dlog_pollard_rho(el.cptr, g.cptr, h.cptr)
+	return el
+}
diff --git a/element_unchecked.go b/element_unchecked.go
deleted file mode 100644
index 5ab103d..0000000
--- a/element_unchecked.go
+++ /dev/null
@@ -1,386 +0,0 @@
-package pbc
-
-/*
-#include <pbc/pbc.h>
-*/
-import "C"
-
-import (
-	"hash"
-	"math/big"
-	"runtime"
-	"unsafe"
-)
-
-type uncheckedElement struct {
-	pairing *pairingImpl
-	d       *C.struct_element_s
-}
-
-func clearUnchecked(element *uncheckedElement) {
-	C.element_clear(element.d)
-}
-
-func initUnchecked(element *uncheckedElement, pairing *pairingImpl, initialize bool, field Field) {
-	element.d = &C.struct_element_s{}
-	element.pairing = pairing
-	if initialize {
-		switch field {
-		case G1:
-			C.element_init_G1(element.d, pairing.data)
-		case G2:
-			C.element_init_G2(element.d, pairing.data)
-		case GT:
-			C.element_init_GT(element.d, pairing.data)
-		case Zr:
-			C.element_init_Zr(element.d, pairing.data)
-		default:
-			panic(ErrUnknownField)
-		}
-	}
-	runtime.SetFinalizer(element, clearUnchecked)
-}
-
-func makeUnchecked(pairing *pairingImpl, field Field) *uncheckedElement {
-	element := &uncheckedElement{}
-	initUnchecked(element, pairing, true, field)
-	return element
-}
-
-func (el *uncheckedElement) data() *C.struct_element_s { return el.d }
-
-func (el *uncheckedElement) Pairing() Pairing { return el.pairing }
-
-func (el *uncheckedElement) NewFieldElement() Element {
-	newElement := &uncheckedElement{}
-	initUnchecked(newElement, el.pairing, false, G1)
-	C.element_init_same_as(newElement.d, el.d)
-	return newElement
-}
-
-func (el *uncheckedElement) Set0() Element {
-	C.element_set0(el.d)
-	return el
-}
-
-func (el *uncheckedElement) Set1() Element {
-	C.element_set1(el.d)
-	return el
-}
-
-func (el *uncheckedElement) SetInt32(i int32) Element {
-	C.element_set_si(el.d, C.long(i))
-	return el
-}
-
-func (el *uncheckedElement) SetBig(i *big.Int) Element {
-	C.element_set_mpz(el.d, &big2mpz(i)[0])
-	return el
-}
-
-func (el *uncheckedElement) Set(src Element) Element {
-	C.element_set(el.d, src.data())
-	return el
-}
-
-func (el *uncheckedElement) SetFromHash(hash []byte) Element {
-	C.element_from_hash(el.d, unsafe.Pointer(&hash[0]), C.int(len(hash)))
-	return el
-}
-
-func (el *uncheckedElement) SetFromStringHash(s string, h hash.Hash) Element {
-	h.Reset()
-	if _, err := h.Write([]byte(s)); err != nil {
-		panic(ErrHashFailure)
-	}
-	return el.SetFromHash(h.Sum([]byte{}))
-}
-
-func (el *uncheckedElement) SetBytes(buf []byte) Element {
-	C.element_from_bytes(el.d, (*C.uchar)(unsafe.Pointer(&buf[0])))
-	return el
-}
-
-func (el *uncheckedElement) SetXBytes(buf []byte) Element {
-	C.element_from_bytes_x_only(el.d, (*C.uchar)(unsafe.Pointer(&buf[0])))
-	return el
-}
-
-func (el *uncheckedElement) SetCompressedBytes(buf []byte) Element {
-	C.element_from_bytes_compressed(el.d, (*C.uchar)(unsafe.Pointer(&buf[0])))
-	return el
-}
-
-func (el *uncheckedElement) SetString(s string, base int) (Element, bool) {
-	cstr := C.CString(s)
-	defer C.free(unsafe.Pointer(cstr))
-
-	if ok := C.element_set_str(el.d, cstr, C.int(base)); ok == 0 {
-		return nil, false
-	}
-	return el, true
-}
-
-func (el *uncheckedElement) BigInt() *big.Int {
-	mpz := newMpz()
-	C.element_to_mpz(&mpz[0], el.d)
-	return mpz2big(mpz)
-}
-
-func (el *uncheckedElement) BytesLen() int {
-	return int(C.element_length_in_bytes(el.d))
-}
-
-func (el *uncheckedElement) writeBytes(buf []byte) C.int {
-	return C.element_to_bytes((*C.uchar)(unsafe.Pointer(&buf[0])), el.d)
-}
-
-func (el *uncheckedElement) Bytes() []byte {
-	buf := make([]byte, el.BytesLen())
-	el.writeBytes(buf)
-	return buf
-}
-
-func (el *uncheckedElement) XBytesLen() int {
-	return int(C.element_length_in_bytes_x_only(el.d))
-}
-
-func (el *uncheckedElement) writeXBytes(buf []byte) C.int {
-	return C.element_to_bytes_x_only((*C.uchar)(unsafe.Pointer(&buf[0])), el.d)
-}
-
-func (el *uncheckedElement) XBytes() []byte {
-	buf := make([]byte, el.XBytesLen())
-	el.writeXBytes(buf)
-	return buf
-}
-
-func (el *uncheckedElement) CompressedBytesLen() int {
-	return int(C.element_length_in_bytes_compressed(el.d))
-}
-
-func (el *uncheckedElement) writeCompressedBytes(buf []byte) C.int {
-	return C.element_to_bytes_compressed((*C.uchar)(unsafe.Pointer(&buf[0])), el.d)
-}
-
-func (el *uncheckedElement) CompressedBytes() []byte {
-	buf := make([]byte, el.CompressedBytesLen())
-	el.writeCompressedBytes(buf)
-	return buf
-}
-
-func (el *uncheckedElement) Len() int {
-	return int(C.element_item_count(el.d))
-}
-
-func (el *uncheckedElement) Item(i int) Element {
-	return &uncheckedElement{
-		pairing: el.pairing,
-		d:       C.element_item(el.d, C.int(i)),
-	}
-}
-
-func (el *uncheckedElement) X() *big.Int {
-	return el.Item(0).BigInt()
-}
-
-func (el *uncheckedElement) Y() *big.Int {
-	return el.Item(1).BigInt()
-}
-
-func (el *uncheckedElement) Is0() bool {
-	return C.element_is0(el.d) != 0
-}
-
-func (el *uncheckedElement) Is1() bool {
-	return C.element_is1(el.d) != 0
-}
-
-func (el *uncheckedElement) IsSquare() bool {
-	return C.element_is_sqr(el.d) != 0
-}
-
-func normalizeSign(sign int64) int {
-	if sign > 0 {
-		return 1
-	}
-	if sign < 0 {
-		return -1
-	}
-	return 0
-}
-
-func (el *uncheckedElement) Sign() int {
-	return normalizeSign(int64(C.element_sign(el.d)))
-}
-
-func (el *uncheckedElement) Cmp(x Element) int {
-	return normalizeSign(int64(C.element_cmp(el.d, x.data())))
-}
-
-func (el *uncheckedElement) Equals(x Element) bool { return el.Cmp(x) == 0 }
-
-func (el *uncheckedElement) Add(x, y Element) Element {
-	C.element_add(el.d, x.data(), y.data())
-	return el
-}
-
-func (el *uncheckedElement) Sub(x, y Element) Element {
-	C.element_sub(el.d, x.data(), y.data())
-	return el
-}
-
-func (el *uncheckedElement) Mul(x, y Element) Element {
-	C.element_mul(el.d, x.data(), y.data())
-	return el
-}
-
-func (el *uncheckedElement) MulBig(x Element, i *big.Int) Element {
-	C.element_mul_mpz(el.d, x.data(), &big2mpz(i)[0])
-	return el
-}
-
-func (el *uncheckedElement) MulInt32(x Element, i int32) Element {
-	C.element_mul_si(el.d, x.data(), C.long(i))
-	return el
-}
-
-func (el *uncheckedElement) MulZn(x, y Element) Element {
-	C.element_mul_zn(el.d, x.data(), y.data())
-	return el
-}
-
-func (el *uncheckedElement) Div(x, y Element) Element {
-	C.element_div(el.d, x.data(), y.data())
-	return el
-}
-
-func (el *uncheckedElement) Double(x Element) Element {
-	C.element_double(el.d, x.data())
-	return el
-}
-
-func (el *uncheckedElement) Halve(x Element) Element {
-	C.element_halve(el.d, x.data())
-	return el
-}
-
-func (el *uncheckedElement) Square(x Element) Element {
-	C.element_square(el.d, x.data())
-	return el
-}
-
-func (el *uncheckedElement) Neg(x Element) Element {
-	C.element_neg(el.d, x.data())
-	return el
-}
-
-func (el *uncheckedElement) Invert(x Element) Element {
-	C.element_invert(el.d, x.data())
-	return el
-}
-
-func (el *uncheckedElement) PowBig(x Element, i *big.Int) Element {
-	C.element_pow_mpz(el.d, x.data(), &big2mpz(i)[0])
-	return el
-}
-
-func (el *uncheckedElement) PowZn(x, i Element) Element {
-	C.element_pow_zn(el.d, x.data(), i.data())
-	return el
-}
-
-func (el *uncheckedElement) Pow2Big(x Element, i *big.Int, y Element, j *big.Int) Element {
-	C.element_pow2_mpz(el.d, x.data(), &big2mpz(i)[0], y.data(), &big2mpz(j)[0])
-	return el
-}
-
-func (el *uncheckedElement) Pow2Zn(x, i, y, j Element) Element {
-	C.element_pow2_zn(el.d, x.data(), i.data(), y.data(), j.data())
-	return el
-}
-
-func (el *uncheckedElement) Pow3Big(x Element, i *big.Int, y Element, j *big.Int, z Element, k *big.Int) Element {
-	C.element_pow3_mpz(el.d, x.data(), &big2mpz(i)[0], y.data(), &big2mpz(j)[0], z.data(), &big2mpz(k)[0])
-	return el
-}
-
-func (el *uncheckedElement) Pow3Zn(x, i, y, j, z, k Element) Element {
-	C.element_pow3_zn(el.d, x.data(), i.data(), y.data(), j.data(), z.data(), k.data())
-	return el
-}
-
-func (el *uncheckedElement) PreparePower() Power { return initPower(el) }
-
-func (el *uncheckedElement) PowerBig(power Power, i *big.Int) Element {
-	C.element_pp_pow(el.d, &big2mpz(i)[0], power.(*powerImpl).data)
-	return el
-}
-
-func (el *uncheckedElement) PowerZn(power Power, i Element) Element {
-	C.element_pp_pow_zn(el.d, i.data(), power.(*powerImpl).data)
-	return el
-}
-
-func (el *uncheckedElement) Pair(x, y Element) Element {
-	C.pairing_apply(el.d, x.data(), y.data(), el.pairing.data)
-	return el
-}
-
-func (el *uncheckedElement) doProdPair(in1, in2 []C.struct_element_s) Element {
-	x := (*C.element_t)(unsafe.Pointer(&in1[0]))
-	y := (*C.element_t)(unsafe.Pointer(&in2[0]))
-	C.element_prod_pairing(el.d, x, y, C.int(len(in1)))
-	return el
-}
-
-func (el *uncheckedElement) ProdPair(elements ...Element) Element {
-	n := len(elements)
-	if n%2 != 0 {
-		panic(ErrBadPairList)
-	}
-	half := n / 2
-	in1 := make([]C.struct_element_s, half)
-	in2 := make([]C.struct_element_s, half)
-	for i, j := 0, 0; j < n; i, j = i+1, j+2 {
-		in1[i] = *elements[j].data()
-		in2[i] = *elements[j+1].data()
-	}
-	return el.doProdPair(in1, in2)
-}
-
-func (el *uncheckedElement) ProdPairSlice(x, y []Element) Element {
-	n := len(x)
-	if n != len(y) {
-		panic(ErrBadPairList)
-	}
-	in1 := make([]C.struct_element_s, n)
-	in2 := make([]C.struct_element_s, n)
-	for i := 0; i < n; i++ {
-		in1[i] = *x[i].data()
-		in2[i] = *y[i].data()
-	}
-	return el.doProdPair(in1, in2)
-}
-
-func (el *uncheckedElement) PreparePairer() Pairer { return initPairer(el) }
-
-func (el *uncheckedElement) PairerPair(pairer Pairer, y Element) Element {
-	C.pairing_pp_apply(el.d, y.data(), pairer.(*pairerImpl).data)
-	return el
-}
-
-func (el *uncheckedElement) BruteForceDL(g, h Element) Element {
-	C.element_dlog_brute_force(el.d, g.data(), h.data())
-	return el
-}
-
-func (el *uncheckedElement) PollardRhoDL(g, h Element) Element {
-	C.element_dlog_pollard_rho(el.d, g.data(), h.data())
-	return el
-}
-
-func (el *uncheckedElement) Rand() Element {
-	C.element_random(el.d)
-	return el
-}
diff --git a/generation.go b/generation.go
index 8de154d..e35ea3d 100644
--- a/generation.go
+++ b/generation.go
@@ -44,9 +44,9 @@ import (
 //
 // For example:
 // 	params := pbc.GenerateA(160, 512)
-func GenerateA(rbits uint32, qbits uint32) Params {
+func GenerateA(rbits uint32, qbits uint32) *Params {
 	params := makeParams()
-	C.pbc_param_init_a_gen(params.data, C.int(rbits), C.int(qbits))
+	C.pbc_param_init_a_gen(params.cptr, C.int(rbits), C.int(qbits))
 	return params
 }
 
@@ -55,9 +55,9 @@ func GenerateA(rbits uint32, qbits uint32) Params {
 // r is the product of two large primes. In this case, r should infeasible to
 // factor. Each prime should be at least 512 bits (causing r to be 1024 bits in
 // general), but preferably 1024 bits or more.
-func GenerateA1(r *big.Int) Params {
+func GenerateA1(r *big.Int) *Params {
 	params := makeParams()
-	C.pbc_param_init_a1_gen(params.data, &big2mpz(r)[0])
+	C.pbc_param_init_a1_gen(params.cptr, &big2mpz(r)[0])
 	return params
 }
 
@@ -81,7 +81,7 @@ func GenerateA1(r *big.Int) Params {
 //
 // For example:
 // 	params, err := pbc.GenerateD(9563, 160, 171, 500)
-func GenerateD(d uint32, rbits uint32, qbits uint32, bitlimit uint32) (Params, error) {
+func GenerateD(d uint32, rbits uint32, qbits uint32, bitlimit uint32) (*Params, error) {
 	return generateWithCM(true, d, rbits, qbits, bitlimit)
 }
 
@@ -97,9 +97,9 @@ func GenerateD(d uint32, rbits uint32, qbits uint32, bitlimit uint32) (Params, e
 //
 // For example:
 // 	params, err := pbc.GenerateE(160, 1024)
-func GenerateE(rbits uint32, qbits uint32) Params {
+func GenerateE(rbits uint32, qbits uint32) *Params {
 	params := makeParams()
-	C.pbc_param_init_e_gen(params.data, C.int(rbits), C.int(qbits))
+	C.pbc_param_init_e_gen(params.cptr, C.int(rbits), C.int(qbits))
 	return params
 }
 
@@ -115,9 +115,9 @@ func GenerateE(rbits uint32, qbits uint32) Params {
 //
 // For example:
 // 	params, err := pbc.GenerateF(160)
-func GenerateF(bits uint32) Params {
+func GenerateF(bits uint32) *Params {
 	params := makeParams()
-	C.pbc_param_init_f_gen(params.data, C.int(bits))
+	C.pbc_param_init_f_gen(params.cptr, C.int(bits))
 	return params
 }
 
@@ -132,14 +132,14 @@ func GenerateF(bits uint32) Params {
 //
 // For example:
 // 	params, err := pbc.GenerateG(9563, 160, 171, 500)
-func GenerateG(d uint32, rbits uint32, qbits uint32, bitlimit uint32) (Params, error) {
+func GenerateG(d uint32, rbits uint32, qbits uint32, bitlimit uint32) (*Params, error) {
 	return generateWithCM(false, d, qbits, rbits, bitlimit)
 }
 
-func generateWithCM(typeD bool, d uint32, rbits uint32, qbits uint32, bitlimit uint32) (Params, error) {
+func generateWithCM(typeD bool, d uint32, rbits uint32, qbits uint32, bitlimit uint32) (*Params, error) {
 	params := makeParams()
 	settings := &C.check_pairing_settings_t{
-		params: params.data,
+		params: params.cptr,
 		rbits:  C.uint32_t(rbits),
 		qbits:  C.uint32_t(qbits),
 	}
diff --git a/pairing.go b/pairing.go
index fb45fc6..e7f532c 100644
--- a/pairing.go
+++ b/pairing.go
@@ -29,47 +29,20 @@ const (
 // or unchecked. Unchecked elements are slightly faster, but do not check to
 // ensure that operations make sense. Checked elements defend against a variety
 // of errors. For more details, see the Element documentation.
-type Pairing interface {
-	// IsSymmetric returns true if G1 == G2 for this pairing.
-	IsSymmetric() bool
-
-	// Various methods to return the sizes of group elements.
-	// *Length() returns the length of an element in bytes.
-	// *XLength() returns the length of an X coordinate only, in bytes.
-	// *CompressedLength() returns the length of a compressed element in bytes.
-	G1Length() uint
-	G1XLength() uint
-	G1CompressedLength() uint
-	G2Length() uint
-	G2XLength() uint
-	G2CompressedLength() uint
-	GTLength() uint
-	ZrLength() uint
-
-	// Initialization methods for group elements.
-	NewG1() Element
-	NewG2() Element
-	NewGT() Element
-	NewZr() Element
-
-	// Initializes an element without type checking.
-	NewElement(Field) Element
-}
-
-type pairingImpl struct {
-	data *C.struct_pairing_s
+type Pairing struct {
+	cptr *C.struct_pairing_s
 }
 
 // NewPairing instantiates a pairing from a set of parameters.
-func NewPairing(params Params) Pairing {
+func NewPairing(params *Params) *Pairing {
 	pairing := makePairing()
-	C.pairing_init_pbc_param(pairing.data, params.(*paramsImpl).data)
+	C.pairing_init_pbc_param(pairing.cptr, params.cptr)
 	return pairing
 }
 
 // NewPairingFromReader loads pairing parameters from a Reader and instantiates
 // a pairing.
-func NewPairingFromReader(params io.Reader) (Pairing, error) {
+func NewPairingFromReader(params io.Reader) (*Pairing, error) {
 	buf := new(bytes.Buffer)
 	buf.ReadFrom(params)
 	return NewPairingFromString(buf.String())
@@ -77,7 +50,7 @@ func NewPairingFromReader(params io.Reader) (Pairing, error) {
 
 // NewPairingFromString loads pairing parameters from a string and instantiates
 // a pairing.
-func NewPairingFromString(params string) (Pairing, error) {
+func NewPairingFromString(params string) (*Pairing, error) {
 	p, err := NewParamsFromString(params)
 	if err != nil {
 		return nil, err
@@ -85,54 +58,69 @@ func NewPairingFromString(params string) (Pairing, error) {
 	return NewPairing(p), nil
 }
 
-func (pairing *pairingImpl) IsSymmetric() bool {
-	return C.pairing_is_symmetric(pairing.data) != 0
+// IsSymmetric returns true if G1 == G2 for this pairing.
+func (pairing *Pairing) IsSymmetric() bool {
+	return C.pairing_is_symmetric(pairing.cptr) != 0
+}
+
+func (pairing *Pairing) G1Length() uint {
+	return uint(C.pairing_length_in_bytes_G1(pairing.cptr))
 }
 
-func (pairing *pairingImpl) G1Length() uint {
-	return uint(C.pairing_length_in_bytes_G1(pairing.data))
+func (pairing *Pairing) G1XLength() uint {
+	return uint(C.pairing_length_in_bytes_x_only_G1(pairing.cptr))
 }
 
-func (pairing *pairingImpl) G1XLength() uint {
-	return uint(C.pairing_length_in_bytes_x_only_G1(pairing.data))
+func (pairing *Pairing) G1CompressedLength() uint {
+	return uint(C.pairing_length_in_bytes_compressed_G1(pairing.cptr))
 }
 
-func (pairing *pairingImpl) G1CompressedLength() uint {
-	return uint(C.pairing_length_in_bytes_compressed_G1(pairing.data))
+func (pairing *Pairing) G2Length() uint {
+	return uint(C.pairing_length_in_bytes_G2(pairing.cptr))
 }
 
-func (pairing *pairingImpl) G2Length() uint {
-	return uint(C.pairing_length_in_bytes_G2(pairing.data))
+func (pairing *Pairing) G2XLength() uint {
+	return uint(C.pairing_length_in_bytes_x_only_G2(pairing.cptr))
 }
 
-func (pairing *pairingImpl) G2XLength() uint {
-	return uint(C.pairing_length_in_bytes_x_only_G2(pairing.data))
+func (pairing *Pairing) G2CompressedLength() uint {
+	return uint(C.pairing_length_in_bytes_compressed_G2(pairing.cptr))
 }
 
-func (pairing *pairingImpl) G2CompressedLength() uint {
-	return uint(C.pairing_length_in_bytes_compressed_G2(pairing.data))
+func (pairing *Pairing) GTLength() uint {
+	return uint(C.pairing_length_in_bytes_GT(pairing.cptr))
 }
 
-func (pairing *pairingImpl) GTLength() uint {
-	return uint(C.pairing_length_in_bytes_GT(pairing.data))
+func (pairing *Pairing) ZrLength() uint {
+	return uint(C.pairing_length_in_bytes_Zr(pairing.cptr))
 }
 
-func (pairing *pairingImpl) ZrLength() uint {
-	return uint(C.pairing_length_in_bytes_Zr(pairing.data))
+func (pairing *Pairing) NewG1() *Element {
+	return makeCheckedElement(pairing, G1, pairing.cptr.G1)
 }
 
-func (pairing *pairingImpl) NewG1() Element                 { return makeChecked(pairing, G1, pairing.data.G1) }
-func (pairing *pairingImpl) NewG2() Element                 { return makeChecked(pairing, G2, pairing.data.G2) }
-func (pairing *pairingImpl) NewGT() Element                 { return makeChecked(pairing, GT, &pairing.data.GT[0]) }
-func (pairing *pairingImpl) NewZr() Element                 { return makeChecked(pairing, Zr, &pairing.data.Zr[0]) }
-func (pairing *pairingImpl) NewElement(field Field) Element { return makeUnchecked(pairing, field) }
+func (pairing *Pairing) NewG2() *Element {
+	return makeCheckedElement(pairing, G2, pairing.cptr.G2)
+}
+
+func (pairing *Pairing) NewGT() *Element {
+	return makeCheckedElement(pairing, GT, &pairing.cptr.GT[0])
+}
+
+func (pairing *Pairing) NewZr() *Element {
+	return makeCheckedElement(pairing, Zr, &pairing.cptr.Zr[0])
+}
+
+func (pairing *Pairing) NewElement(field Field) *Element {
+	return makeUncheckedElement(pairing, true, field)
+}
 
-func clearPairing(pairing *pairingImpl) {
-	C.pairing_clear(pairing.data)
+func clearPairing(pairing *Pairing) {
+	C.pairing_clear(pairing.cptr)
 }
 
-func makePairing() *pairingImpl {
-	pairing := &pairingImpl{data: &C.struct_pairing_s{}}
+func makePairing() *Pairing {
+	pairing := &Pairing{cptr: &C.struct_pairing_s{}}
 	runtime.SetFinalizer(pairing, clearPairing)
 	return pairing
 }
diff --git a/params.go b/params.go
index 82212a4..87fb87b 100644
--- a/params.go
+++ b/params.go
@@ -25,18 +25,12 @@ import (
 // Normally, parameters are generated once using a generation program and then
 // distributed with the final application. Parameters can be exported for this
 // purpose using the WriteTo or String methods.
-type Params interface {
-	NewPairing() Pairing
-	WriteTo(w io.Writer) (n int64, err error)
-	String() string
-}
-
-type paramsImpl struct {
-	data *C.struct_pbc_param_s
+type Params struct {
+	cptr *C.struct_pbc_param_s
 }
 
 // NewParams loads pairing parameters from a Reader.
-func NewParams(r io.Reader) (Params, error) {
+func NewParams(r io.Reader) (*Params, error) {
 	b, err := ioutil.ReadAll(r)
 	if err != nil {
 		return nil, err
@@ -45,31 +39,31 @@ func NewParams(r io.Reader) (Params, error) {
 }
 
 // NewParamsFromString loads pairing parameters from a string.
-func NewParamsFromString(s string) (Params, error) {
+func NewParamsFromString(s string) (*Params, error) {
 	cstr := C.CString(s)
 	defer C.free(unsafe.Pointer(cstr))
 
 	params := makeParams()
-	if ok := C.pbc_param_init_set_str(params.data, cstr); ok != 0 {
+	if ok := C.pbc_param_init_set_str(params.cptr, cstr); ok != 0 {
 		return nil, ErrInvalidParamString
 	}
 	return params, nil
 }
 
 // NewPairing creates a Pairing using these parameters.
-func (params *paramsImpl) NewPairing() Pairing {
+func (params *Params) NewPairing() *Pairing {
 	return NewPairing(params)
 }
 
-func (params *paramsImpl) WriteTo(w io.Writer) (n int64, err error) {
+func (params *Params) WriteTo(w io.Writer) (n int64, err error) {
 	count, err := io.WriteString(w, params.String())
 	return int64(count), err
 }
 
-func (params *paramsImpl) String() string {
+func (params *Params) String() string {
 	var buf *C.char
 	var bufLen C.size_t
-	if C.param_out_str_wrapper(&buf, &bufLen, params.data) == 0 {
+	if C.param_out_str_wrapper(&buf, &bufLen, params.cptr) == 0 {
 		return ""
 	}
 	str := C.GoStringN(buf, C.int(bufLen))
@@ -77,12 +71,12 @@ func (params *paramsImpl) String() string {
 	return str
 }
 
-func clearParams(params *paramsImpl) {
-	C.pbc_param_clear(params.data)
+func clearParams(params *Params) {
+	C.pbc_param_clear(params.cptr)
 }
 
-func makeParams() *paramsImpl {
-	params := &paramsImpl{data: &C.struct_pbc_param_s{}}
+func makeParams() *Params {
+	params := &Params{cptr: &C.struct_pbc_param_s{}}
 	runtime.SetFinalizer(params, clearParams)
 	return params
 }
-- 
GitLab