diff --git a/element.go b/element.go
new file mode 100644
index 0000000000000000000000000000000000000000..03bd8b8ae3ba91dc140b803ebecbae4f7e61d55c
--- /dev/null
+++ b/element.go
@@ -0,0 +1,170 @@
+package pbc
+
+/*
+#cgo LDFLAGS: /usr/local/lib/libpbc.a -lgmp
+#include <pbc/pbc.h>
+*/
+import "C"
+
+import (
+	"errors"
+	"fmt"
+	"math/big"
+	"runtime"
+)
+
+var ErrUnknownField = errors.New("unchecked element initialized in unknown field")
+
+type Element interface {
+	NewFieldElement() Element
+
+	Set0() Element
+	Set1() Element
+	SetInt32(int32) Element
+	SetBig(*big.Int) Element
+	Set(Element) Element
+
+	SetFromHash([]byte) Element
+	SetBytes([]byte) Element
+	SetXBytes([]byte) Element
+	SetCompressedBytes([]byte) Element
+
+	SetString(s string, base int) (Element, bool)
+
+	Format(fmt.State, rune)
+	Scan(fmt.ScanState, rune) error
+
+	BigInt() *big.Int
+	String() string
+
+	BytesLen() int
+	Bytes() []byte
+	XBytesLen() int
+	XBytes() []byte
+	CompressedBytesLen() int
+	CompressedBytes() []byte
+
+	Len() int
+	Item(int) Element
+	X() *big.Int
+	Y() *big.Int
+
+	Is0() bool
+	Is1() bool
+	IsSquare() bool
+	Sign() int
+
+	Cmp(x Element) int
+
+	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
+
+	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
+
+	PreparePower() Power
+
+	BruteForceDL(g, h Element) Element
+	PollardRhoDL(g, h Element) Element
+
+	Rand() Element
+
+	impl() *elementImpl
+}
+
+type elementImpl struct {
+	pairing *pairingImpl
+	data    *C.struct_element_s
+}
+
+type checkedElement struct {
+	elementImpl
+	fieldPtr  *C.struct_field_s
+	isInteger bool
+}
+
+type Power interface {
+	PowBig(i *big.Int) Element
+	PowZn(i Element) Element
+}
+
+type powerImpl struct {
+	target *elementImpl
+	data   *C.struct_element_pp_s
+}
+
+type checkedPower struct {
+	powerImpl
+}
+
+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 makeElement(pairing, field) }
+
+func clearElement(element *elementImpl) {
+	println("clearelement")
+	C.element_clear(element.data)
+}
+
+func initElement(element *elementImpl, pairing *pairingImpl, initialize bool, field Field) {
+	element.data = &C.struct_element_s{}
+	element.pairing = pairing
+	if initialize {
+		switch field {
+		case G1:
+			C.element_init_G1(element.data, pairing.data)
+		case G2:
+			C.element_init_G2(element.data, pairing.data)
+		case GT:
+			C.element_init_GT(element.data, pairing.data)
+		case Zr:
+			C.element_init_Zr(element.data, pairing.data)
+		default:
+			panic(ErrUnknownField)
+		}
+	}
+	runtime.SetFinalizer(element, clearElement)
+}
+
+func makeElement(pairing *pairingImpl, field Field) *elementImpl {
+	element := &elementImpl{}
+	initElement(element, pairing, true, field)
+	return element
+}
+
+func makeChecked(pairing *pairingImpl, field Field, fieldPtr *C.struct_field_s) *checkedElement {
+	element := &checkedElement{
+		fieldPtr:  fieldPtr,
+		isInteger: field == Zr,
+	}
+	initElement(&element.elementImpl, pairing, true, field)
+	return element
+}
+
+func clearPower(power *powerImpl) {
+	println("clearpower")
+	C.element_pp_clear(power.data)
+}
+
+func initPower(power *powerImpl, target *elementImpl) {
+	power.target = target
+	power.data = &C.struct_element_pp_s{}
+	C.element_pp_init(power.data, target.data)
+	runtime.SetFinalizer(power, clearPower)
+}
diff --git a/element_checked.go b/element_checked.go
new file mode 100644
index 0000000000000000000000000000000000000000..de4dd76fa2451a129ca93981214c7ecf04bca66a
--- /dev/null
+++ b/element_checked.go
@@ -0,0 +1,237 @@
+package pbc
+
+/*
+#cgo LDFLAGS: /usr/local/lib/libpbc.a -lgmp
+#include <pbc/pbc.h>
+*/
+import "C"
+
+import (
+	"errors"
+	"math/big"
+)
+
+var (
+	ErrIllegalOp    = errors.New("operation is illegal for elements of this type")
+	ErrUncheckedOp  = errors.New("unchecked element passed to checked operation")
+	ErrIncompatible = errors.New("elements are from incompatible fields or pairings")
+	ErrOutOfRange   = errors.New("index out of range")
+	ErrInternal     = errors.New("a severe internal error has lead to possible memory corruption")
+)
+
+func (el *checkedElement) impl() *elementImpl { return &el.elementImpl }
+
+func element2Checked(x Element) *checkedElement {
+	checked, ok := x.(*checkedElement)
+	if !ok {
+		panic(ErrUncheckedOp)
+	}
+	return checked
+}
+
+func (el *checkedElement) checkCompatible(other Element) {
+	otherChecked := element2Checked(other)
+	if el.fieldPtr != otherChecked.fieldPtr {
+		panic(ErrIncompatible)
+	}
+}
+
+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
+	initElement(&newElement.elementImpl, el.pairing, false, G1)
+	C.element_init_same_as(newElement.elementImpl.data, el.data)
+	return newElement
+}
+
+func (el *checkedElement) SetInt32(i int32) Element {
+	el.checkInteger()
+	return el.elementImpl.SetInt32(i)
+}
+
+func (el *checkedElement) SetBig(i *big.Int) Element {
+	el.checkInteger()
+	return el.elementImpl.SetBig(i)
+}
+
+func (el *checkedElement) Set(src Element) Element {
+	el.checkCompatible(src)
+	return el.elementImpl.Set(src)
+}
+
+func (el *checkedElement) BigInt() *big.Int {
+	el.checkInteger()
+	return el.elementImpl.BigInt()
+}
+
+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.elementImpl.writeBytes(buf), buf)
+}
+
+func (el *checkedElement) XBytes() []byte {
+	buf := make([]byte, el.XBytesLen())
+	return checkedWrite(el.elementImpl.writeXBytes(buf), buf)
+}
+
+func (el *checkedElement) CompressedBytes() []byte {
+	buf := make([]byte, el.CompressedBytesLen())
+	return checkedWrite(el.elementImpl.writeCompressedBytes(buf), buf)
+}
+
+func (el *checkedElement) Item(i int) Element {
+	if i >= el.Len() {
+		panic(ErrOutOfRange)
+	}
+	uncheckedData := el.elementImpl.Item(i).(*elementImpl)
+	item := &checkedElement{
+		fieldPtr:  uncheckedData.data.field,
+		isInteger: uncheckedData.Len() == 0,
+	}
+	item.elementImpl = *uncheckedData
+	return item
+}
+
+func (el *checkedElement) Cmp(x Element) int {
+	el.checkCompatible(x)
+	return el.elementImpl.Cmp(x)
+}
+
+func (el *checkedElement) Add(x Element, y Element) Element {
+	el.checkAllCompatible(x, y)
+	return el.elementImpl.Add(x, y)
+}
+
+func (el *checkedElement) Sub(x, y Element) Element {
+	el.checkAllCompatible(x, y)
+	return el.elementImpl.Sub(x, y)
+}
+
+func (el *checkedElement) Mul(x, y Element) Element {
+	el.checkAllCompatible(x, y)
+	return el.elementImpl.Mul(x, y)
+}
+
+func (el *checkedElement) MulBig(x Element, i *big.Int) Element {
+	el.checkCompatible(x)
+	return el.elementImpl.MulBig(x, i)
+}
+
+func (el *checkedElement) MulInt32(x Element, i int32) Element {
+	el.checkCompatible(x)
+	return el.elementImpl.MulInt32(x, i)
+}
+
+func (el *checkedElement) MulZn(x, y Element) Element {
+	el.checkCompatible(x)
+	element2Checked(y).checkInteger()
+	return el.elementImpl.MulZn(x, y)
+}
+
+func (el *checkedElement) Div(x, y Element) Element {
+	el.checkAllCompatible(x, y)
+	return el.elementImpl.Div(x, y)
+}
+
+func (el *checkedElement) Double(x Element) Element {
+	el.checkCompatible(x)
+	return el.elementImpl.Double(x)
+}
+
+func (el *checkedElement) Halve(x Element) Element {
+	el.checkCompatible(x)
+	return el.elementImpl.Halve(x)
+}
+
+func (el *checkedElement) Square(x Element) Element {
+	el.checkCompatible(x)
+	return el.elementImpl.Square(x)
+}
+
+func (el *checkedElement) Neg(x Element) Element {
+	el.checkCompatible(x)
+	return el.elementImpl.Neg(x)
+}
+
+func (el *checkedElement) Invert(x Element) Element {
+	el.checkCompatible(x)
+	return el.elementImpl.Invert(x)
+}
+
+func (el *checkedElement) PowBig(x Element, i *big.Int) Element {
+	el.checkCompatible(x)
+	return el.elementImpl.PowBig(x, i)
+}
+
+func (el *checkedElement) PowZn(x, i Element) Element {
+	el.checkCompatible(x)
+	element2Checked(i).checkInteger()
+	return el.elementImpl.PowZn(x, i)
+}
+
+func (el *checkedElement) Pow2Big(x Element, i *big.Int, y Element, j *big.Int) Element {
+	el.checkAllCompatible(x, y)
+	return el.elementImpl.Pow2Big(x, i, y, j)
+}
+
+func (el *checkedElement) Pow2Zn(x, i, y, j Element) Element {
+	el.checkAllCompatible(x, y)
+	element2Checked(i).checkInteger()
+	element2Checked(j).checkInteger()
+	return el.elementImpl.Pow2Zn(x, i, y, j)
+}
+
+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)
+	return el.elementImpl.Pow3Big(x, i, y, j, z, k)
+}
+
+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()
+	return el.elementImpl.Pow3Zn(x, i, y, j, z, k)
+}
+
+func (el *checkedElement) PreparePower() Power {
+	power := &checkedPower{}
+	initPower(&power.powerImpl, &el.elementImpl)
+	return power
+}
+
+func (power *checkedPower) PowZn(i Element) Element {
+	element2Checked(i).checkInteger()
+	return power.powerImpl.PowZn(i)
+}
+
+func (el *checkedElement) BruteForceDL(g, h Element) Element {
+	el.checkInteger()
+	element2Checked(g).checkCompatible(h)
+	return el.elementImpl.BruteForceDL(g, h)
+}
+
+func (el *checkedElement) PollardRhoDL(g, h Element) Element {
+	el.checkInteger()
+	element2Checked(g).checkCompatible(h)
+	return el.elementImpl.PollardRhoDL(g, h)
+}
diff --git a/element_fmt.go b/element_fmt.go
new file mode 100644
index 0000000000000000000000000000000000000000..055746354544222ba1ce9c00945bfd4fbc8d1bb0
--- /dev/null
+++ b/element_fmt.go
@@ -0,0 +1,184 @@
+package pbc
+
+/*
+#cgo LDFLAGS: /usr/local/lib/libpbc.a -lgmp
+#include <pbc/pbc.h>
+
+int element_out_str_wrapper(char** bufp, size_t* sizep, int base, element_t e) {
+	FILE* handle = open_memstream(bufp, sizep);
+	if (!handle) return 0;
+	element_out_str(handle, base, e);
+	fclose(handle);
+	return 1;
+}
+*/
+import "C"
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"io"
+	"unsafe"
+)
+
+var (
+	ErrBadInput = errors.New("invalid element format during scan")
+	ErrBadVerb  = errors.New("invalid verb specified for scan")
+)
+
+func (el *elementImpl) errorFormat(f fmt.State, c rune, err string) {
+	fmt.Fprintf(f, "%%!%c(%s pbc.Element)", c, err)
+}
+
+func (el *elementImpl) nativeFormat(f fmt.State, c rune) {
+	base := 10
+	if width, ok := f.Width(); ok {
+		if width < 2 || width > 36 {
+			el.errorFormat(f, c, "BADBASE")
+			return
+		}
+		base = width
+	}
+	var buf *C.char
+	var bufLen C.size_t
+	if C.element_out_str_wrapper(&buf, &bufLen, C.int(base), el.data) == 0 {
+		el.errorFormat(f, c, "INTERNALERROR")
+		return
+	}
+	str := C.GoStringN(buf, C.int(bufLen))
+	C.free(unsafe.Pointer(buf))
+	fmt.Fprintf(f, "%s", str)
+}
+
+func (el *elementImpl) 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).impl().customFormat(f, c)
+			if i+1 < count {
+				fmt.Fprintf(f, ", ")
+			}
+		}
+		fmt.Fprintf(f, "]")
+	}
+}
+
+func (el *elementImpl) 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)
+			break
+		}
+		fallthrough
+	case 's':
+		el.nativeFormat(f, c)
+	case 'd', 'b', 'o', 'x', 'X':
+		el.customFormat(f, c)
+	default:
+		el.errorFormat(f, c, "BADVERB")
+	}
+}
+
+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.pairing, el)
+	} else {
+		el.elementImpl.Format(f, c)
+	}
+}
+
+func (el *elementImpl) String() string {
+	return fmt.Sprintf("%s", el)
+}
+
+func (el *elementImpl) Scan(state fmt.ScanState, verb rune) error {
+	if verb != 's' && verb != 'v' {
+		return ErrBadVerb
+	}
+	base, ok := state.Width()
+	if !ok {
+		base = 10
+	} else if base < 2 || base > 36 {
+		return ErrBadVerb
+	}
+	maxDigit := '9'
+	maxAlpha := 'z'
+	if base < 10 {
+		maxDigit = rune('0' + (base - 1))
+	}
+	if base < 36 {
+		maxAlpha = rune('a' + (base - 11))
+	}
+
+	state.SkipSpace()
+
+	tokensFound := make([]uint, 0, 5)
+	inToken := false
+	justDescended := false
+	expectTokenDone := false
+	var buf bytes.Buffer
+
+ReadLoop:
+	for {
+		r, _, err := state.ReadRune()
+		if err != nil {
+			if err == io.EOF {
+				if len(tokensFound) == 0 {
+					break ReadLoop
+				}
+				return ErrBadInput
+			}
+			return err
+		}
+		buf.WriteRune(r)
+
+		if expectTokenDone && r != ',' && r != ']' {
+			return ErrBadInput
+		}
+		expectTokenDone = false
+
+		switch r {
+		case '[':
+			if inToken {
+				return ErrBadInput
+			}
+			tokensFound = append(tokensFound, 0)
+		case ']':
+			if !inToken || len(tokensFound) == 0 || tokensFound[len(tokensFound)-1] == 0 {
+				return ErrBadInput
+			}
+			tokensFound = tokensFound[:len(tokensFound)-1]
+			if len(tokensFound) == 0 {
+				break ReadLoop
+			}
+		case ',':
+			if len(tokensFound) == 0 || (!inToken && !justDescended) {
+				return ErrBadInput
+			}
+			tokensFound[len(tokensFound)-1]++
+			inToken = false
+			state.SkipSpace()
+		case 'O':
+			if inToken {
+				return ErrBadInput
+			}
+			expectTokenDone = true
+			inToken = true
+		default:
+			if (r < '0' || r > maxDigit) && (r < 'a' || r > maxAlpha) {
+				return ErrBadInput
+			}
+			inToken = true
+		}
+		justDescended = (r == ']')
+	}
+	if _, ok := el.SetString(buf.String(), base); !ok {
+		return ErrBadInput
+	}
+	return nil
+}
diff --git a/element_unchecked.go b/element_unchecked.go
new file mode 100644
index 0000000000000000000000000000000000000000..8da3acfbd85202a4b8d45646d44aa5256285044f
--- /dev/null
+++ b/element_unchecked.go
@@ -0,0 +1,294 @@
+package pbc
+
+/*
+#cgo LDFLAGS: /usr/local/lib/libpbc.a -lgmp
+#include <pbc/pbc.h>
+*/
+import "C"
+
+import (
+	"math/big"
+	"unsafe"
+)
+
+func (el *elementImpl) impl() *elementImpl { return el }
+
+func (el *elementImpl) NewFieldElement() Element {
+	newElement := &elementImpl{}
+	initElement(newElement, el.pairing, false, G1)
+	C.element_init_same_as(newElement.data, el.data)
+	return newElement
+}
+
+func (el *elementImpl) Set0() Element {
+	C.element_set0(el.data)
+	return el
+}
+
+func (el *elementImpl) Set1() Element {
+	C.element_set1(el.data)
+	return el
+}
+
+func (el *elementImpl) SetInt32(i int32) Element {
+	C.element_set_si(el.data, C.long(i))
+	return el
+}
+
+func (el *elementImpl) SetBig(i *big.Int) Element {
+	C.element_set_mpz(el.data, &big2mpz(i)[0])
+	return el
+}
+
+func (el *elementImpl) Set(src Element) Element {
+	C.element_set(el.data, src.impl().data)
+	return el
+}
+
+func (el *elementImpl) SetBytes(buf []byte) Element {
+	C.element_from_bytes(el.data, (*C.uchar)(unsafe.Pointer(&buf[0])))
+	return el
+}
+
+func (el *elementImpl) SetXBytes(buf []byte) Element {
+	C.element_from_bytes_x_only(el.data, (*C.uchar)(unsafe.Pointer(&buf[0])))
+	return el
+}
+
+func (el *elementImpl) SetCompressedBytes(buf []byte) Element {
+	C.element_from_bytes_compressed(el.data, (*C.uchar)(unsafe.Pointer(&buf[0])))
+	return el
+}
+
+func (el *elementImpl) SetFromHash(hash []byte) Element {
+	C.element_from_hash(el.data, unsafe.Pointer(&hash[0]), C.int(len(hash)))
+	return el
+}
+
+func (el *elementImpl) SetString(s string, base int) (Element, bool) {
+	cstr := C.CString(s)
+	defer C.free(unsafe.Pointer(cstr))
+
+	if ok := C.element_set_str(el.data, cstr, C.int(base)); ok == 0 {
+		return nil, false
+	}
+	return el, true
+}
+
+func (el *elementImpl) BigInt() *big.Int {
+	mpz := newmpz()
+	C.element_to_mpz(&mpz[0], el.data)
+	return mpz2big(mpz)
+}
+
+func (el *elementImpl) BytesLen() int {
+	return int(C.element_length_in_bytes(el.data))
+}
+
+func (el *elementImpl) writeBytes(buf []byte) C.int {
+	return C.element_to_bytes((*C.uchar)(unsafe.Pointer(&buf[0])), el.data)
+}
+
+func (el *elementImpl) Bytes() []byte {
+	buf := make([]byte, el.BytesLen())
+	el.writeBytes(buf)
+	return buf
+}
+
+func (el *elementImpl) XBytesLen() int {
+	return int(C.element_length_in_bytes_x_only(el.data))
+}
+
+func (el *elementImpl) writeXBytes(buf []byte) C.int {
+	return C.element_to_bytes_x_only((*C.uchar)(unsafe.Pointer(&buf[0])), el.data)
+}
+
+func (el *elementImpl) XBytes() []byte {
+	buf := make([]byte, el.XBytesLen())
+	el.writeXBytes(buf)
+	return buf
+}
+
+func (el *elementImpl) CompressedBytesLen() int {
+	return int(C.element_length_in_bytes_compressed(el.data))
+}
+
+func (el *elementImpl) writeCompressedBytes(buf []byte) C.int {
+	return C.element_to_bytes_compressed((*C.uchar)(unsafe.Pointer(&buf[0])), el.data)
+}
+
+func (el *elementImpl) CompressedBytes() []byte {
+	buf := make([]byte, el.CompressedBytesLen())
+	el.writeCompressedBytes(buf)
+	return buf
+}
+
+func (el *elementImpl) Len() int {
+	return int(C.element_item_count(el.data))
+}
+
+func (el *elementImpl) Item(i int) Element {
+	return &elementImpl{
+		pairing: el.pairing,
+		data:    C.element_item(el.data, C.int(i)),
+	}
+}
+
+func (el *elementImpl) X() *big.Int {
+	return el.Item(0).BigInt()
+}
+
+func (el *elementImpl) Y() *big.Int {
+	return el.Item(1).BigInt()
+}
+
+func (el *elementImpl) Is0() bool {
+	return C.element_is0(el.data) != 0
+}
+
+func (el *elementImpl) Is1() bool {
+	return C.element_is1(el.data) != 0
+}
+
+func (el *elementImpl) IsSquare() bool {
+	return C.element_is_sqr(el.data) != 0
+}
+
+func normalizeSign(sign int64) int {
+	if sign > 0 {
+		return 1
+	}
+	if sign < 0 {
+		return -1
+	}
+	return 0
+}
+
+func (el *elementImpl) Sign() int {
+	return normalizeSign(int64(C.element_sign(el.data)))
+}
+
+func (el *elementImpl) Cmp(x Element) int {
+	return normalizeSign(int64(C.element_cmp(el.data, x.impl().data)))
+}
+
+func (el *elementImpl) Add(x, y Element) Element {
+	C.element_add(el.data, x.impl().data, y.impl().data)
+	return el
+}
+
+func (el *elementImpl) Sub(x, y Element) Element {
+	C.element_sub(el.data, x.impl().data, y.impl().data)
+	return el
+}
+
+func (el *elementImpl) Mul(x, y Element) Element {
+	C.element_mul(el.data, x.impl().data, y.impl().data)
+	return el
+}
+
+func (el *elementImpl) MulBig(x Element, i *big.Int) Element {
+	C.element_mul_mpz(el.data, x.impl().data, &big2mpz(i)[0])
+	return el
+}
+
+func (el *elementImpl) MulInt32(x Element, i int32) Element {
+	C.element_mul_si(el.data, x.impl().data, C.long(i))
+	return el
+}
+
+func (el *elementImpl) MulZn(x, y Element) Element {
+	C.element_mul_zn(el.data, x.impl().data, y.impl().data)
+	return el
+}
+
+func (el *elementImpl) Div(x, y Element) Element {
+	C.element_div(el.data, x.impl().data, y.impl().data)
+	return el
+}
+
+func (el *elementImpl) Double(x Element) Element {
+	C.element_double(el.data, x.impl().data)
+	return el
+}
+
+func (el *elementImpl) Halve(x Element) Element {
+	C.element_halve(el.data, x.impl().data)
+	return el
+}
+
+func (el *elementImpl) Square(x Element) Element {
+	C.element_square(el.data, x.impl().data)
+	return el
+}
+
+func (el *elementImpl) Neg(x Element) Element {
+	C.element_neg(el.data, x.impl().data)
+	return el
+}
+
+func (el *elementImpl) Invert(x Element) Element {
+	C.element_invert(el.data, x.impl().data)
+	return el
+}
+
+func (el *elementImpl) PowBig(x Element, i *big.Int) Element {
+	C.element_pow_mpz(el.data, x.impl().data, &big2mpz(i)[0])
+	return el
+}
+
+func (el *elementImpl) PowZn(x, i Element) Element {
+	C.element_pow_zn(el.data, x.impl().data, i.impl().data)
+	return el
+}
+
+func (el *elementImpl) Pow2Big(x Element, i *big.Int, y Element, j *big.Int) Element {
+	C.element_pow2_mpz(el.data, x.impl().data, &big2mpz(i)[0], y.impl().data, &big2mpz(j)[0])
+	return el
+}
+
+func (el *elementImpl) Pow2Zn(x, i, y, j Element) Element {
+	C.element_pow2_zn(el.data, x.impl().data, i.impl().data, y.impl().data, j.impl().data)
+	return el
+}
+
+func (el *elementImpl) Pow3Big(x Element, i *big.Int, y Element, j *big.Int, z Element, k *big.Int) Element {
+	C.element_pow3_mpz(el.data, x.impl().data, &big2mpz(i)[0], y.impl().data, &big2mpz(j)[0], z.impl().data, &big2mpz(k)[0])
+	return el
+}
+
+func (el *elementImpl) Pow3Zn(x, i, y, j, z, k Element) Element {
+	C.element_pow3_zn(el.data, x.impl().data, i.impl().data, y.impl().data, j.impl().data, z.impl().data, k.impl().data)
+	return el
+}
+
+func (el *elementImpl) PreparePower() Power {
+	power := &powerImpl{}
+	initPower(power, el)
+	return power
+}
+
+func (power *powerImpl) PowBig(i *big.Int) Element {
+	C.element_pp_pow(power.target.data, &big2mpz(i)[0], power.data)
+	return power.target
+}
+
+func (power *powerImpl) PowZn(i Element) Element {
+	C.element_pp_pow_zn(power.target.data, i.impl().data, power.data)
+	return power.target
+}
+
+func (el *elementImpl) BruteForceDL(g, h Element) Element {
+	C.element_dlog_brute_force(el.data, g.impl().data, h.impl().data)
+	return el
+}
+
+func (el *elementImpl) PollardRhoDL(g, h Element) Element {
+	C.element_dlog_pollard_rho(el.data, g.impl().data, h.impl().data)
+	return el
+}
+
+func (el *elementImpl) Rand() Element {
+	C.element_random(el.data)
+	return el
+}
diff --git a/generation.go b/generation.go
index 2ee46a6effcb2bd26afedf57d6f184b970887878..368013593e602940b58c2245d79d2dc98f593765 100644
--- a/generation.go
+++ b/generation.go
@@ -19,7 +19,7 @@ void genPairingD(pbc_param_ptr p, unsigned int D, unsigned int bitlimit) {
 }
 
 void genPairingG(pbc_param_ptr p, unsigned int D, unsigned int bitlimit) {
-	pbc_cm_search_d(acceptPairingG, p, D, bitlimit);
+	pbc_cm_search_g(acceptPairingG, p, D, bitlimit);
 }
 */
 import "C"
diff --git a/gmp_big.go b/gmp_big.go
index c4378683cc81046ee2cbde418d7bf936a8d4124d..1f8baadc6dde6806d04bf0289e60f4d90296788e 100644
--- a/gmp_big.go
+++ b/gmp_big.go
@@ -17,6 +17,7 @@ var wordSize C.size_t
 var bitsPerWord C.size_t
 
 func clearmpz(x *C.mpz_t) {
+	println("clearmpz")
 	C.mpz_clear(&x[0])
 }
 
diff --git a/pairing.go b/pairing.go
index a17a803034f06cd9a8a9059b29ab5c8a7a3033d5..4b60eb04c3336a6509af713c89edd321463b290d 100644
--- a/pairing.go
+++ b/pairing.go
@@ -6,29 +6,107 @@ package pbc
 */
 import "C"
 
-import "io"
-import "runtime"
+import (
+	"bytes"
+	"io"
+	"runtime"
+)
 
-type Pairing interface{}
+type Field int
+
+const (
+	G1 Field = iota
+	G2 Field = iota
+	GT Field = iota
+	Zr Field = iota
+)
+
+type Pairing interface {
+	IsSymmetric() bool
+
+	G1Length() uint
+	G1XLength() uint
+	G1CompressedLength() uint
+	G2Length() uint
+	G2XLength() uint
+	G2CompressedLength() uint
+	GTLength() uint
+	ZrLength() uint
+
+	NewG1() Element
+	NewG2() Element
+	NewGT() Element
+	NewZr() Element
+	NewElement(Field) Element
+}
 
 type pairingImpl struct {
-	data C.pairing_ptr
+	data *C.struct_pairing_s
 }
 
-func pairingFinalize(p *pairingImpl) {
+func NewPairing(params io.Reader) (Pairing, error) {
+	buf := new(bytes.Buffer)
+	buf.ReadFrom(params)
+	return NewPairingFromString(buf.String())
+}
 
+func NewPairingFromString(params string) (Pairing, error) {
+	p, err := NewParamsFromString(params)
+	if err != nil {
+		return nil, err
+	}
+	return NewPairingFromParams(p), nil
 }
 
-func NewPairing(params io.Reader) Pairing {
-	x := &pairingImpl{}
-	runtime.SetFinalizer(x, pairingFinalize)
-	return x
+func NewPairingFromParams(params Params) Pairing {
+	pairing := makePairing()
+	C.pairing_init_pbc_param(pairing.data, params.(*C.struct_pbc_param_s))
+	return pairing
 }
 
-func NewPairingFromString(params string) Pairing {
-	return nil
+func (pairing *pairingImpl) IsSymmetric() bool {
+	return C.pairing_is_symmetric(pairing.data) != 0
 }
 
-func NewPairingFromParams(params Params) Pairing {
-	return nil
+func (pairing *pairingImpl) G1Length() uint {
+	return uint(C.pairing_length_in_bytes_G1(pairing.data))
+}
+
+func (pairing *pairingImpl) G1XLength() uint {
+	return uint(C.pairing_length_in_bytes_x_only_G1(pairing.data))
+}
+
+func (pairing *pairingImpl) G1CompressedLength() uint {
+	return uint(C.pairing_length_in_bytes_compressed_G1(pairing.data))
+}
+
+func (pairing *pairingImpl) G2Length() uint {
+	return uint(C.pairing_length_in_bytes_G2(pairing.data))
+}
+
+func (pairing *pairingImpl) G2XLength() uint {
+	return uint(C.pairing_length_in_bytes_x_only_G2(pairing.data))
+}
+
+func (pairing *pairingImpl) G2CompressedLength() uint {
+	return uint(C.pairing_length_in_bytes_compressed_G2(pairing.data))
+}
+
+func (pairing *pairingImpl) GTLength() uint {
+	return uint(C.pairing_length_in_bytes_GT(pairing.data))
+}
+
+func (pairing *pairingImpl) ZrLength() uint {
+	return uint(C.pairing_length_in_bytes_Zr(pairing.data))
+}
+
+func clearPairing(pairing *pairingImpl) {
+	println("clearpairing")
+	C.pairing_clear(pairing.data)
+}
+
+func makePairing() *pairingImpl {
+	pairing := &pairingImpl{data: &C.struct_pairing_s{}}
+	runtime.SetFinalizer(pairing, clearPairing)
+	return pairing
 }
diff --git a/params.go b/params.go
index 81ea4a7c8b82b6d55a165e191e5765ebc2f4d783..c1a9e6dc32b826ef1c0e1bc6bf9437266302497a 100644
--- a/params.go
+++ b/params.go
@@ -4,7 +4,7 @@ package pbc
 #cgo LDFLAGS: /usr/local/lib/libpbc.a -lgmp
 #include <pbc/pbc.h>
 
-int param_out_str(char** bufp, size_t* sizep, pbc_param_t p) {
+int param_out_str_wrapper(char** bufp, size_t* sizep, pbc_param_t p) {
 	FILE* handle = open_memstream(bufp, sizep);
 	if (!handle) return 0;
 	pbc_param_out_str(handle, p);
@@ -29,8 +29,8 @@ type Params interface {
 	String() string
 }
 
-func NewParamsFromString(data string) (Pairing, error) {
-	cstr := C.CString(data)
+func NewParamsFromString(s string) (Params, error) {
+	cstr := C.CString(s)
 	defer C.free(unsafe.Pointer(cstr))
 
 	params := makeParams()
@@ -52,7 +52,7 @@ func (params *C.struct_pbc_param_s) WriteTo(w io.Writer) (n int64, err error) {
 func (params *C.struct_pbc_param_s) String() string {
 	var buf *C.char
 	var bufLen C.size_t
-	if C.param_out_str(&buf, &bufLen, params) == 0 {
+	if C.param_out_str_wrapper(&buf, &bufLen, params) == 0 {
 		return ""
 	}
 	str := C.GoStringN(buf, C.int(bufLen))
@@ -60,12 +60,13 @@ func (params *C.struct_pbc_param_s) String() string {
 	return str
 }
 
-func paramsFinalizer(params *C.struct_pbc_param_s) {
+func clearParams(params *C.struct_pbc_param_s) {
+	println("clearparams")
 	C.pbc_param_clear(params)
 }
 
 func makeParams() *C.struct_pbc_param_s {
 	params := &C.struct_pbc_param_s{}
-	runtime.SetFinalizer(params, paramsFinalizer)
+	runtime.SetFinalizer(params, clearParams)
 	return params
 }