initial commit
diff --git a/BenchtestALL.rs b/BenchtestALL.rs
new file mode 100644
index 0000000..55961be
--- /dev/null
+++ b/BenchtestALL.rs
@@ -0,0 +1,957 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+#![allow(non_snake_case)]
+extern crate amcl;
+
+//use std::str;
+//use std::io;
+
+use amcl::arch;
+use amcl::rand::RAND;
+use amcl::types::{CurveType, CurvePairingType, ModType};
+
+use std::time::Instant;
+
+const MIN_ITERS: isize = 10;
+const MIN_TIME: isize = 10;
+
+fn ed25519(mut rng: &mut RAND) {
+	//use amcl::ed25519;
+	use amcl::ed25519::big;
+	use amcl::ed25519::ecp;
+	use amcl::ed25519::fp;
+	use amcl::ed25519::rom;
+	let mut fail = false;
+	println!("\nTesting/Timing ed25519 ECC");
+
+	if ecp::CURVETYPE == CurveType::WEIERSTRASS {
+		println!("Weierstrass parameterization");
+	}
+	if ecp::CURVETYPE == CurveType::EDWARDS {
+		println!("Edwards parameterization");
+	}
+	if ecp::CURVETYPE == CurveType::MONTGOMERY {
+		println!("Montgomery parameterization");
+	}
+
+	if fp::MODTYPE == ModType::PSEUDO_MERSENNE {
+		println!("Pseudo-Mersenne Modulus");
+	}
+	if fp::MODTYPE == ModType::MONTGOMERY_FRIENDLY {
+		println!("Montgomery friendly Modulus");
+	}
+	if fp::MODTYPE == ModType::GENERALISED_MERSENNE {
+		println!("Generalised-Mersenne Modulus");
+	}
+	if fp::MODTYPE == ModType::NOT_SPECIAL {
+		println!("Not special Modulus");
+	}
+
+	println!("Modulus size {:} bits", fp::MODBITS);
+	println!("{:} bit build", arch::CHUNK);
+
+	let G = ecp::ECP::generator();
+
+	let mut r = big::BIG::new_ints(&rom::CURVE_ORDER);
+	let mut s = big::BIG::randomnum(&r, &mut rng);
+
+	let P = G.mul(&mut r);
+	if !P.is_infinity() {
+		println!("FAILURE - rG!=O");
+		fail = true;
+	}
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		let _ = G.mul(&mut s);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("EC  mul - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+	if !fail {
+		println!("All tests pass");
+	}
+}
+
+fn nist256(mut rng: &mut RAND) {
+	//use amcl::nist256;
+	use amcl::nist256::big;
+	use amcl::nist256::ecp;
+	use amcl::nist256::fp;
+	use amcl::nist256::rom;
+	let mut fail = false;
+	println!("\nTesting/Timing nist256 ECC");
+
+	if ecp::CURVETYPE == CurveType::WEIERSTRASS {
+		println!("Weierstrass parameterization");
+	}
+	if ecp::CURVETYPE == CurveType::EDWARDS {
+		println!("Edwards parameterization");
+	}
+	if ecp::CURVETYPE == CurveType::MONTGOMERY {
+		println!("Montgomery parameterization");
+	}
+
+	if fp::MODTYPE == ModType::PSEUDO_MERSENNE {
+		println!("Pseudo-Mersenne Modulus");
+	}
+	if fp::MODTYPE == ModType::MONTGOMERY_FRIENDLY {
+		println!("Montgomery friendly Modulus");
+	}
+	if fp::MODTYPE == ModType::GENERALISED_MERSENNE {
+		println!("Generalised-Mersenne Modulus");
+	}
+	if fp::MODTYPE == ModType::NOT_SPECIAL {
+		println!("Not special Modulus");
+	}
+
+	println!("Modulus size {:} bits", fp::MODBITS);
+	println!("{:} bit build", arch::CHUNK);
+
+	let G = ecp::ECP::generator();
+
+	let mut r = big::BIG::new_ints(&rom::CURVE_ORDER);
+	let mut s = big::BIG::randomnum(&r, &mut rng);
+
+	let P = G.mul(&mut r);
+	if !P.is_infinity() {
+		println!("FAILURE - rG!=O");
+		fail = true;
+	}
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		let _ = G.mul(&mut s);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("EC  mul - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+	if !fail {
+		println!("All tests pass");
+	}
+}
+
+fn goldilocks(mut rng: &mut RAND) {
+	//use amcl::goldilocks;
+	use amcl::goldilocks::big;
+	use amcl::goldilocks::ecp;
+	use amcl::goldilocks::fp;
+	use amcl::goldilocks::rom;
+	let mut fail = false;
+	println!("\nTesting/Timing goldilocks ECC");
+
+	if ecp::CURVETYPE == CurveType::WEIERSTRASS {
+		println!("Weierstrass parameterization");
+	}
+	if ecp::CURVETYPE == CurveType::EDWARDS {
+		println!("Edwards parameterization");
+	}
+	if ecp::CURVETYPE == CurveType::MONTGOMERY {
+		println!("Montgomery parameterization");
+	}
+
+	if fp::MODTYPE == ModType::PSEUDO_MERSENNE {
+		println!("Pseudo-Mersenne Modulus");
+	}
+	if fp::MODTYPE == ModType::MONTGOMERY_FRIENDLY {
+		println!("Montgomery friendly Modulus");
+	}
+	if fp::MODTYPE == ModType::GENERALISED_MERSENNE {
+		println!("Generalised-Mersenne Modulus");
+	}
+	if fp::MODTYPE == ModType::NOT_SPECIAL {
+		println!("Not special Modulus");
+	}
+
+	println!("Modulus size {:} bits", fp::MODBITS);
+	println!("{:} bit build", arch::CHUNK);
+
+	let G = ecp::ECP::generator();
+
+	let mut r = big::BIG::new_ints(&rom::CURVE_ORDER);
+	let mut s = big::BIG::randomnum(&r, &mut rng);
+
+	let P = G.mul(&mut r);
+	if !P.is_infinity() {
+		println!("FAILURE - rG!=O");
+		fail = true;
+	}
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		let _ = G.mul(&mut s);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("EC  mul - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+	if !fail {
+		println!("All tests pass");
+	}
+}
+
+fn bn254(mut rng: &mut RAND) {
+	//use amcl::bn254;
+	use amcl::bn254::big;
+	use amcl::bn254::ecp;
+	use amcl::bn254::ecp2;
+	use amcl::bn254::fp;
+	use amcl::bn254::pair;
+	use amcl::bn254::rom;
+	let mut fail = false;
+	println!("\nTesting/Timing BN254 Pairings");
+
+	if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BN {
+		println!("BN Pairing-Friendly Curve");
+	}
+	if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BLS {
+		println!("BLS Pairing-Friendly Curve");
+	}
+
+	println!("Modulus size {:} bits", fp::MODBITS);
+	println!("{:} bit build", arch::CHUNK);
+
+	let mut G = ecp::ECP::generator();
+
+	let mut r = big::BIG::new_ints(&rom::CURVE_ORDER);
+	let mut s = big::BIG::randomnum(&r, &mut rng);
+
+	let mut P = pair::g1mul(&mut G, &mut r);
+
+	if !P.is_infinity() {
+		println!("FAILURE - rP!=O");
+		fail = true;
+	}
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		P = pair::g1mul(&mut G, &mut s);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("G1  mul              - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let mut Q = ecp2::ECP2::generator();
+	let mut W = pair::g2mul(&mut Q, &mut r);
+
+	if !W.is_infinity() {
+		println!("FAILURE - rQ!=O");
+		fail = true;
+	}
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		W = pair::g2mul(&mut Q, &mut s);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("G2  mul              - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let mut w = pair::ate(&mut Q, &mut P);
+	w = pair::fexp(&w);
+
+	let mut g = pair::gtpow(&mut w, &mut r);
+
+	if !g.isunity() {
+		println!("FAILURE - g^r!=1");
+		return;
+	}
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		let _ = pair::gtpow(&mut w, &mut s);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("GT  pow              - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		let _ = w.compow(&s, &mut r);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("GT  pow (compressed) - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		w = pair::ate(&mut Q, &mut P);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("PAIRing ATE          - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		let _ = pair::fexp(&w);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("PAIRing FEXP         - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	P.copy(&G);
+	Q.copy(&W);
+
+	P = pair::g1mul(&mut P, &mut s);
+	g = pair::ate(&mut Q, &mut P);
+	g = pair::fexp(&g);
+
+	P.copy(&G);
+	Q = pair::g2mul(&mut Q, &mut s);
+	w = pair::ate(&mut Q, &mut P);
+	w = pair::fexp(&w);
+
+	if !g.equals(&mut w) {
+		println!("FAILURE - e(sQ,p)!=e(Q,sP) ");
+		fail = true;
+	}
+
+	Q.copy(&W);
+	g = pair::ate(&mut Q, &mut P);
+	g = pair::fexp(&g);
+	g = pair::gtpow(&mut g, &mut s);
+
+	if !g.equals(&mut w) {
+		println!("FAILURE - e(sQ,p)!=e(Q,P)^s ");
+		fail = true;
+	}
+	if !fail {
+		println!("All tests pass");
+	}
+}
+
+fn bls383(mut rng: &mut RAND) {
+	//use amcl::bls383;
+	use amcl::bls383::big;
+	use amcl::bls383::ecp;
+	use amcl::bls383::ecp2;
+	use amcl::bls383::fp;
+	use amcl::bls383::pair;
+	use amcl::bls383::rom;
+	let mut fail = false;
+	println!("\nTesting/Timing BLS383 Pairings");
+
+	if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BN {
+		println!("BN Pairing-Friendly Curve");
+	}
+	if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BLS {
+		println!("BLS Pairing-Friendly Curve");
+	}
+
+	println!("Modulus size {:} bits", fp::MODBITS);
+	println!("{:} bit build", arch::CHUNK);
+
+	let mut G = ecp::ECP::generator();
+
+	let mut r = big::BIG::new_ints(&rom::CURVE_ORDER);
+	let mut s = big::BIG::randomnum(&r, &mut rng);
+
+	let mut P = pair::g1mul(&mut G, &mut r);
+
+	if !P.is_infinity() {
+		println!("FAILURE - rP!=O");
+		fail = true;
+	}
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		P = pair::g1mul(&mut G, &mut s);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("G1  mul              - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let mut Q = ecp2::ECP2::generator();
+	let mut W = pair::g2mul(&mut Q, &mut r);
+
+	if !W.is_infinity() {
+		println!("FAILURE - rQ!=O");
+		fail = true;
+	}
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		W = pair::g2mul(&mut Q, &mut s);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("G2  mul              - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let mut w = pair::ate(&mut Q, &mut P);
+	w = pair::fexp(&w);
+
+	let mut g = pair::gtpow(&mut w, &mut r);
+
+	if !g.isunity() {
+		println!("FAILURE - g^r!=1");
+		return;
+	}
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		let _ = pair::gtpow(&mut w, &mut s);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("GT  pow              - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		let _ = w.compow(&s, &mut r);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("GT  pow (compressed) - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		w = pair::ate(&mut Q, &mut P);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("PAIRing ATE          - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		let _ = pair::fexp(&w);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("PAIRing FEXP         - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	P.copy(&G);
+	Q.copy(&W);
+
+	P = pair::g1mul(&mut P, &mut s);
+	g = pair::ate(&mut Q, &mut P);
+	g = pair::fexp(&g);
+
+	P.copy(&G);
+	Q = pair::g2mul(&mut Q, &mut s);
+	w = pair::ate(&mut Q, &mut P);
+	w = pair::fexp(&w);
+
+	if !g.equals(&mut w) {
+		println!("FAILURE - e(sQ,p)!=e(Q,sP) ");
+		fail = true;
+	}
+
+	Q.copy(&W);
+	g = pair::ate(&mut Q, &mut P);
+	g = pair::fexp(&g);
+	g = pair::gtpow(&mut g, &mut s);
+
+	if !g.equals(&mut w) {
+		println!("FAILURE - e(sQ,p)!=e(Q,P)^s ");
+		fail = true;
+	}
+	if !fail {
+		println!("All tests pass");
+	}
+}
+
+fn bls24(mut rng: &mut RAND) {
+	//use amcl::bls24;
+	use amcl::bls24::big;
+	use amcl::bls24::ecp;
+	use amcl::bls24::ecp4;
+	use amcl::bls24::fp;
+	use amcl::bls24::pair192;
+	use amcl::bls24::rom;
+	let mut fail = false;
+	println!("\nTesting/Timing BLS24 Pairings");
+
+	if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BN {
+		println!("BN Pairing-Friendly Curve");
+	}
+	if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BLS {
+		println!("BLS24 Pairing-Friendly Curve");
+	}
+
+	println!("Modulus size {:} bits", fp::MODBITS);
+	println!("{:} bit build", arch::CHUNK);
+
+	let mut G = ecp::ECP::generator();
+
+	let mut r = big::BIG::new_ints(&rom::CURVE_ORDER);
+	let mut s = big::BIG::randomnum(&r, &mut rng);
+
+	let mut P = pair192::g1mul(&mut G, &mut r);
+
+	if !P.is_infinity() {
+		println!("FAILURE - rP!=O");
+		fail = true;
+	}
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		P = pair192::g1mul(&mut G, &mut s);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("G1  mul              - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let mut Q = ecp4::ECP4::generator();
+	let mut W = pair192::g2mul(&mut Q, &mut r);
+
+	if !W.is_infinity() {
+		println!("FAILURE - rQ!=O");
+		fail = true;
+	}
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		W = pair192::g2mul(&mut Q, &mut s);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("G2  mul              - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let mut w = pair192::ate(&mut Q, &mut P);
+	w = pair192::fexp(&w);
+
+	let mut g = pair192::gtpow(&mut w, &mut r);
+
+	if !g.isunity() {
+		println!("FAILURE - g^r!=1");
+		return;
+	}
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		let _ = pair192::gtpow(&mut w, &mut s);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("GT  pow              - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		let _ = w.compow(&s, &mut r);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("GT  pow (compressed) - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		w = pair192::ate(&mut Q, &mut P);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("PAIRing ATE          - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		let _ = pair192::fexp(&w);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("PAIRing FEXP         - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	P.copy(&G);
+	Q.copy(&W);
+
+	P = pair192::g1mul(&mut P, &mut s);
+	g = pair192::ate(&mut Q, &mut P);
+	g = pair192::fexp(&g);
+
+	P.copy(&G);
+	Q = pair192::g2mul(&mut Q, &mut s);
+	w = pair192::ate(&mut Q, &mut P);
+	w = pair192::fexp(&w);
+
+	if !g.equals(&mut w) {
+		println!("FAILURE - e(sQ,p)!=e(Q,sP) ");
+		fail = true;
+	}
+
+	Q.copy(&W);
+	g = pair192::ate(&mut Q, &mut P);
+	g = pair192::fexp(&g);
+	g = pair192::gtpow(&mut g, &mut s);
+
+	if !g.equals(&mut w) {
+		println!("FAILURE - e(sQ,p)!=e(Q,P)^s ");
+		fail = true;
+	}
+	if !fail {
+		println!("All tests pass");
+	}
+}
+
+fn bls48(mut rng: &mut RAND) {
+	//use amcl::bls48;
+	use amcl::bls48::big;
+	use amcl::bls48::ecp;
+	use amcl::bls48::ecp8;
+	use amcl::bls48::fp;
+	use amcl::bls48::pair256;
+	use amcl::bls48::rom;
+	let mut fail = false;
+	println!("\nTesting/Timing BLS48 Pairings");
+
+	if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BN {
+		println!("BN Pairing-Friendly Curve");
+	}
+	if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BLS {
+		println!("BLS48 Pairing-Friendly Curve");
+	}
+
+	println!("Modulus size {:} bits", fp::MODBITS);
+	println!("{:} bit build", arch::CHUNK);
+
+	let mut G = ecp::ECP::generator();
+
+	let mut r = big::BIG::new_ints(&rom::CURVE_ORDER);
+	let mut s = big::BIG::randomnum(&r, &mut rng);
+
+	let mut P = pair256::g1mul(&mut G, &mut r);
+
+	if !P.is_infinity() {
+		println!("FAILURE - rP!=O");
+		fail = true;
+	}
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		P = pair256::g1mul(&mut G, &mut s);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("G1  mul              - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let mut Q = ecp8::ECP8::generator();
+	let mut W = pair256::g2mul(&mut Q, &mut r);
+
+	if !W.is_infinity() {
+		println!("FAILURE - rQ!=O");
+		fail = true;
+	}
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		W = pair256::g2mul(&mut Q, &mut s);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("G2  mul              - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let mut w = pair256::ate(&mut Q, &mut P);
+	w = pair256::fexp(&w);
+
+	let mut g = pair256::gtpow(&mut w, &mut r);
+
+	if !g.isunity() {
+		println!("FAILURE - g^r!=1");
+		return;
+	}
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		let _ = pair256::gtpow(&mut w, &mut s);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("GT  pow              - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		let _ = w.compow(&s, &mut r);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("GT  pow (compressed) - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		w = pair256::ate(&mut Q, &mut P);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("PAIRing ATE          - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		let _ = pair256::fexp(&w);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("PAIRing FEXP         - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	P.copy(&G);
+	Q.copy(&W);
+
+	P = pair256::g1mul(&mut P, &mut s);
+	g = pair256::ate(&mut Q, &mut P);
+	g = pair256::fexp(&g);
+
+	P.copy(&G);
+	Q = pair256::g2mul(&mut Q, &mut s);
+	w = pair256::ate(&mut Q, &mut P);
+	w = pair256::fexp(&w);
+
+	if !g.equals(&mut w) {
+		println!("FAILURE - e(sQ,p)!=e(Q,sP) ");
+		fail = true;
+	}
+
+	Q.copy(&W);
+	g = pair256::ate(&mut Q, &mut P);
+	g = pair256::fexp(&g);
+	g = pair256::gtpow(&mut g, &mut s);
+
+	if !g.equals(&mut w) {
+		println!("FAILURE - e(sQ,p)!=e(Q,P)^s ");
+		fail = true;
+	}
+	if !fail {
+		println!("All tests pass");
+	}
+}
+
+fn rsa2048(mut rng: &mut RAND) {
+	use amcl::rsa2048::ff;
+	use amcl::rsa2048::rsa;
+	let mut pbc = rsa::new_public_key(ff::FFLEN);
+	let mut prv = rsa::new_private_key(ff::HFLEN);
+	let mut c: [u8; rsa::RFS] = [0; rsa::RFS];
+	let mut m: [u8; rsa::RFS] = [0; rsa::RFS];
+	let mut p: [u8; rsa::RFS] = [0; rsa::RFS];
+
+	let mut fail = false;
+	println!("\nTesting/Timing 2048-bit RSA");
+	println!("Generating 2048 -bit RSA public/private key pair");
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		rsa::key_pair(&mut rng, 65537, &mut prv, &mut pbc);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("RSA gen - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	for i in 0..rsa::RFS {
+		m[i] = (i % 128) as u8;
+	}
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		rsa::encrypt(&pbc, &m, &mut c);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("RSA enc - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let start = Instant::now();
+	let mut iterations = 0;
+	let mut dur = 0 as u64;
+	while dur < (MIN_TIME as u64) * 1000 || iterations < MIN_ITERS {
+		rsa::decrypt(&prv, &c, &mut p);
+		iterations += 1;
+		let elapsed = start.elapsed();
+		dur = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+	}
+	let duration = (dur as f64) / (iterations as f64);
+	print!("RSA dec - {:} iterations  ", iterations);
+	println!(" {:0.2} ms per iteration", duration);
+
+	let mut cmp = true;
+	for i in 0..rsa::RFS {
+		if p[i] != m[i] {
+			cmp = false;
+		}
+	}
+
+	if !cmp {
+		println!("FAILURE - RSA decryption");
+		fail = true;
+	}
+
+	if !fail {
+		println!("All tests pass");
+	}
+}
+
+#[allow(non_snake_case)]
+//#[test]
+fn main() {
+	let mut raw: [u8; 100] = [0; 100];
+
+	let mut rng = RAND::new();
+	rng.clean();
+	for i in 0..100 {
+		raw[i] = i as u8
+	}
+
+	rng.seed(100, &raw);
+
+	ed25519(&mut rng);
+	nist256(&mut rng);
+	goldilocks(&mut rng);
+	bn254(&mut rng);
+	bls383(&mut rng);
+	bls24(&mut rng);
+	bls48(&mut rng);
+	rsa2048(&mut rng);
+}
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..ccba62a
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,6 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "amcl"
+version = "0.2.0"
+
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..b3a8fa8
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,46 @@
+[package]
+name = "amcl"
+version = "0.2.0"
+authors = ["Nikita Khateev <nikita.khateev@dsr-corporation.com>"]
+
+description = "The Apache Milagro Cryptographic Library (version 3)"
+license = "Apache-2.0"
+repository = "https://github.com/milagro-crypto/amcl"
+
+[dependencies]
+
+[lib]
+name = "amcl"
+path = "src/lib.rs"
+
+[features]
+default = ["bn254"]
+bn254 = []
+bn254cx = []
+ansii = []
+bls24 = []
+bls48 = []
+bls381 = []
+bls383 = []
+bls461 = []
+brainpool = []
+c25519 = []
+c41417 = []
+ed25519 = []
+fp256Bn = []
+fp512BN = []
+goldilocks = []
+hifive = []
+nist256 = []
+nist384 = []
+nist521 = []
+nums256e = []
+nums256w = []
+nums384e = []
+nums384w = []
+nums512e = []
+nums512w = []
+secp256k1 = []
+rsa2048 = []
+rsa3072 = []
+rsa4096 = []
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7a4a3ea
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
\ No newline at end of file
diff --git a/TestALL.rs b/TestALL.rs
new file mode 100644
index 0000000..2dbf276
--- /dev/null
+++ b/TestALL.rs
@@ -0,0 +1,1459 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+extern crate amcl;
+
+use std::io;
+use std::str;
+
+use amcl::rand::RAND;
+use amcl::types::CurveType;
+
+pub fn printbinary(array: &[u8]) {
+    for i in 0..array.len() {
+        print!("{:02X}", array[i])
+    }
+    println!("")
+}
+
+fn ecdh_ed25519(mut rng: &mut RAND) {
+    //use amcl::ed25519;
+    use amcl::ed25519::ecdh;
+    use amcl::ed25519::ecp;
+
+    let pw = "M0ng00se";
+    let pp: &[u8] = b"M0ng00se";
+    const EFS: usize = ecdh::EFS;
+    const EGS: usize = ecdh::EGS;
+    const EAS: usize = ecp::AESKEY;
+
+    let sha = ecp::HASH_TYPE;
+    let mut salt: [u8; 8] = [0; 8];
+    let mut s1: [u8; EGS] = [0; EGS];
+    let mut w0: [u8; 2 * EFS + 1] = [0; 2 * EFS + 1];
+    let mut w1: [u8; 2 * EFS + 1] = [0; 2 * EFS + 1];
+    let mut z0: [u8; EFS] = [0; EFS];
+    let mut z1: [u8; EFS] = [0; EFS];
+    let mut key: [u8; EAS] = [0; EAS];
+    let mut cs: [u8; EGS] = [0; EGS];
+    let mut ds: [u8; EGS] = [0; EGS];
+    let mut m: Vec<u8> = vec![0; 32]; // array that could be of any length. So use heap.
+    let mut p1: [u8; 3] = [0; 3];
+    let mut p2: [u8; 4] = [0; 4];
+    let mut v: [u8; 2 * EFS + 1] = [0; 2 * EFS + 1];
+    let mut t: [u8; 12] = [0; 12];
+
+    for i in 0..8 {
+        salt[i] = (i + 1) as u8
+    } // set Salt
+
+    println!("\nTesting ECDH/ECDSA/ECIES");
+    println!("Alice's Passphrase= {}", pw);
+
+    let mut s0: [u8; EFS] = [0; EGS];
+    ecdh::pbkdf2(sha, pp, &salt, 1000, EGS, &mut s0);
+
+    print!("Alice's private key= 0x");
+    printbinary(&s0);
+
+    /* Generate Key pair S/W */
+    ecdh::key_pair_generate(None, &mut s0, &mut w0);
+
+    print!("Alice's public key= 0x");
+    printbinary(&w0);
+
+    let mut res = ecdh::public_key_validate(&w0);
+    if res != 0 {
+        println!("ECP Public Key is invalid!");
+        return;
+    }
+
+    /* Random private key for other party */
+    ecdh::key_pair_generate(Some(&mut rng), &mut s1, &mut w1);
+
+    print!("Servers private key= 0x");
+    printbinary(&s1);
+
+    print!("Servers public key= 0x");
+    printbinary(&w1);
+
+    res = ecdh::public_key_validate(&w1);
+    if res != 0 {
+        println!("ECP Public Key is invalid!");
+        return;
+    }
+    /* Calculate common key using DH - IEEE 1363 method */
+
+    ecdh::ecpsvdp_dh(&s0, &w1, &mut z0);
+    ecdh::ecpsvdp_dh(&s1, &w0, &mut z1);
+
+    let mut same = true;
+    for i in 0..EFS {
+        if z0[i] != z1[i] {
+            same = false
+        }
+    }
+
+    if !same {
+        println!("*** ECPSVDP-DH Failed");
+        return;
+    }
+
+    ecdh::kdf2(sha, &z0, None, EAS, &mut key);
+
+    print!("Alice's DH Key=  0x");
+    printbinary(&key);
+    print!("Servers DH Key=  0x");
+    printbinary(&key);
+
+    if ecp::CURVETYPE != CurveType::MONTGOMERY {
+        for i in 0..17 {
+            m[i] = i as u8
+        }
+
+        println!("Testing ECIES");
+
+        p1[0] = 0x0;
+        p1[1] = 0x1;
+        p1[2] = 0x2;
+        p2[0] = 0x0;
+        p2[1] = 0x1;
+        p2[2] = 0x2;
+        p2[3] = 0x3;
+
+        let cc = ecdh::ecies_encrypt(sha, &p1, &p2, &mut rng, &w1, &m[0..17], &mut v, &mut t);
+
+        if let Some(mut c) = cc {
+            println!("Ciphertext= ");
+            print!("V= 0x");
+            printbinary(&v);
+            print!("C= 0x");
+            printbinary(&c);
+            print!("T= 0x");
+            printbinary(&t);
+
+            let mm = ecdh::ecies_decrypt(sha, &p1, &p2, &v, &mut c, &t, &s1);
+            if let Some(rm) = mm {
+                println!("Decryption succeeded");
+                print!("Message is 0x");
+                printbinary(&rm);
+            } else {
+                println!("*** ECIES Decryption Failed");
+                return;
+            }
+        } else {
+            println!("*** ECIES Encryption Failed");
+            return;
+        }
+
+        println!("Testing ECDSA");
+
+        if ecdh::ecpsp_dsa(sha, &mut rng, &s0, &m[0..17], &mut cs, &mut ds) != 0 {
+            println!("***ECDSA Signature Failed");
+            return;
+        }
+        println!("Signature= ");
+        print!("C= 0x");
+        printbinary(&cs);
+        print!("D= 0x");
+        printbinary(&ds);
+
+        if ecdh::ecpvp_dsa(sha, &w0, &m[0..17], &cs, &ds) != 0 {
+            println!("***ECDSA Verification Failed");
+            return;
+        } else {
+            println!("ECDSA Signature/Verification succeeded ")
+        }
+    }
+}
+
+fn ecdh_nist256(mut rng: &mut RAND) {
+    //use amcl::nist256;
+    use amcl::nist256::ecdh;
+    use amcl::nist256::ecp;
+
+    let pw = "M0ng00se";
+    let pp: &[u8] = b"M0ng00se";
+    const EFS: usize = ecdh::EFS;
+    const EGS: usize = ecdh::EGS;
+    const EAS: usize = ecp::AESKEY;
+
+    let sha = ecp::HASH_TYPE;
+    let mut salt: [u8; 8] = [0; 8];
+    let mut s1: [u8; EGS] = [0; EGS];
+    let mut w0: [u8; 2 * EFS + 1] = [0; 2 * EFS + 1];
+    let mut w1: [u8; 2 * EFS + 1] = [0; 2 * EFS + 1];
+    let mut z0: [u8; EFS] = [0; EFS];
+    let mut z1: [u8; EFS] = [0; EFS];
+    let mut key: [u8; EAS] = [0; EAS];
+    let mut cs: [u8; EGS] = [0; EGS];
+    let mut ds: [u8; EGS] = [0; EGS];
+    let mut m: Vec<u8> = vec![0; 32]; // array that could be of any length. So use heap.
+    let mut p1: [u8; 3] = [0; 3];
+    let mut p2: [u8; 4] = [0; 4];
+    let mut v: [u8; 2 * EFS + 1] = [0; 2 * EFS + 1];
+    let mut t: [u8; 12] = [0; 12];
+
+    for i in 0..8 {
+        salt[i] = (i + 1) as u8
+    } // set Salt
+
+    println!("\nTesting ECDH/ECDSA/ECIES");
+    println!("Alice's Passphrase= {}", pw);
+
+    let mut s0: [u8; EFS] = [0; EGS];
+    ecdh::pbkdf2(sha, pp, &salt, 1000, EGS, &mut s0);
+
+    print!("Alice's private key= 0x");
+    printbinary(&s0);
+
+    /* Generate Key pair S/W */
+    ecdh::key_pair_generate(None, &mut s0, &mut w0);
+
+    print!("Alice's public key= 0x");
+    printbinary(&w0);
+
+    let mut res = ecdh::public_key_validate(&w0);
+    if res != 0 {
+        println!("ECP Public Key is invalid!");
+        return;
+    }
+
+    /* Random private key for other party */
+    ecdh::key_pair_generate(Some(&mut rng), &mut s1, &mut w1);
+
+    print!("Servers private key= 0x");
+    printbinary(&s1);
+
+    print!("Servers public key= 0x");
+    printbinary(&w1);
+
+    res = ecdh::public_key_validate(&w1);
+    if res != 0 {
+        println!("ECP Public Key is invalid!");
+        return;
+    }
+    /* Calculate common key using DH - IEEE 1363 method */
+
+    ecdh::ecpsvdp_dh(&s0, &w1, &mut z0);
+    ecdh::ecpsvdp_dh(&s1, &w0, &mut z1);
+
+    let mut same = true;
+    for i in 0..EFS {
+        if z0[i] != z1[i] {
+            same = false
+        }
+    }
+
+    if !same {
+        println!("*** ECPSVDP-DH Failed");
+        return;
+    }
+
+    ecdh::kdf2(sha, &z0, None, EAS, &mut key);
+
+    print!("Alice's DH Key=  0x");
+    printbinary(&key);
+    print!("Servers DH Key=  0x");
+    printbinary(&key);
+
+    if ecp::CURVETYPE != CurveType::MONTGOMERY {
+        for i in 0..17 {
+            m[i] = i as u8
+        }
+
+        println!("Testing ECIES");
+
+        p1[0] = 0x0;
+        p1[1] = 0x1;
+        p1[2] = 0x2;
+        p2[0] = 0x0;
+        p2[1] = 0x1;
+        p2[2] = 0x2;
+        p2[3] = 0x3;
+
+        let cc = ecdh::ecies_encrypt(sha, &p1, &p2, &mut rng, &w1, &m[0..17], &mut v, &mut t);
+
+        if let Some(mut c) = cc {
+            println!("Ciphertext= ");
+            print!("V= 0x");
+            printbinary(&v);
+            print!("C= 0x");
+            printbinary(&c);
+            print!("T= 0x");
+            printbinary(&t);
+
+            let mm = ecdh::ecies_decrypt(sha, &p1, &p2, &v, &mut c, &t, &s1);
+            if let Some(rm) = mm {
+                println!("Decryption succeeded");
+                print!("Message is 0x");
+                printbinary(&rm);
+            } else {
+                println!("*** ECIES Decryption Failed");
+                return;
+            }
+        } else {
+            println!("*** ECIES Encryption Failed");
+            return;
+        }
+
+        println!("Testing ECDSA");
+
+        if ecdh::ecpsp_dsa(sha, &mut rng, &s0, &m[0..17], &mut cs, &mut ds) != 0 {
+            println!("***ECDSA Signature Failed");
+            return;
+        }
+        println!("Signature= ");
+        print!("C= 0x");
+        printbinary(&cs);
+        print!("D= 0x");
+        printbinary(&ds);
+
+        if ecdh::ecpvp_dsa(sha, &w0, &m[0..17], &cs, &ds) != 0 {
+            println!("***ECDSA Verification Failed");
+            return;
+        } else {
+            println!("ECDSA Signature/Verification succeeded ")
+        }
+    }
+}
+
+fn ecdh_goldilocks(mut rng: &mut RAND) {
+    //use amcl::goldilocks;
+    use amcl::goldilocks::ecdh;
+    use amcl::goldilocks::ecp;
+
+    let pw = "M0ng00se";
+    let pp: &[u8] = b"M0ng00se";
+    const EFS: usize = ecdh::EFS;
+    const EGS: usize = ecdh::EGS;
+    const EAS: usize = ecp::AESKEY;
+
+    let sha = ecp::HASH_TYPE;
+    let mut salt: [u8; 8] = [0; 8];
+    let mut s1: [u8; EGS] = [0; EGS];
+    let mut w0: [u8; 2 * EFS + 1] = [0; 2 * EFS + 1];
+    let mut w1: [u8; 2 * EFS + 1] = [0; 2 * EFS + 1];
+    let mut z0: [u8; EFS] = [0; EFS];
+    let mut z1: [u8; EFS] = [0; EFS];
+    let mut key: [u8; EAS] = [0; EAS];
+    let mut cs: [u8; EGS] = [0; EGS];
+    let mut ds: [u8; EGS] = [0; EGS];
+    let mut m: Vec<u8> = vec![0; 32]; // array that could be of any length. So use heap.
+    let mut p1: [u8; 3] = [0; 3];
+    let mut p2: [u8; 4] = [0; 4];
+    let mut v: [u8; 2 * EFS + 1] = [0; 2 * EFS + 1];
+    let mut t: [u8; 12] = [0; 12];
+
+    for i in 0..8 {
+        salt[i] = (i + 1) as u8
+    } // set Salt
+
+    println!("\nTesting ECDH/ECDSA/ECIES");
+    println!("Alice's Passphrase= {}", pw);
+
+    let mut s0: [u8; EFS] = [0; EGS];
+    ecdh::pbkdf2(sha, pp, &salt, 1000, EGS, &mut s0);
+
+    print!("Alice's private key= 0x");
+    printbinary(&s0);
+
+    /* Generate Key pair S/W */
+    ecdh::key_pair_generate(None, &mut s0, &mut w0);
+
+    print!("Alice's public key= 0x");
+    printbinary(&w0);
+
+    let mut res = ecdh::public_key_validate(&w0);
+    if res != 0 {
+        println!("ECP Public Key is invalid!");
+        return;
+    }
+
+    /* Random private key for other party */
+    ecdh::key_pair_generate(Some(&mut rng), &mut s1, &mut w1);
+
+    print!("Servers private key= 0x");
+    printbinary(&s1);
+
+    print!("Servers public key= 0x");
+    printbinary(&w1);
+
+    res = ecdh::public_key_validate(&w1);
+    if res != 0 {
+        println!("ECP Public Key is invalid!");
+        return;
+    }
+    /* Calculate common key using DH - IEEE 1363 method */
+
+    ecdh::ecpsvdp_dh(&s0, &w1, &mut z0);
+    ecdh::ecpsvdp_dh(&s1, &w0, &mut z1);
+
+    let mut same = true;
+    for i in 0..EFS {
+        if z0[i] != z1[i] {
+            same = false
+        }
+    }
+
+    if !same {
+        println!("*** ECPSVDP-DH Failed");
+        return;
+    }
+
+    ecdh::kdf2(sha, &z0, None, EAS, &mut key);
+
+    print!("Alice's DH Key=  0x");
+    printbinary(&key);
+    print!("Servers DH Key=  0x");
+    printbinary(&key);
+
+    if ecp::CURVETYPE != CurveType::MONTGOMERY {
+        for i in 0..17 {
+            m[i] = i as u8
+        }
+
+        println!("Testing ECIES");
+
+        p1[0] = 0x0;
+        p1[1] = 0x1;
+        p1[2] = 0x2;
+        p2[0] = 0x0;
+        p2[1] = 0x1;
+        p2[2] = 0x2;
+        p2[3] = 0x3;
+
+        let cc = ecdh::ecies_encrypt(sha, &p1, &p2, &mut rng, &w1, &m[0..17], &mut v, &mut t);
+
+        if let Some(mut c) = cc {
+            println!("Ciphertext= ");
+            print!("V= 0x");
+            printbinary(&v);
+            print!("C= 0x");
+            printbinary(&c);
+            print!("T= 0x");
+            printbinary(&t);
+
+            let mm = ecdh::ecies_decrypt(sha, &p1, &p2, &v, &mut c, &t, &s1);
+            if let Some(rm) = mm {
+                println!("Decryption succeeded");
+                print!("Message is 0x");
+                printbinary(&rm);
+            } else {
+                println!("*** ECIES Decryption Failed");
+                return;
+            }
+        } else {
+            println!("*** ECIES Encryption Failed");
+            return;
+        }
+
+        println!("Testing ECDSA");
+
+        if ecdh::ecpsp_dsa(sha, &mut rng, &s0, &m[0..17], &mut cs, &mut ds) != 0 {
+            println!("***ECDSA Signature Failed");
+            return;
+        }
+        println!("Signature= ");
+        print!("C= 0x");
+        printbinary(&cs);
+        print!("D= 0x");
+        printbinary(&ds);
+
+        if ecdh::ecpvp_dsa(sha, &w0, &m[0..17], &cs, &ds) != 0 {
+            println!("***ECDSA Verification Failed");
+            return;
+        } else {
+            println!("ECDSA Signature/Verification succeeded ")
+        }
+    }
+}
+
+fn mpin_bn254(mut rng: &mut RAND) {
+    //use amcl::bn254;
+    use amcl::bn254::ecp;
+    use amcl::bn254::mpin;
+    pub const PERMITS: bool = true;
+    pub const PINERROR: bool = true;
+    pub const FULL: bool = true;
+    //pub const SINGLE_PASS:bool=false;
+
+    const EFS: usize = mpin::EFS;
+    const EGS: usize = mpin::EGS;
+
+    let mut s: [u8; EGS] = [0; EGS];
+    const RM: usize = EFS as usize;
+    let mut hcid: [u8; RM] = [0; RM];
+    let mut hsid: [u8; RM] = [0; RM];
+
+    const G1S: usize = 2 * EFS + 1; /* Group 1 Size */
+    const G2S: usize = 4 * EFS; /* Group 2 Size */
+    const EAS: usize = ecp::AESKEY;
+
+    let mut sst: [u8; G2S] = [0; G2S];
+    let mut token: [u8; G1S] = [0; G1S];
+    let mut permit: [u8; G1S] = [0; G1S];
+    let mut g1: [u8; 12 * EFS] = [0; 12 * EFS];
+    let mut g2: [u8; 12 * EFS] = [0; 12 * EFS];
+    let mut xid: [u8; G1S] = [0; G1S];
+    let mut xcid: [u8; G1S] = [0; G1S];
+    let mut x: [u8; EGS] = [0; EGS];
+    let mut y: [u8; EGS] = [0; EGS];
+    let mut sec: [u8; G1S] = [0; G1S];
+    let mut r: [u8; EGS] = [0; EGS];
+    let mut z: [u8; G1S] = [0; G1S];
+    let mut hid: [u8; G1S] = [0; G1S];
+    let mut htid: [u8; G1S] = [0; G1S];
+    let mut rhid: [u8; G1S] = [0; G1S];
+    let mut w: [u8; EGS] = [0; EGS];
+    let mut t: [u8; G1S] = [0; G1S];
+    let mut e: [u8; 12 * EFS] = [0; 12 * EFS];
+    let mut f: [u8; 12 * EFS] = [0; 12 * EFS];
+    let mut h: [u8; RM] = [0; RM];
+    let mut ck: [u8; EAS] = [0; EAS];
+    let mut sk: [u8; EAS] = [0; EAS];
+
+    let sha = ecp::HASH_TYPE;
+
+    println!("\nTesting MPIN - PIN is 1234");
+    /* Trusted Authority set-up */
+
+    mpin::random_generate(&mut rng, &mut s);
+    print!("Master Secret s: 0x");
+    printbinary(&s);
+
+    /* Create Client Identity */
+    let name = "testUser@miracl.com";
+    let client_id = name.as_bytes();
+
+    print!("Client ID= ");
+    printbinary(&client_id);
+
+    mpin::hash_id(sha, &client_id, &mut hcid); /* Either Client or TA calculates Hash(ID) - you decide! */
+
+    /* Client and Server are issued secrets by DTA */
+    mpin::get_server_secret(&s, &mut sst);
+    print!("Server Secret SS: 0x");
+    printbinary(&sst);
+
+    mpin::get_client_secret(&mut s, &hcid, &mut token);
+    print!("Client Secret CS: 0x");
+    printbinary(&token);
+
+    /* Client extracts PIN from secret to create Token */
+    let pin: i32 = 1234;
+    println!("Client extracts PIN= {}", pin);
+    let mut rtn = mpin::extract_pin(sha, &client_id, pin, &mut token);
+    if rtn != 0 {
+        println!("FAILURE: EXTRACT_PIN rtn: {}", rtn);
+    }
+
+    print!("Client Token TK: 0x");
+    printbinary(&token);
+
+    if FULL {
+        mpin::precompute(&token, &hcid, &mut g1, &mut g2);
+    }
+
+    let mut date = 0;
+    if PERMITS {
+        date = mpin::today();
+        /* Client gets "Time Token" permit from DTA */
+
+        mpin::get_client_permit(sha, date, &s, &hcid, &mut permit);
+        print!("Time Permit TP: 0x");
+        printbinary(&permit);
+
+        /* This encoding makes Time permit look random - Elligator squared */
+        mpin::encoding(&mut rng, &mut permit);
+        print!("Encoded Time Permit TP: 0x");
+        printbinary(&permit);
+        mpin::decoding(&mut permit);
+        print!("Decoded Time Permit TP: 0x");
+        printbinary(&permit);
+    }
+
+    print!("\nPIN= ");
+    let _ = io::Write::flush(&mut io::stdout());
+    let mut input_text = String::new();
+    let _ = io::stdin().read_line(&mut input_text);
+
+    let pin = input_text.trim().parse::<usize>().unwrap();
+
+    println!("MPIN Multi Pass");
+    /* Send U=x.ID to server, and recreate secret from token and pin */
+    rtn = mpin::client_1(
+        sha,
+        date,
+        &client_id,
+        Some(&mut rng),
+        &mut x,
+        pin,
+        &token,
+        &mut sec,
+        Some(&mut xid[..]),
+        Some(&mut xcid[..]),
+        Some(&permit[..]),
+    );
+    if rtn != 0 {
+        println!("FAILURE: CLIENT_1 rtn: {}", rtn);
+    }
+
+    if FULL {
+        mpin::hash_id(sha, &client_id, &mut hcid);
+        mpin::get_g1_multiple(Some(&mut rng), 1, &mut r, &hcid, &mut z); /* Also Send Z=r.ID to Server, remember random r */
+    }
+
+    /* Server calculates H(ID) and H(T|H(ID)) (if time mpin::PERMITS enabled), and maps them to points on the curve HID and HTID resp. */
+
+    mpin::server_1(sha, date, &client_id, &mut hid, Some(&mut htid[..]));
+
+    if date != 0 {
+        rhid.clone_from_slice(&htid[..]);
+    } else {
+        rhid.clone_from_slice(&hid[..]);
+    }
+
+    /* Server generates Random number Y and sends it to Client */
+    mpin::random_generate(&mut rng, &mut y);
+
+    if FULL {
+        mpin::hash_id(sha, &client_id, &mut hsid);
+        mpin::get_g1_multiple(Some(&mut rng), 0, &mut w, &rhid, &mut t); /* Also send T=w.ID to client, remember random w  */
+    }
+
+    /* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
+    rtn = mpin::client_2(&x, &y, &mut sec);
+    if rtn != 0 {
+        println!("FAILURE: CLIENT_2 rtn: {}", rtn);
+    }
+
+    /* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
+    /* If PIN error not required, set E and F = null */
+
+    if !PINERROR {
+        rtn = mpin::server_2(
+            date,
+            &hid,
+            Some(&htid[..]),
+            &y,
+            &sst,
+            Some(&xid[..]),
+            Some(&xcid[..]),
+            &sec,
+            None,
+            None,
+        );
+    } else {
+        rtn = mpin::server_2(
+            date,
+            &hid,
+            Some(&htid[..]),
+            &y,
+            &sst,
+            Some(&xid[..]),
+            Some(&xcid[..]),
+            &sec,
+            Some(&mut e),
+            Some(&mut f),
+        );
+    }
+
+    if rtn == mpin::BAD_PIN {
+        println!("Server says - Bad Pin. I don't know you. Feck off.");
+        if PINERROR {
+            let err = mpin::kangaroo(&e, &f);
+            if err != 0 {
+                println!("(Client PIN is out by {})", err)
+            }
+        }
+        return;
+    } else {
+        println!("Server says - PIN is good! You really are {}", name);
+    }
+
+    if FULL {
+        let mut pxcid = None;
+        if PERMITS {
+            pxcid = Some(&xcid[..])
+        };
+
+        mpin::hash_all(sha, &hcid, &xid, pxcid, &sec, &y, &z, &t, &mut h);
+        mpin::client_key(sha, &g1, &g2, pin, &r, &x, &h, &t, &mut ck);
+        print!("Client Key =  0x");
+        printbinary(&ck);
+
+        mpin::hash_all(sha, &hsid, &xid, pxcid, &sec, &y, &z, &t, &mut h);
+        mpin::server_key(sha, &z, &sst, &w, &h, &hid, &xid, pxcid, &mut sk);
+        print!("Server Key =  0x");
+        printbinary(&sk);
+    }
+}
+
+fn mpin_bls383(mut rng: &mut RAND) {
+    //use amcl::bls383;
+    use amcl::bls383::ecp;
+    use amcl::bls383::mpin;
+    pub const PERMITS: bool = true;
+    pub const PINERROR: bool = true;
+    pub const FULL: bool = true;
+    //pub const SINGLE_PASS:bool=false;
+
+    const EFS: usize = mpin::EFS;
+    const EGS: usize = mpin::EGS;
+
+    let mut s: [u8; EGS] = [0; EGS];
+    const RM: usize = EFS as usize;
+    let mut hcid: [u8; RM] = [0; RM];
+    let mut hsid: [u8; RM] = [0; RM];
+
+    const G1S: usize = 2 * EFS + 1; /* Group 1 Size */
+    const G2S: usize = 4 * EFS; /* Group 2 Size */
+    const EAS: usize = ecp::AESKEY;
+
+    let mut sst: [u8; G2S] = [0; G2S];
+    let mut token: [u8; G1S] = [0; G1S];
+    let mut permit: [u8; G1S] = [0; G1S];
+    let mut g1: [u8; 12 * EFS] = [0; 12 * EFS];
+    let mut g2: [u8; 12 * EFS] = [0; 12 * EFS];
+    let mut xid: [u8; G1S] = [0; G1S];
+    let mut xcid: [u8; G1S] = [0; G1S];
+    let mut x: [u8; EGS] = [0; EGS];
+    let mut y: [u8; EGS] = [0; EGS];
+    let mut sec: [u8; G1S] = [0; G1S];
+    let mut r: [u8; EGS] = [0; EGS];
+    let mut z: [u8; G1S] = [0; G1S];
+    let mut hid: [u8; G1S] = [0; G1S];
+    let mut htid: [u8; G1S] = [0; G1S];
+    let mut rhid: [u8; G1S] = [0; G1S];
+    let mut w: [u8; EGS] = [0; EGS];
+    let mut t: [u8; G1S] = [0; G1S];
+    let mut e: [u8; 12 * EFS] = [0; 12 * EFS];
+    let mut f: [u8; 12 * EFS] = [0; 12 * EFS];
+    let mut h: [u8; RM] = [0; RM];
+    let mut ck: [u8; EAS] = [0; EAS];
+    let mut sk: [u8; EAS] = [0; EAS];
+
+    let sha = ecp::HASH_TYPE;
+
+    println!("\nTesting MPIN - PIN is 1234");
+    /* Trusted Authority set-up */
+
+    mpin::random_generate(&mut rng, &mut s);
+    print!("Master Secret s: 0x");
+    printbinary(&s);
+
+    /* Create Client Identity */
+    let name = "testUser@miracl.com";
+    let client_id = name.as_bytes();
+
+    print!("Client ID= ");
+    printbinary(&client_id);
+
+    mpin::hash_id(sha, &client_id, &mut hcid); /* Either Client or TA calculates Hash(ID) - you decide! */
+
+    /* Client and Server are issued secrets by DTA */
+    mpin::get_server_secret(&s, &mut sst);
+    print!("Server Secret SS: 0x");
+    printbinary(&sst);
+
+    mpin::get_client_secret(&mut s, &hcid, &mut token);
+    print!("Client Secret CS: 0x");
+    printbinary(&token);
+
+    /* Client extracts PIN from secret to create Token */
+    let pin: i32 = 1234;
+    println!("Client extracts PIN= {}", pin);
+    let mut rtn = mpin::extract_pin(sha, &client_id, pin, &mut token);
+    if rtn != 0 {
+        println!("FAILURE: EXTRACT_PIN rtn: {}", rtn);
+    }
+
+    print!("Client Token TK: 0x");
+    printbinary(&token);
+
+    if FULL {
+        mpin::precompute(&token, &hcid, &mut g1, &mut g2);
+    }
+
+    let mut date = 0;
+    if PERMITS {
+        date = mpin::today();
+        /* Client gets "Time Token" permit from DTA */
+
+        mpin::get_client_permit(sha, date, &s, &hcid, &mut permit);
+        print!("Time Permit TP: 0x");
+        printbinary(&permit);
+
+        /* This encoding makes Time permit look random - Elligator squared */
+        mpin::encoding(&mut rng, &mut permit);
+        print!("Encoded Time Permit TP: 0x");
+        printbinary(&permit);
+        mpin::decoding(&mut permit);
+        print!("Decoded Time Permit TP: 0x");
+        printbinary(&permit);
+    }
+
+    print!("\nPIN= ");
+    let _ = io::Write::flush(&mut io::stdout());
+    let mut input_text = String::new();
+    let _ = io::stdin().read_line(&mut input_text);
+
+    let pin = input_text.trim().parse::<usize>().unwrap();
+
+    println!("MPIN Multi Pass");
+    /* Send U=x.ID to server, and recreate secret from token and pin */
+    rtn = mpin::client_1(
+        sha,
+        date,
+        &client_id,
+        Some(&mut rng),
+        &mut x,
+        pin,
+        &token,
+        &mut sec,
+        Some(&mut xid[..]),
+        Some(&mut xcid[..]),
+        Some(&permit[..]),
+    );
+    if rtn != 0 {
+        println!("FAILURE: CLIENT_1 rtn: {}", rtn);
+    }
+
+    if FULL {
+        mpin::hash_id(sha, &client_id, &mut hcid);
+        mpin::get_g1_multiple(Some(&mut rng), 1, &mut r, &hcid, &mut z); /* Also Send Z=r.ID to Server, remember random r */
+    }
+
+    /* Server calculates H(ID) and H(T|H(ID)) (if time mpin::PERMITS enabled), and maps them to points on the curve HID and HTID resp. */
+
+    mpin::server_1(sha, date, &client_id, &mut hid, Some(&mut htid[..]));
+
+    if date != 0 {
+        rhid.clone_from_slice(&htid[..]);
+    } else {
+        rhid.clone_from_slice(&hid[..]);
+    }
+
+    /* Server generates Random number Y and sends it to Client */
+    mpin::random_generate(&mut rng, &mut y);
+
+    if FULL {
+        mpin::hash_id(sha, &client_id, &mut hsid);
+        mpin::get_g1_multiple(Some(&mut rng), 0, &mut w, &rhid, &mut t); /* Also send T=w.ID to client, remember random w  */
+    }
+
+    /* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
+    rtn = mpin::client_2(&x, &y, &mut sec);
+    if rtn != 0 {
+        println!("FAILURE: CLIENT_2 rtn: {}", rtn);
+    }
+
+    /* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
+    /* If PIN error not required, set E and F = null */
+
+    if !PINERROR {
+        rtn = mpin::server_2(
+            date,
+            &hid,
+            Some(&htid[..]),
+            &y,
+            &sst,
+            Some(&xid[..]),
+            Some(&xcid[..]),
+            &sec,
+            None,
+            None,
+        );
+    } else {
+        rtn = mpin::server_2(
+            date,
+            &hid,
+            Some(&htid[..]),
+            &y,
+            &sst,
+            Some(&xid[..]),
+            Some(&xcid[..]),
+            &sec,
+            Some(&mut e),
+            Some(&mut f),
+        );
+    }
+
+    if rtn == mpin::BAD_PIN {
+        println!("Server says - Bad Pin. I don't know you. Feck off.");
+        if PINERROR {
+            let err = mpin::kangaroo(&e, &f);
+            if err != 0 {
+                println!("(Client PIN is out by {})", err)
+            }
+        }
+        return;
+    } else {
+        println!("Server says - PIN is good! You really are {}", name);
+    }
+
+    if FULL {
+        let mut pxcid = None;
+        if PERMITS {
+            pxcid = Some(&xcid[..])
+        };
+
+        mpin::hash_all(sha, &hcid, &xid, pxcid, &sec, &y, &z, &t, &mut h);
+        mpin::client_key(sha, &g1, &g2, pin, &r, &x, &h, &t, &mut ck);
+        print!("Client Key =  0x");
+        printbinary(&ck);
+
+        mpin::hash_all(sha, &hsid, &xid, pxcid, &sec, &y, &z, &t, &mut h);
+        mpin::server_key(sha, &z, &sst, &w, &h, &hid, &xid, pxcid, &mut sk);
+        print!("Server Key =  0x");
+        printbinary(&sk);
+    }
+}
+
+fn mpin_bls24(mut rng: &mut RAND) {
+    //use amcl::bls24;
+    use amcl::bls24::ecp;
+    use amcl::bls24::mpin192;
+
+    pub const PERMITS: bool = true;
+    pub const PINERROR: bool = true;
+    pub const FULL: bool = true;
+    //pub const SINGLE_PASS:bool=false;
+
+    const EFS: usize = mpin192::EFS;
+    const EGS: usize = mpin192::EGS;
+
+    let mut s: [u8; EGS] = [0; EGS];
+    const RM: usize = EFS as usize;
+    let mut hcid: [u8; RM] = [0; RM];
+    let mut hsid: [u8; RM] = [0; RM];
+
+    const G1S: usize = 2 * EFS + 1; /* Group 1 Size */
+    const G2S: usize = 8 * EFS; /* Group 2 Size */
+    const EAS: usize = ecp::AESKEY;
+
+    let mut sst: [u8; G2S] = [0; G2S];
+    let mut token: [u8; G1S] = [0; G1S];
+    let mut permit: [u8; G1S] = [0; G1S];
+    let mut g1: [u8; 24 * EFS] = [0; 24 * EFS];
+    let mut g2: [u8; 24 * EFS] = [0; 24 * EFS];
+    let mut xid: [u8; G1S] = [0; G1S];
+    let mut xcid: [u8; G1S] = [0; G1S];
+    let mut x: [u8; EGS] = [0; EGS];
+    let mut y: [u8; EGS] = [0; EGS];
+    let mut sec: [u8; G1S] = [0; G1S];
+    let mut r: [u8; EGS] = [0; EGS];
+    let mut z: [u8; G1S] = [0; G1S];
+    let mut hid: [u8; G1S] = [0; G1S];
+    let mut htid: [u8; G1S] = [0; G1S];
+    let mut rhid: [u8; G1S] = [0; G1S];
+    let mut w: [u8; EGS] = [0; EGS];
+    let mut t: [u8; G1S] = [0; G1S];
+    let mut e: [u8; 24 * EFS] = [0; 24 * EFS];
+    let mut f: [u8; 24 * EFS] = [0; 24 * EFS];
+    let mut h: [u8; RM] = [0; RM];
+    let mut ck: [u8; EAS] = [0; EAS];
+    let mut sk: [u8; EAS] = [0; EAS];
+
+    let sha = ecp::HASH_TYPE;
+
+    println!("\nTesting MPIN - PIN is 1234");
+    /* Trusted Authority set-up */
+
+    mpin192::random_generate(&mut rng, &mut s);
+    print!("Master Secret s: 0x");
+    printbinary(&s);
+
+    /* Create Client Identity */
+    let name = "testUser@miracl.com";
+    let client_id = name.as_bytes();
+
+    print!("Client ID= ");
+    printbinary(&client_id);
+
+    mpin192::hash_id(sha, &client_id, &mut hcid); /* Either Client or TA calculates Hash(ID) - you decide! */
+
+    /* Client and Server are issued secrets by DTA */
+    mpin192::get_server_secret(&s, &mut sst);
+    print!("Server Secret SS: 0x");
+    printbinary(&sst);
+
+    mpin192::get_client_secret(&mut s, &hcid, &mut token);
+    print!("Client Secret CS: 0x");
+    printbinary(&token);
+
+    /* Client extracts PIN from secret to create Token */
+    let pin: i32 = 1234;
+    println!("Client extracts PIN= {}", pin);
+    let mut rtn = mpin192::extract_pin(sha, &client_id, pin, &mut token);
+    if rtn != 0 {
+        println!("FAILURE: EXTRACT_PIN rtn: {}", rtn);
+    }
+
+    print!("Client Token TK: 0x");
+    printbinary(&token);
+
+    if FULL {
+        mpin192::precompute(&token, &hcid, &mut g1, &mut g2);
+    }
+
+    let mut date = 0;
+    if PERMITS {
+        date = mpin192::today();
+        /* Client gets "Time Token" permit from DTA */
+
+        mpin192::get_client_permit(sha, date, &s, &hcid, &mut permit);
+        print!("Time Permit TP: 0x");
+        printbinary(&permit);
+
+        /* This encoding makes Time permit look random - Elligator squared */
+        mpin192::encoding(&mut rng, &mut permit);
+        print!("Encoded Time Permit TP: 0x");
+        printbinary(&permit);
+        mpin192::decoding(&mut permit);
+        print!("Decoded Time Permit TP: 0x");
+        printbinary(&permit);
+    }
+
+    print!("\nPIN= ");
+    let _ = io::Write::flush(&mut io::stdout());
+    let mut input_text = String::new();
+    let _ = io::stdin().read_line(&mut input_text);
+
+    let pin = input_text.trim().parse::<usize>().unwrap();
+
+    println!("MPIN Multi Pass");
+    /* Send U=x.ID to server, and recreate secret from token and pin */
+    rtn = mpin192::client_1(
+        sha,
+        date,
+        &client_id,
+        Some(&mut rng),
+        &mut x,
+        pin,
+        &token,
+        &mut sec,
+        Some(&mut xid[..]),
+        Some(&mut xcid[..]),
+        Some(&permit[..]),
+    );
+    if rtn != 0 {
+        println!("FAILURE: CLIENT_1 rtn: {}", rtn);
+    }
+
+    if FULL {
+        mpin192::hash_id(sha, &client_id, &mut hcid);
+        mpin192::get_g1_multiple(Some(&mut rng), 1, &mut r, &hcid, &mut z); /* Also Send Z=r.ID to Server, remember random r */
+    }
+
+    /* Server calculates H(ID) and H(T|H(ID)) (if time mpin192::PERMITS enabled), and maps them to points on the curve HID and HTID resp. */
+
+    mpin192::server_1(sha, date, &client_id, &mut hid, Some(&mut htid[..]));
+
+    if date != 0 {
+        rhid.clone_from_slice(&htid[..]);
+    } else {
+        rhid.clone_from_slice(&hid[..]);
+    }
+
+    /* Server generates Random number Y and sends it to Client */
+    mpin192::random_generate(&mut rng, &mut y);
+
+    if FULL {
+        mpin192::hash_id(sha, &client_id, &mut hsid);
+        mpin192::get_g1_multiple(Some(&mut rng), 0, &mut w, &rhid, &mut t); /* Also send T=w.ID to client, remember random w  */
+    }
+
+    /* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
+    rtn = mpin192::client_2(&x, &y, &mut sec);
+    if rtn != 0 {
+        println!("FAILURE: CLIENT_2 rtn: {}", rtn);
+    }
+
+    /* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
+    /* If PIN error not required, set E and F = null */
+
+    if !PINERROR {
+        rtn = mpin192::server_2(
+            date,
+            &hid,
+            Some(&htid[..]),
+            &y,
+            &sst,
+            Some(&xid[..]),
+            Some(&xcid[..]),
+            &sec,
+            None,
+            None,
+        );
+    } else {
+        rtn = mpin192::server_2(
+            date,
+            &hid,
+            Some(&htid[..]),
+            &y,
+            &sst,
+            Some(&xid[..]),
+            Some(&xcid[..]),
+            &sec,
+            Some(&mut e),
+            Some(&mut f),
+        );
+    }
+
+    if rtn == mpin192::BAD_PIN {
+        println!("Server says - Bad Pin. I don't know you. Feck off.");
+        if PINERROR {
+            let err = mpin192::kangaroo(&e, &f);
+            if err != 0 {
+                println!("(Client PIN is out by {})", err)
+            }
+        }
+        return;
+    } else {
+        println!("Server says - PIN is good! You really are {}", name);
+    }
+
+    if FULL {
+        let mut pxcid = None;
+        if PERMITS {
+            pxcid = Some(&xcid[..])
+        };
+
+        mpin192::hash_all(sha, &hcid, &xid, pxcid, &sec, &y, &z, &t, &mut h);
+        mpin192::client_key(sha, &g1, &g2, pin, &r, &x, &h, &t, &mut ck);
+        print!("Client Key =  0x");
+        printbinary(&ck);
+
+        mpin192::hash_all(sha, &hsid, &xid, pxcid, &sec, &y, &z, &t, &mut h);
+        mpin192::server_key(sha, &z, &sst, &w, &h, &hid, &xid, pxcid, &mut sk);
+        print!("Server Key =  0x");
+        printbinary(&sk);
+    }
+}
+
+fn mpin_bls48(mut rng: &mut RAND) {
+    //use amcl::bls48;
+    use amcl::bls48::ecp;
+    use amcl::bls48::mpin256;
+
+    pub const PERMITS: bool = true;
+    pub const PINERROR: bool = true;
+    pub const FULL: bool = true;
+    //pub const SINGLE_PASS:bool=false;
+
+    const EFS: usize = mpin256::EFS;
+    const EGS: usize = mpin256::EGS;
+
+    let mut s: [u8; EGS] = [0; EGS];
+    const RM: usize = EFS as usize;
+    let mut hcid: [u8; RM] = [0; RM];
+    let mut hsid: [u8; RM] = [0; RM];
+
+    const G1S: usize = 2 * EFS + 1; /* Group 1 Size */
+    const G2S: usize = 16 * EFS; /* Group 2 Size */
+    const EAS: usize = ecp::AESKEY;
+
+    let mut sst: [u8; G2S] = [0; G2S];
+    let mut token: [u8; G1S] = [0; G1S];
+    let mut permit: [u8; G1S] = [0; G1S];
+    let mut g1: [u8; 48 * EFS] = [0; 48 * EFS];
+    let mut g2: [u8; 48 * EFS] = [0; 48 * EFS];
+    let mut xid: [u8; G1S] = [0; G1S];
+    let mut xcid: [u8; G1S] = [0; G1S];
+    let mut x: [u8; EGS] = [0; EGS];
+    let mut y: [u8; EGS] = [0; EGS];
+    let mut sec: [u8; G1S] = [0; G1S];
+    let mut r: [u8; EGS] = [0; EGS];
+    let mut z: [u8; G1S] = [0; G1S];
+    let mut hid: [u8; G1S] = [0; G1S];
+    let mut htid: [u8; G1S] = [0; G1S];
+    let mut rhid: [u8; G1S] = [0; G1S];
+    let mut w: [u8; EGS] = [0; EGS];
+    let mut t: [u8; G1S] = [0; G1S];
+    let mut e: [u8; 48 * EFS] = [0; 48 * EFS];
+    let mut f: [u8; 48 * EFS] = [0; 48 * EFS];
+    let mut h: [u8; RM] = [0; RM];
+    let mut ck: [u8; EAS] = [0; EAS];
+    let mut sk: [u8; EAS] = [0; EAS];
+
+    let sha = ecp::HASH_TYPE;
+
+    println!("\nTesting MPIN - PIN is 1234");
+    /* Trusted Authority set-up */
+
+    mpin256::random_generate(&mut rng, &mut s);
+    print!("Master Secret s: 0x");
+    printbinary(&s);
+
+    /* Create Client Identity */
+    let name = "testUser@miracl.com";
+    let client_id = name.as_bytes();
+
+    print!("Client ID= ");
+    printbinary(&client_id);
+
+    mpin256::hash_id(sha, &client_id, &mut hcid); /* Either Client or TA calculates Hash(ID) - you decide! */
+
+    /* Client and Server are issued secrets by DTA */
+    mpin256::get_server_secret(&s, &mut sst);
+    print!("Server Secret SS: 0x");
+    printbinary(&sst);
+
+    mpin256::get_client_secret(&mut s, &hcid, &mut token);
+    print!("Client Secret CS: 0x");
+    printbinary(&token);
+
+    /* Client extracts PIN from secret to create Token */
+    let pin: i32 = 1234;
+    println!("Client extracts PIN= {}", pin);
+    let mut rtn = mpin256::extract_pin(sha, &client_id, pin, &mut token);
+    if rtn != 0 {
+        println!("FAILURE: EXTRACT_PIN rtn: {}", rtn);
+    }
+
+    print!("Client Token TK: 0x");
+    printbinary(&token);
+
+    if FULL {
+        mpin256::precompute(&token, &hcid, &mut g1, &mut g2);
+    }
+
+    let mut date = 0;
+    if PERMITS {
+        date = mpin256::today();
+        /* Client gets "Time Token" permit from DTA */
+
+        mpin256::get_client_permit(sha, date, &s, &hcid, &mut permit);
+        print!("Time Permit TP: 0x");
+        printbinary(&permit);
+
+        /* This encoding makes Time permit look random - Elligator squared */
+        mpin256::encoding(&mut rng, &mut permit);
+        print!("Encoded Time Permit TP: 0x");
+        printbinary(&permit);
+        mpin256::decoding(&mut permit);
+        print!("Decoded Time Permit TP: 0x");
+        printbinary(&permit);
+    }
+
+    print!("\nPIN= ");
+    let _ = io::Write::flush(&mut io::stdout());
+    let mut input_text = String::new();
+    let _ = io::stdin().read_line(&mut input_text);
+
+    let pin = input_text.trim().parse::<usize>().unwrap();
+
+    println!("MPIN Multi Pass");
+    /* Send U=x.ID to server, and recreate secret from token and pin */
+    rtn = mpin256::client_1(
+        sha,
+        date,
+        &client_id,
+        Some(&mut rng),
+        &mut x,
+        pin,
+        &token,
+        &mut sec,
+        Some(&mut xid[..]),
+        Some(&mut xcid[..]),
+        Some(&permit[..]),
+    );
+    if rtn != 0 {
+        println!("FAILURE: CLIENT_1 rtn: {}", rtn);
+    }
+
+    if FULL {
+        mpin256::hash_id(sha, &client_id, &mut hcid);
+        mpin256::get_g1_multiple(Some(&mut rng), 1, &mut r, &hcid, &mut z); /* Also Send Z=r.ID to Server, remember random r */
+    }
+
+    /* Server calculates H(ID) and H(T|H(ID)) (if time mpin256::PERMITS enabled), and maps them to points on the curve HID and HTID resp. */
+
+    mpin256::server_1(sha, date, &client_id, &mut hid, Some(&mut htid[..]));
+
+    if date != 0 {
+        rhid.clone_from_slice(&htid[..]);
+    } else {
+        rhid.clone_from_slice(&hid[..]);
+    }
+
+    /* Server generates Random number Y and sends it to Client */
+    mpin256::random_generate(&mut rng, &mut y);
+
+    if FULL {
+        mpin256::hash_id(sha, &client_id, &mut hsid);
+        mpin256::get_g1_multiple(Some(&mut rng), 0, &mut w, &rhid, &mut t); /* Also send T=w.ID to client, remember random w  */
+    }
+
+    /* Client Second Pass: Inputs Client secret SEC, x and y. Outputs -(x+y)*SEC */
+    rtn = mpin256::client_2(&x, &y, &mut sec);
+    if rtn != 0 {
+        println!("FAILURE: CLIENT_2 rtn: {}", rtn);
+    }
+
+    /* Server Second pass. Inputs hashed client id, random Y, -(x+y)*SEC, xID and xCID and Server secret SST. E and F help kangaroos to find error. */
+    /* If PIN error not required, set E and F = null */
+
+    if !PINERROR {
+        rtn = mpin256::server_2(
+            date,
+            &hid,
+            Some(&htid[..]),
+            &y,
+            &sst,
+            Some(&xid[..]),
+            Some(&xcid[..]),
+            &sec,
+            None,
+            None,
+        );
+    } else {
+        rtn = mpin256::server_2(
+            date,
+            &hid,
+            Some(&htid[..]),
+            &y,
+            &sst,
+            Some(&xid[..]),
+            Some(&xcid[..]),
+            &sec,
+            Some(&mut e),
+            Some(&mut f),
+        );
+    }
+
+    if rtn == mpin256::BAD_PIN {
+        println!("Server says - Bad Pin. I don't know you. Feck off.");
+        if PINERROR {
+            let err = mpin256::kangaroo(&e, &f);
+            if err != 0 {
+                println!("(Client PIN is out by {})", err)
+            }
+        }
+        return;
+    } else {
+        println!("Server says - PIN is good! You really are {}", name);
+    }
+
+    if FULL {
+        let mut pxcid = None;
+        if PERMITS {
+            pxcid = Some(&xcid[..])
+        };
+
+        mpin256::hash_all(sha, &hcid, &xid, pxcid, &sec, &y, &z, &t, &mut h);
+        mpin256::client_key(sha, &g1, &g2, pin, &r, &x, &h, &t, &mut ck);
+        print!("Client Key =  0x");
+        printbinary(&ck);
+
+        mpin256::hash_all(sha, &hsid, &xid, pxcid, &sec, &y, &z, &t, &mut h);
+        mpin256::server_key(sha, &z, &sst, &w, &h, &hid, &xid, pxcid, &mut sk);
+        print!("Server Key =  0x");
+        printbinary(&sk);
+    }
+}
+
+fn rsa_2048(mut rng: &mut RAND) {
+    //use amcl::rsa2048;
+    use amcl::rsa2048::ff;
+    use amcl::rsa2048::rsa;
+
+    let sha = rsa::HASH_TYPE;
+    let message: &[u8] = b"Hello World\n";
+    const RFS: usize = rsa::RFS;
+
+    let mut pbc = rsa::new_public_key(ff::FFLEN);
+    let mut prv = rsa::new_private_key(ff::HFLEN);
+
+    let mut ml: [u8; RFS] = [0; RFS];
+    let mut ms: [u8; RFS] = [0; RFS];
+    let mut c: [u8; RFS] = [0; RFS];
+    let mut s: [u8; RFS] = [0; RFS];
+    let mut e: [u8; RFS] = [0; RFS];
+
+    println!("\nTesting RSA");
+    println!("Generating public/private key pair");
+    rsa::key_pair(&mut rng, 65537, &mut prv, &mut pbc);
+
+    println!("Encrypting test string\n");
+    rsa::oaep_encode(sha, &message, &mut rng, None, &mut e); /* OAEP encode message M to E  */
+
+    rsa::encrypt(&pbc, &e, &mut c); /* encrypt encoded message */
+    print!("Ciphertext= 0x");
+    printbinary(&c);
+
+    println!("Decrypting test string");
+    rsa::decrypt(&prv, &c, &mut ml);
+    let mlen = rsa::oaep_decode(sha, None, &mut ml); /* OAEP decode message  */
+
+    let mess = str::from_utf8(&ml[0..mlen]).unwrap();
+    print!("{}", &mess);
+
+    println!("Signing message");
+    rsa::pkcs15(sha, message, &mut c);
+
+    rsa::decrypt(&prv, &c, &mut s); /* create signature in S */
+
+    print!("Signature= 0x");
+    printbinary(&s);
+
+    rsa::encrypt(&pbc, &s, &mut ms);
+
+    let mut cmp = true;
+    if c.len() != ms.len() {
+        cmp = false;
+    } else {
+        for j in 0..c.len() {
+            if c[j] != ms[j] {
+                cmp = false
+            }
+        }
+    }
+    if cmp {
+        println!("Signature is valid");
+    } else {
+        println!("Signature is INVALID");
+    }
+
+    rsa::private_key_kill(&mut prv);
+}
+
+//#[test]
+fn main() {
+    let mut raw: [u8; 100] = [0; 100];
+
+    let mut rng = RAND::new();
+    rng.clean();
+    for i in 0..100 {
+        raw[i] = i as u8
+    }
+
+    rng.seed(100, &raw);
+
+    ecdh_ed25519(&mut rng);
+    ecdh_nist256(&mut rng);
+    ecdh_goldilocks(&mut rng);
+    mpin_bn254(&mut rng);
+    mpin_bls383(&mut rng);
+    mpin_bls24(&mut rng);
+    mpin_bls48(&mut rng);
+    rsa_2048(&mut rng);
+}
diff --git a/TestBLS.rs b/TestBLS.rs
new file mode 100644
index 0000000..1a54ee4
--- /dev/null
+++ b/TestBLS.rs
@@ -0,0 +1,190 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+extern crate amcl;
+
+use amcl::rand::RAND;
+
+pub fn printbinary(array: &[u8]) {
+    for i in 0..array.len() {
+        print!("{:02X}", array[i])
+    }
+    println!("")
+}
+
+fn bls_bn254(mut rng: &mut RAND) {
+    use amcl::bn254::bls;
+
+    const BFS: usize = bls::BFS;
+    const BGS: usize = bls::BGS;
+
+    let mut s: [u8; BGS] = [0; BGS];
+
+    const G1S: usize = BFS + 1; /* Group 1 Size */
+    const G2S: usize = 4 * BFS; /* Group 2 Size */
+
+    let mut w: [u8; G2S] = [0; G2S];
+    let mut sig: [u8; G1S] = [0; G1S];
+
+    let m = String::from("This is a test message");
+
+    bls::key_pair_generate(&mut rng, &mut s, &mut w);
+    print!("Private key : 0x");
+    printbinary(&s);
+    print!("Public  key : 0x");
+    printbinary(&w);
+
+    bls::sign(&mut sig, &m, &s);
+    print!("Signature : 0x");
+    printbinary(&sig);
+
+    let res = bls::verify(&sig, &m, &w);
+    if res == 0 {
+        println!("Signature is OK");
+    } else {
+        println!("Signature is *NOT* OK");
+    }
+}
+
+fn bls_bls383(mut rng: &mut RAND) {
+    use amcl::bls383::bls;
+
+    const BFS: usize = bls::BFS;
+    const BGS: usize = bls::BGS;
+
+    let mut s: [u8; BGS] = [0; BGS];
+
+    const G1S: usize = BFS + 1; /* Group 1 Size */
+    const G2S: usize = 4 * BFS; /* Group 2 Size */
+
+    let mut w: [u8; G2S] = [0; G2S];
+    let mut sig: [u8; G1S] = [0; G1S];
+
+    let m = String::from("This is a test message");
+
+    bls::key_pair_generate(&mut rng, &mut s, &mut w);
+    print!("Private key : 0x");
+    printbinary(&s);
+    print!("Public  key : 0x");
+    printbinary(&w);
+
+    bls::sign(&mut sig, &m, &s);
+    print!("Signature : 0x");
+    printbinary(&sig);
+
+    let res = bls::verify(&sig, &m, &w);
+    if res == 0 {
+        println!("Signature is OK");
+    } else {
+        println!("Signature is *NOT* OK");
+    }
+}
+
+fn bls_bls24(mut rng: &mut RAND) {
+    use amcl::bls24::bls192;
+
+    const BFS: usize = bls192::BFS;
+    const BGS: usize = bls192::BGS;
+
+    let mut s: [u8; BGS] = [0; BGS];
+
+    const G1S: usize = BFS + 1; /* Group 1 Size */
+    const G2S: usize = 8 * BFS; /* Group 2 Size */
+
+    let mut w: [u8; G2S] = [0; G2S];
+    let mut sig: [u8; G1S] = [0; G1S];
+
+    let m = String::from("This is a test message");
+
+    bls192::key_pair_generate(&mut rng, &mut s, &mut w);
+    print!("Private key : 0x");
+    printbinary(&s);
+    print!("Public  key : 0x");
+    printbinary(&w);
+
+    bls192::sign(&mut sig, &m, &s);
+    print!("Signature : 0x");
+    printbinary(&sig);
+
+    let res = bls192::verify(&sig, &m, &w);
+    if res == 0 {
+        println!("Signature is OK");
+    } else {
+        println!("Signature is *NOT* OK");
+    }
+}
+
+fn bls_bls48(mut rng: &mut RAND) {
+    use amcl::bls48::bls256;
+
+    const BFS: usize = bls256::BFS;
+    const BGS: usize = bls256::BGS;
+
+    let mut s: [u8; BGS] = [0; BGS];
+
+    const G1S: usize = BFS + 1; /* Group 1 Size */
+    const G2S: usize = 16 * BFS; /* Group 2 Size */
+
+    let mut w: [u8; G2S] = [0; G2S];
+    let mut sig: [u8; G1S] = [0; G1S];
+
+    let m = String::from("This is a test message");
+
+    bls256::key_pair_generate(&mut rng, &mut s, &mut w);
+    print!("Private key : 0x");
+    printbinary(&s);
+    print!("Public  key : 0x");
+    printbinary(&w);
+
+    bls256::sign(&mut sig, &m, &s);
+    print!("Signature : 0x");
+    printbinary(&sig);
+
+    let res = bls256::verify(&sig, &m, &w);
+    if res == 0 {
+        println!("Signature is OK");
+    } else {
+        println!("Signature is *NOT* OK");
+    }
+}
+
+fn main() {
+    use amcl::arch;
+
+    let mut raw: [u8; 100] = [0; 100];
+
+    let mut rng = RAND::new();
+    rng.clean();
+    for i in 0..100 {
+        raw[i] = i as u8
+    }
+
+    rng.seed(100, &raw);
+
+    println!("{} bit build", arch::CHUNK);
+
+    println!("Testing BLS signature for curve BN254");
+    bls_bn254(&mut rng);
+    println!("\nTesting BLS signature for curve BLS383");
+    bls_bls383(&mut rng);
+    println!("\nTesting BLS signature for curve BLS24");
+    bls_bls24(&mut rng);
+    println!("\nTesting BLS signature for curve BLS48");
+    bls_bls48(&mut rng);
+}
diff --git a/TestNHS.rs b/TestNHS.rs
new file mode 100644
index 0000000..4e7ed02
--- /dev/null
+++ b/TestNHS.rs
@@ -0,0 +1,77 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+//  TestNHS.rs - Tests NewHope Simple API
+
+// See https://eprint.iacr.org/2016/1157 (Alkim, Ducas, Popplemann and Schwabe)
+
+// rustc TestNHS.rs --extern amcl=libamcl.rlib
+
+extern crate amcl;
+
+//use std::str;
+//use std::io;
+
+use amcl::rand::RAND;
+//use amcl::aes;
+use amcl::nhs;
+
+//#[test]
+fn main()
+{
+	let mut raw:[u8;100]=[0;100];	
+
+	let mut srng=RAND::new();
+	srng.clean();
+	for i in 0..100 {raw[i]=(i+1) as u8}
+
+	srng.seed(100,&raw);	
+
+
+									let mut crng=RAND::new();
+									crng.clean();
+									for i in 0..100 {raw[i]=(i+2) as u8}
+
+									crng.seed(100,&raw);	
+
+
+	let mut ss:[u8;1792]=[0;1792];
+					let mut sb:[u8;1824]=[0;1824];
+					let mut uc:[u8;2176]=[0;2176];
+
+	let mut keya:[u8;32]=[0;32];
+									let mut keyb:[u8;32]=[0;32];
+
+	nhs::server_1(&mut srng,&mut sb,&mut ss);
+
+									nhs::client(&mut crng,&sb,&mut uc,&mut keyb);
+
+	nhs::server_2(&ss,&uc,&mut keya);
+
+	for i in 0..keya.len() {
+		print!("{:02X}", keya[i]);
+	}
+	println!("");	
+
+									for i in 0..keyb.len() {
+										print!("{:02X}", keyb[i]);
+									}
+									println!("");		
+
+}
\ No newline at end of file
diff --git a/deploy.token b/deploy.token
new file mode 100644
index 0000000..32b7050
--- /dev/null
+++ b/deploy.token
@@ -0,0 +1 @@
+1YhwbnDDEE2ZdX8P7g4U9WY3uzKTZHvIk5qKEivXYQbQwtLsugOlh8plPhFuL9M6lQeiHX7GeuQkevsymgXHrXVV5QFB7JtsJmh4tJcIiJ8z+9YWchHmCLRkXWoWgoXxwEGIne48KT3At43gCKJGNFGmYAl00XtgiQ1SAfop0LmImrWWHyDIAxeou6GBSg+S2Gz5+AePf3HDnXCfgX2f+tw/SGlEi6LDAtyoU7+yF584g9d5PK6Ctm7GMCdS4M65mqhcqUkrqy4jGomAkf57/j4zauj2ISmyHcmfkKNFvK2qFlf/vy65hacFV3+nIUYvcAv8aasT5Qx895LsQ3xXmCIJnorqd1c7xeafjQudLwpDlyJs5j82NZUIQn+mMkCuSj5g686gpfuKMNs0Gthl2Z7IzmQWiP5PgDZd/QEx/4Q4jUoy8CtrZu6BCxAK7muLLWaI91gWpducaJKZ6dVHZ5tBz7XHG7NFpqRssCUfIMhganVHQ6HRzeADqYXswkH005uexmKRKBvXFZjvbz3e21uSY4HTWZwmcQH+BWEDjfIqY4g0BOUukZH/bOsHYIcKsRT6eanHCsiDKpGbcRB+s6qSquFgtRohUbncUTU1UpaGGPqRiT21WzkbGhVS/oA9uS+NDgB824f9aNShZ0havjCUEpbIfWV+gQdl/j66aUo=
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..da805e0
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,83 @@
+NOTE: Updated to Rust 2018
+
+NOTE: This version of the library requires Version 1.31+ of Rust for 64-bit 
+integer support and for Rust 2018. 
+
+Now AMCL version 3 is distributed as a cargo crate.
+
+Namespaces are used to separate different curves.
+
+To build the library and see it in action, copy all of the files in this 
+directory and its subdirectories to a fresh root directory. 
+
+Then for example execute
+
+cargo rustc  --release --features "bn254 bls383 bls24 bls48 ed25519 nist256 goldilocks rsa2048"
+
+This will create a build of the library for the current default target (be it 32 or 64 bits). 
+
+(To test a 32-bit environment you can follow the Web Assembly (wasm) readme instructions for rust)
+
+Next copy the library from target/release/libamcl.rlib into the root 
+directory and execute
+
+rustc TestALL.rs --extern amcl=libamcl.rlib
+
+rustc TestBLS.rs --extern amcl=libamcl.rlib
+
+rustc BenchtestALL.rs --extern amcl=libamcl.rlib
+
+rustc TestNHS.rs --extern amcl=libamcl.rlib
+
+Finally execute these programs.
+
+To add amcl functionality to your own programs, add a dependency to your 
+Cargo.toml file. For example to use the curve bls48, add this dependency
+
+[dependencies]
+
+amcl = { version = "0.2.0",  optional = true, default-features = false, features = ["bls48"]}
+
+if published to crates.io, or 
+
+amcl = { version = "0.2.0",  optional = true, default-features = false, features = ["bls48"], path="your_amcl_location" }
+
+And to use primitives of the needed curve in your source code:
+
+use amcl::bls48::{ECP, ECP8}; //any primitive you need
+
+Full list of features:
+
+* Elliptic Curves
+  * ed25519
+  * c25519
+  * nist256
+  * brainpool
+  * anssi
+  * hifive
+  * goldilocks
+  * nist384
+  * c41417
+  * nist521
+  * nums256w
+  * nums256e
+  * nums384w
+  * nums384e
+  * nums512w
+  * nums512e
+  * secp256k1
+* Pairing-Friendly Elliptic Curves
+  * bn254
+  * bn254CX
+  * bls383
+  * bls381
+  * fp256BN
+  * fp512BN
+  * bls461
+  * bls24
+  * bls48
+  
+* RSA
+  * rsa2048
+  * rsa3072
+  * rsa4096
diff --git a/src/aes.rs b/src/aes.rs
new file mode 100644
index 0000000..eedea79
--- /dev/null
+++ b/src/aes.rs
@@ -0,0 +1,772 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+pub const ECB: usize = 0;
+pub const CBC: usize = 1;
+pub const CFB1: usize = 2;
+pub const CFB2: usize = 3;
+pub const CFB4: usize = 5;
+pub const OFB1: usize = 14;
+pub const OFB2: usize = 15;
+pub const OFB4: usize = 17;
+pub const OFB8: usize = 21;
+pub const OFB16: usize = 29;
+pub const CTR1: usize = 30;
+pub const CTR2: usize = 31;
+pub const CTR4: usize = 33;
+pub const CTR8: usize = 37;
+pub const CTR16: usize = 45;
+
+const INCO: [u8; 4] = [0xB, 0xD, 0x9, 0xE]; /* Inverse Coefficients */
+
+const PTAB: [u8; 256] = [
+    1, 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248, 19, 53, 95, 225, 56, 72, 216, 115,
+    149, 164, 247, 2, 6, 10, 30, 34, 102, 170, 229, 52, 92, 228, 55, 89, 235, 38, 106, 190, 217,
+    112, 144, 171, 230, 49, 83, 245, 4, 12, 20, 60, 68, 204, 79, 209, 104, 184, 211, 110, 178, 205,
+    76, 212, 103, 169, 224, 59, 77, 215, 98, 166, 241, 8, 24, 40, 120, 136, 131, 158, 185, 208,
+    107, 189, 220, 127, 129, 152, 179, 206, 73, 219, 118, 154, 181, 196, 87, 249, 16, 48, 80, 240,
+    11, 29, 39, 105, 187, 214, 97, 163, 254, 25, 43, 125, 135, 146, 173, 236, 47, 113, 147, 174,
+    233, 32, 96, 160, 251, 22, 58, 78, 210, 109, 183, 194, 93, 231, 50, 86, 250, 21, 63, 65, 195,
+    94, 226, 61, 71, 201, 64, 192, 91, 237, 44, 116, 156, 191, 218, 117, 159, 186, 213, 100, 172,
+    239, 42, 126, 130, 157, 188, 223, 122, 142, 137, 128, 155, 182, 193, 88, 232, 35, 101, 175,
+    234, 37, 111, 177, 200, 67, 197, 84, 252, 31, 33, 99, 165, 244, 7, 9, 27, 45, 119, 153, 176,
+    203, 70, 202, 69, 207, 74, 222, 121, 139, 134, 145, 168, 227, 62, 66, 198, 81, 243, 14, 18, 54,
+    90, 238, 41, 123, 141, 140, 143, 138, 133, 148, 167, 242, 13, 23, 57, 75, 221, 124, 132, 151,
+    162, 253, 28, 36, 108, 180, 199, 82, 246, 1,
+];
+
+const LTAB: [u8; 256] = [
+    0, 255, 25, 1, 50, 2, 26, 198, 75, 199, 27, 104, 51, 238, 223, 3, 100, 4, 224, 14, 52, 141,
+    129, 239, 76, 113, 8, 200, 248, 105, 28, 193, 125, 194, 29, 181, 249, 185, 39, 106, 77, 228,
+    166, 114, 154, 201, 9, 120, 101, 47, 138, 5, 33, 15, 225, 36, 18, 240, 130, 69, 53, 147, 218,
+    142, 150, 143, 219, 189, 54, 208, 206, 148, 19, 92, 210, 241, 64, 70, 131, 56, 102, 221, 253,
+    48, 191, 6, 139, 98, 179, 37, 226, 152, 34, 136, 145, 16, 126, 110, 72, 195, 163, 182, 30, 66,
+    58, 107, 40, 84, 250, 133, 61, 186, 43, 121, 10, 21, 155, 159, 94, 202, 78, 212, 172, 229, 243,
+    115, 167, 87, 175, 88, 168, 80, 244, 234, 214, 116, 79, 174, 233, 213, 231, 230, 173, 232, 44,
+    215, 117, 122, 235, 22, 11, 245, 89, 203, 95, 176, 156, 169, 81, 160, 127, 12, 246, 111, 23,
+    196, 73, 236, 216, 67, 31, 45, 164, 118, 123, 183, 204, 187, 62, 90, 251, 96, 177, 134, 59, 82,
+    161, 108, 170, 85, 41, 157, 151, 178, 135, 144, 97, 190, 220, 252, 188, 149, 207, 205, 55, 63,
+    91, 209, 83, 57, 132, 60, 65, 162, 109, 71, 20, 42, 158, 93, 86, 242, 211, 171, 68, 17, 146,
+    217, 35, 32, 46, 137, 180, 124, 184, 38, 119, 153, 227, 165, 103, 74, 237, 222, 197, 49, 254,
+    24, 13, 99, 140, 128, 192, 247, 112, 7,
+];
+
+const FBSUB: [u8; 256] = [
+    99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, 125,
+    250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204,
+    52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235,
+    39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, 83, 209, 0,
+    237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133,
+    69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16,
+    255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129,
+    79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92,
+    194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234,
+    101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138,
+    112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, 248, 152, 17, 105,
+    217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65,
+    153, 45, 15, 176, 84, 187, 22,
+];
+
+const RBSUB: [u8; 256] = [
+    82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215, 251, 124, 227, 57, 130,
+    155, 47, 255, 135, 52, 142, 67, 68, 196, 222, 233, 203, 84, 123, 148, 50, 166, 194, 35, 61,
+    238, 76, 149, 11, 66, 250, 195, 78, 8, 46, 161, 102, 40, 217, 36, 178, 118, 91, 162, 73, 109,
+    139, 209, 37, 114, 248, 246, 100, 134, 104, 152, 22, 212, 164, 92, 204, 93, 101, 182, 146, 108,
+    112, 72, 80, 253, 237, 185, 218, 94, 21, 70, 87, 167, 141, 157, 132, 144, 216, 171, 0, 140,
+    188, 211, 10, 247, 228, 88, 5, 184, 179, 69, 6, 208, 44, 30, 143, 202, 63, 15, 2, 193, 175,
+    189, 3, 1, 19, 138, 107, 58, 145, 17, 65, 79, 103, 220, 234, 151, 242, 207, 206, 240, 180, 230,
+    115, 150, 172, 116, 34, 231, 173, 53, 133, 226, 249, 55, 232, 28, 117, 223, 110, 71, 241, 26,
+    113, 29, 41, 197, 137, 111, 183, 98, 14, 170, 24, 190, 27, 252, 86, 62, 75, 198, 210, 121, 32,
+    154, 219, 192, 254, 120, 205, 90, 244, 31, 221, 168, 51, 136, 7, 199, 49, 177, 18, 16, 89, 39,
+    128, 236, 95, 96, 81, 127, 169, 25, 181, 74, 13, 45, 229, 122, 159, 147, 201, 156, 239, 160,
+    224, 59, 77, 174, 42, 245, 176, 200, 235, 187, 60, 131, 83, 153, 97, 23, 43, 4, 126, 186, 119,
+    214, 38, 225, 105, 20, 99, 85, 33, 12, 125,
+];
+
+const RCO: [u8; 16] = [
+    1, 2, 4, 8, 16, 32, 64, 128, 27, 54, 108, 216, 171, 77, 154, 47,
+];
+
+const FTABLE: [u32; 256] = [
+    0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0xdf2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591,
+    0x50303060, 0x3010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec,
+    0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0xbf0f0fb,
+    0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b,
+    0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x2f7f7f5, 0x4fcccc83,
+    0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x8f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a,
+    0xc040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0xf05050a, 0xb59a9a2f,
+    0x907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea,
+    0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b,
+    0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413,
+    0xf55353a6, 0x68d1d1b9, 0x0, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6,
+    0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85,
+    0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511,
+    0xcf45458a, 0x10f9f9e9, 0x6020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b,
+    0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x4f5f5f1,
+    0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5, 0xef3f3fd, 0x6dd2d2bf,
+    0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e,
+    0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6,
+    0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b,
+    0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad,
+    0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0xa06060c, 0x6c242448, 0xe45c5cb8,
+    0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2,
+    0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949,
+    0xb46c6cd8, 0xfa5656ac, 0x7f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810,
+    0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697,
+    0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f,
+    0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x5030306, 0x1f6f6f7, 0x120e0e1c,
+    0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27,
+    0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433,
+    0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
+    0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0,
+    0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c,
+];
+
+const RTABLE: [u32; 256] = [
+    0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, 0xf1459d1f, 0xab58faac, 0x9303e34b,
+    0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, 0x8fa362b5,
+    0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, 0x2752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b,
+    0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e,
+    0x6a89c275, 0x78798ef4, 0x6b3e5899, 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d,
+    0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9,
+    0x58684870, 0x19fd458f, 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566,
+    0x728ebb2, 0x3c2b52f, 0x9a7bc586, 0xa50837d3, 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed,
+    0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, 0x1f6234d1, 0x8afea6c4,
+    0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x69f715e, 0x51106ebd,
+    0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, 0x55dc471, 0x6fd40604, 0xff155060,
+    0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879,
+    0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x0, 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c,
+    0xfbff0efd, 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624,
+    0xb1670a0c, 0xfe75793, 0xd296eeb4, 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c,
+    0xaba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0xb0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14,
+    0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b,
+    0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684,
+    0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, 0xec52860d, 0xd0e3c177,
+    0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322,
+    0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f,
+    0xe49d3a2c, 0xd927850, 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382,
+    0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb,
+    0x97826cd, 0xf418596e, 0x1b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, 0x8cfbc21, 0xe6e815ef,
+    0xd99be7ba, 0xce366f4a, 0xd4099fea, 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235,
+    0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1, 0xf7daec41, 0xe50cd7f, 0x2ff69117,
+    0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, 0x7f516546,
+    0x4ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d,
+    0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, 0xede51ce1, 0x3cb1477a,
+    0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478,
+    0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, 0xc25e2bc, 0x8b493c28, 0x41950dff,
+    0x7101a839, 0xdeb30c08, 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0,
+];
+
+pub struct AES {
+    nk: usize,
+    nr: usize,
+    mode: usize,
+    fkey: [u32; 60],
+    rkey: [u32; 60],
+    pub f: [u8; 16],
+}
+
+impl AES {
+    fn rotl8(x: u32) -> u32 {
+        return ((x) << 8) | ((x) >> 24);
+    }
+
+    fn rotl16(x: u32) -> u32 {
+        return ((x) << 16) | ((x) >> 16);
+    }
+
+    fn rotl24(x: u32) -> u32 {
+        return ((x) << 24) | ((x) >> 8);
+    }
+
+    fn pack(b: [u8; 4]) -> u32 {
+        /* pack bytes into a 32-bit Word */
+        return ((((b[3]) & 0xff) as u32) << 24)
+            | ((((b[2]) & 0xff) as u32) << 16)
+            | ((((b[1]) & 0xff) as u32) << 8)
+            | (((b[0]) & 0xff) as u32);
+    }
+
+    fn unpack(a: u32) -> [u8; 4] {
+        /* unpack bytes from a word */
+        let b: [u8; 4] = [
+            (a & 0xff) as u8,
+            ((a >> 8) & 0xff) as u8,
+            ((a >> 16) & 0xff) as u8,
+            ((a >> 24) & 0xff) as u8,
+        ];
+        return b;
+    }
+
+    fn bmul(x: u8, y: u8) -> u8 {
+        /* x.y= AntiLog(Log(x) + Log(y)) */
+        let ix = (x as usize) & 0xff;
+        let iy = (y as usize) & 0xff;
+        let lx = (LTAB[ix] as usize) & 0xff;
+        let ly = (LTAB[iy] as usize) & 0xff;
+
+        if x != 0 && y != 0 {
+            return PTAB[(lx + ly) % 255];
+        } else {
+            return 0;
+        }
+    }
+
+    fn subbyte(a: u32) -> u32 {
+        let mut b = AES::unpack(a);
+        b[0] = FBSUB[b[0] as usize];
+        b[1] = FBSUB[b[1] as usize];
+        b[2] = FBSUB[b[2] as usize];
+        b[3] = FBSUB[b[3] as usize];
+        return AES::pack(b);
+    }
+
+    fn product(x: u32, y: u32) -> u8 {
+        /* dot product of two 4-byte arrays */
+        let xb = AES::unpack(x);
+        let yb = AES::unpack(y);
+
+        return AES::bmul(xb[0], yb[0])
+            ^ AES::bmul(xb[1], yb[1])
+            ^ AES::bmul(xb[2], yb[2])
+            ^ AES::bmul(xb[3], yb[3]);
+    }
+
+    fn invmixcol(x: u32) -> u32 {
+        /* matrix Multiplication */
+        let mut b: [u8; 4] = [0; 4];
+        let mut m = AES::pack(INCO);
+        b[3] = AES::product(m, x);
+        m = AES::rotl24(m);
+        b[2] = AES::product(m, x);
+        m = AES::rotl24(m);
+        b[1] = AES::product(m, x);
+        m = AES::rotl24(m);
+        b[0] = AES::product(m, x);
+        let y = AES::pack(b);
+        return y;
+    }
+
+    fn increment(f: &mut [u8; 16]) {
+        for i in 0..16 {
+            f[i] += 1;
+            if f[i] != 0 {
+                break;
+            }
+        }
+    }
+
+    pub fn new() -> AES {
+        AES {
+            nk: 0,
+            nr: 0,
+            mode: 0,
+            fkey: [0; 60],
+            rkey: [0; 60],
+            f: [0; 16],
+        }
+    }
+
+    /* reset cipher */
+    pub fn reset(&mut self, m: usize, iv: Option<[u8; 16]>) {
+        /* reset mode, or reset iv */
+        self.mode = m;
+        for i in 0..16 {
+            self.f[i] = 0
+        }
+        if self.mode != ECB {
+            if let Some(x) = iv {
+                for i in 0..16 {
+                    self.f[i] = x[i]
+                }
+            }
+        }
+    }
+
+    pub fn init(&mut self, m: usize, nkey: usize, key: &[u8], iv: Option<[u8; 16]>) -> bool {
+        /* Key Scheduler. Create expanded encryption key */
+        let mut cipherkey: [u32; 8] = [0; 8];
+        let mut b: [u8; 4] = [0; 4];
+        let nk = nkey / 4;
+        if nk != 4 && nk != 6 && nk != 8 {
+            return false;
+        }
+        let nr = 6 + nk;
+        self.nk = nk;
+        self.nr = nr;
+        self.reset(m, iv);
+        let n = 4 * (nr + 1);
+
+        let mut j = 0;
+        for i in 0..nk {
+            for k in 0..4 {
+                b[k] = key[j + k]
+            }
+            cipherkey[i] = AES::pack(b);
+            j += 4;
+        }
+
+        for i in 0..nk {
+            self.fkey[i] = cipherkey[i]
+        }
+
+        j = nk;
+        let mut k = 0;
+        while j < n {
+            self.fkey[j] =
+                self.fkey[j - nk] ^ AES::subbyte(AES::rotl24(self.fkey[j - 1])) ^ (RCO[k] as u32);
+            if nk<=6 { 
+		for i in 1..nk {
+			if (i + j) >= n {
+				break;
+			}
+			self.fkey[i + j] = self.fkey[i + j - nk] ^ self.fkey[i + j - 1];
+		}
+	    } else {
+		for i in 1..4  {
+			if (i + j) >= n {
+				break;
+			}
+			self.fkey[i + j] = self.fkey[i + j - nk] ^ self.fkey[i + j - 1];
+		}
+		
+		if (j + 4) < n {
+			self.fkey[j + 4] = self.fkey[j + 4 - nk] ^ AES::subbyte(self.fkey[j + 3]);
+		}
+		for i in 5..nk {
+			if (i + j) >= n {
+				break;
+			}
+			self.fkey[i + j] = self.fkey[i + j - nk] ^ self.fkey[i + j - 1];
+		}	        
+	    }
+            j += nk;
+            k += 1;
+        }
+
+        /* now for the expanded decrypt key in reverse order */
+
+        for j in 0..4 {
+            self.rkey[j + n - 4] = self.fkey[j]
+        }
+        let mut i = 4;
+        while i < n - 4 {
+            let k = n - 4 - i;
+            for j in 0..4 {
+                self.rkey[k + j] = AES::invmixcol(self.fkey[i + j])
+            }
+            i += 4;
+        }
+        for j in n - 4..n {
+            self.rkey[j + 4 - n] = self.fkey[j]
+        }
+        return true;
+    }
+
+    pub fn getreg(&mut self) -> [u8; 16] {
+        let mut ir: [u8; 16] = [0; 16];
+        for i in 0..16 {
+            ir[i] = self.f[i]
+        }
+        return ir;
+    }
+
+    /* Encrypt a single block */
+    pub fn ecb_encrypt(&mut self, buff: &mut [u8; 16]) {
+        let mut b: [u8; 4] = [0; 4];
+        let mut p: [u32; 4] = [0; 4];
+        let mut q: [u32; 4] = [0; 4];
+
+        let mut j = 0;
+        for i in 0..4 {
+            for k in 0..4 {
+                b[k] = buff[j + k]
+            }
+            p[i] = AES::pack(b);
+            p[i] ^= self.fkey[i];
+            j += 4;
+        }
+
+        let mut k = 4;
+
+        /* State alternates between p and q */
+        for _ in 1..self.nr {
+            q[0] = self.fkey[k]
+                ^ FTABLE[(p[0] & 0xff) as usize]
+                ^ AES::rotl8(FTABLE[((p[1] >> 8) & 0xff) as usize])
+                ^ AES::rotl16(FTABLE[((p[2] >> 16) & 0xff) as usize])
+                ^ AES::rotl24(FTABLE[((p[3] >> 24) & 0xff) as usize]);
+
+            q[1] = self.fkey[k + 1]
+                ^ FTABLE[(p[1] & 0xff) as usize]
+                ^ AES::rotl8(FTABLE[((p[2] >> 8) & 0xff) as usize])
+                ^ AES::rotl16(FTABLE[((p[3] >> 16) & 0xff) as usize])
+                ^ AES::rotl24(FTABLE[((p[0] >> 24) & 0xff) as usize]);
+
+            q[2] = self.fkey[k + 2]
+                ^ FTABLE[(p[2] & 0xff) as usize]
+                ^ AES::rotl8(FTABLE[((p[3] >> 8) & 0xff) as usize])
+                ^ AES::rotl16(FTABLE[((p[0] >> 16) & 0xff) as usize])
+                ^ AES::rotl24(FTABLE[((p[1] >> 24) & 0xff) as usize]);
+
+            q[3] = self.fkey[k + 3]
+                ^ FTABLE[(p[3] & 0xff) as usize]
+                ^ AES::rotl8(FTABLE[((p[0] >> 8) & 0xff) as usize])
+                ^ AES::rotl16(FTABLE[((p[1] >> 16) & 0xff) as usize])
+                ^ AES::rotl24(FTABLE[((p[2] >> 24) & 0xff) as usize]);
+
+            k += 4;
+            for j in 0..4 {
+                let t = p[j];
+                p[j] = q[j];
+                q[j] = t;
+            }
+        }
+
+        /* Last Round */
+
+        q[0] = self.fkey[k]
+            ^ (FBSUB[(p[0] & 0xff) as usize] as u32)
+            ^ AES::rotl8((FBSUB[((p[1] >> 8) & 0xff) as usize]) as u32)
+            ^ AES::rotl16((FBSUB[((p[2] >> 16) & 0xff) as usize]) as u32)
+            ^ AES::rotl24((FBSUB[((p[3] >> 24) & 0xff) as usize]) as u32);
+
+        q[1] = self.fkey[k + 1]
+            ^ (FBSUB[(p[1] & 0xff) as usize] as u32)
+            ^ AES::rotl8((FBSUB[((p[2] >> 8) & 0xff) as usize]) as u32)
+            ^ AES::rotl16((FBSUB[((p[3] >> 16) & 0xff) as usize]) as u32)
+            ^ AES::rotl24((FBSUB[((p[0] >> 24) & 0xff) as usize]) as u32);
+
+        q[2] = self.fkey[k + 2]
+            ^ (FBSUB[(p[2] & 0xff) as usize] as u32)
+            ^ AES::rotl8((FBSUB[((p[3] >> 8) & 0xff) as usize]) as u32)
+            ^ AES::rotl16((FBSUB[((p[0] >> 16) & 0xff) as usize]) as u32)
+            ^ AES::rotl24((FBSUB[((p[1] >> 24) & 0xff) as usize]) as u32);
+
+        q[3] = self.fkey[k + 3]
+            ^ (FBSUB[(p[3] & 0xff) as usize] as u32)
+            ^ AES::rotl8((FBSUB[((p[0] >> 8) & 0xff) as usize]) as u32)
+            ^ AES::rotl16((FBSUB[((p[1] >> 16) & 0xff) as usize]) as u32)
+            ^ AES::rotl24((FBSUB[((p[2] >> 24) & 0xff) as usize]) as u32);
+
+        j = 0;
+        for i in 0..4 {
+            b = AES::unpack(q[i]);
+            for k in 0..4 {
+                buff[j + k] = b[k]
+            }
+            j += 4;
+        }
+    }
+
+    /* Decrypt a single block */
+    pub fn ecb_decrypt(&mut self, buff: &mut [u8; 16]) {
+        let mut b: [u8; 4] = [0; 4];
+        let mut p: [u32; 4] = [0; 4];
+        let mut q: [u32; 4] = [0; 4];
+
+        let mut j = 0;
+        for i in 0..4 {
+            for k in 0..4 {
+                b[k] = buff[j + k]
+            }
+            p[i] = AES::pack(b);
+            p[i] ^= self.rkey[i];
+            j += 4;
+        }
+
+        let mut k = 4;
+
+        /* State alternates between p and q */
+        for _ in 1..self.nr {
+            q[0] = self.rkey[k]
+                ^ RTABLE[(p[0] & 0xff) as usize]
+                ^ AES::rotl8(RTABLE[((p[3] >> 8) & 0xff) as usize])
+                ^ AES::rotl16(RTABLE[((p[2] >> 16) & 0xff) as usize])
+                ^ AES::rotl24(RTABLE[((p[1] >> 24) & 0xff) as usize]);
+
+            q[1] = self.rkey[k + 1]
+                ^ RTABLE[(p[1] & 0xff) as usize]
+                ^ AES::rotl8(RTABLE[((p[0] >> 8) & 0xff) as usize])
+                ^ AES::rotl16(RTABLE[((p[3] >> 16) & 0xff) as usize])
+                ^ AES::rotl24(RTABLE[((p[2] >> 24) & 0xff) as usize]);
+
+            q[2] = self.rkey[k + 2]
+                ^ RTABLE[(p[2] & 0xff) as usize]
+                ^ AES::rotl8(RTABLE[((p[1] >> 8) & 0xff) as usize])
+                ^ AES::rotl16(RTABLE[((p[0] >> 16) & 0xff) as usize])
+                ^ AES::rotl24(RTABLE[((p[3] >> 24) & 0xff) as usize]);
+
+            q[3] = self.rkey[k + 3]
+                ^ RTABLE[(p[3] & 0xff) as usize]
+                ^ AES::rotl8(RTABLE[((p[2] >> 8) & 0xff) as usize])
+                ^ AES::rotl16(RTABLE[((p[1] >> 16) & 0xff) as usize])
+                ^ AES::rotl24(RTABLE[((p[0] >> 24) & 0xff) as usize]);
+
+            k += 4;
+            for j in 0..4 {
+                let t = p[j];
+                p[j] = q[j];
+                q[j] = t;
+            }
+        }
+
+        /* Last Round */
+
+        q[0] = self.rkey[k]
+            ^ (RBSUB[(p[0] & 0xff) as usize] as u32)
+            ^ AES::rotl8((RBSUB[((p[3] >> 8) & 0xff) as usize]) as u32)
+            ^ AES::rotl16((RBSUB[((p[2] >> 16) & 0xff) as usize]) as u32)
+            ^ AES::rotl24((RBSUB[((p[1] >> 24) & 0xff) as usize]) as u32);
+
+        q[1] = self.rkey[k + 1]
+            ^ (RBSUB[(p[1] & 0xff) as usize] as u32)
+            ^ AES::rotl8((RBSUB[((p[0] >> 8) & 0xff) as usize]) as u32)
+            ^ AES::rotl16((RBSUB[((p[3] >> 16) & 0xff) as usize]) as u32)
+            ^ AES::rotl24((RBSUB[((p[2] >> 24) & 0xff) as usize]) as u32);
+
+        q[2] = self.rkey[k + 2]
+            ^ (RBSUB[(p[2] & 0xff) as usize] as u32)
+            ^ AES::rotl8((RBSUB[((p[1] >> 8) & 0xff) as usize]) as u32)
+            ^ AES::rotl16((RBSUB[((p[0] >> 16) & 0xff) as usize]) as u32)
+            ^ AES::rotl24((RBSUB[((p[3] >> 24) & 0xff) as usize]) as u32);
+
+        q[3] = self.rkey[k + 3]
+            ^ (RBSUB[((p[3]) & 0xff) as usize] as u32)
+            ^ AES::rotl8((RBSUB[((p[2] >> 8) & 0xff) as usize]) as u32)
+            ^ AES::rotl16((RBSUB[((p[1] >> 16) & 0xff) as usize]) as u32)
+            ^ AES::rotl24((RBSUB[((p[0] >> 24) & 0xff) as usize]) as u32);
+
+        j = 0;
+        for i in 0..4 {
+            b = AES::unpack(q[i]);
+            for k in 0..4 {
+                buff[j + k] = b[k]
+            }
+            j += 4;
+        }
+    }
+
+    /* Encrypt using selected mode of operation */
+    pub fn encrypt(&mut self, buff: &mut [u8; 16]) -> u32 {
+        let mut st: [u8; 16] = [0; 16];
+
+        // Supported Modes of Operation
+
+        let mut fell_off: u32 = 0;
+
+        match self.mode {
+            ECB => {
+                self.ecb_encrypt(buff);
+                return 0;
+            }
+            CBC => {
+                for j in 0..16 {
+                    buff[j] ^= self.f[j]
+                }
+                self.ecb_encrypt(buff);
+                for j in 0..16 {
+                    self.f[j] = buff[j]
+                }
+                return 0;
+            }
+
+            CFB1 | CFB2 | CFB4 => {
+                let bytes = self.mode - CFB1 + 1;
+                for j in 0..bytes {
+                    fell_off = (fell_off << 8) | (self.f[j] as u32)
+                }
+                for j in 0..16 {
+                    st[j] = self.f[j]
+                }
+                for j in bytes..16 {
+                    self.f[j - bytes] = self.f[j]
+                }
+                self.ecb_encrypt(&mut st);
+                for j in 0..bytes {
+                    buff[j] ^= st[j];
+                    self.f[16 - bytes + j] = buff[j];
+                }
+                return fell_off;
+            }
+
+            OFB1 | OFB2 | OFB4 | OFB8 | OFB16 => {
+                let bytes = self.mode - OFB1 + 1;
+                for j in 0..16 {
+                    st[j] = self.f[j]
+                }
+                self.ecb_encrypt(&mut st);
+                for j in 0..bytes {
+                    buff[j] ^= st[j]
+                }
+                for j in 0..16 {
+                    self.f[j] = st[j]
+                }
+
+                //self.ecb_encrypt(&mut (self.f));
+                //for j in 0..bytes {buff[j]^=self.f[j]}
+                return 0;
+            }
+
+            CTR1 | CTR2 | CTR4 | CTR8 | CTR16 => {
+                let bytes = self.mode - CTR1 + 1;
+                for j in 0..16 {
+                    st[j] = self.f[j]
+                }
+                self.ecb_encrypt(&mut st);
+                for j in 0..bytes {
+                    buff[j] ^= st[j]
+                }
+                AES::increment(&mut (self.f));
+                return 0;
+            }
+
+            _ => {
+                return 0;
+            }
+        }
+    }
+
+    /* Decrypt using selected mode of operation */
+    pub fn decrypt(&mut self, buff: &mut [u8; 16]) -> u32 {
+        let mut st: [u8; 16] = [0; 16];
+
+        // Supported Modes of Operation
+
+        let mut fell_off: u32 = 0;
+
+        match self.mode {
+            ECB => {
+                self.ecb_decrypt(buff);
+                return 0;
+            }
+            CBC => {
+                for j in 0..16 {
+                    st[j] = self.f[j];
+                    self.f[j] = buff[j];
+                }
+                self.ecb_decrypt(buff);
+                for j in 0..16 {
+                    buff[j] ^= st[j];
+                    st[j] = 0;
+                }
+                return 0;
+            }
+            CFB1 | CFB2 | CFB4 => {
+                let bytes = self.mode - CFB1 + 1;
+                for j in 0..bytes {
+                    fell_off = (fell_off << 8) | (self.f[j] as u32)
+                }
+                for j in 0..16 {
+                    st[j] = self.f[j]
+                }
+                for j in bytes..16 {
+                    self.f[j - bytes] = self.f[j]
+                }
+                self.ecb_encrypt(&mut st);
+                for j in 0..bytes {
+                    self.f[16 - bytes + j] = buff[j];
+                    buff[j] ^= st[j];
+                }
+                return fell_off;
+            }
+            OFB1 | OFB2 | OFB4 | OFB8 | OFB16 => {
+                let bytes = self.mode - OFB1 + 1;
+                for j in 0..16 {
+                    st[j] = self.f[j]
+                }
+                self.ecb_encrypt(&mut st);
+                for j in 0..bytes {
+                    buff[j] ^= st[j]
+                }
+                for j in 0..16 {
+                    self.f[j] = st[j]
+                }
+                //  self.ecb_encrypt(A.f[:]);
+                //  for j in 0..bytes {buff[j]^=self.f[j]}
+                return 0;
+            }
+
+            CTR1 | CTR2 | CTR4 | CTR8 | CTR16 => {
+                let bytes = self.mode - CTR1 + 1;
+                for j in 0..16 {
+                    st[j] = self.f[j]
+                }
+                self.ecb_encrypt(&mut st);
+                for j in 0..bytes {
+                    buff[j] ^= st[j]
+                }
+                AES::increment(&mut (self.f));
+                return 0;
+            }
+
+            _ => {
+                return 0;
+            }
+        }
+    }
+
+    /* Clean up and delete left-overs */
+    pub fn end(&mut self) {
+        // clean up
+        for i in 0..4 * (self.nr + 1) {
+            self.fkey[i] = 0;
+            self.rkey[i] = 0
+        }
+        for i in 0..16 {
+            self.f[i] = 0
+        }
+    }
+}
+
+/*
+fn main()
+{
+    let mut key:[u8;32]=[0;32];
+    let mut block:[u8;16]=[0;16];
+    let mut iv: [u8;16] = [0;16];
+
+    for i in 0..32 {key[i]=0}
+    key[0]=1;
+    for i in 0..16 {iv[i]=i as u8}
+    for i in 0..16 {block[i]=i as u8}
+
+    let mut aes=AES::new();
+    aes.init(CTR16,32,&key,Some(iv));
+
+    println!("Plain= ");
+    for i in 0..16 {print!("{:02x} ",block[i])}
+    println!("");
+
+    aes.encrypt(&mut block);
+
+    println!("Encrypt= ");
+    for i in 0..16 {print!("{:02x} ",block[i])}
+    println!("");
+
+    aes.reset(CTR16,Some(iv));
+    aes.decrypt(&mut block);
+
+    println!("Decrypt= ");
+    for i in 0..16 {print!("{:02x} ",block[i])}
+    println!("");
+
+    aes.end();
+}
+*/
diff --git a/src/arch/arch32.rs b/src/arch/arch32.rs
new file mode 100644
index 0000000..2df2c97
--- /dev/null
+++ b/src/arch/arch32.rs
@@ -0,0 +1,22 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+pub type Chunk = i32;
+pub type DChunk = i64;
+pub const CHUNK: usize = 32;
diff --git a/src/arch/arch64.rs b/src/arch/arch64.rs
new file mode 100644
index 0000000..76b95d9
--- /dev/null
+++ b/src/arch/arch64.rs
@@ -0,0 +1,22 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+pub type Chunk = i64;
+pub type DChunk = i128;
+pub const CHUNK: usize = 64;
diff --git a/src/big.rs b/src/big.rs
new file mode 100644
index 0000000..7267ad4
--- /dev/null
+++ b/src/big.rs
@@ -0,0 +1,1070 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use super::super::arch;
+use super::super::arch::Chunk;
+
+use super::super::arch::DChunk;
+
+use super::dbig::DBIG;
+use rand::RAND;
+
+pub use super::rom::MODBYTES;
+pub use super::rom::BASEBITS;
+use std::cmp::Ordering;
+use std::fmt;
+
+pub const NLEN: usize = (1 + ((8 * MODBYTES - 1) / BASEBITS));
+pub const DNLEN: usize = 2 * NLEN;
+pub const BMASK: Chunk = ((1 << BASEBITS) - 1);
+pub const HBITS: usize = (BASEBITS / 2);
+pub const HMASK: Chunk = ((1 << HBITS) - 1);
+pub const NEXCESS: isize = (1 << ((arch::CHUNK) - BASEBITS - 1));
+pub const BIGBITS: usize = (MODBYTES * 8);
+
+#[derive(Copy)]
+pub struct BIG {
+    pub w: [Chunk; NLEN],
+}
+
+impl Clone for BIG {
+    fn clone(&self) -> BIG { *self }
+}
+
+impl fmt::Display for BIG {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let mut big = self.clone();
+        write!(f, "BIG: [ {} ]", big.tostring())
+    }
+}
+
+impl fmt::Debug for BIG {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let mut big = self.clone();
+        write!(f, "BIG: [ {} ]", big.tostring())
+    }
+}
+
+impl PartialEq for BIG {
+    fn eq(&self, other: &BIG) -> bool {
+        if BIG::comp(self,other)==0 {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
+
+impl Ord for BIG {
+    fn cmp(&self, other: &BIG) -> Ordering {
+        let r = BIG::comp(self, other);
+        if r > 0 {
+            return Ordering::Greater;
+        }
+        if r < 0 {
+            return Ordering::Less;
+        }
+        return Ordering::Equal;
+    }
+}
+
+impl Eq for BIG { }
+
+impl PartialOrd for BIG {
+    fn partial_cmp(&self, other: &BIG) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl BIG {
+    pub fn new() -> BIG {
+        BIG { w: [0; NLEN] }
+    }
+
+    pub fn new_int(x: isize) -> BIG {
+        let mut s = BIG::new();
+        s.w[0] = x as Chunk;
+        return s;
+    }
+
+    pub fn new_ints(a: &[Chunk]) -> BIG {
+        let mut s = BIG::new();
+        for i in 0..NLEN {
+            s.w[i] = a[i]
+        }
+        return s;
+    }
+
+    pub fn new_copy(y: &BIG) -> BIG {
+        let mut s = BIG::new();
+        for i in 0..NLEN {
+            s.w[i] = y.w[i]
+        }
+        return s;
+    }
+
+    pub fn new_big(y: &BIG) -> BIG {
+        let mut s = BIG::new();
+        for i in 0..NLEN {
+            s.w[i] = y.w[i]
+        }
+        return s;
+    }
+
+    pub fn new_dcopy(y: &DBIG) -> BIG {
+        let mut s = BIG::new();
+        for i in 0..NLEN {
+            s.w[i] = y.w[i]
+        }
+        return s;
+    }
+
+    pub fn get(&self, i: usize) -> Chunk {
+        return self.w[i];
+    }
+
+    pub fn set(&mut self, i: usize, x: Chunk) {
+        self.w[i] = x;
+    }
+
+    pub fn xortop(&mut self, x: Chunk) {
+        self.w[NLEN - 1] ^= x;
+    }
+
+    pub fn ortop(&mut self, x: Chunk) {
+        self.w[NLEN - 1] |= x;
+    }
+
+    /* test for zero */
+    pub fn iszilch(&self) -> bool {
+        for i in 0..NLEN {
+            if self.w[i] != 0 {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /* set to zero */
+    pub fn zero(&mut self) {
+        for i in 0..NLEN {
+            self.w[i] = 0
+        }
+    }
+
+    /* Test for equal to one */
+    pub fn isunity(&self) -> bool {
+        for i in 1..NLEN {
+            if self.w[i] != 0 {
+                return false;
+            }
+        }
+        if self.w[0] != 1 {
+            return false;
+        }
+        return true;
+    }
+
+    /* set to one */
+    pub fn one(&mut self) {
+        self.w[0] = 1;
+        for i in 1..NLEN {
+            self.w[i] = 0;
+        }
+    }
+
+    /* Copy from another BIG */
+    pub fn copy(&mut self, x: &BIG) {
+        for i in 0..NLEN {
+            self.w[i] = x.w[i]
+        }
+    }
+
+    pub fn dcopy(&mut self, x: &DBIG) {
+        for i in 0..NLEN {
+            self.w[i] = x.w[i]
+        }
+    }
+
+    /* Get top and bottom half of =x*y+c+r */
+    pub fn muladd(a: Chunk, b: Chunk, c: Chunk, r: Chunk) -> (Chunk, Chunk) {
+        let prod: DChunk = (a as DChunk) * (b as DChunk) + (c as DChunk) + (r as DChunk);
+        let bot = (prod & (BMASK as DChunk)) as Chunk;
+        let top = (prod >> BASEBITS) as Chunk;
+        return (top, bot);
+    }
+
+    /* normalise BIG - force all digits < 2^BASEBITS */
+    pub fn norm(&mut self) -> Chunk {
+        let mut carry = 0 as Chunk;
+        for i in 0..NLEN - 1 {
+            let d = self.w[i] + carry;
+            self.w[i] = d & BMASK;
+            carry = d >> BASEBITS;
+        }
+        self.w[NLEN - 1] += carry;
+        return (self.w[NLEN - 1] >> ((8 * MODBYTES) % BASEBITS)) as Chunk;
+    }
+
+    /* Conditional swap of two bigs depending on d using XOR - no branches */
+    pub fn cswap(&mut self, b: &mut BIG, d: isize) {
+        let mut c = d as Chunk;
+        c = !(c - 1);
+        for i in 0..NLEN {
+            let t = c & (self.w[i] ^ b.w[i]);
+            self.w[i] ^= t;
+            b.w[i] ^= t;
+        }
+    }
+
+    pub fn cmove(&mut self, g: &BIG, d: isize) {
+        let b = -d as Chunk;
+        for i in 0..NLEN {
+            self.w[i] ^= (self.w[i] ^ g.w[i]) & b;
+        }
+    }
+
+    /* Shift right by less than a word */
+    pub fn fshr(&mut self, k: usize) -> isize {
+        let n = k;
+        let w = self.w[0] & ((1 << n) - 1); /* shifted out part */
+        for i in 0..NLEN - 1 {
+            self.w[i] = (self.w[i] >> k) | ((self.w[i + 1] << (BASEBITS - n)) & BMASK);
+        }
+        self.w[NLEN - 1] = self.w[NLEN - 1] >> k;
+        return w as isize;
+    }
+
+    /* general shift right */
+    pub fn shr(&mut self, k: usize) {
+        let n = k % BASEBITS;
+        let m = k / BASEBITS;
+        for i in 0..NLEN - m - 1 {
+            self.w[i] = (self.w[m + i] >> n) | ((self.w[m + i + 1] << (BASEBITS - n)) & BMASK)
+        }
+        self.w[NLEN - m - 1] = self.w[NLEN - 1] >> n;
+        for i in NLEN - m..NLEN {
+            self.w[i] = 0
+        }
+    }
+
+    /* Shift right by less than a word */
+    pub fn fshl(&mut self, k: usize) -> isize {
+        let n = k;
+        self.w[NLEN - 1] = (self.w[NLEN - 1] << n) | (self.w[NLEN - 2] >> (BASEBITS - n));
+        for i in (1..NLEN - 1).rev() {
+            self.w[i] = ((self.w[i] << k) & BMASK) | (self.w[i - 1] >> (BASEBITS - n));
+        }
+        self.w[0] = (self.w[0] << n) & BMASK;
+        return (self.w[NLEN - 1] >> ((8 * MODBYTES) % BASEBITS)) as isize; /* return excess - only used in ff.c */
+    }
+
+    /* general shift left */
+    pub fn shl(&mut self, k: usize) {
+        let n = k % BASEBITS;
+        let m = k / BASEBITS;
+
+        self.w[NLEN - 1] = self.w[NLEN - 1 - m] << n;
+        if NLEN >= m + 2 {
+            self.w[NLEN - 1] |= self.w[NLEN - m - 2] >> (BASEBITS - n)
+        }
+        for i in (m + 1..NLEN - 1).rev() {
+            self.w[i] = ((self.w[i - m] << n) & BMASK) | (self.w[i - m - 1] >> (BASEBITS - n));
+        }
+        self.w[m] = (self.w[0] << n) & BMASK;
+        for i in 0..m {
+            self.w[i] = 0
+        }
+    }
+
+    /* return number of bits */
+    pub fn nbits(&self) -> usize {
+        let mut k = NLEN - 1;
+        let mut s = BIG::new_copy(&self);
+        s.norm();
+        while (k as isize) >= 0 && s.w[k] == 0 {
+            k = k.wrapping_sub(1)
+        }
+        if (k as isize) < 0 {
+            return 0;
+        }
+        let mut bts = BASEBITS * k;
+        let mut c = s.w[k];
+        while c != 0 {
+            c /= 2;
+            bts += 1;
+        }
+        return bts;
+    }
+
+    /* Convert to Hex String */
+    pub fn tostring(&mut self) -> String {
+        let mut s = String::new();
+        let mut len = self.nbits();
+
+        if len % 4 == 0 {
+            len /= 4;
+        } else {
+            len /= 4;
+            len += 1;
+        }
+        let mb = (MODBYTES * 2) as usize;
+        if len < mb {
+            len = mb
+        }
+
+        for i in (0..len).rev() {
+            let mut b = BIG::new_copy(&self);
+            b.shr(i * 4);
+            s = s + &format!("{:X}", b.w[0] & 15);
+        }
+        return s;
+    }
+
+    pub fn fromstring(val: String) -> BIG {
+        let mut res = BIG::new();
+        let len = val.len();
+        let op = &val[0..1];
+        let n = u8::from_str_radix(op, 16).unwrap();
+        res.w[0] += n as Chunk;
+        for i in 1..len {
+            res.shl(4);
+            let op = &val[i..i+1];
+            let n = u8::from_str_radix(op, 16).unwrap();
+            res.w[0] += n as Chunk;
+        }
+        return res;
+    }
+
+    pub fn from_hex(val: String) -> BIG {
+        BIG::fromstring(val)
+    }
+    
+    pub fn to_hex(&mut self) -> String {
+        self.tostring()
+    }
+
+    pub fn add(&mut self, r: &BIG) {
+        for i in 0..NLEN {
+            self.w[i] += r.w[i]
+        }
+    }
+
+    pub fn or(&mut self, r: &BIG) {
+        for i in 0..NLEN {
+            self.w[i] |= r.w[i]
+        }
+    }
+
+    pub fn dbl(&mut self) {
+        for i in 0..NLEN {
+            self.w[i] += self.w[i]
+        }
+    }
+
+    /* return this+x */
+    pub fn plus(&self, x: &BIG) -> BIG {
+        let mut s = BIG::new();
+        for i in 0..NLEN {
+            s.w[i] = self.w[i] + x.w[i];
+        }
+        return s;
+    }
+
+    pub fn inc(&mut self, x: isize) {
+        self.norm();
+        self.w[0] += x as Chunk;
+    }
+
+    /* return self-x */
+    pub fn minus(&self, x: &BIG) -> BIG {
+        let mut d = BIG::new();
+        for i in 0..NLEN {
+            d.w[i] = self.w[i] - x.w[i];
+        }
+        return d;
+    }
+
+    /* self-=x */
+    pub fn sub(&mut self, x: &BIG) {
+        for i in 0..NLEN {
+            self.w[i] -= x.w[i];
+        }
+    }
+
+    /* reverse subtract this=x-this */
+
+    pub fn rsub(&mut self, x: &BIG) {
+        for i in 0..NLEN {
+            self.w[i] = x.w[i] - self.w[i]
+        }
+    }
+
+    /* self-=x, where x is int */
+    pub fn dec(&mut self, x: isize) {
+        self.norm();
+        self.w[0] -= x as Chunk;
+    }
+
+    /* self*=x, where x is small int<NEXCESS */
+    pub fn imul(&mut self, c: isize) {
+        for i in 0..NLEN {
+            self.w[i] *= c as Chunk;
+        }
+    }
+
+    /* convert this BIG to byte array */
+    pub fn tobytearray(&mut self, b: &mut [u8], n: usize) {
+        let mut c = BIG::new_copy(self);
+        c.norm();
+
+        for i in (0..(MODBYTES as usize)).rev() {
+            b[i + n] = (c.w[0] & 0xff) as u8;
+            c.fshr(8);
+        }
+    }
+
+    /* convert from byte array to BIG */
+    pub fn frombytearray(b: &[u8], n: usize) -> BIG {
+        let mut m = BIG::new();
+        for i in 0..(MODBYTES as usize) {
+            m.fshl(8);
+            m.w[0] += (b[i + n] & 0xff) as Chunk;
+        }
+        return m;
+    }
+
+    pub fn tobytes(&mut self, b: &mut [u8]) {
+        self.tobytearray(b, 0)
+    }
+
+    pub fn frombytes(b: &[u8]) -> BIG {
+        return BIG::frombytearray(b, 0);
+    }
+
+    /* self*=x, where x is >NEXCESS */
+    pub fn pmul(&mut self, c: isize) -> Chunk {
+        let mut carry = 0 as Chunk;
+        for i in 0..NLEN {
+            let ak = self.w[i];
+            let tuple = BIG::muladd(ak, c as Chunk, carry, 0 as Chunk);
+            carry = tuple.0;
+            self.w[i] = tuple.1;
+        }
+        return carry;
+    }
+
+    /* self*=c and catch overflow in DBIG */
+    pub fn pxmul(&mut self, c: isize) -> DBIG {
+        let mut m = DBIG::new();
+        let mut carry = 0 as Chunk;
+        for j in 0..NLEN {
+            let tuple = BIG::muladd(self.w[j], c as Chunk, carry, m.w[j]);
+            carry = tuple.0;
+            m.w[j] = tuple.1;
+        }
+        m.w[NLEN] = carry;
+        return m;
+    }
+
+    /* divide by 3 */
+    pub fn div3(&mut self) -> Chunk {
+        let mut carry = 0 as Chunk;
+        self.norm();
+        let base = 1 << BASEBITS;
+        for i in (0..NLEN).rev() {
+            let ak = carry * base + self.w[i];
+            self.w[i] = ak / 3;
+            carry = ak % 3;
+        }
+        return carry;
+    }
+
+    /* return a*b where result fits in a BIG */
+    pub fn smul(a: &BIG, b: &BIG) -> BIG {
+        let mut c = BIG::new();
+        for i in 0..NLEN {
+            let mut carry = 0 as Chunk;
+            for j in 0..NLEN {
+                if i + j < NLEN {
+                    let tuple = BIG::muladd(a.w[i], b.w[j], carry, c.w[i + j]);
+                    carry = tuple.0;
+                    c.w[i + j] = tuple.1;
+                }
+            }
+        }
+        return c;
+    }
+
+    /* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+    pub fn comp(a: &BIG, b: &BIG) -> isize {
+        for i in (0..NLEN).rev() {
+            if a.w[i] == b.w[i] {
+                continue;
+            }
+            if a.w[i] > b.w[i] {
+                return 1;
+            } else {
+                return -1;
+            }
+        }
+        return 0;
+    }
+
+    /* set x = x mod 2^m */
+    pub fn mod2m(&mut self, m: usize) {
+        let wd = m / BASEBITS;
+        let bt = m % BASEBITS;
+        let msk = (1 << bt) - 1;
+        self.w[wd] &= msk;
+        for i in wd + 1..NLEN {
+            self.w[i] = 0
+        }
+    }
+
+    /* Arazi and Qi inversion mod 256 */
+    pub fn invmod256(a: isize) -> isize {
+        let mut t1: isize = 0;
+        let mut c = (a >> 1) & 1;
+        t1 += c;
+        t1 &= 1;
+        t1 = 2 - t1;
+        t1 <<= 1;
+        let mut u = t1 + 1;
+
+        // i=2
+        let mut b = a & 3;
+        t1 = u * b;
+        t1 >>= 2;
+        c = (a >> 2) & 3;
+        let mut t2 = (u * c) & 3;
+        t1 += t2;
+        t1 *= u;
+        t1 &= 3;
+        t1 = 4 - t1;
+        t1 <<= 2;
+        u += t1;
+
+        // i=4
+        b = a & 15;
+        t1 = u * b;
+        t1 >>= 4;
+        c = (a >> 4) & 15;
+        t2 = (u * c) & 15;
+        t1 += t2;
+        t1 *= u;
+        t1 &= 15;
+        t1 = 16 - t1;
+        t1 <<= 4;
+        u += t1;
+
+        return u;
+    }
+
+    /* return parity */
+    pub fn parity(&self) -> isize {
+        return (self.w[0] % 2) as isize;
+    }
+
+    /* return n-th bit */
+    pub fn bit(&self, n: usize) -> isize {
+        if (self.w[n / (BASEBITS as usize)] & (1 << (n % BASEBITS))) > 0 {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
+    /* return n last bits */
+    pub fn lastbits(&mut self, n: usize) -> isize {
+        let msk = ((1 << n) - 1) as Chunk;
+        self.norm();
+        return (self.w[0] & msk) as isize;
+    }
+
+    /* a=1/a mod 2^256. This is very fast! */
+    pub fn invmod2m(&mut self) {
+        let mut u = BIG::new();
+        let mut b = BIG::new();
+        let mut c = BIG::new();
+
+        u.inc(BIG::invmod256(self.lastbits(8)));
+
+        let mut i = 8;
+        while i < BIGBITS {
+            u.norm();
+            b.copy(self);
+            b.mod2m(i);
+            let mut t1 = BIG::smul(&u, &b);
+            t1.shr(i);
+            c.copy(self);
+            c.shr(i);
+            c.mod2m(i);
+
+            let mut t2 = BIG::smul(&u, &c);
+            t2.mod2m(i);
+            t1.add(&t2);
+            t1.norm();
+            b = BIG::smul(&t1, &u);
+            t1.copy(&b);
+            t1.mod2m(i);
+
+            t2.one();
+            t2.shl(i);
+            t1.rsub(&t2);
+            t1.norm();
+            t1.shl(i);
+            u.add(&t1);
+            i <<= 1;
+        }
+        u.mod2m(BIGBITS);
+        self.copy(&u);
+        self.norm();
+    }
+
+    /* reduce self mod m */
+    pub fn rmod(&mut self, n: &BIG) {
+        let mut k = 0;
+        let mut m = BIG::new_copy(n);
+        let mut r = BIG::new();
+        self.norm();
+        if BIG::comp(self, &m) < 0 {
+            return;
+        }
+        loop {
+            m.fshl(1);
+            k += 1;
+            if BIG::comp(self, &m) < 0 {
+                break;
+            }
+        }
+
+        while k > 0 {
+            m.fshr(1);
+
+            r.copy(self);
+            r.sub(&m);
+            r.norm();
+            self.cmove(
+                &r,
+                (1 - ((r.w[NLEN - 1] >> (arch::CHUNK - 1)) & 1)) as isize,
+            );
+            k -= 1;
+        }
+    }
+
+    /* divide self by m */
+    pub fn div(&mut self, n: &BIG) {
+        let mut k = 0;
+        self.norm();
+        let mut e = BIG::new_int(1);
+        let mut b = BIG::new_copy(self);
+        let mut m = BIG::new_copy(n);
+        let mut r = BIG::new();
+        self.zero();
+
+        while BIG::comp(&b, &m) >= 0 {
+            e.fshl(1);
+            m.fshl(1);
+            k += 1;
+        }
+
+        while k > 0 {
+            m.fshr(1);
+            e.fshr(1);
+
+            r.copy(&b);
+            r.sub(&m);
+            r.norm();
+            let d = (1 - ((r.w[NLEN - 1] >> (arch::CHUNK - 1)) & 1)) as isize;
+            b.cmove(&r, d);
+            r.copy(self);
+            r.add(&e);
+            r.norm();
+            self.cmove(&r, d);
+            k -= 1;
+        }
+    }
+
+    /* get 8*MODBYTES size random number */
+    pub fn random(rng: &mut RAND) -> BIG {
+        let mut m = BIG::new();
+        let mut j = 0;
+        let mut r: u8 = 0;
+        /* generate random BIG */
+
+        for _ in 0..8 * (MODBYTES as usize) {
+            if j == 0 {
+                r = rng.getbyte()
+            } else {
+                r >>= 1
+            }
+
+            let b = (r as Chunk) & 1;
+            m.shl(1);
+            m.w[0] += b;
+            j += 1;
+            j &= 7;
+        }
+        return m;
+    }
+
+    /* Create random BIG in portable way, one bit at a time */
+    pub fn randomnum(q: &BIG, rng: &mut RAND) -> BIG {
+        let mut d = DBIG::new();
+        let mut j = 0;
+        let mut r: u8 = 0;
+        let t = BIG::new_copy(q);
+        for _ in 0..2 * t.nbits() {
+            if j == 0 {
+                r = rng.getbyte();
+            } else {
+                r >>= 1
+            }
+
+            let b = (r as Chunk) & 1;
+            d.shl(1);
+            d.w[0] += b;
+            j += 1;
+            j &= 7;
+        }
+        let m = d.dmod(q);
+        return m;
+    }
+
+    /* Jacobi Symbol (this/p). Returns 0, 1 or -1 */
+    pub fn jacobi(&mut self, p: &BIG) -> isize {
+        let mut m: usize = 0;
+        let mut t = BIG::new();
+        let mut x = BIG::new();
+        let mut n = BIG::new();
+        let zilch = BIG::new();
+        let one = BIG::new_int(1);
+        if p.parity() == 0 || BIG::comp(self, &zilch) == 0 || BIG::comp(p, &one) <= 0 {
+            return 0;
+        }
+        self.norm();
+
+        x.copy(self);
+        n.copy(p);
+        x.rmod(p);
+
+        while BIG::comp(&n, &one) > 0 {
+            if BIG::comp(&x, &zilch) == 0 {
+                return 0;
+            }
+            let n8 = n.lastbits(3) as usize;
+            let mut k = 0;
+            while x.parity() == 0 {
+                k += 1;
+                x.shr(1);
+            }
+            if k % 2 == 1 {
+                m += (n8 * n8 - 1) / 8
+            }
+            m += (n8 - 1) * ((x.lastbits(2) as usize) - 1) / 4;
+            t.copy(&n);
+            t.rmod(&x);
+            n.copy(&x);
+            x.copy(&t);
+            m %= 2;
+        }
+        if m == 0 {
+            return 1;
+        } else {
+            return -1;
+        }
+    }
+
+    /* self=1/self mod p. Binary method */
+    pub fn invmodp(&mut self, p: &BIG) {
+        self.rmod(p);
+        let mut u = BIG::new_copy(self);
+        let mut v = BIG::new_copy(p);
+        let mut x1 = BIG::new_int(1);
+        let mut x2 = BIG::new();
+        let mut t = BIG::new();
+        let one = BIG::new_int(1);
+
+        while (BIG::comp(&u, &one) != 0) && (BIG::comp(&v, &one) != 0) {
+            while u.parity() == 0 {
+                u.fshr(1);
+                if x1.parity() != 0 {
+                    x1.add(p);
+                    x1.norm();
+                }
+                x1.fshr(1);
+            }
+            while v.parity() == 0 {
+                v.fshr(1);
+                if x2.parity() != 0 {
+                    x2.add(p);
+                    x2.norm();
+                }
+                x2.fshr(1);
+            }
+            if BIG::comp(&u, &v) >= 0 {
+                u.sub(&v);
+                u.norm();
+                if BIG::comp(&x1, &x2) >= 0 {
+                    x1.sub(&x2)
+                } else {
+                    t.copy(p);
+                    t.sub(&x2);
+                    x1.add(&t);
+                }
+                x1.norm();
+            } else {
+                v.sub(&u);
+                v.norm();
+                if BIG::comp(&x2, &x1) >= 0 {
+                    x2.sub(&x1)
+                } else {
+                    t.copy(p);
+                    t.sub(&x1);
+                    x2.add(&t);
+                }
+                x2.norm();
+            }
+        }
+        if BIG::comp(&u, &one) == 0 {
+            self.copy(&x1)
+        } else {
+            self.copy(&x2)
+        }
+    }
+
+    /* return a*b as DBIG */
+
+    pub fn mul(a: &BIG, b: &BIG) -> DBIG {
+        let mut c = DBIG::new();
+        let rm = BMASK as DChunk;
+        let rb = BASEBITS;
+
+        let mut d: [DChunk; DNLEN] = [0; DNLEN];
+        for i in 0..NLEN {
+            d[i] = (a.w[i] as DChunk) * (b.w[i] as DChunk);
+        }
+        let mut s = d[0];
+        let mut t = s;
+        c.w[0] = (t & rm) as Chunk;
+        let mut co = t >> rb;
+        for k in 1..NLEN {
+            s += d[k];
+            t = co + s;
+            for i in 1 + k / 2..k + 1 {
+                t += ((a.w[i] - a.w[k - i]) as DChunk) * ((b.w[k - i] - b.w[i]) as DChunk)
+            }
+            c.w[k] = (t & rm) as Chunk;
+            co = t >> rb;
+        }
+        for k in NLEN..2 * NLEN - 1 {
+            s -= d[k - NLEN];
+            t = co + s;
+            let mut i = 1 + k / 2;
+            while i < NLEN {
+                t += ((a.w[i] - a.w[k - i]) as DChunk) * ((b.w[k - i] - b.w[i]) as DChunk);
+                i += 1;
+            }
+
+            c.w[k] = (t & rm) as Chunk;
+            co = t >> rb;
+        }
+        c.w[2 * NLEN - 1] = co as Chunk;
+        return c;
+    }
+
+    /* return a^2 as DBIG */
+    pub fn sqr(a: &BIG) -> DBIG {
+        let mut c = DBIG::new();
+        let rm = BMASK as DChunk;
+        let rb = BASEBITS;
+
+        let mut t = (a.w[0] as DChunk) * (a.w[0] as DChunk);
+        c.w[0] = (t & rm) as Chunk;
+        let mut co = t >> rb;
+
+        let mut j = 1;
+        while j < NLEN - 1 {
+            t = (a.w[j] as DChunk) * (a.w[0] as DChunk);
+            for i in 1..(j + 1) / 2 {
+                t += (a.w[j - i] as DChunk) * (a.w[i] as DChunk);
+            }
+            t += t;
+            t += co;
+            c.w[j] = (t & rm) as Chunk;
+            co = t >> rb;
+            j += 1;
+            t = (a.w[j] as DChunk) * (a.w[0] as DChunk);
+            for i in 1..(j + 1) / 2 {
+                t += (a.w[j - i] as DChunk) * (a.w[i] as DChunk);
+            }
+            t += t;
+            t += co;
+            t += (a.w[j / 2] as DChunk) * (a.w[j / 2] as DChunk);
+            c.w[j] = (t & rm) as Chunk;
+            co = t >> rb;
+            j += 1;
+        }
+
+        j = NLEN + (NLEN % 2) - 1;
+        while j < DNLEN - 3 {
+            t = (a.w[NLEN - 1] as DChunk) * (a.w[j + 1 - NLEN] as DChunk);
+            for i in j + 2 - NLEN..(j + 1) / 2 {
+                t += (a.w[j - i] as DChunk) * (a.w[i] as DChunk);
+            }
+            t += t;
+            t += co;
+            c.w[j] = (t & rm) as Chunk;
+            co = t >> rb;
+            j += 1;
+            t = (a.w[NLEN - 1] as DChunk) * (a.w[j + 1 - NLEN] as DChunk);
+            for i in j + 2 - NLEN..(j + 1) / 2 {
+                t += (a.w[j - i] as DChunk) * (a.w[i] as DChunk);
+            }
+            t += t;
+            t += co;
+            t += (a.w[j / 2] as DChunk) * (a.w[j / 2] as DChunk);
+            c.w[j] = (t & rm) as Chunk;
+            co = t >> rb;
+            j += 1;
+        }
+
+        t = (a.w[NLEN - 2] as DChunk) * (a.w[NLEN - 1] as DChunk);
+        t += t;
+        t += co;
+        c.w[DNLEN - 3] = (t & rm) as Chunk;
+        co = t >> rb;
+
+        t = (a.w[NLEN - 1] as DChunk) * (a.w[NLEN - 1] as DChunk) + co;
+        c.w[DNLEN - 2] = (t & rm) as Chunk;
+        co = t >> rb;
+        c.w[DNLEN - 1] = co as Chunk;
+
+        return c;
+    }
+
+    pub fn monty(md: &BIG, mc: Chunk, d: &mut DBIG) -> BIG {
+        let mut b = BIG::new();
+        let rm = BMASK as DChunk;
+        let rb = BASEBITS;
+
+        let mut dd: [DChunk; NLEN] = [0; NLEN];
+        let mut v: [Chunk; NLEN] = [0; NLEN];
+
+        b.zero();
+
+        let mut t = d.w[0] as DChunk;
+        v[0] = (((t & rm) as Chunk).wrapping_mul(mc)) & BMASK;
+        t += (v[0] as DChunk) * (md.w[0] as DChunk);
+        let mut c = (d.w[1] as DChunk) + (t >> rb);
+        let mut s: DChunk = 0;
+        for k in 1..NLEN {
+            t = c + s + (v[0] as DChunk) * (md.w[k] as DChunk);
+            let mut i = 1 + k / 2;
+            while i < k {
+                t += ((v[k - i] - v[i]) as DChunk) * ((md.w[i] - md.w[k - i]) as DChunk);
+                i += 1;
+            }
+            v[k] = (((t & rm) as Chunk).wrapping_mul(mc)) & BMASK;
+            t += (v[k] as DChunk) * (md.w[0] as DChunk);
+            c = (d.w[k + 1] as DChunk) + (t >> rb);
+            dd[k] = (v[k] as DChunk) * (md.w[k] as DChunk);
+            s += dd[k];
+        }
+
+        for k in NLEN..2 * NLEN - 1 {
+            t = c + s;
+            let mut i = 1 + k / 2;
+            while i < NLEN {
+                t += ((v[k - i] - v[i]) as DChunk) * ((md.w[i] - md.w[k - i]) as DChunk);
+                i += 1;
+            }
+            b.w[k - NLEN] = (t & rm) as Chunk;
+            c = (d.w[k + 1] as DChunk) + (t >> rb);
+            s -= dd[k + 1 - NLEN];
+        }
+        b.w[NLEN - 1] = (c & rm) as Chunk;
+        return b;
+    }
+
+    pub fn ssn(r: &mut BIG, a: &BIG, m: &mut BIG) -> isize {
+        let n = NLEN - 1;
+        m.w[0] = (m.w[0] >> 1) | ((m.w[1] << (BASEBITS - 1)) & BMASK);
+        r.w[0] = a.w[0] - m.w[0];
+        let mut carry = r.w[0] >> BASEBITS;
+        r.w[0] &= BMASK;
+        for i in 1..n {
+            m.w[i] = (m.w[i] >> 1) | ((m.w[i + 1] << (BASEBITS - 1)) & BMASK);
+            r.w[i] = a.w[i] - m.w[i] + carry;
+            carry = r.w[i] >> BASEBITS;
+            r.w[i] &= BMASK;
+        }
+        m.w[n] >>= 1;
+        r.w[n] = a.w[n] - m.w[n] + carry;
+        return ((r.w[n] >> (arch::CHUNK - 1)) & 1) as isize;
+    }
+
+    /* return a*b mod m */
+    pub fn modmul(a1: &BIG, b1: &BIG, m: &BIG) -> BIG {
+        let mut a = BIG::new_copy(a1);
+        let mut b = BIG::new_copy(b1);
+        a.rmod(m);
+        b.rmod(m);
+        let mut d = BIG::mul(&a, &b);
+        return d.dmod(m);
+    }
+
+    /* return a^2 mod m */
+    pub fn modsqr(a1: &BIG, m: &BIG) -> BIG {
+        let mut a = BIG::new_copy(a1);
+        a.rmod(m);
+        let mut d = BIG::sqr(&a);
+        return d.dmod(m);
+    }
+
+    /* return -a mod m */
+    pub fn modneg(a1: &BIG, m: &BIG) -> BIG {
+        let mut a = BIG::new_copy(a1);
+        a.rmod(m);
+        return m.minus(&a);
+    }
+
+    /* return this^e mod m */
+    pub fn powmod(&mut self, e1: &BIG, m: &BIG) -> BIG {
+        self.norm();
+        let mut e = BIG::new_copy(e1);
+        e.norm();
+        let mut a = BIG::new_int(1);
+        let mut z = BIG::new_copy(&e);
+        let mut s = BIG::new_copy(self);
+        loop {
+            let bt = z.parity();
+            z.fshr(1);
+            if bt == 1 {
+                a = BIG::modmul(&a, &s, m)
+            }
+            if z.iszilch() {
+                break;
+            }
+            s = BIG::modsqr(&mut s, m);
+        }
+        return a;
+    }
+}
diff --git a/src/bls.rs b/src/bls.rs
new file mode 100644
index 0000000..7e7fd7a
--- /dev/null
+++ b/src/bls.rs
@@ -0,0 +1,96 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+use std::str;
+use super::ecp::ECP;
+use super::ecp2::ECP2;
+//use super::fp12::FP12;
+use super::big::BIG;
+use super::pair;
+use super::big;
+use super::rom;
+
+use rand::RAND;
+use sha3::SHA3;
+use sha3::SHAKE256;
+
+/* BLS API Functions */
+
+pub const BFS: usize = big::MODBYTES as usize;
+pub const BGS: usize = big::MODBYTES as usize;
+pub const BLS_OK: isize = 0;
+pub const BLS_FAIL: isize = -1;
+
+/* hash a message to an ECP point, using SHA3 */
+
+#[allow(non_snake_case)]
+fn bls_hashit(m: &str) -> ECP {
+    let mut sh = SHA3::new(SHAKE256);
+    let mut hm: [u8; BFS] = [0; BFS];
+    let t = m.as_bytes();
+    for i in 0..m.len() {
+        sh.process(t[i]);
+    }
+    sh.shake(&mut hm, BFS);
+    let P = ECP::mapit(&hm);
+    return P;
+}
+
+/* generate key pair, private key s, public key w */
+pub fn key_pair_generate(mut rng: &mut RAND, s: &mut [u8], w: &mut [u8]) -> isize {
+    let q = BIG::new_ints(&rom::CURVE_ORDER);
+    let g = ECP2::generator();
+    let mut sc = BIG::randomnum(&q, &mut rng);
+    sc.tobytes(s);
+    pair::g2mul(&g, &mut sc).tobytes(w);
+    return BLS_OK;
+}
+
+/* Sign message m using private key s to produce signature sig */
+
+pub fn sign(sig: &mut [u8], m: &str, s: &[u8]) -> isize {
+    let d = bls_hashit(m);
+    let mut sc = BIG::frombytes(&s);
+    pair::g1mul(&d, &mut sc).tobytes(sig, true);
+    return BLS_OK;
+}
+
+/* Verify signature given message m, the signature sig, and the public key w */
+
+pub fn verify(sig: &[u8], m: &str, w: &[u8]) -> isize {
+    let hm = bls_hashit(m);
+    let mut d = ECP::frombytes(&sig);
+    let g = ECP2::generator();
+    let pk = ECP2::frombytes(&w);
+    d.neg();
+
+// Use new multi-pairing mechanism 
+    let mut r=pair::initmp();
+    pair::another(&mut r,&g,&d);
+    pair::another(&mut r,&pk,&hm);
+    let mut v=pair::miller(&r);
+
+//.. or alternatively
+//    let mut v = pair::ate2(&g, &d, &pk, &hm);
+
+    v = pair::fexp(&v);
+    if v.isunity() {
+        return BLS_OK;
+    }
+    return BLS_FAIL;
+}
diff --git a/src/bls192.rs b/src/bls192.rs
new file mode 100644
index 0000000..20ee92e
--- /dev/null
+++ b/src/bls192.rs
@@ -0,0 +1,96 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+use std::str;
+use super::ecp::ECP;
+use super::ecp4::ECP4;
+//use super::fp24::FP24;
+use super::big::BIG;
+use super::pair192;
+use super::big;
+use super::rom;
+
+use rand::RAND;
+use sha3::SHA3;
+use sha3::SHAKE256;
+
+/* BLS API Functions */
+
+pub const BFS: usize = big::MODBYTES as usize;
+pub const BGS: usize = big::MODBYTES as usize;
+pub const BLS_OK: isize = 0;
+pub const BLS_FAIL: isize = -1;
+
+/* hash a message to an ECP point, using SHA3 */
+
+#[allow(non_snake_case)]
+fn bls_hashit(m: &str) -> ECP {
+    let mut sh = SHA3::new(SHAKE256);
+    let mut hm: [u8; BFS] = [0; BFS];
+    let t = m.as_bytes();
+    for i in 0..m.len() {
+        sh.process(t[i]);
+    }
+    sh.shake(&mut hm, BFS);
+    let P = ECP::mapit(&hm);
+    return P;
+}
+
+/* generate key pair, private key s, public key w */
+pub fn key_pair_generate(mut rng: &mut RAND, s: &mut [u8], w: &mut [u8]) -> isize {
+    let q = BIG::new_ints(&rom::CURVE_ORDER);
+    let g = ECP4::generator();
+    let mut sc = BIG::randomnum(&q, &mut rng);
+    sc.tobytes(s);
+    pair192::g2mul(&g, &mut sc).tobytes(w);
+    return BLS_OK;
+}
+
+/* Sign message m using private key s to produce signature sig */
+
+pub fn sign(sig: &mut [u8], m: &str, s: &[u8]) -> isize {
+    let d = bls_hashit(m);
+    let mut sc = BIG::frombytes(&s);
+    pair192::g1mul(&d, &mut sc).tobytes(sig, true);
+    return BLS_OK;
+}
+
+/* Verify signature given message m, the signature sig, and the public key w */
+
+pub fn verify(sig: &[u8], m: &str, w: &[u8]) -> isize {
+    let hm = bls_hashit(m);
+    let mut d = ECP::frombytes(&sig);
+    let g = ECP4::generator();
+    let pk = ECP4::frombytes(&w);
+    d.neg();
+
+// Use new multi-pairing mechanism 
+    let mut r=pair192::initmp();
+    pair192::another(&mut r,&g,&d);
+    pair192::another(&mut r,&pk,&hm);
+    let mut v=pair192::miller(&r);
+
+//.. or alternatively
+//    let mut v = pair192::ate2(&g, &d, &pk, &hm);
+    
+    v = pair192::fexp(&v);
+    if v.isunity() {
+        return BLS_OK;
+    }
+    return BLS_FAIL;
+}
diff --git a/src/bls256.rs b/src/bls256.rs
new file mode 100644
index 0000000..cdb553d
--- /dev/null
+++ b/src/bls256.rs
@@ -0,0 +1,96 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+use std::str;
+use super::ecp::ECP;
+use super::ecp8::ECP8;
+//use super::fp48::FP48;
+use super::big::BIG;
+use super::pair256;
+use super::big;
+use super::rom;
+
+use rand::RAND;
+use sha3::SHA3;
+use sha3::SHAKE256;
+
+/* BLS API Functions */
+
+pub const BFS: usize = big::MODBYTES as usize;
+pub const BGS: usize = big::MODBYTES as usize;
+pub const BLS_OK: isize = 0;
+pub const BLS_FAIL: isize = -1;
+
+/* hash a message to an ECP point, using SHA3 */
+
+#[allow(non_snake_case)]
+fn bls_hashit(m: &str) -> ECP {
+    let mut sh = SHA3::new(SHAKE256);
+    let mut hm: [u8; BFS] = [0; BFS];
+    let t = m.as_bytes();
+    for i in 0..m.len() {
+        sh.process(t[i]);
+    }
+    sh.shake(&mut hm, BFS);
+    let P = ECP::mapit(&hm);
+    return P;
+}
+
+/* generate key pair, private key s, public key w */
+pub fn key_pair_generate(mut rng: &mut RAND, s: &mut [u8], w: &mut [u8]) -> isize {
+    let q = BIG::new_ints(&rom::CURVE_ORDER);
+    let g = ECP8::generator();
+    let mut sc = BIG::randomnum(&q, &mut rng);
+    sc.tobytes(s);
+    pair256::g2mul(&g, &mut sc).tobytes(w);
+    return BLS_OK;
+}
+
+/* Sign message m using private key s to produce signature sig */
+
+pub fn sign(sig: &mut [u8], m: &str, s: &[u8]) -> isize {
+    let d = bls_hashit(m);
+    let mut sc = BIG::frombytes(&s);
+    pair256::g1mul(&d, &mut sc).tobytes(sig, true);
+    return BLS_OK;
+}
+
+/* Verify signature given message m, the signature sig, and the public key w */
+
+pub fn verify(sig: &[u8], m: &str, w: &[u8]) -> isize {
+    let hm = bls_hashit(m);
+    let mut d = ECP::frombytes(&sig);
+    let g = ECP8::generator();
+    let pk = ECP8::frombytes(&w);
+    d.neg();
+
+// Use new multi-pairing mechanism 
+    let mut r=pair256::initmp();
+    pair256::another(&mut r,&g,&d);
+    pair256::another(&mut r,&pk,&hm);
+    let mut v=pair256::miller(&r);
+
+//.. or alternatively
+//    let mut v = pair256::ate2(&g, &d, &pk, &hm);
+
+    v = pair256::fexp(&v);
+    if v.isunity() {
+        return BLS_OK;
+    }
+    return BLS_FAIL;
+}
diff --git a/src/dbig.rs b/src/dbig.rs
new file mode 100644
index 0000000..353443a
--- /dev/null
+++ b/src/dbig.rs
@@ -0,0 +1,301 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use super::super::arch;
+use super::big;
+use super::big::BIG;
+use super::super::arch::Chunk;
+
+#[derive(Copy)]
+pub struct DBIG {
+    pub w: [Chunk; big::DNLEN],
+}
+
+impl Clone for DBIG {
+    fn clone(&self) -> DBIG { *self }
+}
+
+impl DBIG {
+    pub fn new() -> DBIG {
+        DBIG {
+            w: [0; big::DNLEN as usize],
+        }
+    }
+
+    pub fn new_copy(y: &DBIG) -> DBIG {
+        let mut s = DBIG::new();
+        for i in 0..big::DNLEN {
+            s.w[i] = y.w[i]
+        }
+        return s;
+    }
+
+    pub fn new_scopy(x: &BIG) -> DBIG {
+        let mut b = DBIG::new();
+        for i in 0..big::NLEN {
+            b.w[i] = x.w[i];
+        }
+        b.w[big::NLEN - 1] = x.get(big::NLEN - 1) & big::BMASK; /* top word normalized */
+        b.w[big::NLEN] = x.get(big::NLEN - 1) >> big::BASEBITS;
+
+        for i in big::NLEN + 1..big::DNLEN {
+            b.w[i] = 0
+        }
+        return b;
+    }
+
+    /* split DBIG at position n, return higher half, keep lower half */
+    pub fn split(&mut self, n: usize) -> BIG {
+        let mut t = BIG::new();
+        let m = n % big::BASEBITS;
+        let mut carry = self.w[big::DNLEN - 1] << (big::BASEBITS - m);
+
+        for i in (big::NLEN - 1..big::DNLEN - 1).rev() {
+            let nw = (self.w[i] >> m) | carry;
+            carry = (self.w[i] << (big::BASEBITS - m)) & big::BMASK;
+            t.set(i + 1 - big::NLEN, nw);
+        }
+        self.w[big::NLEN - 1] &= ((1 as Chunk) << m) - 1;
+        return t;
+    }
+
+    /* general shift left */
+    pub fn shl(&mut self, k: usize) {
+        let n = k % big::BASEBITS;
+        let m = k / big::BASEBITS;
+        self.w[big::DNLEN - 1] =
+            (self.w[big::DNLEN - 1 - m] << n) | (self.w[big::DNLEN - m - 2] >> (big::BASEBITS - n));
+        for i in (m + 1..big::DNLEN - 1).rev() {
+            self.w[i] =
+                ((self.w[i - m] << n) & big::BMASK) | (self.w[i - m - 1] >> (big::BASEBITS - n));
+        }
+
+        self.w[m] = (self.w[0] << n) & big::BMASK;
+        for i in 0..m {
+            self.w[i] = 0
+        }
+    }
+
+    /* general shift right */
+    pub fn shr(&mut self, k: usize) {
+        let n = k % big::BASEBITS;
+        let m = k / big::BASEBITS;
+        for i in 0..big::DNLEN - m - 1 {
+            self.w[i] =
+                (self.w[m + i] >> n) | ((self.w[m + i + 1] << (big::BASEBITS - n)) & big::BMASK);
+        }
+        self.w[big::DNLEN - m - 1] = self.w[big::DNLEN - 1] >> n;
+        for i in big::DNLEN - m..big::DNLEN {
+            self.w[i] = 0
+        }
+    }
+
+    /* Copy from another DBIG */
+    pub fn copy(&mut self, x: &DBIG) {
+        for i in 0..big::DNLEN {
+            self.w[i] = x.w[i];
+        }
+    }
+
+    pub fn ucopy(&mut self, x: &BIG) {
+        for i in 0..big::NLEN {
+            self.w[i] = 0;
+        }
+        for i in big::NLEN..big::DNLEN {
+            self.w[i] = x.w[i - big::NLEN];
+        }
+    }
+
+    pub fn cmove(&mut self, g: &DBIG, d: isize) {
+        let b = -d as Chunk;
+        for i in 0..big::DNLEN {
+            self.w[i] ^= (self.w[i] ^ g.w[i]) & b;
+        }
+    }
+
+    /* self+=x */
+    pub fn add(&mut self, x: &DBIG) {
+        for i in 0..big::DNLEN {
+            self.w[i] += x.w[i];
+        }
+    }
+
+    /* self-=x */
+    pub fn sub(&mut self, x: &DBIG) {
+        for i in 0..big::DNLEN {
+            self.w[i] -= x.w[i];
+        }
+    }
+
+    /* self=x-self */
+    pub fn rsub(&mut self, x: &DBIG) {
+        for i in 0..big::DNLEN {
+            self.w[i] = x.w[i] - self.w[i];
+        }
+    }
+
+    /* Compare a and b, return 0 if a==b, -1 if a<b, +1 if a>b. Inputs must be normalised */
+    pub fn comp(a: &DBIG, b: &DBIG) -> isize {
+        for i in (0..big::DNLEN).rev() {
+            if a.w[i] == b.w[i] {
+                continue;
+            }
+            if a.w[i] > b.w[i] {
+                return 1;
+            } else {
+                return -1;
+            }
+        }
+        return 0;
+    }
+
+    /* normalise BIG - force all digits < 2^big::BASEBITS */
+    pub fn norm(&mut self) {
+        let mut carry = 0 as Chunk;
+        for i in 0..big::DNLEN - 1 {
+            let d = self.w[i] + carry;
+            self.w[i] = d & big::BMASK;
+            carry = d >> big::BASEBITS;
+        }
+        self.w[big::DNLEN - 1] += carry
+    }
+
+    /* reduces self DBIG mod a BIG, and returns the BIG */
+    pub fn dmod(&mut self, c: &BIG) -> BIG {
+        let mut k = 0;
+        self.norm();
+        let mut m = DBIG::new_scopy(c);
+        let mut dr = DBIG::new();
+
+        if DBIG::comp(self, &m) < 0 {
+            let r = BIG::new_dcopy(self);
+            return r;
+        }
+
+        loop {
+            m.shl(1);
+            k += 1;
+            if DBIG::comp(self, &m) < 0 {
+                break;
+            }
+        }
+
+        while k > 0 {
+            m.shr(1);
+
+            dr.copy(self);
+            dr.sub(&m);
+            dr.norm();
+            self.cmove(
+                &dr,
+                (1 - ((dr.w[big::DNLEN - 1] >> (arch::CHUNK - 1)) & 1)) as isize,
+            );
+
+            k -= 1;
+        }
+        let r = BIG::new_dcopy(self);
+        return r;
+    }
+
+    /* return this/c */
+    pub fn div(&mut self, c: &BIG) -> BIG {
+        let mut k = 0;
+        let mut m = DBIG::new_scopy(c);
+        let mut a = BIG::new();
+        let mut e = BIG::new_int(1);
+        let mut dr = DBIG::new();
+        let mut r = BIG::new();
+        self.norm();
+
+        while DBIG::comp(self, &m) >= 0 {
+            e.fshl(1);
+            m.shl(1);
+            k += 1;
+        }
+
+        while k > 0 {
+            m.shr(1);
+            e.shr(1);
+
+            dr.copy(self);
+            dr.sub(&m);
+            dr.norm();
+            let d = (1 - ((dr.w[big::DNLEN - 1] >> (arch::CHUNK - 1)) & 1)) as isize;
+            self.cmove(&dr, d);
+            r.copy(&a);
+            r.add(&e);
+            r.norm();
+            a.cmove(&r, d);
+
+            k -= 1;
+        }
+        return a;
+    }
+
+    /* set x = x mod 2^m */
+    pub fn mod2m(&mut self, m: usize) {
+        let wd = m / big::BASEBITS;
+        let bt = m % big::BASEBITS;
+        let msk = (1 << bt) - 1;
+        self.w[wd] &= msk;
+        for i in wd + 1..big::DNLEN {
+            self.w[i] = 0
+        }
+    }
+
+    /* return number of bits */
+    pub fn nbits(&mut self) -> usize {
+        let mut k = big::DNLEN - 1;
+        let mut s = DBIG::new_copy(&self);
+        s.norm();
+        while (k as isize) >= 0 && s.w[k] == 0 {
+            k = k.wrapping_sub(1)
+        }
+        if (k as isize) < 0 {
+            return 0;
+        }
+        let mut bts = (big::BASEBITS as usize) * k;
+        let mut c = s.w[k];
+        while c != 0 {
+            c /= 2;
+            bts += 1;
+        }
+        return bts;
+    }
+
+    /* Convert to Hex String */
+    pub fn to_string(&mut self) -> String {
+        let mut s = String::new();
+        let mut len = self.nbits();
+
+        if len % 4 == 0 {
+            len /= 4;
+        } else {
+            len /= 4;
+            len += 1;
+        }
+
+        for i in (0..len).rev() {
+            let mut b = DBIG::new_copy(&self);
+            b.shr(i * 4);
+            s = s + &format!("{:X}", b.w[0] & 15);
+        }
+        return s;
+    }
+}
diff --git a/src/ecdh.rs b/src/ecdh.rs
new file mode 100644
index 0000000..9b49e18
--- /dev/null
+++ b/src/ecdh.rs
@@ -0,0 +1,744 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use super::ecp;
+use super::ecp::ECP;
+use super::big::BIG;
+use super::rom;
+use super::big;
+
+use rand::RAND;
+use hash256::HASH256;
+use hash384::HASH384;
+use hash512::HASH512;
+use aes;
+use aes::AES;
+
+
+pub const INVALID_PUBLIC_KEY: isize = -2;
+pub const ERROR: isize = -3;
+pub const INVALID: isize = -4;
+pub const EFS: usize = big::MODBYTES as usize;
+pub const EGS: usize = big::MODBYTES as usize;
+pub const SHA256: usize = 32;
+pub const SHA384: usize = 48;
+pub const SHA512: usize = 64;
+
+#[allow(non_snake_case)]
+
+fn inttobytes(n: usize, b: &mut [u8]) {
+    let mut i = b.len();
+    let mut m = n;
+    while m > 0 && i > 0 {
+        i -= 1;
+        b[i] = (m & 0xff) as u8;
+        m /= 256;
+    }
+}
+
+fn hashit(sha: usize, a: &[u8], n: usize, b: Option<&[u8]>, pad: usize, w: &mut [u8]) {
+    let mut r: [u8; 64] = [0; 64];
+    if sha == SHA256 {
+        let mut h = HASH256::new();
+        h.process_array(a);
+        if n > 0 {
+            h.process_num(n as i32)
+        }
+        if let Some(x) = b {
+            h.process_array(x);
+        }
+        let hs = h.hash();
+        for i in 0..sha {
+            r[i] = hs[i];
+        }
+    }
+    if sha == SHA384 {
+        let mut h = HASH384::new();
+        h.process_array(a);
+        if n > 0 {
+            h.process_num(n as i32)
+        }
+        if let Some(x) = b {
+            h.process_array(x);
+        }
+        let hs = h.hash();
+        for i in 0..sha {
+            r[i] = hs[i];
+        }
+    }
+    if sha == SHA512 {
+        let mut h = HASH512::new();
+        h.process_array(a);
+        if n > 0 {
+            h.process_num(n as i32)
+        }
+        if let Some(x) = b {
+            h.process_array(x);
+        }
+        let hs = h.hash();
+        for i in 0..sha {
+            r[i] = hs[i];
+        }
+    }
+
+    if pad == 0 {
+        for i in 0..sha {
+            w[i] = r[i]
+        }
+    } else {
+        if pad <= sha {
+            for i in 0..pad {
+                w[i] = r[i]
+            }
+        } else {
+            for i in 0..sha {
+                w[i + pad - sha] = r[i]
+            }
+            for i in 0..(pad - sha) {
+                w[i] = 0
+            }
+        }
+    }
+}
+
+/* Key Derivation Functions */
+/* Input octet Z */
+/* Output key of length olen */
+pub fn kdf1(sha: usize, z: &[u8], olen: usize, k: &mut [u8]) {
+    /* NOTE: the parameter olen is the length of the output K in bytes */
+    let hlen = sha;
+    let mut lk = 0;
+
+    let mut cthreshold = olen / hlen;
+    if olen % hlen != 0 {
+        cthreshold += 1
+    }
+
+    for counter in 0..cthreshold {
+        let mut b: [u8; 64] = [0; 64];
+        hashit(sha, z, counter, None, 0, &mut b);
+        if lk + hlen > olen {
+            for i in 0..(olen % hlen) {
+                k[lk] = b[i];
+                lk += 1
+            }
+        } else {
+            for i in 0..hlen {
+                k[lk] = b[i];
+                lk += 1
+            }
+        }
+    }
+}
+
+pub fn kdf2(sha: usize, z: &[u8], p: Option<&[u8]>, olen: usize, k: &mut [u8]) {
+    /* NOTE: the parameter olen is the length of the output K in bytes */
+    let hlen = sha;
+    let mut lk = 0;
+
+    let mut cthreshold = olen / hlen;
+    if olen % hlen != 0 {
+        cthreshold += 1
+    }
+
+    for counter in 1..cthreshold + 1 {
+        let mut b: [u8; 64] = [0; 64];
+        hashit(sha, z, counter, p, 0, &mut b);
+        if lk + hlen > olen {
+            for i in 0..(olen % hlen) {
+                k[lk] = b[i];
+                lk += 1
+            }
+        } else {
+            for i in 0..hlen {
+                k[lk] = b[i];
+                lk += 1
+            }
+        }
+    }
+}
+
+/* Password based Key Derivation Function */
+/* Input password p, salt s, and repeat count */
+/* Output key of length olen */
+pub fn pbkdf2(sha: usize, pass: &[u8], salt: &[u8], rep: usize, olen: usize, k: &mut [u8]) {
+    let mut d = olen / sha;
+    if olen % sha != 0 {
+        d += 1
+    }
+    let mut f: [u8; 64] = [0; 64];
+    let mut u: [u8; 64] = [0; 64];
+    let mut ku: [u8; 64] = [0; 64];
+    let mut s: [u8; 36] = [0; 36]; // Maximum salt of 32 bytes + 4
+    let mut n: [u8; 4] = [0; 4];
+
+    let sl = salt.len();
+    let mut kp = 0;
+    for i in 0..d {
+        for j in 0..sl {
+            s[j] = salt[j]
+        }
+        inttobytes(i + 1, &mut n);
+        for j in 0..4 {
+            s[sl + j] = n[j]
+        }
+
+        hmac(sha, &s[0..sl + 4], pass, sha, &mut f);
+
+        for j in 0..sha {
+            u[j] = f[j]
+        }
+        for _ in 1..rep {
+            hmac(sha, &mut u, pass, sha, &mut ku);
+            for k in 0..sha {
+                u[k] = ku[k];
+                f[k] ^= u[k]
+            }
+        }
+        for j in 0..EFS {
+            if kp < olen {
+                k[kp] = f[j]
+            }
+            kp += 1
+        }
+    }
+}
+
+/* Calculate HMAC of m using key k. HMAC is tag of length olen (which is length of tag) */
+pub fn hmac(sha: usize, m: &[u8], k: &[u8], olen: usize, tag: &mut [u8]) -> bool {
+    /* Input is from an octet m        *
+    	* olen is requested output length in bytes. k is the key  *
+    	* The output is the calculated tag */
+    let mut b: [u8; 64] = [0; 64]; /* Not good */
+    let mut k0: [u8; 128] = [0; 128];
+
+    if olen < 4 {
+        return false;
+    }
+
+    let mut lb = 64;
+    if sha > 32 {
+        lb = 128
+    }
+
+    for i in 0..lb {
+        k0[i] = 0
+    }
+
+    if k.len() > lb {
+        hashit(sha, k, 0, None, 0, &mut b);
+        for i in 0..sha {
+            k0[i] = b[i]
+        }
+    } else {
+        for i in 0..k.len() {
+            k0[i] = k[i]
+        }
+    }
+
+    for i in 0..lb {
+        k0[i] ^= 0x36
+    }
+    hashit(sha, &mut k0[0..lb], 0, Some(m), 0, &mut b);
+
+    for i in 0..lb {
+        k0[i] ^= 0x6a
+    }
+    hashit(sha, &mut k0[0..lb], 0, Some(&b[0..sha]), olen, tag);
+
+    return true;
+}
+
+/* AES encryption/decryption. Encrypt byte array m using key k and returns ciphertext c */
+pub fn cbc_iv0_encrypt(k: &[u8], m: &[u8]) -> Vec<u8> {
+    /* AES CBC encryption, with Null IV and key K */
+    /* Input is from an octet string m, output is to an octet string c */
+    /* Input is padded as necessary to make up a full final block */
+    let mut a = AES::new();
+    let mut fin = false;
+    let mut c: Vec<u8> = Vec::new();
+
+    let mut buff: [u8; 16] = [0; 16];
+
+    a.init(aes::CBC, k.len(), k, None);
+
+    let mut ipt = 0;
+    let mut i;
+    loop {
+        i = 0;
+        while i < 16 {
+            if ipt < m.len() {
+                buff[i] = m[ipt];
+                i += 1;
+                ipt += 1;
+            } else {
+                fin = true;
+                break;
+            }
+        }
+        if fin {
+            break;
+        }
+        a.encrypt(&mut buff);
+        for j in 0..16 {
+            c.push(buff[j]);
+        }
+    }
+
+    /* last block, filled up to i-th index */
+
+    let padlen = 16 - i;
+    for j in i..16 {
+        buff[j] = padlen as u8
+    }
+
+    a.encrypt(&mut buff);
+
+    for j in 0..16 {
+        c.push(buff[j]);
+    }
+    a.end();
+    return c;
+}
+
+/* returns plaintext if all consistent, else returns null string */
+pub fn cbc_iv0_decrypt(k: &[u8], c: &[u8]) -> Option<Vec<u8>> {
+    /* padding is removed */
+    let mut a = AES::new();
+    let mut fin = false;
+    let mut m: Vec<u8> = Vec::new();
+
+    let mut buff: [u8; 16] = [0; 16];
+
+    a.init(aes::CBC, k.len(), k, None);
+
+    let mut ipt = 0;
+    let mut i;
+
+    if c.len() == 0 {
+        return None;
+    }
+    let mut ch = c[ipt];
+    ipt += 1;
+
+    loop {
+        i = 0;
+        while i < 16 {
+            buff[i] = ch;
+            if ipt >= c.len() {
+                fin = true;
+                break;
+            } else {
+                ch = c[ipt];
+                ipt += 1
+            }
+            i += 1;
+        }
+        a.decrypt(&mut buff);
+        if fin {
+            break;
+        }
+        for j in 0..16 {
+            m.push(buff[j]);
+        }
+    }
+
+    a.end();
+    let mut bad = false;
+    let padlen = buff[15] as usize;
+    if i != 15 || padlen < 1 || padlen > 16 {
+        bad = true
+    }
+    if padlen >= 2 && padlen <= 16 {
+        for j in 16 - padlen..16 {
+            if buff[j] != padlen as u8 {
+                bad = true
+            }
+        }
+    }
+
+    if !bad {
+        for i in 0..16 - padlen {
+            m.push(buff[i]);
+        }
+    }
+
+    if bad {
+        return None;
+    }
+    return Some(m);
+}
+
+/* Calculate a public/private EC GF(p) key pair w,s where W=s.G mod EC(p),
+ * where s is the secret key and W is the public key
+ * and G is fixed generator.
+ * If RNG is NULL then the private key is provided externally in s
+ * otherwise it is generated randomly internally */
+#[allow(non_snake_case)]
+pub fn key_pair_generate(rng: Option<&mut RAND>, s: &mut [u8], w: &mut [u8]) -> isize {
+    let res = 0;
+    let mut sc: BIG;
+    let G = ECP::generator();
+
+    let r = BIG::new_ints(&rom::CURVE_ORDER);
+
+    if let Some(mut x) = rng {
+        sc = BIG::randomnum(&r, &mut x);
+    } else {
+        sc = BIG::frombytes(&s);
+        sc.rmod(&r);
+    }
+
+    sc.tobytes(s);
+
+    let WP = G.mul(&mut sc);
+
+    WP.tobytes(w, false); // To use point compression on public keys, change to true
+
+    return res;
+}
+
+/* validate public key */
+#[allow(non_snake_case)]
+pub fn public_key_validate(w: &[u8]) -> isize {
+    let mut WP = ECP::frombytes(w);
+    let mut res = 0;
+
+    let r = BIG::new_ints(&rom::CURVE_ORDER);
+
+    if WP.is_infinity() {
+        res = INVALID_PUBLIC_KEY
+    }
+    if res == 0 {
+        let q = BIG::new_ints(&rom::MODULUS);
+        let nb = q.nbits();
+        let mut k = BIG::new();
+        k.one();
+        k.shl((nb + 4) / 2);
+        k.add(&q);
+        k.div(&r);
+
+        while k.parity() == 0 {
+            k.shr(1);
+            WP.dbl();
+        }
+
+        if !k.isunity() {
+            WP = WP.mul(&mut k)
+        }
+        if WP.is_infinity() {
+            res = INVALID_PUBLIC_KEY
+        }
+    }
+    return res;
+}
+
+/* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */
+#[allow(non_snake_case)]
+pub fn ecpsvdp_dh(s: &[u8], wd: &[u8], z: &mut [u8]) -> isize {
+    let mut res = 0;
+    let mut t: [u8; EFS] = [0; EFS];
+
+    let mut sc = BIG::frombytes(&s);
+
+    let mut W = ECP::frombytes(&wd);
+    if W.is_infinity() {
+        res = ERROR
+    }
+
+    if res == 0 {
+        let r = BIG::new_ints(&rom::CURVE_ORDER);
+        sc.rmod(&r);
+        W = W.mul(&mut sc);
+        if W.is_infinity() {
+            res = ERROR;
+        } else {
+            W.getx().tobytes(&mut t);
+            for i in 0..EFS {
+                z[i] = t[i]
+            }
+        }
+    }
+    return res;
+}
+
+/* IEEE ECDSA Signature, C and D are signature on F using private key S */
+#[allow(non_snake_case)]
+pub fn ecpsp_dsa(
+    sha: usize,
+    rng: &mut RAND,
+    s: &[u8],
+    f: &[u8],
+    c: &mut [u8],
+    d: &mut [u8],
+) -> isize {
+    let mut t: [u8; EFS] = [0; EFS];
+    let mut b: [u8; big::MODBYTES as usize] = [0; big::MODBYTES as usize];
+
+    hashit(sha, f, 0, None, big::MODBYTES as usize, &mut b);
+
+    let G = ECP::generator();
+
+    let r = BIG::new_ints(&rom::CURVE_ORDER);
+
+    let mut sc = BIG::frombytes(s); /* s or &s? */
+    let fb = BIG::frombytes(&b);
+
+    let mut cb = BIG::new();
+    let mut db = BIG::new();
+    let mut tb = BIG::new();
+    let mut V = ECP::new();
+
+    while db.iszilch() {
+        let mut u = BIG::randomnum(&r, rng);
+        let mut w = BIG::randomnum(&r, rng); /* side channel masking */
+
+        V.copy(&G);
+        V = V.mul(&mut u);
+        let vx = V.getx();
+        cb.copy(&vx);
+        cb.rmod(&r);
+        if cb.iszilch() {
+            continue;
+        }
+
+        tb.copy(&BIG::modmul(&mut u, &mut w, &r));
+        u.copy(&tb);
+
+        u.invmodp(&r);
+        db.copy(&BIG::modmul(&mut sc, &mut cb, &r));
+        db.add(&fb);
+
+        tb.copy(&BIG::modmul(&mut db, &mut w, &r));
+        db.copy(&tb);
+
+        tb.copy(&BIG::modmul(&mut u, &mut db, &r));
+        db.copy(&tb);
+    }
+
+    cb.tobytes(&mut t);
+    for i in 0..EFS {
+        c[i] = t[i]
+    }
+    db.tobytes(&mut t);
+    for i in 0..EFS {
+        d[i] = t[i]
+    }
+    return 0;
+}
+
+/* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */
+#[allow(non_snake_case)]
+pub fn ecpvp_dsa(sha: usize, w: &[u8], f: &[u8], c: &[u8], d: &[u8]) -> isize {
+    let mut res = 0;
+
+    let mut b: [u8; big::MODBYTES as usize] = [0; big::MODBYTES as usize];
+
+    hashit(sha, f, 0, None, big::MODBYTES as usize, &mut b);
+
+    let mut G = ECP::generator();
+
+    let r = BIG::new_ints(&rom::CURVE_ORDER);
+
+    let mut cb = BIG::frombytes(c); /* c or &c ? */
+    let mut db = BIG::frombytes(d); /* d or &d ? */
+    let mut fb = BIG::frombytes(&b);
+    let mut tb = BIG::new();
+
+    if cb.iszilch() || BIG::comp(&cb, &r) >= 0 || db.iszilch() || BIG::comp(&db, &r) >= 0 {
+        res = INVALID;
+    }
+
+    if res == 0 {
+        db.invmodp(&r);
+        tb.copy(&BIG::modmul(&mut fb, &mut db, &r));
+        fb.copy(&tb);
+        let h2 = BIG::modmul(&mut cb, &mut db, &r);
+
+        let WP = ECP::frombytes(&w);
+        if WP.is_infinity() {
+            res = ERROR;
+        } else {
+            let mut P = ECP::new();
+            P.copy(&WP);
+
+            P = P.mul2(&h2, &mut G, &fb);
+
+            if P.is_infinity() {
+                res = INVALID;
+            } else {
+                db = P.getx();
+                db.rmod(&r);
+
+                if BIG::comp(&db, &cb) != 0 {
+                    res = INVALID
+                }
+            }
+        }
+    }
+
+    return res;
+}
+
+/* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */
+#[allow(non_snake_case)]
+pub fn ecies_encrypt(
+    sha: usize,
+    p1: &[u8],
+    p2: &[u8],
+    rng: &mut RAND,
+    w: &[u8],
+    m: &[u8],
+    v: &mut [u8],
+    t: &mut [u8],
+) -> Option<Vec<u8>> {
+    let mut z: [u8; EFS] = [0; EFS];
+    let mut k1: [u8; ecp::AESKEY] = [0; ecp::AESKEY];
+    let mut k2: [u8; ecp::AESKEY] = [0; ecp::AESKEY];
+    let mut u: [u8; EGS] = [0; EGS];
+    let mut vz: [u8; 3 * EFS + 1] = [0; 3 * EFS + 1];
+    let mut k: [u8; 2 * ecp::AESKEY] = [0; 2 * ecp::AESKEY];
+
+    if key_pair_generate(Some(rng), &mut u, v) != 0 {
+        return None;
+    }
+    if ecpsvdp_dh(&u, &w, &mut z) != 0 {
+        return None;
+    }
+
+    for i in 0..2 * EFS + 1 {
+        vz[i] = v[i]
+    }
+    for i in 0..EFS {
+        vz[2 * EFS + 1 + i] = z[i]
+    }
+
+    kdf2(sha, &vz, Some(p1), 2 * ecp::AESKEY, &mut k);
+
+    for i in 0..ecp::AESKEY {
+        k1[i] = k[i];
+        k2[i] = k[ecp::AESKEY + i]
+    }
+
+    let mut c = cbc_iv0_encrypt(&k1, m);
+
+    let mut l2: [u8; 8] = [0; 8];
+    let p2l = p2.len();
+
+    inttobytes(p2l, &mut l2);
+
+    for i in 0..p2l {
+        c.push(p2[i]);
+    }
+    for i in 0..8 {
+        c.push(l2[i]);
+    }
+
+    hmac(sha, &c, &k2, t.len(), t);
+
+    for _ in 0..p2l + 8 {
+        c.pop();
+    }
+
+    return Some(c);
+}
+
+/* constant time n-byte compare */
+fn ncomp(t1: &[u8], t2: &[u8], n: usize) -> bool {
+    let mut res = 0;
+    for i in 0..n {
+        res |= (t1[i] ^ t2[i]) as isize;
+    }
+    if res == 0 {
+        return true;
+    }
+    return false;
+}
+
+/* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */
+#[allow(non_snake_case)]
+pub fn ecies_decrypt(
+    sha: usize,
+    p1: &[u8],
+    p2: &[u8],
+    v: &[u8],
+    c: &mut Vec<u8>,
+    t: &[u8],
+    u: &[u8],
+) -> Option<Vec<u8>> {
+    let mut z: [u8; EFS] = [0; EFS];
+    let mut k1: [u8; ecp::AESKEY] = [0; ecp::AESKEY];
+    let mut k2: [u8; ecp::AESKEY] = [0; ecp::AESKEY];
+    let mut vz: [u8; 3 * EFS + 1] = [0; 3 * EFS + 1];
+    let mut k: [u8; 2 * ecp::AESKEY] = [0; 2 * ecp::AESKEY];
+
+    let mut tag: [u8; 32] = [0; 32]; /* 32 is max length of tag */
+
+    for i in 0..t.len() {
+        tag[i] = t[i]
+    }
+
+    if ecpsvdp_dh(&u, &v, &mut z) != 0 {
+        return None;
+    }
+
+    for i in 0..2 * EFS + 1 {
+        vz[i] = v[i]
+    }
+    for i in 0..EFS {
+        vz[2 * EFS + 1 + i] = z[i]
+    }
+
+    kdf2(sha, &vz, Some(p1), 2 * ecp::AESKEY, &mut k);
+
+    for i in 0..ecp::AESKEY {
+        k1[i] = k[i];
+        k2[i] = k[ecp::AESKEY + i]
+    }
+
+    let m = cbc_iv0_decrypt(&k1, &c);
+
+    if m == None {
+        return None;
+    }
+
+    let mut l2: [u8; 8] = [0; 8];
+    let p2l = p2.len();
+
+    inttobytes(p2l, &mut l2);
+
+    for i in 0..p2l {
+        c.push(p2[i]);
+    }
+    for i in 0..8 {
+        c.push(l2[i]);
+    }
+
+    hmac(sha, &c, &k2, t.len(), &mut tag);
+
+    for _ in 0..p2l + 8 {
+        c.pop();
+    }
+
+    if !ncomp(&t, &tag, t.len()) {
+        return None;
+    }
+
+    return m;
+}
diff --git a/src/ecp.rs b/src/ecp.rs
new file mode 100644
index 0000000..9e7b29c
--- /dev/null
+++ b/src/ecp.rs
@@ -0,0 +1,1261 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use super::fp::FP;
+use super::big::BIG;
+use super::big;
+use super::rom;
+
+pub use super::rom::{CURVETYPE, CURVE_PAIRING_TYPE, SEXTIC_TWIST, SIGN_OF_X, HASH_TYPE, AESKEY};
+pub use types::CurveType;
+use std::str::SplitWhitespace;
+use std::fmt;
+
+#[derive(Copy, Clone)]
+pub struct ECP {
+    x: FP,
+    y: FP,
+    z: FP,
+}
+
+impl PartialEq for ECP {
+    fn eq(&self, other: &ECP) -> bool {
+        self.equals(other)
+    }
+}
+
+impl fmt::Display for ECP {
+	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+		write!(f, "ECP: [ {}, {}, {} ]", self.x, self.y, self.z)
+	}
+}
+
+impl fmt::Debug for ECP {
+	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+		write!(f, "ECP: [ {}, {}, {} ]", self.x, self.y, self.z)
+	}
+}
+
+#[allow(non_snake_case)]
+impl ECP {
+    pub fn pnew() -> ECP {
+        ECP {
+            x: FP::new(),
+            y: FP::new_int(1),
+            z: FP::new(),
+        }
+    }
+
+    pub fn new() -> ECP {
+        let mut E = ECP::pnew();
+        if CURVETYPE == CurveType::EDWARDS {
+            E.z.one();
+        }
+        return E;
+    }
+
+    /* set (x,y) from two BIGs */
+    pub fn new_bigs(ix: &BIG, iy: &BIG) -> ECP {
+        let mut E = ECP::new();
+        E.x.bcopy(ix);
+        E.y.bcopy(iy);
+        E.z.one();
+        E.x.norm();
+        let mut rhs = ECP::rhs(&E.x);
+        if CURVETYPE == CurveType::MONTGOMERY {
+            if rhs.jacobi() != 1 {
+                E.inf();
+            }
+        } else {
+            let mut y2 = FP::new_copy(&E.y);
+            y2.sqr();
+            if !y2.equals(&mut rhs) {
+                E.inf();
+            }
+        }
+        return E;
+    }
+
+    /* set (x,y) from BIG and a bit */
+    pub fn new_bigint(ix: &BIG, s: isize) -> ECP {
+        let mut E = ECP::new();
+        E.x.bcopy(ix);
+        E.x.norm();
+        E.z.one();
+
+        let mut rhs = ECP::rhs(&E.x);
+
+        if rhs.jacobi() == 1 {
+            let mut ny = rhs.sqrt();
+            if ny.redc().parity() != s {
+                ny.neg()
+            }
+            E.y.copy(&ny);
+        } else {
+            E.inf()
+        }
+        return E;
+    }
+
+    #[allow(non_snake_case)]
+    /* set from x - calculate y from curve equation */
+    pub fn new_big(ix: &BIG) -> ECP {
+        let mut E = ECP::new();
+        E.x.bcopy(ix);
+        E.x.norm();
+        E.z.one();
+        let mut rhs = ECP::rhs(&E.x);
+        if rhs.jacobi() == 1 {
+            if CURVETYPE != CurveType::MONTGOMERY {
+                E.y.copy(&rhs.sqrt())
+            }
+        } else {
+            E.inf();
+        }
+        return E;
+    }
+
+    /* set this=O */
+    pub fn inf(&mut self) {
+        self.x.zero();
+        if CURVETYPE != CurveType::MONTGOMERY {
+            self.y.one();
+        }
+        if CURVETYPE != CurveType::EDWARDS {
+            self.z.zero();
+        } else {
+            self.z.one()
+        }
+    }
+
+    /* Calculate RHS of curve equation */
+    fn rhs(x: &FP) -> FP {
+        let mut r = FP::new_copy(x);
+        r.sqr();
+
+        if CURVETYPE == CurveType::WEIERSTRASS {
+            // x^3+Ax+B
+            let b = FP::new_big(&BIG::new_ints(&rom::CURVE_B));
+            r.mul(x);
+            if rom::CURVE_A == -3 {
+                let mut cx = FP::new_copy(x);
+                cx.imul(3);
+                cx.neg();
+                cx.norm();
+                r.add(&cx);
+            }
+            r.add(&b);
+        }
+        if CURVETYPE == CurveType::EDWARDS {
+            // (Ax^2-1)/(Bx^2-1)
+            let mut b = FP::new_big(&BIG::new_ints(&rom::CURVE_B));
+            let one = FP::new_int(1);
+            b.mul(&r);
+            b.sub(&one);
+            b.norm();
+            if rom::CURVE_A == -1 {
+                r.neg()
+            }
+            r.sub(&one);
+            r.norm();
+            b.inverse();
+            r.mul(&b);
+        }
+        if CURVETYPE == CurveType::MONTGOMERY {
+            // x^3+Ax^2+x
+            let mut x3 = FP::new();
+            x3.copy(&r);
+            x3.mul(x);
+            r.imul(rom::CURVE_A);
+            r.add(&x3);
+            r.add(&x);
+        }
+        r.reduce();
+        return r;
+    }
+
+    /* test for O point-at-infinity */
+    pub fn is_infinity(&self) -> bool {
+        match CURVETYPE {
+            CurveType::EDWARDS=> self.x.iszilch() && self.y.equals(&self.z),
+            CurveType::WEIERSTRASS => self.x.iszilch() && self.z.iszilch(),
+            CurveType::MONTGOMERY => self.z.iszilch(),
+        }
+    }
+
+    /* Conditional swap of P and Q dependant on d */
+    pub fn cswap(&mut self, Q: &mut ECP, d: isize) {
+        self.x.cswap(&mut Q.x, d);
+        if CURVETYPE != CurveType::MONTGOMERY {
+            self.y.cswap(&mut Q.y, d)
+        }
+        self.z.cswap(&mut Q.z, d);
+    }
+
+    /* Conditional move of Q to P dependant on d */
+    pub fn cmove(&mut self, Q: &ECP, d: isize) {
+        self.x.cmove(&Q.x, d);
+        if CURVETYPE != CurveType::MONTGOMERY {
+            self.y.cmove(&Q.y, d)
+        }
+        self.z.cmove(&Q.z, d);
+    }
+
+    /* return 1 if b==c, no branching */
+    fn teq(b: i32, c: i32) -> isize {
+        let mut x = b ^ c;
+        x -= 1; // if x=0, x now -1
+        return ((x >> 31) & 1) as isize;
+    }
+
+    /* this=P */
+    pub fn copy(&mut self, P: &ECP) {
+        self.x.copy(&P.x);
+        if CURVETYPE != CurveType::MONTGOMERY {
+            self.y.copy(&P.y)
+        }
+        self.z.copy(&P.z);
+    }
+
+    /* this=-this */
+    pub fn neg(&mut self) {
+        if CURVETYPE == CurveType::WEIERSTRASS {
+            self.y.neg();
+            self.y.norm();
+        }
+        if CURVETYPE == CurveType::EDWARDS {
+            self.x.neg();
+            self.x.norm();
+        }
+        return;
+    }
+    /* multiply x coordinate */
+    pub fn mulx(&mut self, c: &mut FP) {
+        self.x.mul(c);
+    }
+
+    /* Constant time select from pre-computed table */
+    fn selector(&mut self, W: &[ECP], b: i32) {
+        // unsure about &[& syntax. An array of pointers I hope..
+        let mut MP = ECP::new();
+        let m = b >> 31;
+        let mut babs = (b ^ m) - m;
+
+        babs = (babs - 1) / 2;
+
+        self.cmove(&W[0], ECP::teq(babs, 0)); // conditional move
+        self.cmove(&W[1], ECP::teq(babs, 1));
+        self.cmove(&W[2], ECP::teq(babs, 2));
+        self.cmove(&W[3], ECP::teq(babs, 3));
+        self.cmove(&W[4], ECP::teq(babs, 4));
+        self.cmove(&W[5], ECP::teq(babs, 5));
+        self.cmove(&W[6], ECP::teq(babs, 6));
+        self.cmove(&W[7], ECP::teq(babs, 7));
+
+        MP.copy(self);
+        MP.neg();
+        self.cmove(&MP, (m & 1) as isize);
+    }
+
+    /* Test P == Q */
+    pub fn equals(&self, Q: &ECP) -> bool {
+        let mut a = FP::new();
+        let mut b = FP::new();
+        a.copy(&self.x);
+        a.mul(&Q.z);
+        b.copy(&Q.x);
+        b.mul(&self.z);
+        if !a.equals(&mut b) {
+            return false;
+        }
+        if CURVETYPE != CurveType::MONTGOMERY {
+            a.copy(&self.y);
+            a.mul(&Q.z);
+            b.copy(&Q.y);
+            b.mul(&self.z);
+            if !a.equals(&mut b) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /* set to affine - from (x,y,z) to (x,y) */
+    pub fn affine(&mut self) {
+        if self.is_infinity() {
+            return;
+        }
+        let mut one = FP::new_int(1);
+        if self.z.equals(&mut one) {
+            return;
+        }
+        self.z.inverse();
+
+        self.x.mul(&self.z);
+        self.x.reduce();
+        if CURVETYPE != CurveType::MONTGOMERY {
+            self.y.mul(&self.z);
+            self.y.reduce();
+        }
+        self.z.copy(&one);
+    }
+
+    /* extract x as a BIG */
+    pub fn getx(&self) -> BIG {
+        let mut W = ECP::new();
+        W.copy(self);
+        W.affine();
+        return W.x.redc();
+    }
+
+    /* extract y as a BIG */
+    pub fn gety(&self) -> BIG {
+        let mut W = ECP::new();
+        W.copy(self);
+        W.affine();
+        return W.y.redc();
+    }
+
+    /* get sign of Y */
+    pub fn gets(&self) -> isize {
+        let y = self.gety();
+        return y.parity();
+    }
+
+    /* extract x as an FP */
+    pub fn getpx(&self) -> FP {
+        let w = FP::new_copy(&self.x);
+        return w;
+    }
+    /* extract y as an FP */
+    pub fn getpy(&self) -> FP {
+        let w = FP::new_copy(&self.y);
+        return w;
+    }
+
+    /* extract z as an FP */
+    pub fn getpz(&self) -> FP {
+        let w = FP::new_copy(&self.z);
+        return w;
+    }
+
+    /* convert to byte array */
+    pub fn tobytes(&self, b: &mut [u8], compress: bool) {
+        let mb = big::MODBYTES as usize;
+        let mut t: [u8; big::MODBYTES as usize] = [0; big::MODBYTES as usize];
+        let mut W = ECP::new();
+        W.copy(self);
+
+        W.affine();
+        W.x.redc().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 1] = t[i]
+        }
+
+        if CURVETYPE == CurveType::MONTGOMERY {
+            b[0] = 0x06;
+            return;
+        }
+
+        if compress {
+            b[0] = 0x02;
+            if W.y.redc().parity() == 1 {
+                b[0] = 0x03
+            }
+            return;
+        }
+
+        b[0] = 0x04;
+
+        W.y.redc().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + mb + 1] = t[i]
+        }
+    }
+
+    /* convert from byte array to point */
+    pub fn frombytes(b: &[u8]) -> ECP {
+        let mut t: [u8; big::MODBYTES as usize] = [0; big::MODBYTES as usize];
+        let mb = big::MODBYTES as usize;
+        let p = BIG::new_ints(&rom::MODULUS);
+
+        for i in 0..mb {
+            t[i] = b[i + 1]
+        }
+        let px = BIG::frombytes(&t);
+        if BIG::comp(&px, &p) >= 0 {
+            return ECP::new();
+        }
+
+        if CURVETYPE == CurveType::MONTGOMERY {
+            return ECP::new_big(&px);
+        }
+
+        if b[0] == 0x04 {
+            for i in 0..mb {
+                t[i] = b[i + mb + 1]
+            }
+            let py = BIG::frombytes(&t);
+            if BIG::comp(&py, &p) >= 0 {
+                return ECP::new();
+            }
+            return ECP::new_bigs(&px, &py);
+        }
+
+        if b[0] == 0x02 || b[0] == 0x03 {
+            return ECP::new_bigint(&px, (b[0] & 1) as isize);
+        }
+
+        return ECP::new();
+    }
+
+    /* convert to hex string */
+    pub fn tostring(&self) -> String {
+        let mut W = ECP::new();
+        W.copy(self);
+        if W.is_infinity() {
+            return String::from("infinity");
+        }
+        if CURVETYPE == CurveType::MONTGOMERY {
+            return format!("({})", W.x.redc().tostring());
+        } else {
+            return format!("({},{})", W.x.redc().tostring(), W.y.redc().tostring());
+        };
+    }
+
+    pub fn to_hex(&self) -> String {
+        format!("{} {} {}", self.x.to_hex(), self.y.to_hex(), self.z.to_hex())
+    }
+
+    pub fn from_hex_iter(iter: &mut SplitWhitespace) -> ECP {
+        ECP {
+            x: FP::from_hex_iter(iter),
+            y: FP::from_hex_iter(iter),
+            z: FP::from_hex_iter(iter),
+        }
+    }
+
+    pub fn from_hex(val: String) -> ECP {
+        let mut iter = val.split_whitespace();
+        return ECP::from_hex_iter(&mut iter);
+    }
+
+    /* this*=2 */
+    pub fn dbl(&mut self) {
+        if CURVETYPE == CurveType::WEIERSTRASS {
+            if rom::CURVE_A == 0 {
+                let mut t0 = FP::new_copy(&self.y);
+                t0.sqr();
+                let mut t1 = FP::new_copy(&self.y);
+                t1.mul(&self.z);
+                let mut t2 = FP::new_copy(&self.z);
+                t2.sqr();
+
+                self.z.copy(&t0);
+                self.z.add(&t0);
+                self.z.norm();
+                self.z.dbl();
+                self.z.dbl();
+                self.z.norm();
+                t2.imul(3 * rom::CURVE_B_I);
+
+                let mut x3 = FP::new_copy(&t2);
+                x3.mul(&self.z);
+
+                let mut y3 = FP::new_copy(&t0);
+                y3.add(&t2);
+                y3.norm();
+                self.z.mul(&t1);
+                t1.copy(&t2);
+                t1.add(&t2);
+                t2.add(&t1);
+                t0.sub(&t2);
+                t0.norm();
+                y3.mul(&t0);
+                y3.add(&x3);
+                t1.copy(&self.x);
+                t1.mul(&self.y);
+                self.x.copy(&t0);
+                self.x.norm();
+                self.x.mul(&t1);
+                self.x.dbl();
+                self.x.norm();
+                self.y.copy(&y3);
+                self.y.norm();
+            } else {
+                let mut t0 = FP::new_copy(&self.x);
+                let mut t1 = FP::new_copy(&self.y);
+                let mut t2 = FP::new_copy(&self.z);
+                let mut t3 = FP::new_copy(&self.x);
+                let mut z3 = FP::new_copy(&self.z);
+                let mut y3 = FP::new();
+                let mut x3 = FP::new();
+                let mut b = FP::new();
+
+                if rom::CURVE_B_I == 0 {
+                    b.copy(&FP::new_big(&BIG::new_ints(&rom::CURVE_B)));
+                }
+
+                t0.sqr(); //1    x^2
+                t1.sqr(); //2    y^2
+                t2.sqr(); //3
+
+                t3.mul(&self.y); //4
+                t3.dbl();
+                t3.norm(); //5
+                z3.mul(&self.x); //6
+                z3.dbl();
+                z3.norm(); //7
+                y3.copy(&t2);
+
+                if rom::CURVE_B_I == 0 {
+                    y3.mul(&b); //8
+                } else {
+                    y3.imul(rom::CURVE_B_I);
+                }
+
+                y3.sub(&z3); //9  ***
+                x3.copy(&y3);
+                x3.add(&y3);
+                x3.norm(); //10
+
+                y3.add(&x3); //11
+                x3.copy(&t1);
+                x3.sub(&y3);
+                x3.norm(); //12
+                y3.add(&t1);
+                y3.norm(); //13
+                y3.mul(&x3); //14
+                x3.mul(&t3); //15
+                t3.copy(&t2);
+                t3.add(&t2); //16
+                t2.add(&t3); //17
+
+                if rom::CURVE_B_I == 0 {
+                    z3.mul(&b); //18
+                } else {
+                    z3.imul(rom::CURVE_B_I);
+                }
+
+                z3.sub(&t2); //19
+                z3.sub(&t0);
+                z3.norm(); //20  ***
+                t3.copy(&z3);
+                t3.add(&z3); //21
+
+                z3.add(&t3);
+                z3.norm(); //22
+                t3.copy(&t0);
+                t3.add(&t0); //23
+                t0.add(&t3); //24
+                t0.sub(&t2);
+                t0.norm(); //25
+
+                t0.mul(&z3); //26
+                y3.add(&t0); //27
+                t0.copy(&self.y);
+                t0.mul(&self.z); //28
+                t0.dbl();
+                t0.norm(); //29
+                z3.mul(&t0); //30
+                x3.sub(&z3); //31
+                t0.dbl();
+                t0.norm(); //32
+                t1.dbl();
+                t1.norm(); //33
+                z3.copy(&t0);
+                z3.mul(&t1); //34
+
+                self.x.copy(&x3);
+                self.x.norm();
+                self.y.copy(&y3);
+                self.y.norm();
+                self.z.copy(&z3);
+                self.z.norm();
+            }
+        }
+        if CURVETYPE == CurveType::EDWARDS {
+            let mut c = FP::new_copy(&self.x);
+            let mut d = FP::new_copy(&self.y);
+            let mut h = FP::new_copy(&self.z);
+            let mut j = FP::new();
+
+            self.x.mul(&self.y);
+            self.x.dbl();
+            self.x.norm();
+            c.sqr();
+            d.sqr();
+            if rom::CURVE_A == -1 {
+                c.neg()
+            }
+            self.y.copy(&c);
+            self.y.add(&d);
+            self.y.norm();
+            h.sqr();
+            h.dbl();
+            self.z.copy(&self.y);
+            j.copy(&self.y);
+            j.sub(&h);
+            j.norm();
+            self.x.mul(&j);
+            c.sub(&d);
+            c.norm();
+            self.y.mul(&c);
+            self.z.mul(&j);
+        }
+        if CURVETYPE == CurveType::MONTGOMERY {
+            let mut a = FP::new_copy(&self.x);
+            let mut b = FP::new_copy(&self.x);
+            let mut aa = FP::new();
+            let mut bb = FP::new();
+            let mut c = FP::new();
+
+            a.add(&self.z);
+            a.norm();
+            aa.copy(&a);
+            aa.sqr();
+            b.sub(&self.z);
+            b.norm();
+            bb.copy(&b);
+            bb.sqr();
+            c.copy(&aa);
+            c.sub(&bb);
+            c.norm();
+
+            self.x.copy(&aa);
+            self.x.mul(&bb);
+
+            a.copy(&c);
+            a.imul((rom::CURVE_A + 2) / 4);
+
+            bb.add(&a);
+            bb.norm();
+            self.z.copy(&bb);
+            self.z.mul(&c);
+        }
+        return;
+    }
+
+    /* self+=Q */
+    pub fn add(&mut self, Q: &ECP) {
+        if CURVETYPE == CurveType::WEIERSTRASS {
+            if rom::CURVE_A == 0 {
+                let b = 3 * rom::CURVE_B_I;
+                let mut t0 = FP::new_copy(&self.x);
+                t0.mul(&Q.x);
+                let mut t1 = FP::new_copy(&self.y);
+                t1.mul(&Q.y);
+                let mut t2 = FP::new_copy(&self.z);
+                t2.mul(&Q.z);
+                let mut t3 = FP::new_copy(&self.x);
+                t3.add(&self.y);
+                t3.norm();
+                let mut t4 = FP::new_copy(&Q.x);
+                t4.add(&Q.y);
+                t4.norm();
+                t3.mul(&t4);
+                t4.copy(&t0);
+                t4.add(&t1);
+
+                t3.sub(&t4);
+                t3.norm();
+                t4.copy(&self.y);
+                t4.add(&self.z);
+                t4.norm();
+                let mut x3 = FP::new_copy(&Q.y);
+                x3.add(&Q.z);
+                x3.norm();
+
+                t4.mul(&x3);
+                x3.copy(&t1);
+                x3.add(&t2);
+
+                t4.sub(&x3);
+                t4.norm();
+                x3.copy(&self.x);
+                x3.add(&self.z);
+                x3.norm();
+                let mut y3 = FP::new_copy(&Q.x);
+                y3.add(&Q.z);
+                y3.norm();
+                x3.mul(&y3);
+                y3.copy(&t0);
+                y3.add(&t2);
+                y3.rsub(&x3);
+                y3.norm();
+                x3.copy(&t0);
+                x3.add(&t0);
+                t0.add(&x3);
+                t0.norm();
+                t2.imul(b);
+
+                let mut z3 = FP::new_copy(&t1);
+                z3.add(&t2);
+                z3.norm();
+                t1.sub(&t2);
+                t1.norm();
+                y3.imul(b);
+
+                x3.copy(&y3);
+                x3.mul(&t4);
+                t2.copy(&t3);
+                t2.mul(&t1);
+                x3.rsub(&t2);
+                y3.mul(&t0);
+                t1.mul(&z3);
+                y3.add(&t1);
+                t0.mul(&t3);
+                z3.mul(&t4);
+                z3.add(&t0);
+
+                self.x.copy(&x3);
+                self.x.norm();
+                self.y.copy(&y3);
+                self.y.norm();
+                self.z.copy(&z3);
+                self.z.norm();
+            } else {
+                let mut t0 = FP::new_copy(&self.x);
+                let mut t1 = FP::new_copy(&self.y);
+                let mut t2 = FP::new_copy(&self.z);
+                let mut t3 = FP::new_copy(&self.x);
+                let mut t4 = FP::new_copy(&Q.x);
+                let mut z3 = FP::new();
+                let mut y3 = FP::new_copy(&Q.x);
+                let mut x3 = FP::new_copy(&Q.y);
+                let mut b = FP::new();
+
+                if rom::CURVE_B_I == 0 {
+                    b.copy(&FP::new_big(&BIG::new_ints(&rom::CURVE_B)));
+                }
+
+                t0.mul(&Q.x); //1
+                t1.mul(&Q.y); //2
+                t2.mul(&Q.z); //3
+
+                t3.add(&self.y);
+                t3.norm(); //4
+                t4.add(&Q.y);
+                t4.norm(); //5
+                t3.mul(&t4); //6
+                t4.copy(&t0);
+                t4.add(&t1); //7
+                t3.sub(&t4);
+                t3.norm(); //8
+                t4.copy(&self.y);
+                t4.add(&self.z);
+                t4.norm(); //9
+                x3.add(&Q.z);
+                x3.norm(); //10
+                t4.mul(&x3); //11
+                x3.copy(&t1);
+                x3.add(&t2); //12
+
+                t4.sub(&x3);
+                t4.norm(); //13
+                x3.copy(&self.x);
+                x3.add(&self.z);
+                x3.norm(); //14
+                y3.add(&Q.z);
+                y3.norm(); //15
+
+                x3.mul(&y3); //16
+                y3.copy(&t0);
+                y3.add(&t2); //17
+
+                y3.rsub(&x3);
+                y3.norm(); //18
+                z3.copy(&t2);
+
+                if rom::CURVE_B_I == 0 {
+                    z3.mul(&b); //18
+                } else {
+                    z3.imul(rom::CURVE_B_I);
+                }
+
+                x3.copy(&y3);
+                x3.sub(&z3);
+                x3.norm(); //20
+                z3.copy(&x3);
+                z3.add(&x3); //21
+
+                x3.add(&z3); //22
+                z3.copy(&t1);
+                z3.sub(&x3);
+                z3.norm(); //23
+                x3.add(&t1);
+                x3.norm(); //24
+
+                if rom::CURVE_B_I == 0 {
+                    y3.mul(&b); //18
+                } else {
+                    y3.imul(rom::CURVE_B_I);
+                }
+
+                t1.copy(&t2);
+                t1.add(&t2); //t1.norm();//26
+                t2.add(&t1); //27
+
+                y3.sub(&t2); //28
+
+                y3.sub(&t0);
+                y3.norm(); //29
+                t1.copy(&y3);
+                t1.add(&y3); //30
+                y3.add(&t1);
+                y3.norm(); //31
+
+                t1.copy(&t0);
+                t1.add(&t0); //32
+                t0.add(&t1); //33
+                t0.sub(&t2);
+                t0.norm(); //34
+                t1.copy(&t4);
+                t1.mul(&y3); //35
+                t2.copy(&t0);
+                t2.mul(&y3); //36
+                y3.copy(&x3);
+                y3.mul(&z3); //37
+                y3.add(&t2); //y3.norm();//38
+                x3.mul(&t3); //39
+                x3.sub(&t1); //40
+                z3.mul(&t4); //41
+                t1.copy(&t3);
+                t1.mul(&t0); //42
+                z3.add(&t1);
+                self.x.copy(&x3);
+                self.x.norm();
+                self.y.copy(&y3);
+                self.y.norm();
+                self.z.copy(&z3);
+                self.z.norm();
+            }
+        }
+        if CURVETYPE == CurveType::EDWARDS {
+            let bb = FP::new_big(&BIG::new_ints(&rom::CURVE_B));
+            let mut a = FP::new_copy(&self.z);
+            let mut b = FP::new();
+            let mut c = FP::new_copy(&self.x);
+            let mut d = FP::new_copy(&self.y);
+            let mut e = FP::new();
+            let mut f = FP::new();
+            let mut g = FP::new();
+
+            a.mul(&Q.z);
+            b.copy(&a);
+            b.sqr();
+            c.mul(&Q.x);
+            d.mul(&Q.y);
+
+            e.copy(&c);
+            e.mul(&d);
+            e.mul(&bb);
+            f.copy(&b);
+            f.sub(&e);
+            g.copy(&b);
+            g.add(&e);
+
+            if rom::CURVE_A == 1 {
+                e.copy(&d);
+                e.sub(&c);
+            }
+            c.add(&d);
+
+            b.copy(&self.x);
+            b.add(&self.y);
+            d.copy(&Q.x);
+            d.add(&Q.y);
+            b.norm();
+            d.norm();
+            b.mul(&d);
+            b.sub(&c);
+            b.norm();
+            f.norm();
+            b.mul(&f);
+            self.x.copy(&a);
+            self.x.mul(&b);
+            g.norm();
+            if rom::CURVE_A == 1 {
+                e.norm();
+                c.copy(&e);
+                c.mul(&g);
+            }
+            if rom::CURVE_A == -1 {
+                c.norm();
+                c.mul(&g);
+            }
+            self.y.copy(&a);
+            self.y.mul(&c);
+            self.z.copy(&f);
+            self.z.mul(&g);
+        }
+        return;
+    }
+
+    /* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
+    pub fn dadd(&mut self, Q: &ECP, W: &ECP) {
+        let mut a = FP::new_copy(&self.x);
+        let mut b = FP::new_copy(&self.x);
+        let mut c = FP::new_copy(&Q.x);
+        let mut d = FP::new_copy(&Q.x);
+        let mut da = FP::new();
+        let mut cb = FP::new();
+
+        a.add(&self.z);
+        b.sub(&self.z);
+
+        c.add(&Q.z);
+        d.sub(&Q.z);
+
+        a.norm();
+        d.norm();
+
+        da.copy(&d);
+        da.mul(&a);
+
+        c.norm();
+        b.norm();
+
+        cb.copy(&c);
+        cb.mul(&b);
+
+        a.copy(&da);
+        a.add(&cb);
+        a.norm();
+        a.sqr();
+        b.copy(&da);
+        b.sub(&cb);
+        b.norm();
+        b.sqr();
+
+        self.x.copy(&a);
+        self.z.copy(&W.x);
+        self.z.mul(&b);
+    }
+
+    /* self-=Q */
+    pub fn sub(&mut self, Q: &ECP) {
+        let mut NQ = ECP::new();
+        NQ.copy(Q);
+        NQ.neg();
+        self.add(&NQ);
+    }
+
+    /* constant time multiply by small integer of length bts - use ladder */
+    pub fn pinmul(&self, e: i32, bts: i32) -> ECP {
+        if CURVETYPE == CurveType::MONTGOMERY {
+            return self.mul(&mut BIG::new_int(e as isize));
+        } else {
+            let mut P = ECP::new();
+            let mut R0 = ECP::new();
+            let mut R1 = ECP::new();
+            R1.copy(&self);
+
+            for i in (0..bts).rev() {
+                let b = ((e >> i) & 1) as isize;
+                P.copy(&R1);
+                P.add(&mut R0);
+                R0.cswap(&mut R1, b);
+                R1.copy(&P);
+                R0.dbl();
+                R0.cswap(&mut R1, b);
+            }
+            P.copy(&R0);
+            P.affine();
+            return P;
+        }
+    }
+
+    /* return e.self */
+
+    pub fn mul(&self, e: &BIG) -> ECP {
+        if e.iszilch() || self.is_infinity() {
+            return ECP::new();
+        }
+        let mut P = ECP::new();
+        if CURVETYPE == CurveType::MONTGOMERY {
+            /* use Ladder */
+            let mut D = ECP::new();
+            let mut R0 = ECP::new();
+            R0.copy(&self);
+            let mut R1 = ECP::new();
+            R1.copy(&self);
+            R1.dbl();
+            D.copy(&self);
+            D.affine();
+            let nb = e.nbits();
+
+            for i in (0..nb - 1).rev() {
+                let b = e.bit(i);
+                P.copy(&R1);
+                P.dadd(&mut R0, &D);
+                R0.cswap(&mut R1, b);
+                R1.copy(&P);
+                R0.dbl();
+                R0.cswap(&mut R1, b);
+            }
+            P.copy(&R0)
+        } else {
+            // fixed size windows
+            let mut mt = BIG::new();
+            let mut t = BIG::new();
+            let mut Q = ECP::new();
+            let mut C = ECP::new();
+
+            let mut W: [ECP; 8] = [
+                ECP::new(),
+                ECP::new(),
+                ECP::new(),
+                ECP::new(),
+                ECP::new(),
+                ECP::new(),
+                ECP::new(),
+                ECP::new(),
+            ];
+
+            const CT: usize = 1 + (big::NLEN * (big::BASEBITS as usize) + 3) / 4;
+            let mut w: [i8; CT] = [0; CT];
+
+            Q.copy(&self);
+            Q.dbl();
+
+            W[0].copy(&self);
+
+            for i in 1..8 {
+                C.copy(&W[i - 1]);
+                W[i].copy(&C);
+                W[i].add(&mut Q);
+            }
+
+            // make exponent odd - add 2P if even, P if odd
+            t.copy(&e);
+            let s = t.parity();
+            t.inc(1);
+            t.norm();
+            let ns = t.parity();
+            mt.copy(&t);
+            mt.inc(1);
+            mt.norm();
+            t.cmove(&mt, s);
+            Q.cmove(&self, ns);
+            C.copy(&Q);
+
+            let nb = 1 + (t.nbits() + 3) / 4;
+
+            // convert exponent to signed 4-bit window
+            for i in 0..nb {
+                w[i] = (t.lastbits(5) - 16) as i8;
+                t.dec(w[i] as isize);
+                t.norm();
+                t.fshr(4);
+            }
+            w[nb] = t.lastbits(5) as i8;
+
+            P.copy(&W[((w[nb] as usize) - 1) / 2]);
+            for i in (0..nb).rev() {
+                Q.selector(&W, w[i] as i32);
+                P.dbl();
+                P.dbl();
+                P.dbl();
+                P.dbl();
+                P.add(&mut Q);
+            }
+            P.sub(&mut C); /* apply correction */
+        }
+        P.affine();
+        return P;
+    }
+
+    /* Return e.this+f.Q */
+
+    pub fn mul2(&self, e: &BIG, Q: &ECP, f: &BIG) -> ECP {
+        let mut te = BIG::new();
+        let mut tf = BIG::new();
+        let mut mt = BIG::new();
+        let mut S = ECP::new();
+        let mut T = ECP::new();
+        let mut C = ECP::new();
+
+        let mut W: [ECP; 8] = [
+            ECP::new(),
+            ECP::new(),
+            ECP::new(),
+            ECP::new(),
+            ECP::new(),
+            ECP::new(),
+            ECP::new(),
+            ECP::new(),
+        ];
+
+        const CT: usize = 1 + (big::NLEN * (big::BASEBITS as usize) + 1) / 2;
+        let mut w: [i8; CT] = [0; CT];
+
+        te.copy(e);
+        tf.copy(f);
+
+        // precompute table
+
+        W[1].copy(&self);
+        W[1].sub(Q);
+        W[2].copy(&self);
+        W[2].add(Q);
+        S.copy(&Q);
+        S.dbl();
+        C.copy(&W[1]);
+        W[0].copy(&C);
+        W[0].sub(&mut S); // copy to C is stupid Rust thing..
+        C.copy(&W[2]);
+        W[3].copy(&C);
+        W[3].add(&mut S);
+        T.copy(&self);
+        T.dbl();
+        C.copy(&W[1]);
+        W[5].copy(&C);
+        W[5].add(&mut T);
+        C.copy(&W[2]);
+        W[6].copy(&C);
+        W[6].add(&mut T);
+        C.copy(&W[5]);
+        W[4].copy(&C);
+        W[4].sub(&mut S);
+        C.copy(&W[6]);
+        W[7].copy(&C);
+        W[7].add(&mut S);
+
+        // if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction
+
+        let mut s = te.parity();
+        te.inc(1);
+        te.norm();
+        let mut ns = te.parity();
+        mt.copy(&te);
+        mt.inc(1);
+        mt.norm();
+        te.cmove(&mt, s);
+        T.cmove(&self, ns);
+        C.copy(&T);
+
+        s = tf.parity();
+        tf.inc(1);
+        tf.norm();
+        ns = tf.parity();
+        mt.copy(&tf);
+        mt.inc(1);
+        mt.norm();
+        tf.cmove(&mt, s);
+        S.cmove(&Q, ns);
+        C.add(&mut S);
+
+        mt.copy(&te);
+        mt.add(&tf);
+        mt.norm();
+        let nb = 1 + (mt.nbits() + 1) / 2;
+
+        // convert exponent to signed 2-bit window
+        for i in 0..nb {
+            let a = te.lastbits(3) - 4;
+            te.dec(a);
+            te.norm();
+            te.fshr(2);
+            let b = tf.lastbits(3) - 4;
+            tf.dec(b);
+            tf.norm();
+            tf.fshr(2);
+            w[i] = (4 * a + b) as i8;
+        }
+        w[nb] = (4 * te.lastbits(3) + tf.lastbits(3)) as i8;
+        S.copy(&W[((w[nb] as usize) - 1) / 2]);
+
+        for i in (0..nb).rev() {
+            T.selector(&W, w[i] as i32);
+            S.dbl();
+            S.dbl();
+            S.add(&mut T);
+        }
+        S.sub(&mut C); /* apply correction */
+        S.affine();
+        return S;
+    }
+
+    // Multiply itself by cofactor of the curve
+    pub fn cfp(&mut self) {
+        let cf = rom::CURVE_COF_I;
+        if cf == 1 {
+            return;
+        }
+        if cf == 4 {
+            self.dbl();
+            self.dbl();
+            return;
+        }
+        if cf == 8 {
+            self.dbl();
+            self.dbl();
+            self.dbl();
+            return;
+        }
+        let c = BIG::new_ints(&rom::CURVE_COF);
+        let P = self.mul(&c);
+        self.copy(&P);
+    }
+
+    // Map a given byte slice to a point on the curve. The byte slice should be atleast the size of the modulus
+    #[allow(non_snake_case)]
+    pub fn mapit(h: &[u8]) -> ECP {
+        let mut q = BIG::new_ints(&rom::MODULUS);
+        let mut x = BIG::frombytes(h);
+        x.rmod(&mut q);
+        let mut P: ECP;
+
+        loop {
+            loop {
+                if CURVETYPE != CurveType::MONTGOMERY {
+                    P = ECP::new_bigint(&x, 0);
+                } else {
+                    P = ECP::new_big(&x);
+                }
+                x.inc(1);
+                x.norm();
+                if !P.is_infinity() {
+                    break;
+                }
+            }
+            P.cfp();
+            if !P.is_infinity() {
+                break;
+            }
+        }
+
+        return P;
+    }
+
+    pub fn generator() -> ECP {
+        let G: ECP;
+
+        let gx = BIG::new_ints(&rom::CURVE_GX);
+
+        if CURVETYPE != CurveType::MONTGOMERY {
+            let gy = BIG::new_ints(&rom::CURVE_GY);
+            G = ECP::new_bigs(&gx, &gy);
+        } else {
+            G = ECP::new_big(&gx);
+        }
+        return G;
+    }
+}
diff --git a/src/ecp2.rs b/src/ecp2.rs
new file mode 100644
index 0000000..afd9376
--- /dev/null
+++ b/src/ecp2.rs
@@ -0,0 +1,784 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use super::rom;
+use super::big;
+use super::ecp;
+use super::fp2::FP2;
+use super::big::BIG;
+use types::{SexticTwist, CurvePairingType, SignOfX};
+use std::str::SplitWhitespace;
+use std::fmt;
+
+#[derive(Copy, Clone)]
+pub struct ECP2 {
+    x: FP2,
+    y: FP2,
+    z: FP2,
+}
+
+impl PartialEq for ECP2 {
+	fn eq(&self, other: &ECP2) -> bool {
+		self.equals(other)
+	}
+}
+
+impl fmt::Display for ECP2 {
+	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+		write!(f, "ECP2: [ {}, {}, {} ]", self.x, self.y, self.z)
+	}
+}
+
+impl fmt::Debug for ECP2 {
+	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+		write!(f, "ECP2: [ {}, {}, {} ]", self.x, self.y, self.z)
+	}
+}
+
+#[allow(non_snake_case)]
+impl ECP2 {
+    pub fn new() -> ECP2 {
+        ECP2 {
+            x: FP2::new(),
+            y: FP2::new_int(1),
+            z: FP2::new(),
+        }
+    }
+    #[allow(non_snake_case)]
+    /* construct this from (x,y) - but set to O if not on curve */
+    pub fn new_fp2s(ix: &FP2, iy: &FP2) -> ECP2 {
+        let mut E = ECP2::new();
+        E.x.copy(&ix);
+        E.y.copy(&iy);
+        E.z.one();
+        E.x.norm();
+
+        let mut rhs = ECP2::rhs(&E.x);
+        let mut y2 = FP2::new_copy(&E.y);
+        y2.sqr();
+        if !y2.equals(&mut rhs) {
+            E.inf();
+        }
+        return E;
+    }
+
+    /* construct this from x - but set to O if not on curve */
+    pub fn new_fp2(ix: &FP2) -> ECP2 {
+        let mut E = ECP2::new();
+        E.x.copy(&ix);
+        E.y.one();
+        E.z.one();
+        E.x.norm();
+        let mut rhs = ECP2::rhs(&E.x);
+        if rhs.sqrt() {
+            E.y.copy(&rhs);
+        } else {
+            E.inf();
+        }
+        return E;
+    }
+
+    /* Test this=O? */
+    pub fn is_infinity(&self) -> bool {
+        self.x.iszilch() && self.z.iszilch()
+    }
+
+    /* copy self=P */
+    pub fn copy(&mut self, P: &ECP2) {
+        self.x.copy(&P.x);
+        self.y.copy(&P.y);
+        self.z.copy(&P.z);
+    }
+
+    /* set self=O */
+    pub fn inf(&mut self) {
+        self.x.zero();
+        self.y.one();
+        self.z.zero();
+    }
+
+    /* set self=-self */
+    pub fn neg(&mut self) {
+        self.y.norm();
+        self.y.neg();
+        self.y.norm();
+    }
+
+    /* Conditional move of Q to self dependant on d */
+    pub fn cmove(&mut self, Q: &ECP2, d: isize) {
+        self.x.cmove(&Q.x, d);
+        self.y.cmove(&Q.y, d);
+        self.z.cmove(&Q.z, d);
+    }
+
+    /* return 1 if b==c, no branching */
+    fn teq(b: i32, c: i32) -> isize {
+        let mut x = b ^ c;
+        x -= 1; // if x=0, x now -1
+        return ((x >> 31) & 1) as isize;
+    }
+
+    /* Constant time select from pre-computed table */
+    pub fn selector(&mut self, W: &[ECP2], b: i32) {
+        let mut MP = ECP2::new();
+        let m = b >> 31;
+        let mut babs = (b ^ m) - m;
+
+        babs = (babs - 1) / 2;
+
+        self.cmove(&W[0], ECP2::teq(babs, 0)); // conditional move
+        self.cmove(&W[1], ECP2::teq(babs, 1));
+        self.cmove(&W[2], ECP2::teq(babs, 2));
+        self.cmove(&W[3], ECP2::teq(babs, 3));
+        self.cmove(&W[4], ECP2::teq(babs, 4));
+        self.cmove(&W[5], ECP2::teq(babs, 5));
+        self.cmove(&W[6], ECP2::teq(babs, 6));
+        self.cmove(&W[7], ECP2::teq(babs, 7));
+
+        MP.copy(self);
+        MP.neg();
+        self.cmove(&MP, (m & 1) as isize);
+    }
+
+    /* Test if P == Q */
+    pub fn equals(&self, Q: &ECP2) -> bool {
+        let mut a = FP2::new_copy(&self.x);
+        let mut b = FP2::new_copy(&Q.x);
+
+        a.mul(&Q.z);
+        b.mul(&self.z);
+        if !a.equals(&mut b) {
+            return false;
+        }
+        a.copy(&self.y);
+        a.mul(&Q.z);
+        b.copy(&Q.y);
+        b.mul(&self.z);
+        if !a.equals(&mut b) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /* set to Affine - (x,y,z) to (x,y) */
+    pub fn affine(&mut self) {
+        if self.is_infinity() {
+            return;
+        }
+        let mut one = FP2::new_int(1);
+        if self.z.equals(&mut one) {
+            return;
+        }
+        self.z.inverse();
+
+        self.x.mul(&self.z);
+        self.x.reduce();
+        self.y.mul(&self.z);
+        self.y.reduce();
+        self.z.copy(&one);
+    }
+
+    /* extract affine x as FP2 */
+    pub fn getx(&self) -> FP2 {
+        let mut W = ECP2::new();
+        W.copy(self);
+        W.affine();
+        return FP2::new_copy(&W.x);
+    }
+
+    /* extract affine y as FP2 */
+    pub fn gety(&self) -> FP2 {
+        let mut W = ECP2::new();
+        W.copy(self);
+        W.affine();
+        return FP2::new_copy(&W.y);
+    }
+
+    /* extract projective x */
+    pub fn getpx(&self) -> FP2 {
+        return FP2::new_copy(&self.x);
+    }
+    /* extract projective y */
+    pub fn getpy(&self) -> FP2 {
+        return FP2::new_copy(&self.y);
+    }
+    /* extract projective z */
+    pub fn getpz(&self) -> FP2 {
+        return FP2::new_copy(&self.z);
+    }
+
+    /* convert to byte array */
+    pub fn tobytes(&self, b: &mut [u8]) {
+        let mut t: [u8; big::MODBYTES as usize] = [0; big::MODBYTES as usize];
+        let mb = big::MODBYTES as usize;
+        let mut W = ECP2::new();
+        W.copy(self);
+
+        W.affine();
+        W.x.geta().tobytes(&mut t);
+        for i in 0..mb {
+            b[i] = t[i]
+        }
+        W.x.getb().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + mb] = t[i]
+        }
+
+        W.y.geta().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 2 * mb] = t[i]
+        }
+        W.y.getb().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 3 * mb] = t[i]
+        }
+    }
+
+    /* convert from byte array to point */
+    pub fn frombytes(b: &[u8]) -> ECP2 {
+        let mut t: [u8; big::MODBYTES as usize] = [0; big::MODBYTES as usize];
+        let mb = big::MODBYTES as usize;
+
+        for i in 0..mb {
+            t[i] = b[i]
+        }
+        let mut ra = BIG::frombytes(&t);
+        for i in 0..mb {
+            t[i] = b[i + mb]
+        }
+        let mut rb = BIG::frombytes(&t);
+        let rx = FP2::new_bigs(&ra, &rb);
+
+        for i in 0..mb {
+            t[i] = b[i + 2 * mb]
+        }
+        ra.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = b[i + 3 * mb]
+        }
+        rb.copy(&BIG::frombytes(&t));
+        let ry = FP2::new_bigs(&ra, &rb);
+
+        return ECP2::new_fp2s(&rx, &ry);
+    }
+
+    /* convert this to hex string */
+    pub fn tostring(&self) -> String {
+        let mut W = ECP2::new();
+        W.copy(self);
+        W.affine();
+        if W.is_infinity() {
+            return String::from("infinity");
+        }
+        return format!("({},{})", W.x.tostring(), W.y.tostring());
+    }
+
+    pub fn to_hex(&self) -> String {
+        format!("{} {} {}", self.x.to_hex(), self.y.to_hex(), self.z.to_hex())
+    }
+
+    pub fn from_hex_iter(iter: &mut SplitWhitespace) -> ECP2 {
+        ECP2 {
+            x: FP2::from_hex_iter(iter),
+            y: FP2::from_hex_iter(iter),
+            z: FP2::from_hex_iter(iter)
+        }
+    }
+
+    pub fn from_hex(val: String) -> ECP2 {
+        let mut iter = val.split_whitespace();
+        return ECP2::from_hex_iter(&mut iter);
+    }
+
+    /* Calculate RHS of twisted curve equation x^3+B/i */
+    pub fn rhs(x: &FP2) -> FP2 {
+        let mut r = FP2::new_copy(x);
+        r.sqr();
+        let mut b = FP2::new_big(&BIG::new_ints(&rom::CURVE_B));
+        if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+            b.div_ip();
+        }
+        if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+            b.norm();
+            b.mul_ip();
+            b.norm();
+        }
+
+        r.mul(x);
+        r.add(&b);
+
+        r.reduce();
+        return r;
+    }
+
+    /* self+=self */
+    pub fn dbl(&mut self) -> isize {
+        let mut iy = FP2::new_copy(&self.y);
+        if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+            iy.mul_ip();
+            iy.norm();
+        }
+
+        let mut t0 = FP2::new_copy(&self.y); //***** Change
+        t0.sqr();
+        if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+            t0.mul_ip();
+        }
+        let mut t1 = FP2::new_copy(&iy);
+        t1.mul(&self.z);
+        let mut t2 = FP2::new_copy(&self.z);
+        t2.sqr();
+
+        self.z.copy(&t0);
+        self.z.add(&t0);
+        self.z.norm();
+        self.z.dbl();
+        self.z.dbl();
+        self.z.norm();
+
+        t2.imul(3 * rom::CURVE_B_I);
+        if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+            t2.mul_ip();
+            t2.norm();
+        }
+        let mut x3 = FP2::new_copy(&t2);
+        x3.mul(&self.z);
+
+        let mut y3 = FP2::new_copy(&t0);
+
+        y3.add(&t2);
+        y3.norm();
+        self.z.mul(&t1);
+        t1.copy(&t2);
+        t1.add(&t2);
+        t2.add(&t1);
+        t2.norm();
+        t0.sub(&t2);
+        t0.norm(); //y^2-9bz^2
+        y3.mul(&t0);
+        y3.add(&x3); //(y^2+3z*2)(y^2-9z^2)+3b.z^2.8y^2
+        t1.copy(&self.x);
+        t1.mul(&iy); //
+        self.x.copy(&t0);
+        self.x.norm();
+        self.x.mul(&t1);
+        self.x.dbl(); //(y^2-9bz^2)xy2
+
+        self.x.norm();
+        self.y.copy(&y3);
+        self.y.norm();
+
+        return 1;
+    }
+
+    /* self+=Q - return 0 for add, 1 for double, -1 for O */
+    pub fn add(&mut self, Q: &ECP2) -> isize {
+        let b = 3 * rom::CURVE_B_I;
+        let mut t0 = FP2::new_copy(&self.x);
+        t0.mul(&Q.x); // x.Q.x
+        let mut t1 = FP2::new_copy(&self.y);
+        t1.mul(&Q.y); // y.Q.y
+
+        let mut t2 = FP2::new_copy(&self.z);
+        t2.mul(&Q.z);
+        let mut t3 = FP2::new_copy(&self.x);
+        t3.add(&self.y);
+        t3.norm(); //t3=X1+Y1
+        let mut t4 = FP2::new_copy(&Q.x);
+        t4.add(&Q.y);
+        t4.norm(); //t4=X2+Y2
+        t3.mul(&t4); //t3=(X1+Y1)(X2+Y2)
+        t4.copy(&t0);
+        t4.add(&t1); //t4=X1.X2+Y1.Y2
+
+        t3.sub(&t4);
+        t3.norm();
+        if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+            t3.mul_ip();
+            t3.norm(); //t3=(X1+Y1)(X2+Y2)-(X1.X2+Y1.Y2) = X1.Y2+X2.Y1
+        }
+        t4.copy(&self.y);
+        t4.add(&self.z);
+        t4.norm(); //t4=Y1+Z1
+        let mut x3 = FP2::new_copy(&Q.y);
+        x3.add(&Q.z);
+        x3.norm(); //x3=Y2+Z2
+
+        t4.mul(&x3); //t4=(Y1+Z1)(Y2+Z2)
+        x3.copy(&t1); //
+        x3.add(&t2); //X3=Y1.Y2+Z1.Z2
+
+        t4.sub(&x3);
+        t4.norm();
+        if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+            t4.mul_ip();
+            t4.norm(); //t4=(Y1+Z1)(Y2+Z2) - (Y1.Y2+Z1.Z2) = Y1.Z2+Y2.Z1
+        }
+        x3.copy(&self.x);
+        x3.add(&self.z);
+        x3.norm(); // x3=X1+Z1
+        let mut y3 = FP2::new_copy(&Q.x);
+        y3.add(&Q.z);
+        y3.norm(); // y3=X2+Z2
+        x3.mul(&y3); // x3=(X1+Z1)(X2+Z2)
+        y3.copy(&t0);
+        y3.add(&t2); // y3=X1.X2+Z1+Z2
+        y3.rsub(&x3);
+        y3.norm(); // y3=(X1+Z1)(X2+Z2) - (X1.X2+Z1.Z2) = X1.Z2+X2.Z1
+
+        if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+            t0.mul_ip();
+            t0.norm(); // x.Q.x
+            t1.mul_ip();
+            t1.norm(); // y.Q.y
+        }
+        x3.copy(&t0);
+        x3.add(&t0);
+        t0.add(&x3);
+        t0.norm();
+        t2.imul(b);
+        if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+            t2.mul_ip();
+            t2.norm();
+        }
+        let mut z3 = FP2::new_copy(&t1);
+        z3.add(&t2);
+        z3.norm();
+        t1.sub(&t2);
+        t1.norm();
+        y3.imul(b);
+        if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+            y3.mul_ip();
+            y3.norm();
+        }
+        x3.copy(&y3);
+        x3.mul(&t4);
+        t2.copy(&t3);
+        t2.mul(&t1);
+        x3.rsub(&t2);
+        y3.mul(&t0);
+        t1.mul(&z3);
+        y3.add(&t1);
+        t0.mul(&t3);
+        z3.mul(&t4);
+        z3.add(&t0);
+
+        self.x.copy(&x3);
+        self.x.norm();
+        self.y.copy(&y3);
+        self.y.norm();
+        self.z.copy(&z3);
+        self.z.norm();
+
+        return 0;
+    }
+
+    /* set this-=Q */
+    pub fn sub(&mut self, Q: &ECP2) -> isize {
+        let mut NQ = ECP2::new();
+        NQ.copy(Q);
+        NQ.neg();
+        let d = self.add(&NQ);
+        return d;
+    }
+
+    /* set this*=q, where q is Modulus, using Frobenius */
+    pub fn frob(&mut self, x: &FP2) {
+        let mut x2 = FP2::new_copy(x);
+        x2.sqr();
+        self.x.conj();
+        self.y.conj();
+        self.z.conj();
+        self.z.reduce();
+        self.x.mul(&x2);
+        self.y.mul(&x2);
+        self.y.mul(x);
+    }
+
+    /* self*=e */
+    pub fn mul(&self, e: &BIG) -> ECP2 {
+        /* fixed size windows */
+        let mut mt = BIG::new();
+        let mut t = BIG::new();
+        let mut P = ECP2::new();
+        let mut Q = ECP2::new();
+        let mut C = ECP2::new();
+
+        if self.is_infinity() {
+            return P;
+        }
+
+        let mut W: [ECP2; 8] = [
+            ECP2::new(),
+            ECP2::new(),
+            ECP2::new(),
+            ECP2::new(),
+            ECP2::new(),
+            ECP2::new(),
+            ECP2::new(),
+            ECP2::new(),
+        ];
+
+        const CT: usize = 1 + (big::NLEN * (big::BASEBITS as usize) + 3) / 4;
+        let mut w: [i8; CT] = [0; CT];
+
+        /* precompute table */
+        Q.copy(&self);
+        Q.dbl();
+
+        W[0].copy(&self);
+
+        for i in 1..8 {
+            C.copy(&W[i - 1]);
+            W[i].copy(&C);
+            W[i].add(&mut Q);
+        }
+
+        /* make exponent odd - add 2P if even, P if odd */
+        t.copy(&e);
+        let s = t.parity();
+        t.inc(1);
+        t.norm();
+        let ns = t.parity();
+        mt.copy(&t);
+        mt.inc(1);
+        mt.norm();
+        t.cmove(&mt, s);
+        Q.cmove(&self, ns);
+        C.copy(&Q);
+
+        let nb = 1 + (t.nbits() + 3) / 4;
+
+        /* convert exponent to signed 4-bit window */
+        for i in 0..nb {
+            w[i] = (t.lastbits(5) - 16) as i8;
+            t.dec(w[i] as isize);
+            t.norm();
+            t.fshr(4);
+        }
+        w[nb] = (t.lastbits(5)) as i8;
+
+        P.copy(&W[((w[nb] as usize) - 1) / 2]);
+        for i in (0..nb).rev() {
+            Q.selector(&W, w[i] as i32);
+            P.dbl();
+            P.dbl();
+            P.dbl();
+            P.dbl();
+            P.add(&mut Q);
+        }
+        P.sub(&mut C);
+        P.affine();
+        return P;
+    }
+
+    /* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3 */
+    // Bos & Costello https://eprint.iacr.org/2013/458.pdf
+    // Faz-Hernandez & Longa & Sanchez  https://eprint.iacr.org/2013/158.pdf
+    // Side channel attack secure
+
+    pub fn mul4(Q: &mut [ECP2], u: &[BIG]) -> ECP2 {
+        let mut W = ECP2::new();
+        let mut P = ECP2::new();
+
+        let mut T: [ECP2; 8] = [
+            ECP2::new(),
+            ECP2::new(),
+            ECP2::new(),
+            ECP2::new(),
+            ECP2::new(),
+            ECP2::new(),
+            ECP2::new(),
+            ECP2::new(),
+        ];
+
+        let mut mt = BIG::new();
+
+        let mut t: [BIG; 4] = [
+            BIG::new_copy(&u[0]),
+            BIG::new_copy(&u[1]),
+            BIG::new_copy(&u[2]),
+            BIG::new_copy(&u[3]),
+        ];
+
+        const CT: usize = 1 + big::NLEN * (big::BASEBITS as usize);
+        let mut w: [i8; CT] = [0; CT];
+        let mut s: [i8; CT] = [0; CT];
+
+        for i in 0..4 {
+            t[i].norm();
+        }
+
+        T[0].copy(&Q[0]);
+        W.copy(&T[0]);
+        T[1].copy(&W);
+        T[1].add(&mut Q[1]); // Q[0]+Q[1]
+        T[2].copy(&W);
+        T[2].add(&mut Q[2]);
+        W.copy(&T[1]); // Q[0]+Q[2]
+        T[3].copy(&W);
+        T[3].add(&mut Q[2]);
+        W.copy(&T[0]); // Q[0]+Q[1]+Q[2]
+        T[4].copy(&W);
+        T[4].add(&mut Q[3]);
+        W.copy(&T[1]); // Q[0]+Q[3]
+        T[5].copy(&W);
+        T[5].add(&mut Q[3]);
+        W.copy(&T[2]); // Q[0]+Q[1]+Q[3]
+        T[6].copy(&W);
+        T[6].add(&mut Q[3]);
+        W.copy(&T[3]); // Q[0]+Q[2]+Q[3]
+        T[7].copy(&W);
+        T[7].add(&mut Q[3]); // Q[0]+Q[1]+Q[2]+Q[3]
+
+        // Make it odd
+        let pb = 1 - t[0].parity();
+        t[0].inc(pb);
+        t[0].norm();
+
+        // Number of bits
+        mt.zero();
+        for i in 0..4 {
+            mt.or(&t[i]);
+        }
+
+        let nb = 1 + mt.nbits();
+
+        // Sign pivot
+
+        s[nb - 1] = 1;
+        for i in 0..nb - 1 {
+            t[0].fshr(1);
+            s[i] = (2 * t[0].parity() - 1) as i8;
+        }
+
+        // Recoded exponent
+        for i in 0..nb {
+            w[i] = 0;
+            let mut k = 1;
+            for j in 1..4 {
+                let bt = s[i] * (t[j].parity() as i8);
+                t[j].fshr(1);
+                t[j].dec((bt >> 1) as isize);
+                t[j].norm();
+                w[i] += bt * (k as i8);
+                k = 2 * k;
+            }
+        }
+
+        // Main loop
+        P.selector(&T, (2 * w[nb - 1] + 1) as i32);
+        for i in (0..nb - 1).rev() {
+            P.dbl();
+            W.selector(&T, (2 * w[i] + s[i]) as i32);
+            P.add(&mut W);
+        }
+
+        // apply correction
+        W.copy(&P);
+        W.sub(&mut Q[0]);
+        P.cmove(&W, pb);
+        P.affine();
+
+        return P;
+    }
+
+    #[allow(non_snake_case)]
+    pub fn mapit(h: &[u8]) -> ECP2 {
+        let mut q = BIG::new_ints(&rom::MODULUS);
+        let mut x = BIG::frombytes(h);
+        x.rmod(&mut q);
+        let mut Q: ECP2;
+        let one = BIG::new_int(1);
+
+        loop {
+            let X = FP2::new_bigs(&one, &x);
+            Q = ECP2::new_fp2(&X);
+            if !Q.is_infinity() {
+                break;
+            }
+            x.inc(1);
+            x.norm();
+        }
+        let mut X = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
+        if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+            X.inverse();
+            X.norm();
+        }
+        x = BIG::new_ints(&rom::CURVE_BNX);
+
+        if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BN {
+            let mut T = Q.mul(&mut x);
+            if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+                T.neg();
+            }
+            let mut K = ECP2::new();
+            K.copy(&T);
+            K.dbl();
+            K.add(&T);
+
+            K.frob(&X);
+            Q.frob(&X);
+            Q.frob(&X);
+            Q.frob(&X);
+            Q.add(&T);
+            Q.add(&K);
+            T.frob(&X);
+            T.frob(&X);
+            Q.add(&T);
+        }
+        if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BLS {
+            let mut xQ = Q.mul(&mut x);
+            let mut x2Q = xQ.mul(&mut x);
+
+            if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+                xQ.neg();
+            }
+            x2Q.sub(&xQ);
+            x2Q.sub(&Q);
+
+            xQ.sub(&Q);
+            xQ.frob(&X);
+
+            Q.dbl();
+            Q.frob(&X);
+            Q.frob(&X);
+
+            Q.add(&x2Q);
+            Q.add(&xQ);
+        }
+
+        Q.affine();
+        return Q;
+    }
+
+    pub fn generator() -> ECP2 {
+        return ECP2::new_fp2s(
+            &FP2::new_bigs(
+                &BIG::new_ints(&rom::CURVE_PXA),
+                &BIG::new_ints(&rom::CURVE_PXB),
+            ),
+            &FP2::new_bigs(
+                &BIG::new_ints(&rom::CURVE_PYA),
+                &BIG::new_ints(&rom::CURVE_PYB),
+            ),
+        );
+    }
+}
diff --git a/src/ecp4.rs b/src/ecp4.rs
new file mode 100644
index 0000000..d34b0fd
--- /dev/null
+++ b/src/ecp4.rs
@@ -0,0 +1,866 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use super::rom;
+use super::big;
+use super::ecp;
+use super::fp2::FP2;
+use super::fp4::FP4;
+use super::big::BIG;
+use types::{SexticTwist, SignOfX};
+//use std::str::SplitWhitespace;
+
+pub struct ECP4 {
+    x: FP4,
+    y: FP4,
+    z: FP4,
+}
+
+#[allow(non_snake_case)]
+impl ECP4 {
+    pub fn new() -> ECP4 {
+        ECP4 {
+            x: FP4::new(),
+            y: FP4::new_int(1),
+            z: FP4::new(),
+        }
+    }
+    #[allow(non_snake_case)]
+    /* construct this from (x,y) - but set to O if not on curve */
+    pub fn new_fp4s(ix: &FP4, iy: &FP4) -> ECP4 {
+        let mut E = ECP4::new();
+        E.x.copy(&ix);
+        E.y.copy(&iy);
+        E.z.one();
+        E.x.norm();
+
+        let mut rhs = ECP4::rhs(&E.x);
+        let mut y2 = FP4::new_copy(&E.y);
+        y2.sqr();
+        if !y2.equals(&mut rhs) {
+            E.inf();
+        }
+        return E;
+    }
+
+    /* construct this from x - but set to O if not on curve */
+    pub fn new_fp4(ix: &FP4) -> ECP4 {
+        let mut E = ECP4::new();
+        E.x.copy(&ix);
+        E.y.one();
+        E.z.one();
+        E.x.norm();
+
+        let mut rhs = ECP4::rhs(&E.x);
+        if rhs.sqrt() {
+            E.y.copy(&rhs);
+        } else {
+            E.inf();
+        }
+        return E;
+    }
+
+    /* Test this=O? */
+    pub fn is_infinity(&self) -> bool {
+        let xx = FP4::new_copy(&self.x);
+        let zz = FP4::new_copy(&self.z);
+        return xx.iszilch() && zz.iszilch();
+    }
+
+    /* copy self=P */
+    pub fn copy(&mut self, P: &ECP4) {
+        self.x.copy(&P.x);
+        self.y.copy(&P.y);
+        self.z.copy(&P.z);
+    }
+
+    /* set self=O */
+    pub fn inf(&mut self) {
+        self.x.zero();
+        self.y.one();
+        self.z.zero();
+    }
+
+    /* set self=-self */
+    pub fn neg(&mut self) {
+        self.y.norm();
+        self.y.neg();
+        self.y.norm();
+    }
+
+    /* Conditional move of Q to self dependant on d */
+    pub fn cmove(&mut self, Q: &ECP4, d: isize) {
+        self.x.cmove(&Q.x, d);
+        self.y.cmove(&Q.y, d);
+        self.z.cmove(&Q.z, d);
+    }
+
+    /* return 1 if b==c, no branching */
+    fn teq(b: i32, c: i32) -> isize {
+        let mut x = b ^ c;
+        x -= 1; // if x=0, x now -1
+        return ((x >> 31) & 1) as isize;
+    }
+
+    /* Constant time select from pre-computed table */
+    pub fn selector(&mut self, W: &[ECP4], b: i32) {
+        let mut MP = ECP4::new();
+        let m = b >> 31;
+        let mut babs = (b ^ m) - m;
+
+        babs = (babs - 1) / 2;
+
+        self.cmove(&W[0], ECP4::teq(babs, 0)); // conditional move
+        self.cmove(&W[1], ECP4::teq(babs, 1));
+        self.cmove(&W[2], ECP4::teq(babs, 2));
+        self.cmove(&W[3], ECP4::teq(babs, 3));
+        self.cmove(&W[4], ECP4::teq(babs, 4));
+        self.cmove(&W[5], ECP4::teq(babs, 5));
+        self.cmove(&W[6], ECP4::teq(babs, 6));
+        self.cmove(&W[7], ECP4::teq(babs, 7));
+
+        MP.copy(self);
+        MP.neg();
+        self.cmove(&MP, (m & 1) as isize);
+    }
+
+    /* Test if P == Q */
+    pub fn equals(&mut self, Q: &mut ECP4) -> bool {
+        let mut a = FP4::new_copy(&self.x);
+        let mut b = FP4::new_copy(&Q.x);
+
+        a.mul(&Q.z);
+        b.mul(&self.z);
+        if !a.equals(&mut b) {
+            return false;
+        }
+        a.copy(&self.y);
+        a.mul(&Q.z);
+        b.copy(&Q.y);
+        b.mul(&self.z);
+        if !a.equals(&mut b) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /* set to Affine - (x,y,z) to (x,y) */
+    pub fn affine(&mut self) {
+        if self.is_infinity() {
+            return;
+        }
+        let mut one = FP4::new_int(1);
+        if self.z.equals(&mut one) {
+            return;
+        }
+        self.z.inverse();
+
+        self.x.mul(&self.z);
+        self.x.reduce();
+        self.y.mul(&self.z);
+        self.y.reduce();
+        self.z.copy(&one);
+    }
+
+    /* extract affine x as FP4 */
+    pub fn getx(&self) -> FP4 {
+        let mut W = ECP4::new();
+        W.copy(self);
+        W.affine();
+        return FP4::new_copy(&self.x);
+    }
+
+    /* extract affine y as FP4 */
+    pub fn gety(&self) -> FP4 {
+        let mut W = ECP4::new();
+        W.copy(self);
+        W.affine();
+        return FP4::new_copy(&W.y);
+    }
+
+    /* extract projective x */
+    pub fn getpx(&self) -> FP4 {
+        return FP4::new_copy(&self.x);
+    }
+    /* extract projective y */
+    pub fn getpy(&self) -> FP4 {
+        return FP4::new_copy(&self.y);
+    }
+    /* extract projective z */
+    pub fn getpz(&self) -> FP4 {
+        return FP4::new_copy(&self.z);
+    }
+
+    /* convert to byte array */
+    pub fn tobytes(&self, b: &mut [u8]) {
+        let mut t: [u8; big::MODBYTES as usize] = [0; big::MODBYTES as usize];
+        let mb = big::MODBYTES as usize;
+
+        let mut W = ECP4::new();
+        W.copy(self);
+
+        W.affine();
+
+        W.x.geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            b[i] = t[i]
+        }
+        W.x.geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + mb] = t[i]
+        }
+
+        W.x.getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 2 * mb] = t[i]
+        }
+        W.x.getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 3 * mb] = t[i]
+        }
+
+        W.y.geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 4 * mb] = t[i]
+        }
+        W.y.geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 5 * mb] = t[i]
+        }
+
+        W.y.getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 6 * mb] = t[i]
+        }
+        W.y.getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 7 * mb] = t[i]
+        }
+    }
+
+    /* convert from byte array to point */
+    pub fn frombytes(b: &[u8]) -> ECP4 {
+        let mut t: [u8; big::MODBYTES as usize] = [0; big::MODBYTES as usize];
+        let mb = big::MODBYTES as usize;
+
+        for i in 0..mb {
+            t[i] = b[i]
+        }
+        let mut ra = BIG::frombytes(&t);
+        for i in 0..mb {
+            t[i] = b[i + mb]
+        }
+        let mut rb = BIG::frombytes(&t);
+
+        let mut ra4 = FP2::new_bigs(&ra, &rb);
+
+        for i in 0..mb {
+            t[i] = b[i + 2 * mb]
+        }
+        ra.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = b[i + 3 * mb]
+        }
+        rb.copy(&BIG::frombytes(&t));
+
+        let mut rb4 = FP2::new_bigs(&ra, &rb);
+
+        let rx = FP4::new_fp2s(&ra4, &rb4);
+
+        for i in 0..mb {
+            t[i] = b[i + 4 * mb]
+        }
+        ra.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = b[i + 5 * mb]
+        }
+        rb.copy(&BIG::frombytes(&t));
+
+        ra4.copy(&FP2::new_bigs(&ra, &rb));
+
+        for i in 0..mb {
+            t[i] = b[i + 6 * mb]
+        }
+        ra.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = b[i + 7 * mb]
+        }
+        rb.copy(&BIG::frombytes(&t));
+
+        rb4.copy(&FP2::new_bigs(&ra, &rb));
+
+        let ry = FP4::new_fp2s(&ra4, &rb4);
+
+        return ECP4::new_fp4s(&rx, &ry);
+    }
+
+    /* convert this to hex string */
+    pub fn tostring(&self) -> String {
+        let mut W = ECP4::new();
+        W.copy(self);
+        W.affine();
+        if W.is_infinity() {
+            return String::from("infinity");
+        }
+        return format!("({},{})", W.x.tostring(), W.y.tostring());
+    }
+
+    /* Calculate RHS of twisted curve equation x^3+B/i */
+    pub fn rhs(x: &FP4) -> FP4 {
+        //x.norm();
+        let mut r = FP4::new_copy(x);
+        r.sqr();
+        let mut b = FP4::new_fp2(&FP2::new_big(&BIG::new_ints(&rom::CURVE_B)));
+        if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+            b.div_i();
+        }
+        if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+            b.times_i();
+        }
+
+        r.mul(x);
+        r.add(&b);
+
+        r.reduce();
+        return r;
+    }
+
+    /* self+=self */
+    pub fn dbl(&mut self) -> isize {
+        let mut iy = FP4::new_copy(&self.y);
+        if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+            iy.times_i(); //iy.norm();
+        }
+
+        let mut t0 = FP4::new_copy(&self.y);
+        t0.sqr();
+        if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+            t0.times_i();
+        }
+        let mut t1 = FP4::new_copy(&iy);
+        t1.mul(&self.z);
+        let mut t2 = FP4::new_copy(&self.z);
+        t2.sqr();
+
+        self.z.copy(&t0);
+        self.z.add(&t0);
+        self.z.norm();
+        self.z.dbl();
+        self.z.dbl();
+        self.z.norm();
+
+        t2.imul(3 * rom::CURVE_B_I);
+        if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+            t2.times_i();
+        }
+        let mut x3 = FP4::new_copy(&t2);
+        x3.mul(&self.z);
+
+        let mut y3 = FP4::new_copy(&t0);
+
+        y3.add(&t2);
+        y3.norm();
+        self.z.mul(&t1);
+        t1.copy(&t2);
+        t1.add(&t2);
+        t2.add(&t1);
+        t2.norm();
+        t0.sub(&t2);
+        t0.norm(); //y^2-9bz^2
+        y3.mul(&t0);
+        y3.add(&x3); //(y^2+3z*2)(y^2-9z^2)+3b.z^2.8y^2
+        t1.copy(&self.x);
+        t1.mul(&iy); //
+        self.x.copy(&t0);
+        self.x.norm();
+        self.x.mul(&t1);
+        self.x.dbl(); //(y^2-9bz^2)xy2
+
+        self.x.norm();
+        self.y.copy(&y3);
+        self.y.norm();
+
+        return 1;
+    }
+
+    /* self+=Q - return 0 for add, 1 for double, -1 for O */
+    pub fn add(&mut self, Q: &ECP4) -> isize {
+        let b = 3 * rom::CURVE_B_I;
+        let mut t0 = FP4::new_copy(&self.x);
+        t0.mul(&Q.x); // x.Q.x
+        let mut t1 = FP4::new_copy(&self.y);
+        t1.mul(&Q.y); // y.Q.y
+
+        let mut t2 = FP4::new_copy(&self.z);
+        t2.mul(&Q.z);
+        let mut t3 = FP4::new_copy(&self.x);
+        t3.add(&self.y);
+        t3.norm(); //t3=X1+Y1
+        let mut t4 = FP4::new_copy(&Q.x);
+        t4.add(&Q.y);
+        t4.norm(); //t4=X2+Y2
+        t3.mul(&t4); //t3=(X1+Y1)(X2+Y2)
+        t4.copy(&t0);
+        t4.add(&t1); //t4=X1.X2+Y1.Y2
+
+        t3.sub(&t4);
+        t3.norm();
+        if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+            t3.times_i(); //t3=(X1+Y1)(X2+Y2)-(X1.X2+Y1.Y2) = X1.Y2+X2.Y1
+        }
+        t4.copy(&self.y);
+        t4.add(&self.z);
+        t4.norm(); //t4=Y1+Z1
+        let mut x3 = FP4::new_copy(&Q.y);
+        x3.add(&Q.z);
+        x3.norm(); //x3=Y2+Z2
+
+        t4.mul(&x3); //t4=(Y1+Z1)(Y2+Z2)
+        x3.copy(&t1); //
+        x3.add(&t2); //X3=Y1.Y2+Z1.Z2
+
+        t4.sub(&x3);
+        t4.norm();
+        if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+            t4.times_i(); //t4=(Y1+Z1)(Y2+Z2) - (Y1.Y2+Z1.Z2) = Y1.Z2+Y2.Z1
+        }
+        x3.copy(&self.x);
+        x3.add(&self.z);
+        x3.norm(); // x3=X1+Z1
+        let mut y3 = FP4::new_copy(&Q.x);
+        y3.add(&Q.z);
+        y3.norm(); // y3=X2+Z2
+        x3.mul(&y3); // x3=(X1+Z1)(X2+Z2)
+        y3.copy(&t0);
+        y3.add(&t2); // y3=X1.X2+Z1+Z2
+        y3.rsub(&x3);
+        y3.norm(); // y3=(X1+Z1)(X2+Z2) - (X1.X2+Z1.Z2) = X1.Z2+X2.Z1
+
+        if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+            t0.times_i(); // x.Q.x
+            t1.times_i(); // y.Q.y
+        }
+        x3.copy(&t0);
+        x3.add(&t0);
+        t0.add(&x3);
+        t0.norm();
+        t2.imul(b);
+        if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+            t2.times_i();
+        }
+        let mut z3 = FP4::new_copy(&t1);
+        z3.add(&t2);
+        z3.norm();
+        t1.sub(&t2);
+        t1.norm();
+        y3.imul(b);
+        if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+            y3.times_i();
+        }
+        x3.copy(&y3);
+        x3.mul(&t4);
+        t2.copy(&t3);
+        t2.mul(&t1);
+        x3.rsub(&t2);
+        y3.mul(&t0);
+        t1.mul(&z3);
+        y3.add(&t1);
+        t0.mul(&t3);
+        z3.mul(&t4);
+        z3.add(&t0);
+
+        self.x.copy(&x3);
+        self.x.norm();
+        self.y.copy(&y3);
+        self.y.norm();
+        self.z.copy(&z3);
+        self.z.norm();
+
+        return 0;
+    }
+
+    /* set this-=Q */
+    pub fn sub(&mut self, Q: &ECP4) -> isize {
+        let mut NQ = ECP4::new();
+        NQ.copy(Q);
+        NQ.neg();
+        let d = self.add(&NQ);
+        return d;
+    }
+
+    pub fn frob_constants() -> [FP2; 3] {
+        let f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
+
+        let mut f0 = FP2::new_copy(&f);
+        f0.sqr();
+        let mut f2 = FP2::new_copy(&f0);
+        f2.mul_ip();
+        f2.norm();
+        let mut f1 = FP2::new_copy(&f2);
+        f1.sqr();
+        f2.mul(&f1);
+        f1.copy(&f);
+        if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+            f1.mul_ip();
+            f1.inverse();
+            f0.copy(&f1);
+            f0.sqr();
+        }
+        f0.mul_ip();
+        f0.norm();
+        f1.mul(&f0);
+
+        let F: [FP2; 3] = [f0, f1, f2];
+        return F;
+    }
+
+    /* set this*=q, where q is Modulus, using Frobenius */
+    pub fn frob(&mut self, f: &[FP2; 3], n: isize) {
+        for _i in 0..n {
+            self.x.frob(&f[2]);
+            self.x.pmul(&f[0]);
+
+            self.y.frob(&f[2]);
+            self.y.pmul(&f[1]);
+            self.y.times_i();
+
+            self.z.frob(&f[2]);
+        }
+    }
+
+    /* self*=e */
+    pub fn mul(&self, e: &BIG) -> ECP4 {
+        /* fixed size windows */
+        let mut mt = BIG::new();
+        let mut t = BIG::new();
+        let mut P = ECP4::new();
+        let mut Q = ECP4::new();
+        let mut C = ECP4::new();
+
+        if self.is_infinity() {
+            return P;
+        }
+
+        let mut W: [ECP4; 8] = [
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+        ];
+
+        const CT: usize = 1 + (big::NLEN * (big::BASEBITS as usize) + 3) / 4;
+        let mut w: [i8; CT] = [0; CT];
+
+        /* precompute table */
+        Q.copy(&self);
+        Q.dbl();
+
+        W[0].copy(&self);
+
+        for i in 1..8 {
+            C.copy(&W[i - 1]);
+            W[i].copy(&C);
+            W[i].add(&mut Q);
+        }
+
+        /* make exponent odd - add 2P if even, P if odd */
+        t.copy(&e);
+        let s = t.parity();
+        t.inc(1);
+        t.norm();
+        let ns = t.parity();
+        mt.copy(&t);
+        mt.inc(1);
+        mt.norm();
+        t.cmove(&mt, s);
+        Q.cmove(&self, ns);
+        C.copy(&Q);
+
+        let nb = 1 + (t.nbits() + 3) / 4;
+
+        /* convert exponent to signed 4-bit window */
+        for i in 0..nb {
+            w[i] = (t.lastbits(5) - 16) as i8;
+            t.dec(w[i] as isize);
+            t.norm();
+            t.fshr(4);
+        }
+        w[nb] = (t.lastbits(5)) as i8;
+
+        P.copy(&W[((w[nb] as usize) - 1) / 2]);
+        for i in (0..nb).rev() {
+            Q.selector(&W, w[i] as i32);
+            P.dbl();
+            P.dbl();
+            P.dbl();
+            P.dbl();
+            P.add(&mut Q);
+        }
+        P.sub(&mut C);
+        P.affine();
+        return P;
+    }
+
+    /* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3.. */
+    // Bos & Costello https://eprint.iacr.org/2013/458.pdf
+    // Faz-Hernandez & Longa & Sanchez  https://eprint.iacr.org/2013/158.pdf
+    // Side channel attack secure
+
+    pub fn mul8(Q: &mut [ECP4], u: &[BIG]) -> ECP4 {
+        let mut W = ECP4::new();
+        let mut P = ECP4::new();
+
+        let mut T1: [ECP4; 8] = [
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+        ];
+        let mut T2: [ECP4; 8] = [
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+        ];
+
+        let mut mt = BIG::new();
+
+        let mut t: [BIG; 8] = [
+            BIG::new_copy(&u[0]),
+            BIG::new_copy(&u[1]),
+            BIG::new_copy(&u[2]),
+            BIG::new_copy(&u[3]),
+            BIG::new_copy(&u[4]),
+            BIG::new_copy(&u[5]),
+            BIG::new_copy(&u[6]),
+            BIG::new_copy(&u[7]),
+        ];
+
+        const CT: usize = 1 + big::NLEN * (big::BASEBITS as usize);
+        let mut w1: [i8; CT] = [0; CT];
+        let mut s1: [i8; CT] = [0; CT];
+        let mut w2: [i8; CT] = [0; CT];
+        let mut s2: [i8; CT] = [0; CT];
+
+        for i in 0..8 {
+            //Q[i].affine();
+            t[i].norm();
+        }
+
+        T1[0].copy(&Q[0]);
+        W.copy(&T1[0]);
+        T1[1].copy(&W);
+        T1[1].add(&mut Q[1]); // Q[0]+Q[1]
+        T1[2].copy(&W);
+        T1[2].add(&mut Q[2]);
+        W.copy(&T1[1]); // Q[0]+Q[2]
+        T1[3].copy(&W);
+        T1[3].add(&mut Q[2]);
+        W.copy(&T1[0]); // Q[0]+Q[1]+Q[2]
+        T1[4].copy(&W);
+        T1[4].add(&mut Q[3]);
+        W.copy(&T1[1]); // Q[0]+Q[3]
+        T1[5].copy(&W);
+        T1[5].add(&mut Q[3]);
+        W.copy(&T1[2]); // Q[0]+Q[1]+Q[3]
+        T1[6].copy(&W);
+        T1[6].add(&mut Q[3]);
+        W.copy(&T1[3]); // Q[0]+Q[2]+Q[3]
+        T1[7].copy(&W);
+        T1[7].add(&mut Q[3]); // Q[0]+Q[1]+Q[2]+Q[3]
+
+        // Use frobenius
+        let f = ECP4::frob_constants();
+        for i in 0..8 {
+            T2[i].copy(&T1[i]);
+            T2[i].frob(&f, 4);
+        }
+
+        // Make it odd
+        let pb1 = 1 - t[0].parity();
+        t[0].inc(pb1);
+        t[0].norm();
+
+        let pb2 = 1 - t[4].parity();
+        t[4].inc(pb2);
+        t[4].norm();
+
+        // Number of bits
+        mt.zero();
+        for i in 0..8 {
+            mt.or(&t[i]);
+        }
+
+        let nb = 1 + mt.nbits();
+
+        // Sign pivot
+
+        s1[nb - 1] = 1;
+        s2[nb - 1] = 1;
+        for i in 0..nb - 1 {
+            t[0].fshr(1);
+            s1[i] = (2 * t[0].parity() - 1) as i8;
+            t[4].fshr(1);
+            s2[i] = (2 * t[4].parity() - 1) as i8;
+        }
+
+        // Recoded exponent
+        for i in 0..nb {
+            w1[i] = 0;
+            let mut k = 1;
+            for j in 1..4 {
+                let bt = s1[i] * (t[j].parity() as i8);
+                t[j].fshr(1);
+                t[j].dec((bt >> 1) as isize);
+                t[j].norm();
+                w1[i] += bt * (k as i8);
+                k = 2 * k;
+            }
+
+            w2[i] = 0;
+            k = 1;
+            for j in 5..8 {
+                let bt = s2[i] * (t[j].parity() as i8);
+                t[j].fshr(1);
+                t[j].dec((bt >> 1) as isize);
+                t[j].norm();
+                w2[i] += bt * (k as i8);
+                k = 2 * k;
+            }
+        }
+
+        // Main loop
+        P.selector(&T1, (2 * w1[nb - 1] + 1) as i32);
+        W.selector(&T2, (2 * w2[nb - 1] + 1) as i32);
+        P.add(&mut W);
+        for i in (0..nb - 1).rev() {
+            P.dbl();
+            W.selector(&T1, (2 * w1[i] + s1[i]) as i32);
+            P.add(&mut W);
+            W.selector(&T2, (2 * w2[i] + s2[i]) as i32);
+            P.add(&mut W);
+        }
+
+        // apply correction
+        W.copy(&P);
+        W.sub(&mut Q[0]);
+        P.cmove(&W, pb1);
+
+        W.copy(&P);
+        W.sub(&mut Q[4]);
+        P.cmove(&W, pb2);
+
+        P.affine();
+
+        return P;
+    }
+
+    pub fn generator() -> ECP4 {
+        return ECP4::new_fp4s(
+            &FP4::new_fp2s(
+                &FP2::new_bigs(
+                    &BIG::new_ints(&rom::CURVE_PXAA),
+                    &BIG::new_ints(&rom::CURVE_PXAB),
+                ),
+                &FP2::new_bigs(
+                    &BIG::new_ints(&rom::CURVE_PXBA),
+                    &BIG::new_ints(&rom::CURVE_PXBB),
+                ),
+            ),
+            &FP4::new_fp2s(
+                &FP2::new_bigs(
+                    &BIG::new_ints(&rom::CURVE_PYAA),
+                    &BIG::new_ints(&rom::CURVE_PYAB),
+                ),
+                &FP2::new_bigs(
+                    &BIG::new_ints(&rom::CURVE_PYBA),
+                    &BIG::new_ints(&rom::CURVE_PYBB),
+                ),
+            ),
+        );
+    }
+
+    #[allow(non_snake_case)]
+    pub fn mapit(h: &[u8]) -> ECP4 {
+        let mut q = BIG::new_ints(&rom::MODULUS);
+        let mut x = BIG::frombytes(h);
+        x.rmod(&mut q);
+        let mut Q: ECP4;
+        let one = BIG::new_int(1);
+
+        loop {
+            let X = FP4::new_fp2(&FP2::new_bigs(&one, &x));
+            Q = ECP4::new_fp4(&X);
+            if !Q.is_infinity() {
+                break;
+            }
+            x.inc(1);
+            x.norm();
+        }
+
+        let f = ECP4::frob_constants();
+        x = BIG::new_ints(&rom::CURVE_BNX);
+
+        let mut xQ = Q.mul(&mut x);
+        let mut x2Q = xQ.mul(&mut x);
+        let mut x3Q = x2Q.mul(&mut x);
+        let mut x4Q = x3Q.mul(&mut x);
+
+        if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+            xQ.neg();
+            x3Q.neg();
+        }
+
+        x4Q.sub(&x3Q);
+        x4Q.sub(&Q);
+
+        x3Q.sub(&x2Q);
+        x3Q.frob(&f, 1);
+
+        x2Q.sub(&xQ);
+        x2Q.frob(&f, 2);
+
+        xQ.sub(&Q);
+        xQ.frob(&f, 3);
+
+        Q.dbl();
+        Q.frob(&f, 4);
+
+        Q.add(&x4Q);
+        Q.add(&x3Q);
+        Q.add(&x2Q);
+        Q.add(&xQ);
+
+        Q.affine();
+        return Q;
+    }
+}
diff --git a/src/ecp8.rs b/src/ecp8.rs
new file mode 100644
index 0000000..a6d32a4
--- /dev/null
+++ b/src/ecp8.rs
@@ -0,0 +1,1155 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use super::rom;
+use super::big;
+use super::ecp;
+use super::fp2::FP2;
+use super::fp4::FP4;
+use super::fp8::FP8;
+use super::big::BIG;
+use types::{SexticTwist, SignOfX};
+
+pub struct ECP8 {
+    x: FP8,
+    y: FP8,
+    z: FP8,
+}
+
+#[allow(non_snake_case)]
+impl ECP8 {
+    pub fn new() -> ECP8 {
+        ECP8 {
+            x: FP8::new(),
+            y: FP8::new_int(1),
+            z: FP8::new(),
+        }
+    }
+    #[allow(non_snake_case)]
+    /* construct this from (x,y) - but set to O if not on curve */
+    pub fn new_fp8s(ix: &FP8, iy: &FP8) -> ECP8 {
+        let mut E = ECP8::new();
+        E.x.copy(&ix);
+        E.y.copy(&iy);
+        E.z.one();
+        E.x.norm();
+
+        let mut rhs = ECP8::rhs(&E.x);
+        let mut y2 = FP8::new_copy(&E.y);
+        y2.sqr();
+        if !y2.equals(&mut rhs) {
+            E.inf();
+        }
+        return E;
+    }
+
+    /* construct this from x - but set to O if not on curve */
+    pub fn new_fp8(ix: &FP8) -> ECP8 {
+        let mut E = ECP8::new();
+        E.x.copy(&ix);
+        E.y.one();
+        E.z.one();
+        E.x.norm();
+
+        let mut rhs = ECP8::rhs(&E.x);
+        if rhs.sqrt() {
+            E.y.copy(&rhs);
+        } else {
+            E.inf();
+        }
+        return E;
+    }
+
+    /* Test this=O? */
+    pub fn is_infinity(&self) -> bool {
+        let xx = FP8::new_copy(&self.x);
+        let zz = FP8::new_copy(&self.z);
+        return xx.iszilch() && zz.iszilch();
+    }
+
+    /* copy self=P */
+    pub fn copy(&mut self, P: &ECP8) {
+        self.x.copy(&P.x);
+        self.y.copy(&P.y);
+        self.z.copy(&P.z);
+    }
+
+    /* set self=O */
+    pub fn inf(&mut self) {
+        self.x.zero();
+        self.y.one();
+        self.z.zero();
+    }
+
+    /* set self=-self */
+    pub fn neg(&mut self) {
+        self.y.norm();
+        self.y.neg();
+        self.y.norm();
+    }
+
+    /* Conditional move of Q to self dependant on d */
+    pub fn cmove(&mut self, Q: &ECP8, d: isize) {
+        self.x.cmove(&Q.x, d);
+        self.y.cmove(&Q.y, d);
+        self.z.cmove(&Q.z, d);
+    }
+
+    /* return 1 if b==c, no branching */
+    fn teq(b: i32, c: i32) -> isize {
+        let mut x = b ^ c;
+        x -= 1; // if x=0, x now -1
+        return ((x >> 31) & 1) as isize;
+    }
+
+    /* Constant time select from pre-computed table */
+    pub fn selector(&mut self, W: &[ECP8], b: i32) {
+        let mut MP = ECP8::new();
+        let m = b >> 31;
+        let mut babs = (b ^ m) - m;
+
+        babs = (babs - 1) / 2;
+
+        self.cmove(&W[0], ECP8::teq(babs, 0)); // conditional move
+        self.cmove(&W[1], ECP8::teq(babs, 1));
+        self.cmove(&W[2], ECP8::teq(babs, 2));
+        self.cmove(&W[3], ECP8::teq(babs, 3));
+        self.cmove(&W[4], ECP8::teq(babs, 4));
+        self.cmove(&W[5], ECP8::teq(babs, 5));
+        self.cmove(&W[6], ECP8::teq(babs, 6));
+        self.cmove(&W[7], ECP8::teq(babs, 7));
+
+        MP.copy(self);
+        MP.neg();
+        self.cmove(&MP, (m & 1) as isize);
+    }
+
+    /* Test if P == Q */
+    pub fn equals(&mut self, Q: &mut ECP8) -> bool {
+        let mut a = FP8::new_copy(&self.x);
+        let mut b = FP8::new_copy(&Q.x);
+
+        a.mul(&Q.z);
+        b.mul(&self.z);
+        if !a.equals(&mut b) {
+            return false;
+        }
+        a.copy(&self.y);
+        a.mul(&Q.z);
+        b.copy(&Q.y);
+        b.mul(&self.z);
+        if !a.equals(&mut b) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /* set to Affine - (x,y,z) to (x,y) */
+    pub fn affine(&mut self) {
+        if self.is_infinity() {
+            return;
+        }
+        let mut one = FP8::new_int(1);
+        if self.z.equals(&mut one) {
+            return;
+        }
+        self.z.inverse();
+
+        self.x.mul(&self.z);
+        self.x.reduce();
+        self.y.mul(&self.z);
+        self.y.reduce();
+        self.z.copy(&one);
+    }
+
+    /* extract affine x as FP8 */
+    pub fn getx(&self) -> FP8 {
+        let mut W = ECP8::new();
+        W.copy(self);
+        W.affine();
+        return FP8::new_copy(&W.x);
+    }
+
+    /* extract affine y as FP8 */
+    pub fn gety(&self) -> FP8 {
+        let mut W = ECP8::new();
+        W.copy(self);
+        W.affine();
+        return FP8::new_copy(&W.y);
+    }
+
+    /* extract projective x */
+    pub fn getpx(&self) -> FP8 {
+        return FP8::new_copy(&self.x);
+    }
+    /* extract projective y */
+    pub fn getpy(&self) -> FP8 {
+        return FP8::new_copy(&self.y);
+    }
+    /* extract projective z */
+    pub fn getpz(&self) -> FP8 {
+        return FP8::new_copy(&self.z);
+    }
+
+    /* convert to byte array */
+    pub fn tobytes(&self, b: &mut [u8]) {
+        let mut t: [u8; big::MODBYTES as usize] = [0; big::MODBYTES as usize];
+        let mb = big::MODBYTES as usize;
+        let mut W = ECP8::new();
+        W.copy(self);
+
+        W.affine();
+
+        W.x.geta().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            b[i] = t[i]
+        }
+        W.x.geta().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + mb] = t[i]
+        }
+
+        W.x.geta().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 2 * mb] = t[i]
+        }
+        W.x.geta().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 3 * mb] = t[i]
+        }
+
+        W.x.getb().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 4 * mb] = t[i]
+        }
+        W.x.getb().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 5 * mb] = t[i]
+        }
+
+        W.x.getb().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 6 * mb] = t[i]
+        }
+        W.x.getb().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 7 * mb] = t[i]
+        }
+
+        W.y.geta().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 8 * mb] = t[i]
+        }
+        W.y.geta().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 9 * mb] = t[i]
+        }
+
+        W.y.geta().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 10 * mb] = t[i]
+        }
+        W.y.geta().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 11 * mb] = t[i]
+        }
+
+        W.y.getb().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 12 * mb] = t[i]
+        }
+        W.y.getb().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 13 * mb] = t[i]
+        }
+
+        W.y.getb().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 14 * mb] = t[i]
+        }
+        W.y.getb().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            b[i + 15 * mb] = t[i]
+        }
+    }
+
+    /* convert from byte array to point */
+    pub fn frombytes(b: &[u8]) -> ECP8 {
+        let mut t: [u8; big::MODBYTES as usize] = [0; big::MODBYTES as usize];
+        let mb = big::MODBYTES as usize;
+
+        for i in 0..mb {
+            t[i] = b[i]
+        }
+        let mut ra = BIG::frombytes(&t);
+        for i in 0..mb {
+            t[i] = b[i + mb]
+        }
+        let mut rb = BIG::frombytes(&t);
+
+        let mut ra4 = FP2::new_bigs(&ra, &rb);
+
+        for i in 0..mb {
+            t[i] = b[i + 2 * mb]
+        }
+        ra.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = b[i + 3 * mb]
+        }
+        rb.copy(&BIG::frombytes(&t));
+
+        let mut rb4 = FP2::new_bigs(&ra, &rb);
+
+        let mut ra8 = FP4::new_fp2s(&ra4, &rb4);
+
+        for i in 0..mb {
+            t[i] = b[i + 4 * mb]
+        }
+        let mut ra = BIG::frombytes(&t);
+        for i in 0..mb {
+            t[i] = b[i + 5 * mb]
+        }
+        let mut rb = BIG::frombytes(&t);
+
+        ra4.copy(&FP2::new_bigs(&ra, &rb));
+
+        for i in 0..mb {
+            t[i] = b[i + 6 * mb]
+        }
+        ra.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = b[i + 7 * mb]
+        }
+        rb.copy(&BIG::frombytes(&t));
+
+        rb4.copy(&FP2::new_bigs(&ra, &rb));
+
+        let mut rb8 = FP4::new_fp2s(&ra4, &rb4);
+
+        let rx = FP8::new_fp4s(&ra8, &rb8);
+
+        for i in 0..mb {
+            t[i] = b[i + 8 * mb]
+        }
+        ra.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = b[i + 9 * mb]
+        }
+        rb.copy(&BIG::frombytes(&t));
+
+        ra4.copy(&FP2::new_bigs(&ra, &rb));
+
+        for i in 0..mb {
+            t[i] = b[i + 10 * mb]
+        }
+        ra.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = b[i + 11 * mb]
+        }
+        rb.copy(&BIG::frombytes(&t));
+
+        rb4.copy(&FP2::new_bigs(&ra, &rb));
+
+        ra8.copy(&FP4::new_fp2s(&ra4, &rb4));
+
+        for i in 0..mb {
+            t[i] = b[i + 12 * mb]
+        }
+        ra.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = b[i + 13 * mb]
+        }
+        rb.copy(&BIG::frombytes(&t));
+
+        ra4.copy(&FP2::new_bigs(&ra, &rb));
+
+        for i in 0..mb {
+            t[i] = b[i + 14 * mb]
+        }
+        ra.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = b[i + 15 * mb]
+        }
+        rb.copy(&BIG::frombytes(&t));
+
+        rb4.copy(&FP2::new_bigs(&ra, &rb));
+
+        rb8.copy(&FP4::new_fp2s(&ra4, &rb4));
+
+        let ry = FP8::new_fp4s(&ra8, &rb8);
+
+        return ECP8::new_fp8s(&rx, &ry);
+    }
+
+    /* convert this to hex string */
+    pub fn tostring(&self) -> String {
+        let mut W = ECP8::new();
+        W.copy(self);
+        W.affine();
+        if W.is_infinity() {
+            return String::from("infinity");
+        }
+        return format!("({},{})", W.x.tostring(), W.y.tostring());
+    }
+
+    /* Calculate RHS of twisted curve equation x^3+B/i */
+    pub fn rhs(x: &FP8) -> FP8 {
+        let mut r = FP8::new_copy(x);
+        r.sqr();
+        let mut b = FP8::new_fp4(&FP4::new_fp2(&FP2::new_big(&BIG::new_ints(&rom::CURVE_B))));
+        if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+            b.div_i();
+        }
+        if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+            b.times_i();
+        }
+
+        r.mul(x);
+        r.add(&b);
+
+        r.reduce();
+        return r;
+    }
+
+    /* self+=self */
+    pub fn dbl(&mut self) -> isize {
+        let mut iy = FP8::new_copy(&self.y);
+        if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+            iy.times_i(); //iy.norm();
+        }
+
+        let mut t0 = FP8::new_copy(&self.y);
+        t0.sqr();
+        if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+            t0.times_i();
+        }
+        let mut t1 = FP8::new_copy(&iy);
+        t1.mul(&self.z);
+        let mut t2 = FP8::new_copy(&self.z);
+        t2.sqr();
+
+        self.z.copy(&t0);
+        self.z.add(&t0);
+        self.z.norm();
+        self.z.dbl();
+        self.z.dbl();
+        self.z.norm();
+
+        t2.imul(3 * rom::CURVE_B_I);
+        if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+            t2.times_i();
+        }
+        let mut x3 = FP8::new_copy(&t2);
+        x3.mul(&self.z);
+
+        let mut y3 = FP8::new_copy(&t0);
+
+        y3.add(&t2);
+        y3.norm();
+        self.z.mul(&t1);
+        t1.copy(&t2);
+        t1.add(&t2);
+        t2.add(&t1);
+        t2.norm();
+        t0.sub(&t2);
+        t0.norm(); //y^2-9bz^2
+        y3.mul(&t0);
+        y3.add(&x3); //(y^2+3z*2)(y^2-9z^2)+3b.z^2.8y^2
+        t1.copy(&self.x);
+        t1.mul(&iy); //
+        self.x.copy(&t0);
+        self.x.norm();
+        self.x.mul(&t1);
+        self.x.dbl(); //(y^2-9bz^2)xy2
+
+        self.x.norm();
+        self.y.copy(&y3);
+        self.y.norm();
+
+        return 1;
+    }
+
+    /* self+=Q - return 0 for add, 1 for double, -1 for O */
+    pub fn add(&mut self, Q: &ECP8) -> isize {
+        let b = 3 * rom::CURVE_B_I;
+        let mut t0 = FP8::new_copy(&self.x);
+        t0.mul(&Q.x); // x.Q.x
+        let mut t1 = FP8::new_copy(&self.y);
+        t1.mul(&Q.y); // y.Q.y
+
+        let mut t2 = FP8::new_copy(&self.z);
+        t2.mul(&Q.z);
+        let mut t3 = FP8::new_copy(&self.x);
+        t3.add(&self.y);
+        t3.norm(); //t3=X1+Y1
+        let mut t4 = FP8::new_copy(&Q.x);
+        t4.add(&Q.y);
+        t4.norm(); //t4=X2+Y2
+        t3.mul(&t4); //t3=(X1+Y1)(X2+Y2)
+        t4.copy(&t0);
+        t4.add(&t1); //t4=X1.X2+Y1.Y2
+
+        t3.sub(&t4);
+        t3.norm();
+        if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+            t3.times_i(); //t3=(X1+Y1)(X2+Y2)-(X1.X2+Y1.Y2) = X1.Y2+X2.Y1
+        }
+        t4.copy(&self.y);
+        t4.add(&self.z);
+        t4.norm(); //t4=Y1+Z1
+        let mut x3 = FP8::new_copy(&Q.y);
+        x3.add(&Q.z);
+        x3.norm(); //x3=Y2+Z2
+
+        t4.mul(&x3); //t4=(Y1+Z1)(Y2+Z2)
+        x3.copy(&t1); //
+        x3.add(&t2); //X3=Y1.Y2+Z1.Z2
+
+        t4.sub(&x3);
+        t4.norm();
+        if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+            t4.times_i(); //t4=(Y1+Z1)(Y2+Z2) - (Y1.Y2+Z1.Z2) = Y1.Z2+Y2.Z1
+        }
+        x3.copy(&self.x);
+        x3.add(&self.z);
+        x3.norm(); // x3=X1+Z1
+        let mut y3 = FP8::new_copy(&Q.x);
+        y3.add(&Q.z);
+        y3.norm(); // y3=X2+Z2
+        x3.mul(&y3); // x3=(X1+Z1)(X2+Z2)
+        y3.copy(&t0);
+        y3.add(&t2); // y3=X1.X2+Z1+Z2
+        y3.rsub(&x3);
+        y3.norm(); // y3=(X1+Z1)(X2+Z2) - (X1.X2+Z1.Z2) = X1.Z2+X2.Z1
+
+        if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+            t0.times_i(); // x.Q.x
+            t1.times_i(); // y.Q.y
+        }
+        x3.copy(&t0);
+        x3.add(&t0);
+        t0.add(&x3);
+        t0.norm();
+        t2.imul(b);
+        if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+            t2.times_i();
+        }
+        let mut z3 = FP8::new_copy(&t1);
+        z3.add(&t2);
+        z3.norm();
+        t1.sub(&t2);
+        t1.norm();
+        y3.imul(b);
+        if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+            y3.times_i();
+        }
+        x3.copy(&y3);
+        x3.mul(&t4);
+        t2.copy(&t3);
+        t2.mul(&t1);
+        x3.rsub(&t2);
+        y3.mul(&t0);
+        t1.mul(&z3);
+        y3.add(&t1);
+        t0.mul(&t3);
+        z3.mul(&t4);
+        z3.add(&t0);
+
+        self.x.copy(&x3);
+        self.x.norm();
+        self.y.copy(&y3);
+        self.y.norm();
+        self.z.copy(&z3);
+        self.z.norm();
+
+        return 0;
+    }
+
+    /* set this-=Q */
+    pub fn sub(&mut self, Q: &ECP8) -> isize {
+        let mut NQ = ECP8::new();
+        NQ.copy(Q);
+        NQ.neg();
+        let d = self.add(&NQ);
+        return d;
+    }
+
+    pub fn frob_constants() -> [FP2; 3] {
+        let f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
+
+        let mut f0 = FP2::new_copy(&f);
+        f0.sqr();
+        let mut f2 = FP2::new_copy(&f0);
+        f2.mul_ip();
+        f2.norm();
+        let mut f1 = FP2::new_copy(&f2);
+        f1.sqr();
+        f2.mul(&f1);
+
+        f2.mul_ip();
+        f2.norm();
+
+        f1.copy(&f);
+        if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+            f1.mul_ip();
+            f1.inverse();
+            f0.copy(&f1);
+            f0.sqr();
+        }
+        f0.mul_ip();
+        f0.norm();
+        f1.mul(&f0);
+
+        let F: [FP2; 3] = [f0, f1, f2];
+        return F;
+    }
+
+    /* set this*=q, where q is Modulus, using Frobenius */
+    pub fn frob(&mut self, f: &[FP2; 3], n: isize) {
+        for _i in 0..n {
+            self.x.frob(&f[2]);
+            self.x.qmul(&f[0]);
+            if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+                self.x.div_i2();
+            }
+            if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+                self.x.times_i2();
+            }
+            self.y.frob(&f[2]);
+            self.y.qmul(&f[1]);
+            if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+                self.y.div_i();
+            }
+            if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+                self.y.times_i2();
+                self.y.times_i2();
+                self.y.times_i();
+            }
+
+            self.z.frob(&f[2]);
+        }
+    }
+
+    /* self*=e */
+    pub fn mul(&self, e: &BIG) -> ECP8 {
+        /* fixed size windows */
+        let mut mt = BIG::new();
+        let mut t = BIG::new();
+        let mut P = ECP8::new();
+        let mut Q = ECP8::new();
+        let mut C = ECP8::new();
+
+        if self.is_infinity() {
+            return P;
+        }
+
+        let mut W: [ECP8; 8] = [
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+        ];
+
+        const CT: usize = 1 + (big::NLEN * (big::BASEBITS as usize) + 3) / 4;
+        let mut w: [i8; CT] = [0; CT];
+
+        /* precompute table */
+        Q.copy(&self);
+        Q.dbl();
+
+        W[0].copy(&self);
+
+        for i in 1..8 {
+            C.copy(&W[i - 1]);
+            W[i].copy(&C);
+            W[i].add(&mut Q);
+        }
+
+        /* make exponent odd - add 2P if even, P if odd */
+        t.copy(&e);
+        let s = t.parity();
+        t.inc(1);
+        t.norm();
+        let ns = t.parity();
+        mt.copy(&t);
+        mt.inc(1);
+        mt.norm();
+        t.cmove(&mt, s);
+        Q.cmove(&self, ns);
+        C.copy(&Q);
+
+        let nb = 1 + (t.nbits() + 3) / 4;
+
+        /* convert exponent to signed 4-bit window */
+        for i in 0..nb {
+            w[i] = (t.lastbits(5) - 16) as i8;
+            t.dec(w[i] as isize);
+            t.norm();
+            t.fshr(4);
+        }
+        w[nb] = (t.lastbits(5)) as i8;
+
+        P.copy(&W[((w[nb] as usize) - 1) / 2]);
+        for i in (0..nb).rev() {
+            Q.selector(&W, w[i] as i32);
+            P.dbl();
+            P.dbl();
+            P.dbl();
+            P.dbl();
+            P.add(&mut Q);
+        }
+        P.sub(&mut C);
+        P.affine();
+        return P;
+    }
+
+    /* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3.. */
+    // Bos & Costello https://eprint.iacr.org/2013/458.pdf
+    // Faz-Hernandez & Longa & Sanchez  https://eprint.iacr.org/2013/158.pdf
+    // Side channel attack secure
+
+    pub fn mul16(Q: &mut [ECP8], u: &[BIG]) -> ECP8 {
+        let mut W = ECP8::new();
+        let mut P = ECP8::new();
+
+        let mut T1: [ECP8; 8] = [
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+        ];
+        let mut T2: [ECP8; 8] = [
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+        ];
+        let mut T3: [ECP8; 8] = [
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+        ];
+        let mut T4: [ECP8; 8] = [
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+        ];
+
+        let mut mt = BIG::new();
+
+        let mut t: [BIG; 16] = [
+            BIG::new_copy(&u[0]),
+            BIG::new_copy(&u[1]),
+            BIG::new_copy(&u[2]),
+            BIG::new_copy(&u[3]),
+            BIG::new_copy(&u[4]),
+            BIG::new_copy(&u[5]),
+            BIG::new_copy(&u[6]),
+            BIG::new_copy(&u[7]),
+            BIG::new_copy(&u[8]),
+            BIG::new_copy(&u[9]),
+            BIG::new_copy(&u[10]),
+            BIG::new_copy(&u[11]),
+            BIG::new_copy(&u[12]),
+            BIG::new_copy(&u[13]),
+            BIG::new_copy(&u[14]),
+            BIG::new_copy(&u[15]),
+        ];
+
+        const CT: usize = 1 + big::NLEN * (big::BASEBITS as usize);
+        let mut w1: [i8; CT] = [0; CT];
+        let mut s1: [i8; CT] = [0; CT];
+        let mut w2: [i8; CT] = [0; CT];
+        let mut s2: [i8; CT] = [0; CT];
+        let mut w3: [i8; CT] = [0; CT];
+        let mut s3: [i8; CT] = [0; CT];
+        let mut w4: [i8; CT] = [0; CT];
+        let mut s4: [i8; CT] = [0; CT];
+
+        for i in 0..16 {
+            //Q[i].affine();
+            t[i].norm();
+        }
+
+        T1[0].copy(&Q[0]);
+        W.copy(&T1[0]);
+        T1[1].copy(&W);
+        T1[1].add(&mut Q[1]); // Q[0]+Q[1]
+        T1[2].copy(&W);
+        T1[2].add(&mut Q[2]);
+        W.copy(&T1[1]); // Q[0]+Q[2]
+        T1[3].copy(&W);
+        T1[3].add(&mut Q[2]);
+        W.copy(&T1[0]); // Q[0]+Q[1]+Q[2]
+        T1[4].copy(&W);
+        T1[4].add(&mut Q[3]);
+        W.copy(&T1[1]); // Q[0]+Q[3]
+        T1[5].copy(&W);
+        T1[5].add(&mut Q[3]);
+        W.copy(&T1[2]); // Q[0]+Q[1]+Q[3]
+        T1[6].copy(&W);
+        T1[6].add(&mut Q[3]);
+        W.copy(&T1[3]); // Q[0]+Q[2]+Q[3]
+        T1[7].copy(&W);
+        T1[7].add(&mut Q[3]); // Q[0]+Q[1]+Q[2]+Q[3]
+
+        T2[0].copy(&Q[4]);
+        W.copy(&T2[0]);
+        T2[1].copy(&W);
+        T2[1].add(&mut Q[5]); // Q[0]+Q[1]
+        T2[2].copy(&W);
+        T2[2].add(&mut Q[6]);
+        W.copy(&T2[1]); // Q[0]+Q[2]
+        T2[3].copy(&W);
+        T2[3].add(&mut Q[6]);
+        W.copy(&T2[0]); // Q[0]+Q[1]+Q[2]
+        T2[4].copy(&W);
+        T2[4].add(&mut Q[7]);
+        W.copy(&T2[1]); // Q[0]+Q[3]
+        T2[5].copy(&W);
+        T2[5].add(&mut Q[7]);
+        W.copy(&T2[2]); // Q[0]+Q[1]+Q[3]
+        T2[6].copy(&W);
+        T2[6].add(&mut Q[7]);
+        W.copy(&T2[3]); // Q[0]+Q[2]+Q[3]
+        T2[7].copy(&W);
+        T2[7].add(&mut Q[7]); // Q[0]+Q[1]+Q[2]+Q[3]
+
+        T3[0].copy(&Q[8]);
+        W.copy(&T3[0]);
+        T3[1].copy(&W);
+        T3[1].add(&mut Q[9]); // Q[0]+Q[1]
+        T3[2].copy(&W);
+        T3[2].add(&mut Q[10]);
+        W.copy(&T3[1]); // Q[0]+Q[2]
+        T3[3].copy(&W);
+        T3[3].add(&mut Q[10]);
+        W.copy(&T3[0]); // Q[0]+Q[1]+Q[2]
+        T3[4].copy(&W);
+        T3[4].add(&mut Q[11]);
+        W.copy(&T3[1]); // Q[0]+Q[3]
+        T3[5].copy(&W);
+        T3[5].add(&mut Q[11]);
+        W.copy(&T3[2]); // Q[0]+Q[1]+Q[3]
+        T3[6].copy(&W);
+        T3[6].add(&mut Q[11]);
+        W.copy(&T3[3]); // Q[0]+Q[2]+Q[3]
+        T3[7].copy(&W);
+        T3[7].add(&mut Q[11]); // Q[0]+Q[1]+Q[2]+Q[3]
+
+        T4[0].copy(&Q[12]);
+        W.copy(&T4[0]);
+        T4[1].copy(&W);
+        T4[1].add(&mut Q[13]); // Q[0]+Q[1]
+        T4[2].copy(&W);
+        T4[2].add(&mut Q[14]);
+        W.copy(&T4[1]); // Q[0]+Q[2]
+        T4[3].copy(&W);
+        T4[3].add(&mut Q[14]);
+        W.copy(&T4[0]); // Q[0]+Q[1]+Q[2]
+        T4[4].copy(&W);
+        T4[4].add(&mut Q[15]);
+        W.copy(&T4[1]); // Q[0]+Q[3]
+        T4[5].copy(&W);
+        T4[5].add(&mut Q[15]);
+        W.copy(&T4[2]); // Q[0]+Q[1]+Q[3]
+        T4[6].copy(&W);
+        T4[6].add(&mut Q[15]);
+        W.copy(&T4[3]); // Q[0]+Q[2]+Q[3]
+        T4[7].copy(&W);
+        T4[7].add(&mut Q[15]); // Q[0]+Q[1]+Q[2]+Q[3]
+
+        // Make it odd
+        let pb1 = 1 - t[0].parity();
+        t[0].inc(pb1);
+
+        let pb2 = 1 - t[4].parity();
+        t[4].inc(pb2);
+
+        let pb3 = 1 - t[8].parity();
+        t[8].inc(pb3);
+
+        let pb4 = 1 - t[12].parity();
+        t[12].inc(pb4);
+
+        // Number of bits
+        mt.zero();
+        for i in 0..16 {
+	    t[i].norm();
+            mt.or(&t[i]);
+        }
+
+        let nb = 1 + mt.nbits();
+
+        // Sign pivot
+
+        s1[nb - 1] = 1;
+        s2[nb - 1] = 1;
+        s3[nb - 1] = 1;
+        s4[nb - 1] = 1;
+        for i in 0..nb - 1 {
+            t[0].fshr(1);
+            s1[i] = (2 * t[0].parity() - 1) as i8;
+            t[4].fshr(1);
+            s2[i] = (2 * t[4].parity() - 1) as i8;
+            t[8].fshr(1);
+            s3[i] = (2 * t[8].parity() - 1) as i8;
+            t[12].fshr(1);
+            s4[i] = (2 * t[12].parity() - 1) as i8;
+        }
+
+        // Recoded exponent
+        for i in 0..nb {
+            w1[i] = 0;
+            let mut k = 1;
+            for j in 1..4 {
+                let bt = s1[i] * (t[j].parity() as i8);
+                t[j].fshr(1);
+                t[j].dec((bt >> 1) as isize);
+                t[j].norm();
+                w1[i] += bt * (k as i8);
+                k = 2 * k;
+            }
+
+            w2[i] = 0;
+            k = 1;
+            for j in 5..8 {
+                let bt = s2[i] * (t[j].parity() as i8);
+                t[j].fshr(1);
+                t[j].dec((bt >> 1) as isize);
+                t[j].norm();
+                w2[i] += bt * (k as i8);
+                k = 2 * k;
+            }
+
+            w3[i] = 0;
+            k = 1;
+            for j in 9..12 {
+                let bt = s3[i] * (t[j].parity() as i8);
+                t[j].fshr(1);
+                t[j].dec((bt >> 1) as isize);
+                t[j].norm();
+                w3[i] += bt * (k as i8);
+                k = 2 * k;
+            }
+
+            w4[i] = 0;
+            k = 1;
+            for j in 13..16 {
+                let bt = s4[i] * (t[j].parity() as i8);
+                t[j].fshr(1);
+                t[j].dec((bt >> 1) as isize);
+                t[j].norm();
+                w4[i] += bt * (k as i8);
+                k = 2 * k;
+            }
+        }
+
+        // Main loop
+        P.selector(&T1, (2 * w1[nb - 1] + 1) as i32);
+        W.selector(&T2, (2 * w2[nb - 1] + 1) as i32);
+        P.add(&mut W);
+        W.selector(&T3, (2 * w3[nb - 1] + 1) as i32);
+        P.add(&mut W);
+        W.selector(&T4, (2 * w4[nb - 1] + 1) as i32);
+        P.add(&mut W);
+        for i in (0..nb - 1).rev() {
+            P.dbl();
+            W.selector(&T1, (2 * w1[i] + s1[i]) as i32);
+            P.add(&mut W);
+            W.selector(&T2, (2 * w2[i] + s2[i]) as i32);
+            P.add(&mut W);
+            W.selector(&T3, (2 * w3[i] + s3[i]) as i32);
+            P.add(&mut W);
+            W.selector(&T4, (2 * w4[i] + s4[i]) as i32);
+            P.add(&mut W);
+        }
+
+        // apply correction
+        W.copy(&P);
+        W.sub(&mut Q[0]);
+        P.cmove(&W, pb1);
+
+        W.copy(&P);
+        W.sub(&mut Q[4]);
+        P.cmove(&W, pb2);
+
+        W.copy(&P);
+        W.sub(&mut Q[8]);
+        P.cmove(&W, pb3);
+
+        W.copy(&P);
+        W.sub(&mut Q[12]);
+        P.cmove(&W, pb4);
+
+        P.affine();
+
+        return P;
+    }
+
+    pub fn generator() -> ECP8 {
+        return ECP8::new_fp8s(
+            &FP8::new_fp4s(
+                &FP4::new_fp2s(
+                    &FP2::new_bigs(
+                        &BIG::new_ints(&rom::CURVE_PXAAA),
+                        &BIG::new_ints(&rom::CURVE_PXAAB),
+                    ),
+                    &FP2::new_bigs(
+                        &BIG::new_ints(&rom::CURVE_PXABA),
+                        &BIG::new_ints(&rom::CURVE_PXABB),
+                    ),
+                ),
+                &FP4::new_fp2s(
+                    &FP2::new_bigs(
+                        &BIG::new_ints(&rom::CURVE_PXBAA),
+                        &BIG::new_ints(&rom::CURVE_PXBAB),
+                    ),
+                    &FP2::new_bigs(
+                        &BIG::new_ints(&rom::CURVE_PXBBA),
+                        &BIG::new_ints(&rom::CURVE_PXBBB),
+                    ),
+                ),
+            ),
+            &FP8::new_fp4s(
+                &FP4::new_fp2s(
+                    &FP2::new_bigs(
+                        &BIG::new_ints(&rom::CURVE_PYAAA),
+                        &BIG::new_ints(&rom::CURVE_PYAAB),
+                    ),
+                    &FP2::new_bigs(
+                        &BIG::new_ints(&rom::CURVE_PYABA),
+                        &BIG::new_ints(&rom::CURVE_PYABB),
+                    ),
+                ),
+                &FP4::new_fp2s(
+                    &FP2::new_bigs(
+                        &BIG::new_ints(&rom::CURVE_PYBAA),
+                        &BIG::new_ints(&rom::CURVE_PYBAB),
+                    ),
+                    &FP2::new_bigs(
+                        &BIG::new_ints(&rom::CURVE_PYBBA),
+                        &BIG::new_ints(&rom::CURVE_PYBBB),
+                    ),
+                ),
+            ),
+        );
+    }
+
+    #[allow(non_snake_case)]
+    pub fn mapit(h: &[u8]) -> ECP8 {
+        let mut q = BIG::new_ints(&rom::MODULUS);
+        let mut x = BIG::frombytes(h);
+        x.rmod(&mut q);
+        let mut Q: ECP8;
+        let one = BIG::new_int(1);
+
+        loop {
+            let X = FP8::new_fp4(&FP4::new_fp2(&FP2::new_bigs(&one, &x)));
+            Q = ECP8::new_fp8(&X);
+            if !Q.is_infinity() {
+                break;
+            }
+            x.inc(1);
+            x.norm();
+        }
+
+        let f = ECP8::frob_constants();
+        x = BIG::new_ints(&rom::CURVE_BNX);
+
+        let mut xQ = Q.mul(&mut x);
+        let mut x2Q = xQ.mul(&mut x);
+        let mut x3Q = x2Q.mul(&mut x);
+        let mut x4Q = x3Q.mul(&mut x);
+        let mut x5Q = x4Q.mul(&mut x);
+        let mut x6Q = x5Q.mul(&mut x);
+        let mut x7Q = x6Q.mul(&mut x);
+        let mut x8Q = x7Q.mul(&mut x);
+
+        if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+            xQ.neg();
+            x3Q.neg();
+            x5Q.neg();
+            x7Q.neg();
+        }
+
+        x8Q.sub(&x7Q);
+        x8Q.sub(&Q);
+
+        x7Q.sub(&x6Q);
+        x7Q.frob(&f, 1);
+
+        x6Q.sub(&x5Q);
+        x6Q.frob(&f, 2);
+
+        x5Q.sub(&x4Q);
+        x5Q.frob(&f, 3);
+
+        x4Q.sub(&x3Q);
+        x4Q.frob(&f, 4);
+
+        x3Q.sub(&x2Q);
+        x3Q.frob(&f, 5);
+
+        x2Q.sub(&xQ);
+        x2Q.frob(&f, 6);
+
+        xQ.sub(&Q);
+        xQ.frob(&f, 7);
+
+        Q.dbl();
+        Q.frob(&f, 8);
+
+        Q.add(&x8Q);
+        Q.add(&x7Q);
+        Q.add(&x6Q);
+        Q.add(&x5Q);
+
+        Q.add(&x4Q);
+        Q.add(&x3Q);
+        Q.add(&x2Q);
+        Q.add(&xQ);
+
+        Q.affine();
+        return Q;
+    }
+}
diff --git a/src/ff.rs b/src/ff.rs
new file mode 100644
index 0000000..4737ee8
--- /dev/null
+++ b/src/ff.rs
@@ -0,0 +1,1058 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+if debug {println!("sf2= {}",self.tostring())}
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use super::big;
+use super::dbig::DBIG;
+use super::big::BIG;
+use super::super::arch::Chunk;
+use rand::RAND;
+
+use super::super::arch::DChunk;
+
+/* Finite field support - for RSA, DH etc. */
+/* RSA/DH modulus length as multiple of BIGBITS */
+
+pub use super::rom::FFLEN;
+//use std::str::SplitWhitespace;
+
+pub const FF_BITS: usize = (big::BIGBITS * FFLEN); /* Finite Field Size in bits - must be 256.2^n */
+pub const HFLEN: usize = (FFLEN / 2); /* Useful for half-size RSA private key operations */
+
+pub const P_MBITS: usize = (big::MODBYTES as usize) * 8;
+pub const P_OMASK: Chunk = ((-1) << (P_MBITS % big::BASEBITS));
+pub const P_FEXCESS: Chunk = (1 << (big::BASEBITS * big::NLEN - P_MBITS - 1));
+pub const P_TBITS: usize = (P_MBITS % big::BASEBITS);
+
+pub struct FF {
+    v: Vec<BIG>,
+    length: usize,
+}
+
+impl FF {
+    pub fn excess(a: &BIG) -> Chunk {
+        return ((a.w[big::NLEN - 1] & P_OMASK) >> (P_TBITS)) + 1;
+    }
+
+    pub fn pexceed(a: &BIG, b: &BIG) -> bool {
+        let ea = FF::excess(a);
+        let eb = FF::excess(b);
+        if ((ea + 1) as DChunk) * ((eb + 1) as DChunk) > P_FEXCESS as DChunk {
+            return true;
+        }
+        return false;
+    }
+
+    pub fn sexceed(a: &BIG) -> bool {
+        let ea = FF::excess(a);
+        if ((ea + 1) as DChunk) * ((ea + 1) as DChunk) > P_FEXCESS as DChunk {
+            return true;
+        }
+        return false;
+    }
+
+    /* Constructors */
+    pub fn new_int(n: usize) -> FF {
+        let mut f = FF {
+            v: Vec::new(),
+            length: 0,
+        };
+        for _ in 0..n {
+            f.v.push(BIG::new());
+        }
+        f.length = n;
+        return f;
+    }
+
+    pub fn zero(&mut self) {
+        for i in 0..self.length {
+            self.v[i].zero();
+        }
+    }
+
+    pub fn getlen(&self) -> usize {
+        return self.length;
+    }
+
+    /* set to integer */
+    pub fn set(&mut self, m: isize) {
+        self.zero();
+        self.v[0].set(0, m as Chunk);
+    }
+
+    /* copy from FF b */
+    pub fn copy(&mut self, b: &FF) {
+        for i in 0..self.length {
+            self.v[i].copy(&b.v[i]);
+        }
+    }
+
+    /* x=y<<n */
+    pub fn dsucopy(&mut self, b: &FF) {
+        for i in 0..b.length {
+            self.v[b.length + i].copy(&b.v[i]);
+            self.v[i].zero();
+        }
+    }
+
+    /* x=y */
+    pub fn dscopy(&mut self, b: &FF) {
+        for i in 0..b.length {
+            self.v[i].copy(&b.v[i]);
+            self.v[b.length + i].zero();
+        }
+    }
+
+    /* x=y>>n */
+    pub fn sducopy(&mut self, b: &FF) {
+        for i in 0..self.length {
+            self.v[i].copy(&b.v[self.length + i]);
+        }
+    }
+
+    pub fn one(&mut self) {
+        self.v[0].one();
+        for i in 1..self.length {
+            self.v[i].zero();
+        }
+    }
+
+    /* test equals 0 */
+    pub fn iszilch(&mut self) -> bool {
+        for i in 0..self.length {
+            if !self.v[i].iszilch() {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /* shift right by BIGBITS-bit words */
+    pub fn shrw(&mut self, n: usize) {
+        let mut t = BIG::new();
+        for i in 0..n {
+            t.copy(&self.v[i + n]);
+            self.v[i].copy(&t);
+            self.v[i + n].zero();
+        }
+    }
+
+    /* shift left by BIGBITS-bit words */
+    pub fn shlw(&mut self, n: usize) {
+        let mut t = BIG::new();
+        for i in 0..n {
+            t.copy(&self.v[i]);
+            self.v[n + i].copy(&t);
+            self.v[i].zero();
+        }
+    }
+
+    /* extract last bit */
+    pub fn parity(&self) -> isize {
+        return self.v[0].parity();
+    }
+
+    pub fn lastbits(&mut self, m: usize) -> isize {
+        return self.v[0].lastbits(m);
+    }
+
+    /* compare x and y - must be normalised, and of same length */
+    pub fn comp(a: &FF, b: &FF) -> isize {
+        let mut i = a.length - 1;
+
+        loop {
+            let j = BIG::comp(&a.v[i], &b.v[i]);
+            if j != 0 {
+                return j;
+            }
+            if i == 0 {
+                break;
+            }
+            i -= 1;
+        }
+        return 0;
+    }
+
+    /* recursive add */
+    pub fn radd(&mut self, vp: usize, x: &FF, xp: usize, y: &FF, yp: usize, n: usize) {
+        for i in 0..n {
+            self.v[vp + i].copy(&x.v[xp + i]);
+            self.v[vp + i].add(&y.v[yp + i]);
+        }
+    }
+
+    /* recursive inc */
+    pub fn rinc(&mut self, vp: usize, y: &FF, yp: usize, n: usize) {
+        for i in 0..n {
+            self.v[vp + i].add(&y.v[yp + i]);
+        }
+    }
+
+    pub fn rsinc(&mut self, n: usize) {
+        let mut t = BIG::new();
+        for i in 0..n {
+            t.copy(&self.v[i]);
+            self.v[n + i].add(&t);
+        }
+    }
+
+    /* recursive sub */
+    pub fn rsub(&mut self, vp: usize, x: &FF, xp: usize, y: &FF, yp: usize, n: usize) {
+        for i in 0..n {
+            self.v[vp + i].copy(&x.v[xp + i]);
+            self.v[vp + i].sub(&y.v[yp + i]);
+        }
+    }
+
+    /* recursive dec */
+    pub fn rdec(&mut self, vp: usize, y: &FF, yp: usize, n: usize) {
+        for i in 0..n {
+            self.v[vp + i].sub(&y.v[yp + i]);
+        }
+    }
+
+    /* simple add */
+    pub fn add(&mut self, b: &FF) {
+        for i in 0..self.length {
+            self.v[i].add(&b.v[i]);
+        }
+    }
+
+    /* simple sub */
+    pub fn sub(&mut self, b: &FF) {
+        for i in 0..self.length {
+            self.v[i].sub(&b.v[i]);
+        }
+    }
+
+    /* reverse sub */
+    pub fn revsub(&mut self, b: &FF) {
+        for i in 0..self.length {
+            self.v[i].rsub(&b.v[i]);
+        }
+    }
+
+    /* normalise - but hold any overflow in top part unless n<0 */
+    pub fn rnorm(&mut self, vp: usize, n: isize) {
+        let mut trunc = false;
+        let mut carry: Chunk;
+        let mut nn: usize = n as usize;
+        if n < 0 {
+            /* -v n signals to do truncation */
+            nn = (-n) as usize;
+            trunc = true;
+        }
+        for i in 0..nn - 1 {
+            carry = self.v[vp + i].norm();
+            self.v[vp + i].xortop(carry << P_TBITS);
+            self.v[vp + i + 1].w[0] += carry;
+        }
+        carry = self.v[vp + nn - 1].norm();
+        if trunc {
+            self.v[vp + nn - 1].xortop(carry << P_TBITS);
+        }
+    }
+
+    pub fn norm(&mut self) {
+        let n: isize = self.length as isize;
+        self.rnorm(0, n);
+    }
+
+    /* increment/decrement by a small integer */
+    pub fn inc(&mut self, m: isize) {
+        self.v[0].inc(m);
+        self.norm();
+    }
+
+    pub fn dec(&mut self, m: isize) {
+        self.v[0].dec(m);
+        self.norm();
+    }
+
+    /* shift left by one bit */
+    pub fn shl(&mut self) {
+        let mut delay_carry: isize = 0;
+        for i in 0..self.length - 1 {
+            let carry = self.v[i].fshl(1);
+            self.v[i].inc(delay_carry);
+            self.v[i].xortop((carry as Chunk) << P_TBITS);
+            delay_carry = carry;
+        }
+        self.v[self.length - 1].fshl(1);
+        self.v[self.length - 1].inc(delay_carry);
+    }
+
+    /* shift right by one bit */
+
+    pub fn shr(&mut self) {
+        let mut i = self.length - 1;
+        while i > 0 {
+            let carry = self.v[i].fshr(1);
+            self.v[i - 1].xortop((carry as Chunk) << P_TBITS);
+            i -= 1;
+        }
+        self.v[0].fshr(1);
+    }
+
+    /* Convert to Hex String */
+    pub fn tostring(&mut self) -> String {
+        self.norm();
+        let mut s = String::new();
+        let mut i: usize = self.length - 1;
+        loop {
+            s = s + self.v[i].tostring().as_ref();
+            if i == 0 {
+                break;
+            }
+            i -= 1;
+        }
+        return s;
+    }
+
+    /* Convert FFs to/from byte arrays */
+    pub fn tobytes(&mut self, b: &mut [u8]) {
+        for i in 0..self.length {
+            self.v[i].tobytearray(b, (self.length - i - 1) * (big::MODBYTES as usize))
+        }
+    }
+
+    pub fn frombytes(x: &mut FF, b: &[u8]) {
+        for i in 0..x.length {
+            x.v[i] = BIG::frombytearray(b, (x.length - i - 1) * (big::MODBYTES as usize))
+        }
+    }
+
+    /* in-place swapping using xor - side channel resistant - lengths must be the same */
+    pub fn cswap(a: &mut FF, b: &mut FF, d: isize) {
+        for i in 0..a.length {
+            a.v[i].cswap(&mut b.v[i], d);
+        }
+    }
+
+    /* z=x*y, t is workspace */
+    fn karmul(
+        &mut self,
+        vp: usize,
+        x: &FF,
+        xp: usize,
+        y: &FF,
+        yp: usize,
+        t: *mut FF,
+        tp: usize,
+        n: usize,
+    ) {
+        if n == 1 {
+            let xx = BIG::new_copy(&x.v[xp]);
+            let yy = BIG::new_copy(&y.v[yp]);
+            let mut d = BIG::mul(&xx, &yy);
+            self.v[vp + 1] = d.split(8 * big::MODBYTES);
+            self.v[vp].dcopy(&d);
+            return;
+        }
+        let nd2 = n / 2;
+        self.radd(vp, x, xp, x, xp + nd2, nd2);
+        self.rnorm(vp, nd2 as isize); /* Important - required for 32-bit build */
+        self.radd(vp + nd2, y, yp, y, yp + nd2, nd2);
+        self.rnorm(vp + nd2, nd2 as isize); /* Important - required for 32-bit build */
+        unsafe {
+            (*t).karmul(tp, self, vp, self, vp + nd2, t, tp + n, nd2);
+        }
+        self.karmul(vp, x, xp, y, yp, t, tp + n, nd2);
+        self.karmul(vp + n, x, xp + nd2, y, yp + nd2, t, tp + n, nd2);
+        unsafe {
+            (*t).rdec(tp, self, vp, n);
+            (*t).rdec(tp, self, vp + n, n);
+            self.rinc(vp + nd2, &(*t), tp, n);
+        }
+        self.rnorm(vp, (2 * n) as isize);
+    }
+
+    fn karsqr(&mut self, vp: usize, x: &FF, xp: usize, t: *mut FF, tp: usize, n: usize) {
+        if n == 1 {
+            let xx = BIG::new_copy(&x.v[xp]);
+            let mut d = BIG::sqr(&xx);
+            self.v[vp + 1].copy(&d.split(8 * big::MODBYTES));
+            self.v[vp].dcopy(&d);
+            return;
+        }
+
+        let nd2 = n / 2;
+        self.karsqr(vp, x, xp, t, tp + n, nd2);
+        self.karsqr(vp + n, x, xp + nd2, t, tp + n, nd2);
+        unsafe {
+            (*t).karmul(tp, x, xp, x, xp + nd2, t, tp + n, nd2);
+            self.rinc(vp + nd2, &(*t), tp, n);
+            self.rinc(vp + nd2, &(*t), tp, n);
+        }
+        self.rnorm(vp + nd2, n as isize);
+    }
+
+    /* Calculates Least Significant bottom half of x*y */
+    fn karmul_lower(
+        &mut self,
+        vp: usize,
+        x: &FF,
+        xp: usize,
+        y: &FF,
+        yp: usize,
+        t: *mut FF,
+        tp: usize,
+        n: usize,
+    ) {
+        if n == 1 {
+            /* only calculate bottom half of product */
+            self.v[vp].copy(&BIG::smul(&x.v[xp], &y.v[yp]));
+            return;
+        }
+        let nd2 = n / 2;
+
+        self.karmul(vp, x, xp, y, yp, t, tp + n, nd2);
+        unsafe {
+            (*t).karmul_lower(tp, x, xp + nd2, y, yp, t, tp + n, nd2);
+            self.rinc(vp + nd2, &(*t), tp, nd2);
+            (*t).karmul_lower(tp, x, xp, y, yp + nd2, t, tp + n, nd2);
+            self.rinc(vp + nd2, &(*t), tp, nd2);
+        }
+        let sn: isize = nd2 as isize;
+        self.rnorm(vp + nd2, -sn); /* truncate it */
+    }
+
+    /* Calculates Most Significant upper half of x*y, given lower part */
+    fn karmul_upper(&mut self, x: &FF, y: &FF, t: *mut FF, n: usize) {
+        let nd2 = n / 2;
+        self.radd(n, x, 0, x, nd2, nd2);
+        self.radd(n + nd2, y, 0, y, nd2, nd2);
+        self.rnorm(n, nd2 as isize);
+        self.rnorm(n + nd2, nd2 as isize);
+
+        unsafe {
+            (*t).karmul(0, self, n + nd2, self, n, t, n, nd2); /* t = (a0+a1)(b0+b1) */
+        }
+        self.karmul(n, x, nd2, y, nd2, t, n, nd2); /* z[n]= a1*b1 */
+        /* z[0-nd2]=l(a0b0) z[nd2-n]= h(a0b0)+l(t)-l(a0b0)-l(a1b1) */
+        unsafe {
+            (*t).rdec(0, self, n, n); /* t=t-a1b1  */
+
+            self.rsinc(nd2); /* z[nd2-n]+=l(a0b0) = h(a0b0)+l(t)-l(a1b1)  */
+            self.rdec(nd2, &(*t), 0, nd2); /* z[nd2-n]=h(a0b0)+l(t)-l(a1b1)-l(t-a1b1)=h(a0b0) */
+        }
+
+        let sn: isize = n as isize;
+        self.rnorm(0, -sn); /* a0b0 now in z - truncate it */
+        unsafe {
+            (*t).rdec(0, self, 0, n); /* (a0+a1)(b0+b1) - a0b0 */
+            self.rinc(nd2, &(*t), 0, n);
+        }
+        self.rnorm(nd2, sn);
+    }
+
+    /* z=x*y. Assumes x and y are of same length. */
+    pub fn mul(x: &FF, y: &FF) -> FF {
+        let n = x.length;
+        let mut z = FF::new_int(2 * n);
+        let mut t = FF::new_int(2 * n);
+        z.karmul(0, &x, 0, &y, 0, &mut t, 0, n);
+        return z;
+    }
+
+    /* return low part of product this*y */
+    pub fn lmul(&mut self, y: &FF) {
+        let n = self.length;
+        let mut t = FF::new_int(2 * n);
+        let mut x = FF::new_int(n);
+        x.copy(&self);
+        self.karmul_lower(0, &x, 0, &y, 0, &mut t, 0, n);
+    }
+
+    /* Set b=b mod c */
+    pub fn rmod(&mut self, m: &FF) {
+        let mut k = 1;
+        let n = m.length;
+        let mut c = FF::new_int(n);
+        c.copy(m);
+
+        self.norm();
+        if FF::comp(&self, &c) < 0 {
+            return;
+        }
+
+        c.shl();
+        while FF::comp(&self, &c) >= 0 {
+            c.shl();
+            k += 1;
+        }
+
+        while k > 0 {
+            c.shr();
+            if FF::comp(&self, &c) >= 0 {
+                self.sub(&c);
+                self.norm();
+            }
+            k -= 1;
+        }
+    }
+
+    /* z=x^2 */
+    pub fn sqr(x: &FF) -> FF {
+        let n = x.length;
+        let mut z = FF::new_int(2 * n);
+        let mut t = FF::new_int(2 * n);
+        z.karsqr(0, &x, 0, &mut t, 0, n);
+        return z;
+    }
+
+    /* return This mod modulus, ms is modulus, md is Montgomery Constant */
+    pub fn reduce(&mut self, ms: &FF, md: &FF) -> FF {
+        /* fast karatsuba Montgomery reduction */
+        let n = ms.length;
+        let mut t = FF::new_int(2 * n);
+        let mut r = FF::new_int(n);
+        let mut m = FF::new_int(n);
+
+        r.sducopy(&self);
+        m.karmul_lower(0, &self, 0, &md, 0, &mut t, 0, n);
+        self.karmul_upper(&ms, &m, &mut t, n);
+
+        m.sducopy(self);
+        r.add(&ms);
+        r.sub(&m);
+        r.norm();
+
+        return r;
+    }
+
+    /* Set r=this mod b */
+    /* this is of length - 2*n */
+    /* r,b is of length - n */
+    pub fn dmod(&mut self, b: &FF) -> FF {
+        let n = b.length;
+        let mut m = FF::new_int(2 * n);
+        let mut x = FF::new_int(2 * n);
+        let mut r = FF::new_int(n);
+
+        x.copy(&self);
+        x.norm();
+        m.dsucopy(&b);
+        let mut k = big::BIGBITS * n;
+
+        while FF::comp(&x, &m) >= 0 {
+            x.sub(&m);
+            x.norm();
+        }
+
+        while k > 0 {
+            m.shr();
+
+            if FF::comp(&x, &m) >= 0 {
+                x.sub(&m);
+                x.norm();
+            }
+            k -= 1;
+        }
+
+        r.copy(&x);
+        r.rmod(b);
+        return r;
+    }
+
+    /* Set return=1/this mod p. Binary method - a<p on entry */
+
+    pub fn invmodp(&mut self, p: &FF) {
+        let n = p.length;
+
+        let mut u = FF::new_int(n);
+        let mut v = FF::new_int(n);
+        let mut x1 = FF::new_int(n);
+        let mut x2 = FF::new_int(n);
+        let mut t = FF::new_int(n);
+        let mut one = FF::new_int(n);
+
+        one.one();
+        u.copy(&self);
+        v.copy(&p);
+        x1.copy(&one);
+        x2.zero();
+
+        // reduce n in here as well!
+        while FF::comp(&u, &one) != 0 && FF::comp(&v, &one) != 0 {
+            while u.parity() == 0 {
+                u.shr();
+                if x1.parity() != 0 {
+                    x1.add(&p);
+                    x1.norm();
+                }
+                x1.shr();
+            }
+            while v.parity() == 0 {
+                v.shr();
+                if x2.parity() != 0 {
+                    x2.add(&p);
+                    x2.norm();
+                }
+                x2.shr();
+            }
+            if FF::comp(&u, &v) >= 0 {
+                u.sub(&v);
+                u.norm();
+                if FF::comp(&x1, &x2) >= 0 {
+                    x1.sub(&x2);
+                } else {
+                    t.copy(&p);
+                    t.sub(&x2);
+                    x1.add(&t);
+                }
+                x1.norm();
+            } else {
+                v.sub(&u);
+                v.norm();
+                if FF::comp(&x2, &x1) >= 0 {
+                    x2.sub(&x1);
+                } else {
+                    t.copy(&p);
+                    t.sub(&x1);
+                    x2.add(&t);
+                }
+                x2.norm();
+            }
+        }
+        if FF::comp(&u, &one) == 0 {
+            self.copy(&x1);
+        } else {
+            self.copy(&x2);
+        }
+    }
+
+    /* nresidue mod m */
+    pub fn nres(&mut self, m: &FF) {
+        let n = m.length;
+        if n == 1 {
+            let mut d = DBIG::new_scopy(&(self.v[0]));
+            d.shl(big::NLEN * (big::BASEBITS as usize));
+            self.v[0].copy(&d.dmod(&(m.v[0])));
+        } else {
+            let mut d = FF::new_int(2 * n);
+            d.dsucopy(&self);
+            self.copy(&d.dmod(m));
+        }
+    }
+
+    pub fn redc(&mut self, m: &FF, md: &FF) {
+        let n = m.length;
+        if n == 1 {
+            let mut d = DBIG::new_scopy(&(self.v[0]));
+            self.v[0].copy(&BIG::monty(
+                &(m.v[0]),
+                ((1 as Chunk) << big::BASEBITS) - md.v[0].w[0],
+                &mut d,
+            ));
+        } else {
+            let mut d = FF::new_int(2 * n);
+            self.rmod(m);
+            d.dscopy(&self);
+            self.copy(&d.reduce(&m, &md));
+            self.rmod(m);
+        }
+    }
+
+    pub fn mod2m(&mut self, m: usize) {
+        for i in m..self.length {
+            self.v[i].zero()
+        }
+    }
+
+    /* U=1/a mod 2^m - Arazi & Qi */
+    pub fn invmod2m(&self) -> FF {
+        let n = self.length;
+
+        let mut b = FF::new_int(n);
+        let mut c = FF::new_int(n);
+        let mut u = FF::new_int(n);
+
+        u.zero();
+        u.v[0].copy(&self.v[0]);
+        u.v[0].invmod2m();
+
+        let mut i = 1;
+        while i < n {
+            b.copy(&self);
+            b.mod2m(i);
+            let mut t = FF::mul(&u, &b);
+            t.shrw(i);
+            b.copy(&t);
+            c.copy(&self);
+            c.shrw(i);
+            c.mod2m(i);
+            c.lmul(&u);
+            c.mod2m(i);
+
+            b.add(&c);
+            b.norm();
+            b.lmul(&u);
+            b.mod2m(i);
+
+            c.one();
+            c.shlw(i);
+            b.revsub(&c);
+            b.norm();
+            b.shlw(i);
+            u.add(&b);
+            i <<= 1;
+        }
+        u.norm();
+        return u;
+    }
+
+    pub fn random(&mut self, rng: &mut RAND) {
+        let n = self.length;
+        for i in 0..n {
+            self.v[i].copy(&BIG::random(rng))
+        }
+        /* make sure top bit is 1 */
+        while self.v[n - 1].nbits() < (big::MODBYTES as usize) * 8 {
+            self.v[n - 1].copy(&BIG::random(rng));
+        }
+    }
+
+    /* generate random x less than p */
+    pub fn randomnum(&mut self, p: &FF, rng: &mut RAND) {
+        let n = self.length;
+        let mut d = FF::new_int(2 * n);
+
+        for i in 0..2 * n {
+            d.v[i].copy(&BIG::random(rng));
+        }
+        self.copy(&d.dmod(p));
+    }
+
+    /* this*=y mod p */
+    pub fn modmul(&mut self, y: &FF, p: &FF, nd: &FF) {
+        if FF::pexceed(&self.v[self.length - 1], &y.v[y.length - 1]) {
+            self.rmod(p)
+        }
+        let n = p.length;
+        if n == 1 {
+            let mut d = BIG::mul(&self.v[0], &y.v[0]);
+            self.v[0].copy(&BIG::monty(
+                &(p.v[0]),
+                ((1 as Chunk) << big::BASEBITS) - nd.v[0].w[0],
+                &mut d,
+            ));
+        } else {
+            let mut d = FF::mul(&self, y);
+            self.copy(&d.reduce(p, nd));
+        }
+    }
+
+    /* this*=y mod p */
+    pub fn modsqr(&mut self, p: &FF, nd: &FF) {
+        if FF::sexceed(&self.v[self.length - 1]) {
+            self.rmod(p);
+        }
+        let n = p.length;
+        if n == 1 {
+            let mut d = BIG::sqr(&self.v[0]);
+            self.v[0].copy(&BIG::monty(
+                &(p.v[0]),
+                ((1 as Chunk) << big::BASEBITS) - nd.v[0].w[0],
+                &mut d,
+            ));
+        } else {
+            let mut d = FF::sqr(&self);
+            d.norm();
+            self.copy(&d.reduce(p, nd));
+        }
+    }
+
+    /* this=this^e mod p using side-channel resistant Montgomery Ladder, for large e */
+    pub fn skpow(&mut self, e: &FF, p: &FF) {
+        let n = p.length;
+        let mut r0 = FF::new_int(n);
+        let mut r1 = FF::new_int(n);
+        let nd = p.invmod2m();
+
+        self.rmod(p);
+        r0.one();
+        r1.copy(&self);
+        r0.nres(p);
+        r1.nres(p);
+
+        let mut i = 8 * (big::MODBYTES as usize) * n - 1;
+        loop {
+            let b = (e.v[i / (big::BIGBITS as usize)]).bit(i % (big::BIGBITS as usize)) as isize;
+            self.copy(&r0);
+            self.modmul(&r1, p, &nd);
+
+            FF::cswap(&mut r0, &mut r1, b);
+            r0.modsqr(p, &nd);
+
+            r1.copy(&self);
+            FF::cswap(&mut r0, &mut r1, b);
+            if i == 0 {
+                break;
+            }
+            i -= 1;
+        }
+        self.copy(&r0);
+        self.redc(p, &nd);
+    }
+
+    /* this =this^e mod p using side-channel resistant Montgomery Ladder, for short e */
+    pub fn skpows(&mut self, e: &BIG, p: &FF) {
+        let n = p.length;
+        let mut r0 = FF::new_int(n);
+        let mut r1 = FF::new_int(n);
+        let nd = p.invmod2m();
+
+        self.rmod(p);
+        r0.one();
+        r1.copy(&self);
+        r0.nres(p);
+        r1.nres(p);
+
+        let mut i = 8 * (big::MODBYTES as usize) - 1;
+        loop {
+            let b = e.bit(i);
+            self.copy(&r0);
+            self.modmul(&r1, p, &nd);
+
+            FF::cswap(&mut r0, &mut r1, b);
+            r0.modsqr(p, &nd);
+
+            r1.copy(&self);
+            FF::cswap(&mut r0, &mut r1, b);
+            if i == 0 {
+                break;
+            }
+            i -= 1;
+        }
+        self.copy(&r0);
+        self.redc(p, &nd);
+    }
+
+    /* raise to an integer power - right-to-left method */
+    pub fn power(&mut self, e: isize, p: &FF) {
+        let n = p.length;
+        let mut w = FF::new_int(n);
+        let nd = p.invmod2m();
+        let mut f = true;
+        let mut ee = e;
+
+        w.copy(&self);
+        w.nres(p);
+
+        if ee == 2 {
+            self.copy(&w);
+            self.modsqr(p, &nd);
+        } else {
+            loop {
+                if ee % 2 == 1 {
+                    if f {
+                        self.copy(&w);
+                    } else {
+                        self.modmul(&w, p, &nd)
+                    }
+                    f = false;
+                }
+                ee >>= 1;
+                if ee == 0 {
+                    break;
+                }
+                w.modsqr(p, &nd);
+            }
+        }
+
+        self.redc(p, &nd);
+    }
+
+    /* this=this^e mod p, faster but not side channel resistant */
+    pub fn pow(&mut self, e: &FF, p: &FF) {
+        let n = p.length;
+        let mut w = FF::new_int(n);
+        let nd = p.invmod2m();
+
+        w.copy(&self);
+        self.one();
+        self.nres(p);
+        w.nres(p);
+        let mut i = 8 * (big::MODBYTES as usize) * n - 1;
+        loop {
+            self.modsqr(p, &nd);
+            let b = (e.v[i / (big::BIGBITS as usize)]).bit(i % (big::BIGBITS as usize)) as isize;
+            if b == 1 {
+                self.modmul(&w, p, &nd)
+            }
+            if i == 0 {
+                break;
+            }
+            i -= 1;
+        }
+        self.redc(p, &nd);
+    }
+
+    /* double exponentiation r=x^e.y^f mod p */
+    pub fn pow2(&mut self, e: &BIG, y: &FF, f: &BIG, p: &FF) {
+        let n = p.length;
+        let mut xn = FF::new_int(n);
+        let mut yn = FF::new_int(n);
+        let mut xy = FF::new_int(n);
+        let nd = p.invmod2m();
+
+        xn.copy(&self);
+        yn.copy(y);
+        xn.nres(p);
+        yn.nres(p);
+        xy.copy(&xn);
+        xy.modmul(&yn, p, &nd);
+        self.one();
+        self.nres(p);
+
+        let mut i = 8 * (big::MODBYTES as usize) - 1;
+        loop {
+            let eb = e.bit(i);
+            let fb = f.bit(i);
+            self.modsqr(p, &nd);
+            if eb == 1 {
+                if fb == 1 {
+                    self.modmul(&xy, p, &nd);
+                } else {
+                    self.modmul(&xn, p, &nd)
+                }
+            } else {
+                if fb == 1 {
+                    self.modmul(&yn, p, &nd)
+                }
+            }
+            if i == 0 {
+                break;
+            }
+            i -= 1;
+        }
+        self.redc(p, &nd);
+    }
+
+    pub fn igcd(x: isize, y: isize) -> isize {
+        /* integer GCD, returns GCD of x and y */
+
+        if y == 0 {
+            return x;
+        }
+        let mut xx = x;
+        let mut yy = y;
+        loop {
+            let r = xx % yy;
+            if r == 0 {
+                break;
+            }
+            xx = yy;
+            yy = r;
+        }
+        return yy;
+    }
+
+    /* quick and dirty check for common factor with n */
+    pub fn cfactor(&self, s: isize) -> bool {
+        let n = self.length;
+
+        let mut x = FF::new_int(n);
+        let mut y = FF::new_int(n);
+
+        y.set(s);
+        x.copy(&self);
+        x.norm();
+
+        x.sub(&y);
+        x.norm();
+
+        while !x.iszilch() && x.parity() == 0 {
+            x.shr()
+        }
+
+        while FF::comp(&x, &y) > 0 {
+            x.sub(&y);
+            x.norm();
+            while !x.iszilch() && x.parity() == 0 {
+                x.shr()
+            }
+        }
+
+        let g = x.v[0].get(0) as isize;
+        let r = FF::igcd(s, g);
+        if r > 1 {
+            return true;
+        }
+        return false;
+    }
+
+    /* Miller-Rabin test for primality. Slow. */
+    pub fn prime(pp: &FF, rng: &mut RAND) -> bool {
+        let mut s = 0;
+        let n = pp.length;
+        let mut d = FF::new_int(n);
+        let mut x = FF::new_int(n);
+        let mut unity = FF::new_int(n);
+        let mut nm1 = FF::new_int(n);
+        let mut p = FF::new_int(n);
+        p.copy(pp);
+
+        let sf = 4849845; /* 3*5*.. *19 */
+        p.norm();
+
+        if p.cfactor(sf) {
+            return false;
+        }
+        unity.one();
+        nm1.copy(&p);
+        nm1.sub(&unity);
+        nm1.norm();
+        d.copy(&nm1);
+
+        while d.parity() == 0 {
+            d.shr();
+            s += 1;
+        }
+        if s == 0 {
+            return false;
+        }
+        for _ in 0..10 {
+            x.randomnum(&p, rng);
+
+            x.pow(&d, &p);
+
+            if FF::comp(&x, &unity) == 0 || FF::comp(&x, &nm1) == 0 {
+                continue;
+            }
+            let mut looper = false;
+            for _ in 1..s {
+                x.power(2, &p);
+                if FF::comp(&x, &unity) == 0 {
+                    return false;
+                }
+                if FF::comp(&x, &nm1) == 0 {
+                    looper = true;
+                    break;
+                }
+            }
+            if looper {
+                continue;
+            }
+            return false;
+        }
+
+        return true;
+    }
+}
diff --git a/src/fp.rs b/src/fp.rs
new file mode 100644
index 0000000..57345c1
--- /dev/null
+++ b/src/fp.rs
@@ -0,0 +1,717 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use super::big;
+use super::big::BIG;
+use super::dbig::DBIG;
+use super::rom;
+use super::super::arch::Chunk;
+use super::super::arch;
+use types::ModType;
+use std::str::FromStr;
+
+#[derive(Copy, Clone)]
+pub struct FP {
+    pub x: BIG,
+    pub xes: i32,
+}
+
+impl PartialEq for FP {
+    fn eq(&self, other: &FP) -> bool {
+        self.equals(other)
+    }
+}
+
+impl fmt::Display for FP {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "FP: [ {} ]", self.x)
+    }
+}
+
+impl fmt::Debug for FP {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "FP: [ {} ]", self.x)
+    }
+}
+
+pub use super::rom::{MODBITS, MOD8, MODTYPE, SH};
+use std::str::SplitWhitespace;
+use std::fmt;
+
+pub const FEXCESS:i32 = (((1 as i32)<<SH)-1);
+pub const OMASK:Chunk = (-1)<<(MODBITS%big::BASEBITS);
+pub const TBITS:usize=MODBITS%big::BASEBITS; // Number of active bits in top word
+pub const TMASK:Chunk=(1<<TBITS)-1;
+
+impl FP {
+    /* Constructors */
+    pub fn new() -> FP {
+        FP {
+            x: BIG::new(),
+            xes: 1,
+        }
+    }
+
+    pub fn new_int(a: isize) -> FP {
+        let mut f = FP::new();
+        f.x.inc(a);
+        f.nres();
+        return f;
+    }
+
+    pub fn new_copy(y: &FP) -> FP {
+        let mut f = FP::new();
+        f.x.copy(&(y.x));
+        f.xes = y.xes;
+        return f;
+    }
+
+    pub fn new_big(y: &BIG) -> FP {
+        let mut f = FP::new();
+        f.x.copy(y);
+        f.nres();
+        return f;
+    }
+
+    pub fn nres(&mut self) {
+        if MODTYPE != ModType::PSEUDO_MERSENNE && MODTYPE != ModType::GENERALISED_MERSENNE {
+            let r=BIG::new_ints(&rom::R2MODP);
+            let mut d=BIG::mul(&(self.x),&r);
+            self.x.copy(&FP::modulo(&mut d));
+            self.xes = 2;
+        } else {
+            self.xes = 1;
+        }
+    }
+
+    pub fn from_hex_iter(iter: &mut SplitWhitespace) -> FP {
+        let xes = i32::from_str(iter.next().unwrap()).unwrap();
+        let x = iter.next().unwrap();
+        FP {
+            x: BIG::from_hex(x.to_string()),
+            xes
+        }
+    }
+
+    pub fn from_hex(val: String) -> FP {
+        let mut s = val.split_whitespace();
+        FP::from_hex_iter(&mut s)
+    }
+
+    pub fn to_hex(&self) -> String {
+        let mut x = self.x;
+        let big = x.to_hex();
+        format!("{} {}", self.xes, big)
+    }
+
+/* convert back to regular form */
+    pub fn redc(&mut self) -> BIG {
+        if MODTYPE != ModType::PSEUDO_MERSENNE && MODTYPE != ModType::GENERALISED_MERSENNE {
+            let mut d=DBIG::new_scopy(&(self.x));
+            return FP::modulo(&mut d);
+        } else {
+            let r = BIG::new_copy(&(self.x));
+            return r;
+        }
+    }
+
+    /* reduce a DBIG to a BIG using the appropriate form of the modulus */
+    /* dd */
+    pub fn modulo(d: &mut DBIG) -> BIG {
+        if MODTYPE==ModType::PSEUDO_MERSENNE {
+            let mut b=BIG::new();
+            let mut t=d.split(MODBITS);
+            b.dcopy(&d);
+            let v = t.pmul(rom::MCONST as isize);
+
+            t.add(&b);
+            t.norm();
+
+            let tw = t.w[big::NLEN - 1];
+            t.w[big::NLEN - 1] &= TMASK;
+            t.w[0] += rom::MCONST * ((tw >> TBITS) + (v << (big::BASEBITS - TBITS)));
+            t.norm();
+            return t;
+        }
+    
+        if MODTYPE==ModType::MONTGOMERY_FRIENDLY {
+            let mut b = BIG::new();
+            for i in 0..big::NLEN {
+                let x = d.w[i];
+
+                let tuple = BIG::muladd(x, rom::MCONST - 1, x, d.w[big::NLEN + i - 1]);
+                d.w[big::NLEN + i] += tuple.0;
+                d.w[big::NLEN + i - 1] = tuple.1;
+            }
+
+            b.zero();
+
+            for i in 0..big::NLEN {
+                b.w[i] = d.w[big::NLEN + i];
+            }
+            b.norm();
+            return b;
+        }
+
+        if MODTYPE == ModType::GENERALISED_MERSENNE {
+            // GoldiLocks Only
+            let mut b = BIG::new();
+            let t = d.split(MODBITS);
+            let rm2 = (MODBITS / 2) as usize;
+            b.dcopy(&d);
+            b.add(&t);
+            let mut dd = DBIG::new_scopy(&t);
+            dd.shl(rm2);
+
+            let mut tt = dd.split(MODBITS);
+            let lo = BIG::new_dcopy(&dd);
+            b.add(&tt);
+            b.add(&lo);
+            b.norm();
+            tt.shl(rm2);
+            b.add(&tt);
+
+            let carry = b.w[big::NLEN - 1] >> TBITS;
+            b.w[big::NLEN - 1] &= TMASK;
+            b.w[0] += carry;
+
+            b.w[(224 / big::BASEBITS) as usize] += carry << (224 % big::BASEBITS);
+            b.norm();
+            return b;
+        }
+        if MODTYPE == ModType::NOT_SPECIAL {
+            let m = BIG::new_ints(&rom::MODULUS);
+            return BIG::monty(&m, rom::MCONST, d);
+        }
+        return BIG::new();
+    }
+
+    /* convert to string */
+    pub fn tostring(&mut self) -> String {
+        let s = self.redc().tostring();
+        return s;
+    }
+
+    /* reduce this mod Modulus */
+    pub fn reduce(&mut self) {
+        let mut m = BIG::new_ints(&rom::MODULUS);
+        let mut r = BIG::new_copy(&m);
+        let mut sb: usize;
+        self.x.norm();
+        if self.xes > 16 {
+            let q = FP::quo(&self.x, &m);
+            let carry = r.pmul(q);
+            r.w[big::NLEN - 1] += carry << big::BASEBITS; // correction - put any carry out back in again
+            self.x.sub(&r);
+            self.x.norm();
+            sb = 2;
+        } else {
+            sb = FP::logb2((self.xes - 1) as u32);
+        }
+        m.fshl(sb);
+
+        while sb > 0 {
+            let sr = BIG::ssn(&mut r, &self.x, &mut m);
+            self.x.cmove(&r, 1 - sr);
+            sb = sb - 1;
+        }
+
+        self.xes = 1;
+    }
+
+    /* test this=0? */
+    pub fn iszilch(&self) -> bool {
+        let mut a = FP::new_copy(self);
+        a.reduce();
+        return a.x.iszilch();
+    }
+
+    /* copy from FP b */
+    pub fn copy(&mut self, b: &FP) {
+        self.x.copy(&(b.x));
+        self.xes = b.xes;
+    }
+
+    /* copy from BIG b */
+    pub fn bcopy(&mut self, b: &BIG) {
+        self.x.copy(&b);
+        self.nres();
+    }
+
+    /* set this=0 */
+    pub fn zero(&mut self) {
+        self.x.zero();
+        self.xes = 1;
+    }
+
+    /* set this=1 */
+    pub fn one(&mut self) {
+        self.x.one();
+        self.nres()
+    }
+
+    /* normalise this */
+    pub fn norm(&mut self) {
+        self.x.norm();
+    }
+    /* swap FPs depending on d */
+    pub fn cswap(&mut self, b: &mut FP, d: isize) {
+        self.x.cswap(&mut (b.x), d);
+        let mut c = d as i32;
+        c = !(c - 1);
+        let t = c & (self.xes ^ b.xes);
+        self.xes ^= t;
+        b.xes ^= t;
+    }
+
+    /* copy FPs depending on d */
+    pub fn cmove(&mut self, b: &FP, d: isize) {
+        self.x.cmove(&(b.x), d);
+        let c = d as i32;
+        self.xes ^= (self.xes ^ b.xes) & (-c);
+    }
+
+    /* this*=b mod Modulus */
+    pub fn mul(&mut self, b: &FP) {
+        if (self.xes as i64) * (b.xes as i64) > FEXCESS as i64 {
+            self.reduce()
+        }
+
+        let mut d = BIG::mul(&(self.x), &(b.x));
+        self.x.copy(&FP::modulo(&mut d));
+        self.xes = 2;
+    }
+
+    fn logb2(w: u32) -> usize {
+        let mut v = w;
+        v |= v >> 1;
+        v |= v >> 2;
+        v |= v >> 4;
+        v |= v >> 8;
+        v |= v >> 16;
+
+        v = v - ((v >> 1) & 0x55555555);
+        v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
+        let r = ((((v + (v >> 4)) & 0xF0F0F0F).wrapping_mul(0x1010101)) >> 24) as usize;
+        return r;
+    }
+
+    // find approximation to quotient of a/m
+    // Out by at most 2.
+    // Note that MAXXES is bounded to be 2-bits less than half a word
+    fn quo(n: &BIG, m: &BIG) -> isize {
+        let hb = arch::CHUNK / 2;
+
+        if TBITS < hb {
+            let sh = hb - TBITS;
+            let num = (n.w[big::NLEN - 1] << sh) | (n.w[big::NLEN - 2] >> (big::BASEBITS - sh));
+            let den = (m.w[big::NLEN - 1] << sh) | (m.w[big::NLEN - 2] >> (big::BASEBITS - sh));
+            return (num / (den + 1)) as isize;
+        } else {
+            let num = n.w[big::NLEN - 1];
+            let den = m.w[big::NLEN - 1];
+            return (num / (den + 1)) as isize;
+        }
+    }
+
+    /* this = -this mod Modulus */
+    pub fn neg(&mut self) {
+        let mut p = BIG::new_ints(&rom::MODULUS);
+        let sb = FP::logb2((self.xes - 1) as u32);
+
+        p.fshl(sb);
+        self.x.rsub(&p);
+        self.xes = 1 << (sb as i32) + 1;
+        if self.xes > FEXCESS {
+            self.reduce()
+        }
+    }
+
+    /* this*=c mod Modulus, where c is a small int */
+    pub fn imul(&mut self, c: isize) {
+        let mut cc = c;
+        let mut s = false;
+        if cc < 0 {
+            cc = -cc;
+            s = true;
+        }
+
+        if MODTYPE == ModType::PSEUDO_MERSENNE || MODTYPE == ModType::GENERALISED_MERSENNE {
+            let mut d = self.x.pxmul(cc);
+            self.x.copy(&FP::modulo(&mut d));
+            self.xes = 2
+        } else {
+            if self.xes * (cc as i32) <= FEXCESS {
+                self.x.pmul(cc);
+                self.xes *= cc as i32;
+            } else {
+                let n = FP::new_int(cc);
+                self.mul(&n);
+            }
+        }
+
+        if s {
+            self.neg();
+            self.norm();
+        }
+    }
+
+    /* self*=self mod Modulus */
+    pub fn sqr(&mut self) {
+        if (self.xes as i64) * (self.xes as i64) > FEXCESS as i64 {
+            self.reduce()
+        }
+
+        let mut d = BIG::sqr(&(self.x));
+        self.x.copy(&FP::modulo(&mut d));
+        self.xes = 2
+    }
+
+    /* self+=b */
+    pub fn add(&mut self, b: &FP) {
+        self.x.add(&(b.x));
+        self.xes += b.xes;
+        if self.xes > FEXCESS {
+            self.reduce()
+        }
+    }
+
+    /* self+=self */
+    pub fn dbl(&mut self) {
+        self.x.dbl();
+        self.xes += self.xes;
+        if self.xes > FEXCESS {
+            self.reduce()
+        }
+    }
+
+    /* self-=b */
+    pub fn sub(&mut self, b: &FP) {
+        let mut n = FP::new_copy(b);
+        n.neg();
+        self.add(&n);
+    }
+
+    /* self=b-self */
+    pub fn rsub(&mut self, b: &FP) {
+        self.neg();
+        self.add(&b);
+    }
+
+    /* self/=2 mod Modulus */
+    pub fn div2(&mut self) {
+        if self.x.parity() == 0 {
+            self.x.fshr(1);
+        } else {
+            let p = BIG::new_ints(&rom::MODULUS);
+            self.x.add(&p);
+            self.x.norm();
+            self.x.fshr(1);
+        }
+    }
+
+    // See eprint paper https://eprint.iacr.org/2018/1038
+    // return this^(p-3)/4 or this^(p-5)/8
+    pub fn fpow(&mut self) -> FP {
+        let ac: [isize; 11] = [1, 2, 3, 6, 12, 15, 30, 60, 120, 240, 255];
+        let mut xp: [FP; 11] = [
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+        ];
+        // phase 1
+        let mut t = FP::new();
+        xp[0].copy(&self); // 1
+        xp[1].copy(&self);
+        xp[1].sqr(); // 2
+        t.copy(&xp[1]);
+        xp[2].copy(&t);
+        xp[2].mul(&self); // 3
+        t.copy(&xp[2]);
+        xp[3].copy(&t);
+        xp[3].sqr(); // 6
+        t.copy(&xp[3]);
+        xp[4].copy(&t);
+        xp[4].sqr(); // 12
+        t.copy(&xp[4]);
+        t.mul(&xp[2]);
+        xp[5].copy(&t); // 15
+        t.copy(&xp[5]);
+        xp[6].copy(&t);
+        xp[6].sqr(); // 30
+        t.copy(&xp[6]);
+        xp[7].copy(&t);
+        xp[7].sqr(); // 60
+        t.copy(&xp[7]);
+        xp[8].copy(&t);
+        xp[8].sqr(); // 120
+        t.copy(&xp[8]);
+        xp[9].copy(&t);
+        xp[9].sqr(); // 240
+        t.copy(&xp[9]);
+        t.mul(&xp[5]);
+        xp[10].copy(&t); // 255
+
+        let mut n = MODBITS as isize;
+        let c: isize;
+
+        if MODTYPE == ModType::GENERALISED_MERSENNE {
+            // Goldilocks ONLY
+            n /= 2;
+        }
+
+        if MOD8 == 5 {
+            n -= 3;
+            c = ((rom::MCONST as isize) + 5) / 8;
+        } else {
+            n -= 2;
+            c = ((rom::MCONST as isize) + 3) / 4;
+        }
+        let mut bw = 0;
+        let mut w = 1;
+        while w < c {
+            w *= 2;
+            bw += 1;
+        }
+        let mut k = w - c;
+
+        let mut i = 10;
+        let mut key = FP::new();
+        if k != 0 {
+            while ac[i] > k {
+                i -= 1;
+            }
+            key.copy(&xp[i]);
+            k -= ac[i];
+        }
+        while k != 0 {
+            i -= 1;
+            if ac[i] > k {
+                continue;
+            }
+            key.mul(&xp[i]);
+            k -= ac[i];
+        }
+        // phase 2
+        t.copy(&xp[2]);
+        xp[1].copy(&t);
+        t.copy(&xp[5]);
+        xp[2].copy(&t);
+        t.copy(&xp[10]);
+        xp[3].copy(&t);
+
+        let mut j = 3;
+        let mut m = 8;
+        let nw = n - bw;
+        let mut r = FP::new();
+
+        while 2 * m < nw {
+            t.copy(&xp[j]);
+            j += 1;
+            for _ in 0..m {
+                t.sqr();
+            }
+            r.copy(&xp[j - 1]);
+            r.mul(&t);
+            xp[j].copy(&r);
+            m *= 2;
+        }
+        let mut lo = nw - m;
+        r.copy(&xp[j]);
+
+        while lo != 0 {
+            m /= 2;
+            j -= 1;
+            if lo < m {
+                continue;
+            }
+            lo -= m;
+            t.copy(&r);
+            for _ in 0..m {
+                t.sqr();
+            }
+            r.copy(&t);
+            r.mul(&xp[j]);
+        }
+        // phase 3
+        if bw != 0 {
+            for _ in 0..bw {
+                r.sqr();
+            }
+            r.mul(&key);
+        }
+        if MODTYPE == ModType::GENERALISED_MERSENNE {
+            // Goldilocks ONLY
+            key.copy(&r);
+            r.sqr();
+            r.mul(&self);
+            for _ in 0..n + 1 {
+                r.sqr();
+            }
+            r.mul(&key);
+        }
+        return r;
+    }
+    /* self=1/self mod Modulus */
+    pub fn inverse(&mut self) {
+        if MODTYPE == ModType::PSEUDO_MERSENNE || MODTYPE == ModType::GENERALISED_MERSENNE {
+            let mut y = self.fpow();
+            if MOD8 == 5 {
+                let mut t = FP::new_copy(self);
+                t.sqr();
+                self.mul(&t);
+                y.sqr();
+            }
+            y.sqr();
+            y.sqr();
+            self.mul(&y);
+        } else {
+            // Constant time inversion using Fermat's little theorem.
+            // Fermat's little theorem says for a prime p and for any a < p, a^p = a % p => a^(p-1) = 1 % p => a^(p-2) = a^-1 % p
+            let mut m2 = BIG::new_ints(&rom::MODULUS);
+            m2.dec(2);
+            m2.norm();
+            let inv = self.pow(&mut m2);
+            self.copy(&inv);
+        }
+    }
+
+    /* return TRUE if self==a */
+    pub fn equals(&self, a: &FP) -> bool {
+        let mut f = FP::new_copy(self);
+        let mut s = FP::new_copy(a);
+        f.reduce();
+        s.reduce();
+        if BIG::comp(&(f.x), &(s.x)) == 0 {
+            return true;
+        }
+        return false;
+    }
+
+    /* return self^e mod Modulus */
+    pub fn pow(&mut self, e: &mut BIG) -> FP {
+        let mut tb: [FP; 16] = [
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+            FP::new(),
+        ];
+        const CT: usize = 1 + (big::NLEN * (big::BASEBITS as usize) + 3) / 4;
+        let mut w: [i8; CT] = [0; CT];
+
+        self.norm();
+        let mut t = BIG::new_copy(e);
+        t.norm();
+        let nb = 1 + (t.nbits() + 3) / 4;
+
+        for i in 0..nb {
+            let lsbs = t.lastbits(4);
+            t.dec(lsbs);
+            t.norm();
+            w[i] = lsbs as i8;
+            t.fshr(4);
+        }
+        tb[0].one();
+        tb[1].copy(&self);
+
+        let mut c = FP::new();
+        for i in 2..16 {
+            c.copy(&tb[i - 1]);
+            tb[i].copy(&c);
+            tb[i].mul(&self);
+        }
+        let mut r = FP::new_copy(&tb[w[nb - 1] as usize]);
+        for i in (0..nb - 1).rev() {
+            r.sqr();
+            r.sqr();
+            r.sqr();
+            r.sqr();
+            r.mul(&tb[w[i] as usize])
+        }
+        r.reduce();
+        return r;
+    }
+
+    /* return sqrt(this) mod Modulus */
+    pub fn sqrt(&mut self) -> FP {
+        self.reduce();
+
+        if MOD8 == 5 {
+            let v: FP;
+            let mut i = FP::new_copy(self);
+            i.x.shl(1);
+            if MODTYPE == ModType::PSEUDO_MERSENNE || MODTYPE == ModType::GENERALISED_MERSENNE {
+                v = i.fpow();
+            } else {
+                let mut p = BIG::new_ints(&rom::MODULUS);
+                p.dec(5);
+                p.norm();
+                p.shr(3);
+                v = i.pow(&mut p);
+            }
+            i.mul(&v);
+            i.mul(&v);
+            i.x.dec(1);
+            let mut r = FP::new_copy(self);
+            r.mul(&v);
+            r.mul(&i);
+            r.reduce();
+            return r;
+        } else {
+            let mut r: FP;
+            if MODTYPE == ModType::PSEUDO_MERSENNE || MODTYPE == ModType::GENERALISED_MERSENNE {
+                r = self.fpow();
+                r.mul(self);
+            } else {
+                let mut p = BIG::new_ints(&rom::MODULUS);
+                p.inc(1);
+                p.norm();
+                p.shr(2);
+                r = self.pow(&mut p);
+            }
+            return r;
+        }
+    }
+    /* return jacobi symbol (this/Modulus) */
+    pub fn jacobi(&mut self) -> isize {
+        let mut p = BIG::new_ints(&rom::MODULUS);
+        let mut w = self.redc();
+        return w.jacobi(&mut p);
+    }
+}
diff --git a/src/fp12.rs b/src/fp12.rs
new file mode 100644
index 0000000..9c06a3e
--- /dev/null
+++ b/src/fp12.rs
@@ -0,0 +1,1109 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use super::big;
+use super::fp2::FP2;
+use super::fp4::FP4;
+use super::big::BIG;
+use super::rom;
+use types::SexticTwist;
+use std::str::SplitWhitespace;
+use super::ecp;
+
+pub const ZERO: usize=0;
+pub const ONE: usize=1;
+pub const SPARSER: usize=2;
+pub const SPARSE: usize=3;
+pub const DENSE: usize=4;
+
+#[derive(Copy, Clone)]
+pub struct FP12 {
+    a: FP4,
+    b: FP4,
+    c: FP4,
+    stype: usize,
+}
+
+impl PartialEq for FP12 {
+	fn eq(&self, other: &FP12) -> bool {
+		self.equals(other)
+	}
+}
+
+impl FP12 {
+    pub fn new() -> FP12 {
+        FP12 {
+            a: FP4::new(),
+            b: FP4::new(),
+            c: FP4::new(),
+	    stype: ZERO,
+        }
+    }
+
+    pub fn settype(&mut self,t: usize)  {
+	self.stype = t;
+    }
+
+    pub fn gettype(&self) -> usize {
+        return self.stype;
+    }
+
+    pub fn new_int(a: isize) -> FP12 {
+        let mut f = FP12::new();
+        f.a.copy(&FP4::new_int(a));
+        f.b.zero();
+        f.c.zero();
+	if a == 1 {
+	    f.stype=ONE;
+	} else {
+	    f.stype=SPARSER;
+	}
+        return f;
+    }
+
+    pub fn new_copy(x: &FP12) -> FP12 {
+        let mut f = FP12::new();
+        f.a.copy(&x.a);
+        f.b.copy(&x.b);
+        f.c.copy(&x.c);
+	f.stype=x.stype;
+        return f;
+    }
+
+    pub fn new_fp4s(d: &FP4, e: &FP4, f: &FP4) -> FP12 {
+        let mut g = FP12::new();
+        g.a.copy(d);
+        g.b.copy(e);
+        g.c.copy(f);
+	g.stype=DENSE;
+        return g;
+    }
+
+    pub fn new_fp4(d: &FP4) -> FP12 {
+        let mut g = FP12::new();
+        g.a.copy(d);
+        g.b.zero();
+        g.c.zero();
+	g.stype=SPARSER;
+        return g;
+    }
+
+    /* reduce components mod Modulus */
+    pub fn reduce(&mut self) {
+        self.a.reduce();
+        self.b.reduce();
+        self.c.reduce();
+    }
+
+    /* normalise components of w */
+    pub fn norm(&mut self) {
+        self.a.norm();
+        self.b.norm();
+        self.c.norm();
+    }
+
+    /* test self=0 ? */
+    pub fn iszilch(&self) -> bool {
+        //self.reduce();
+        return self.a.iszilch() && self.b.iszilch() && self.c.iszilch();
+    }
+
+    /* Conditional move of g to self dependant on d */
+    pub fn cmove(&mut self, g: &FP12, d: isize) {
+        self.a.cmove(&g.a, d);
+        self.b.cmove(&g.b, d);
+        self.c.cmove(&g.c, d);
+	let mut u=d as usize;
+	u=!(u-1);
+	self.stype^=(self.stype^g.stype)&u;
+    }
+
+    /* return 1 if b==c, no branching */
+    fn teq(b: i32, c: i32) -> isize {
+        let mut x = b ^ c;
+        x -= 1; // if x=0, x now -1
+        return ((x >> 31) & 1) as isize;
+    }
+
+    /* Constant time select from pre-computed table */
+    pub fn selector(&mut self, g: &[FP12], b: i32) {
+        let m = b >> 31;
+        let mut babs = (b ^ m) - m;
+
+        babs = (babs - 1) / 2;
+
+        self.cmove(&g[0], FP12::teq(babs, 0)); // conditional move
+        self.cmove(&g[1], FP12::teq(babs, 1));
+        self.cmove(&g[2], FP12::teq(babs, 2));
+        self.cmove(&g[3], FP12::teq(babs, 3));
+        self.cmove(&g[4], FP12::teq(babs, 4));
+        self.cmove(&g[5], FP12::teq(babs, 5));
+        self.cmove(&g[6], FP12::teq(babs, 6));
+        self.cmove(&g[7], FP12::teq(babs, 7));
+
+        let mut invf = FP12::new_copy(self);
+        invf.conj();
+        self.cmove(&invf, (m & 1) as isize);
+    }
+
+    /* test self=1 ? */
+    pub fn isunity(&self) -> bool {
+        let one = FP4::new_int(1);
+        return self.a.equals(&one) && self.b.iszilch() && self.c.iszilch();
+    }
+
+    /* test self=x */
+    pub fn equals(&self, x: &FP12) -> bool {
+        return self.a.equals(&x.a) && self.b.equals(&x.b) && self.c.equals(&x.c);
+    }
+
+    pub fn geta(&mut self) -> FP4 {
+        return self.a;
+//        let f = FP4::new_copy(&self.a);
+//        return f;
+    }
+
+    pub fn getb(&mut self) -> FP4 {
+        return self.b;
+//        let f = FP4::new_copy(&self.b);
+//        return f;
+    }
+
+    pub fn getc(&mut self) -> FP4 {
+        return self.c;
+//        let f = FP4::new_copy(&self.c);
+//        return f;
+    }
+
+    /* copy self=x */
+    pub fn copy(&mut self, x: &FP12) {
+        self.a.copy(&x.a);
+        self.b.copy(&x.b);
+        self.c.copy(&x.c);
+	self.stype=x.stype;
+    }
+
+    /* set self=1 */
+    pub fn one(&mut self) {
+        self.a.one();
+        self.b.zero();
+        self.c.zero();
+	self.stype=ONE;
+    }
+
+    /* set self=0 */
+    pub fn zero(&mut self) {
+        self.a.zero();
+        self.b.zero();
+        self.c.zero();
+	self.stype=ZERO;
+    }
+
+    /* this=conj(this) */
+    pub fn conj(&mut self) {
+        self.a.conj();
+        self.b.nconj();
+        self.c.conj();
+    }
+
+    /* Granger-Scott Unitary Squaring */
+    pub fn usqr(&mut self) {
+        let mut a = FP4::new_copy(&self.a);
+        let mut b = FP4::new_copy(&self.c);
+        let mut c = FP4::new_copy(&self.b);
+        let mut d = FP4::new();
+
+        self.a.sqr();
+        d.copy(&self.a);
+        d.add(&self.a);
+        self.a.add(&d);
+
+        self.a.norm();
+        a.nconj();
+
+        a.dbl();
+        self.a.add(&a);
+        b.sqr();
+        b.times_i();
+
+        d.copy(&b);
+        d.add(&b);
+        b.add(&d);
+        b.norm();
+
+        c.sqr();
+        d.copy(&c);
+        d.add(&c);
+        c.add(&d);
+        c.norm();
+
+        self.b.conj();
+        self.b.dbl();
+        self.c.nconj();
+
+        self.c.dbl();
+        self.b.add(&b);
+        self.c.add(&c);
+        self.stype=DENSE;
+        self.reduce();
+    }
+
+    /* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
+    pub fn sqr(&mut self) {
+        if self.stype==ONE {
+            return;
+        }
+
+        let mut a = FP4::new_copy(&self.a);
+        let mut b = FP4::new_copy(&self.b);
+        let mut c = FP4::new_copy(&self.c);
+        let mut d = FP4::new_copy(&self.a);
+
+        a.sqr();
+        b.mul(&self.c);
+        b.dbl();
+        b.norm();
+        c.sqr();
+        d.mul(&self.b);
+        d.dbl();
+
+        self.c.add(&self.a);
+        self.c.add(&self.b);
+        self.c.norm();
+        self.c.sqr();
+
+        self.a.copy(&a);
+        a.add(&b);
+        a.norm();
+        a.add(&c);
+        a.add(&d);
+        a.norm();
+
+        a.neg();
+        b.times_i();
+        c.times_i();
+
+        self.a.add(&b);
+
+        self.b.copy(&c);
+        self.b.add(&d);
+        self.c.add(&a);
+        if self.stype==SPARSER {
+            self.stype=SPARSE;
+        } else {
+            self.stype=DENSE;
+        }
+        self.norm();
+    }
+
+    /* FP12 full multiplication self=self*y */
+    pub fn mul(&mut self, y: &FP12) {
+        let mut z0 = FP4::new_copy(&self.a);
+        let mut z1 = FP4::new();
+        let mut z2 = FP4::new_copy(&mut self.b);
+        let mut z3 = FP4::new();
+        let mut t0 = FP4::new_copy(&self.a);
+        let mut t1 = FP4::new_copy(&y.a);
+
+        z0.mul(&y.a);
+        z2.mul(&y.b);
+
+        t0.add(&self.b);
+        t1.add(&y.b);
+
+        t0.norm();
+        t1.norm();
+
+        z1.copy(&t0);
+        z1.mul(&t1);
+        t0.copy(&self.b);
+        t0.add(&self.c);
+        t1.copy(&y.b);
+        t1.add(&y.c);
+
+        t0.norm();
+        t1.norm();
+
+        z3.copy(&t0);
+        z3.mul(&t1);
+
+        t0.copy(&z0);
+        t0.neg();
+        t1.copy(&z2);
+        t1.neg();
+
+        z1.add(&t0);
+        self.b.copy(&z1);
+        self.b.add(&t1);
+
+        z3.add(&t1);
+        z2.add(&t0);
+
+        t0.copy(&self.a);
+        t0.add(&self.c);
+        t0.norm();
+        t1.copy(&y.a);
+        t1.add(&y.c);
+        t1.norm();
+        t0.mul(&t1);
+        z2.add(&t0);
+
+        t0.copy(&self.c);
+        t0.mul(&y.c);
+        t1.copy(&t0);
+        t1.neg();
+
+        self.c.copy(&z2);
+        self.c.add(&t1);
+        z3.add(&t1);
+        t0.times_i();
+        self.b.add(&t0);
+        z3.norm();
+
+        z3.times_i();
+        self.a.copy(&z0);
+        self.a.add(&z3);
+        self.stype=DENSE;
+        self.norm();
+    }
+
+
+/* FP12 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+    pub fn ssmul(&mut self, y: &FP12) {
+        if self.stype==ONE {
+            self.copy(&y);
+            return;
+        }
+        if y.stype==ONE {
+            return;
+        }
+        if y.stype>=SPARSE {
+            let mut z0=FP4::new_copy(&self.a);
+            let mut z1=FP4::new_int(0);
+            let mut z2=FP4::new_int(0);
+            let mut z3=FP4::new_int(0);
+            z0.mul(&y.a);
+
+            if ecp::SEXTIC_TWIST==SexticTwist::M_TYPE {
+                if y.stype==SPARSE || self.stype==SPARSE {
+
+                    let mut ga=FP2::new_int(0);
+		    let mut gb=FP2::new_int(0);
+
+                    gb.copy(&self.b.getb());
+                    gb.mul(&y.b.getb());
+                    ga.zero();
+                    if y.stype!=SPARSE {
+                        ga.copy(&self.b.getb());
+                        ga.mul(&y.b.geta());
+                    }
+                    if self.stype!=SPARSE {
+                        ga.copy(&self.b.geta());
+                        ga.mul(&y.b.getb());
+                    }
+		    z2.set_fp2s(&ga,&gb);
+                    z2.times_i();
+                } else {
+                    z2.copy(&self.b);
+                    z2.mul(&y.b);
+                }
+            } else { 
+               z2.copy(&self.b);
+               z2.mul(&y.b);
+            }
+            let mut t0=FP4::new_copy(&self.a);
+            let mut t1=FP4::new_copy(&y.a);
+            t0.add(&self.b); t0.norm();
+            t1.add(&y.b); t1.norm();
+
+            z1.copy(&t0); z1.mul(&t1);
+            t0.copy(&self.b); t0.add(&self.c); t0.norm();
+            t1.copy(&y.b); t1.add(&y.c); t1.norm();
+
+            z3.copy(&t0); z3.mul(&t1);
+
+            t0.copy(&z0); t0.neg();
+            t1.copy(&z2); t1.neg();
+ 
+            z1.add(&t0);
+            self.b.copy(&z1); self.b.add(&t1);
+
+            z3.add(&t1);
+            z2.add(&t0);
+
+            t0.copy(&self.a); t0.add(&self.c); t0.norm();
+            t1.copy(&y.a); t1.add(&y.c); t1.norm();
+	
+            t0.mul(&t1);
+            z2.add(&t0);
+
+            if ecp::SEXTIC_TWIST==SexticTwist::D_TYPE {
+                if y.stype==SPARSE || self.stype==SPARSE {
+
+                    let mut ga=FP2::new_int(0);
+		    let mut gb=FP2::new_int(0);
+
+                    ga.copy(&self.c.geta());
+                    ga.mul(&y.c.geta());
+                    gb.zero();
+                    if y.stype!=SPARSE {
+                        gb.copy(&self.c.geta());
+                        gb.mul(&y.c.getb());
+                    }
+                    if self.stype!=SPARSE {
+                        gb.copy(&self.c.getb());
+                        gb.mul(&y.c.geta());
+                    }
+		    t0.set_fp2s(&ga,&gb);
+                } else {
+                    t0.copy(&self.c);
+                    t0.mul(&y.c);
+                }
+            } else { 
+                t0.copy(&self.c);
+                t0.mul(&y.c);
+            }
+            t1.copy(&t0); t1.neg();
+
+            self.c.copy(&z2); self.c.add(&t1);
+            z3.add(&t1);
+            t0.times_i();
+            self.b.add(&t0);
+            z3.norm();
+            z3.times_i();
+            self.a.copy(&z0); self.a.add(&z3);
+        } else {
+            if self.stype==SPARSER {
+                self.smul(&y);
+                return;
+            }
+            if ecp::SEXTIC_TWIST==SexticTwist::D_TYPE { // dense by sparser - 13m 
+                let mut z0=FP4::new_copy(&self.a);
+                let mut z2=FP4::new_copy(&self.b);
+                let mut z3=FP4::new_copy(&self.b);
+                let mut t0=FP4::new_int(0);
+                let mut t1=FP4::new_copy(&y.a);
+
+                z0.mul(&y.a);
+                z2.pmul(&y.b.geta());
+                self.b.add(&self.a);
+                t1.padd(&y.b.geta());
+
+                t1.norm();
+                self.b.norm();
+                self.b.mul(&t1);
+                z3.add(&self.c);
+                z3.norm();
+                z3.pmul(&y.b.geta());
+
+                t0.copy(&z0); t0.neg();
+                t1.copy(&z2); t1.neg();
+
+                self.b.add(&t0);
+
+                self.b.add(&t1);
+                z3.add(&t1);
+                z2.add(&t0);
+
+                t0.copy(&self.a); t0.add(&self.c); t0.norm();
+                z3.norm();
+                t0.mul(&y.a);
+                self.c.copy(&z2); self.c.add(&t0);
+
+                z3.times_i();
+                self.a.copy(&z0); self.a.add(&z3);
+            }
+            if ecp::SEXTIC_TWIST==SexticTwist::M_TYPE {
+
+                let mut z0 = FP4::new_copy(&self.a);
+                let mut z1 = FP4::new();
+                let mut z2 = FP4::new();
+                let mut z3 = FP4::new();
+                let mut t0 = FP4::new_copy(&self.a);
+                let mut t1 = FP4::new();
+	
+                z0.mul(&y.a);
+                t0.add(&self.b); t0.norm();
+
+                z1.copy(&t0); z1.mul(&y.a);
+                t0.copy(&self.b); t0.add(&self.c);
+                t0.norm();
+
+                z3.copy(&t0);
+                z3.pmul(&y.c.getb());
+                z3.times_i();
+
+                t0.copy(&z0); t0.neg();
+                z1.add(&t0);
+                self.b.copy(&z1);
+                z2.copy(&t0);
+
+                t0.copy(&self.a); t0.add(&self.c); t0.norm();
+                t1.copy(&y.a); t1.add(&y.c); t1.norm();
+
+                t0.mul(&t1);
+                z2.add(&t0);
+                t0.copy(&self.c);
+			
+                t0.pmul(&y.c.getb());
+                t0.times_i();
+                t1.copy(&t0); t1.neg();
+
+                self.c.copy(&z2); self.c.add(&t1);
+                z3.add(&t1);
+                t0.times_i();
+                self.b.add(&t0);
+                z3.norm();
+                z3.times_i();
+                self.a.copy(&z0); self.a.add(&z3);
+           }	
+        }
+        self.stype=DENSE;
+        self.norm();
+    }
+
+    /* Special case of multiplication arises from special form of ATE pairing line function */
+    pub fn smul(&mut self, y: &FP12) {
+        if ecp::SEXTIC_TWIST==SexticTwist::D_TYPE {	
+            let mut w1=FP2::new_copy(&self.a.geta());
+            let mut w2=FP2::new_copy(&self.a.getb());
+            let mut w3=FP2::new_copy(&self.b.geta());
+
+            w1.mul(&y.a.geta());
+            w2.mul(&y.a.getb());
+            w3.mul(&y.b.geta());
+
+            let mut ta=FP2::new_copy(&self.a.geta());
+            let mut tb=FP2::new_copy(&y.a.geta());
+            ta.add(&self.a.getb()); ta.norm();
+            tb.add(&y.a.getb()); tb.norm();
+            let mut tc=FP2::new_copy(&ta);
+            tc.mul(&tb);
+            let mut t=FP2::new_copy(&w1);
+            t.add(&w2);
+            t.neg();
+            tc.add(&t);
+
+            ta.copy(&self.a.geta()); ta.add(&self.b.geta()); ta.norm();
+            tb.copy(&y.a.geta()); tb.add(&y.b.geta()); tb.norm();
+            let mut td=FP2::new_copy(&ta);
+            td.mul(&tb);
+            t.copy(&w1);
+            t.add(&w3);
+            t.neg();
+            td.add(&t);
+
+            ta.copy(&self.a.getb()); ta.add(&self.b.geta()); ta.norm();
+            tb.copy(&y.a.getb()); tb.add(&y.b.geta()); tb.norm();
+            let mut te=FP2::new_copy(&ta);
+            te.mul(&tb);
+            t.copy(&w2);
+            t.add(&w3);
+            t.neg();
+            te.add(&t);
+
+            w2.mul_ip();
+            w1.add(&w2);
+
+	    self.a.set_fp2s(&w1,&tc);
+	    self.b.set_fp2s(&td,&te);
+	    self.c.set_fp2(&w3);
+           
+            self.a.norm();
+            self.b.norm();
+        } else {
+            let mut w1=FP2::new_copy(&self.a.geta());
+            let mut w2=FP2::new_copy(&self.a.getb());
+            let mut w3=FP2::new_copy(&self.c.getb());
+
+            w1.mul(&y.a.geta());
+            w2.mul(&y.a.getb());
+            w3.mul(&y.c.getb());
+
+            let mut ta=FP2::new_copy(&self.a.geta());
+            let mut tb=FP2::new_copy(&y.a.geta());
+            ta.add(&self.a.getb()); ta.norm();
+            tb.add(&y.a.getb()); tb.norm();
+            let mut tc=FP2::new_copy(&ta);
+            tc.mul(&tb);
+            let mut t=FP2::new_copy(&w1);
+            t.add(&w2);
+            t.neg();
+            tc.add(&t);
+
+            ta.copy(&self.a.geta()); ta.add(&self.c.getb()); ta.norm();
+            tb.copy(&y.a.geta()); tb.add(&y.c.getb()); tb.norm();
+            let mut td=FP2::new_copy(&ta);
+            td.mul(&tb);
+            t.copy(&w1);
+            t.add(&w3);
+            t.neg();
+            td.add(&t);
+
+            ta.copy(&self.a.getb()); ta.add(&self.c.getb()); ta.norm();
+            tb.copy(&y.a.getb()); tb.add(&y.c.getb()); tb.norm();
+            let mut te=FP2::new_copy(&ta);
+            te.mul(&tb);
+            t.copy(&w2);
+            t.add(&w3);
+            t.neg();
+            te.add(&t);
+
+            w2.mul_ip();
+            w1.add(&w2);
+	    self.a.set_fp2s(&w1,&tc);
+
+            w3.mul_ip();
+            w3.norm();
+	    self.b.set_fp2h(&w3);
+
+            te.norm();
+            te.mul_ip();
+	    self.c.set_fp2s(&te,&td);
+
+            self.a.norm();
+            self.c.norm();
+	}
+	self.stype=SPARSE;
+    }
+
+    /* self=1/self */
+    pub fn inverse(&mut self) {
+        let mut f0 = FP4::new_copy(&self.a);
+        let mut f1 = FP4::new_copy(&self.b);
+        let mut f2 = FP4::new_copy(&self.a);
+        let mut f3 = FP4::new();
+
+        self.norm();
+        f0.sqr();
+        f1.mul(&self.c);
+        f1.times_i();
+        f0.sub(&f1);
+        f0.norm();
+
+        f1.copy(&self.c);
+        f1.sqr();
+        f1.times_i();
+        f2.mul(&self.b);
+        f1.sub(&f2);
+        f1.norm();
+
+        f2.copy(&self.b);
+        f2.sqr();
+        f3.copy(&self.a);
+        f3.mul(&self.c);
+        f2.sub(&f3);
+        f2.norm();
+
+        f3.copy(&self.b);
+        f3.mul(&f2);
+        f3.times_i();
+        self.a.mul(&f0);
+        f3.add(&self.a);
+        self.c.mul(&f1);
+        self.c.times_i();
+
+        f3.add(&self.c);
+        f3.norm();
+        f3.inverse();
+        self.a.copy(&f0);
+        self.a.mul(&f3);
+        self.b.copy(&f1);
+        self.b.mul(&f3);
+        self.c.copy(&f2);
+        self.c.mul(&f3);
+        self.stype=DENSE;
+    }
+
+    /* self=self^p using Frobenius */
+    pub fn frob(&mut self, f: &FP2) {
+        let mut f2 = FP2::new_copy(f);
+        let mut f3 = FP2::new_copy(f);
+
+        f2.sqr();
+        f3.mul(&f2);
+
+        self.a.frob(&f3);
+        self.b.frob(&f3);
+        self.c.frob(&f3);
+
+        self.b.pmul(f);
+        self.c.pmul(&f2);
+        self.stype=DENSE;
+    }
+
+    /* trace function */
+    pub fn trace(&mut self) -> FP4 {
+        let mut t = FP4::new();
+        t.copy(&self.a);
+        t.imul(3);
+        t.reduce();
+        return t;
+    }
+
+    /* convert from byte array to FP12 */
+    pub fn frombytes(w: &[u8]) -> FP12 {
+        let mut t: [u8; big::MODBYTES as usize] = [0; big::MODBYTES as usize];
+        let mb = big::MODBYTES as usize;
+
+        for i in 0..mb {
+            t[i] = w[i]
+        }
+        let mut a = BIG::frombytes(&t);
+        for i in 0..mb {
+            t[i] = w[i + mb]
+        }
+        let mut b = BIG::frombytes(&t);
+        let mut c = FP2::new_bigs(&a, &b);
+
+        for i in 0..mb {
+            t[i] = w[i + 2 * mb]
+        }
+        a = BIG::frombytes(&t);
+        for i in 0..mb {
+            t[i] = w[i + 3 * mb]
+        }
+        b = BIG::frombytes(&t);
+        let mut d = FP2::new_bigs(&a, &b);
+
+        let e = FP4::new_fp2s(&c, &d);
+
+        for i in 0..mb {
+            t[i] = w[i + 4 * mb]
+        }
+        a = BIG::frombytes(&t);
+        for i in 0..mb {
+            t[i] = w[i + 5 * mb]
+        }
+        b = BIG::frombytes(&t);
+        c = FP2::new_bigs(&a, &b);
+
+        for i in 0..mb {
+            t[i] = w[i + 6 * mb]
+        }
+        a = BIG::frombytes(&t);
+        for i in 0..mb {
+            t[i] = w[i + 7 * mb]
+        }
+        b = BIG::frombytes(&t);
+        d = FP2::new_bigs(&a, &b);
+
+        let f = FP4::new_fp2s(&c, &d);
+
+        for i in 0..mb {
+            t[i] = w[i + 8 * mb]
+        }
+        a = BIG::frombytes(&t);
+        for i in 0..mb {
+            t[i] = w[i + 9 * mb]
+        }
+        b = BIG::frombytes(&t);
+
+        c = FP2::new_bigs(&a, &b);
+
+        for i in 0..mb {
+            t[i] = w[i + 10 * mb]
+        }
+        a = BIG::frombytes(&t);
+        for i in 0..mb {
+            t[i] = w[i + 11 * mb]
+        }
+        b = BIG::frombytes(&t);
+        d = FP2::new_bigs(&a, &b);
+
+        let g = FP4::new_fp2s(&c, &d);
+
+        return FP12::new_fp4s(&e, &f, &g);
+    }
+
+    /* convert this to byte array */
+    pub fn tobytes(&mut self, w: &mut [u8]) {
+        let mut t: [u8; big::MODBYTES as usize] = [0; big::MODBYTES as usize];
+        let mb = big::MODBYTES as usize;
+
+        self.a.geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i] = t[i]
+        }
+        self.a.geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + mb] = t[i]
+        }
+        self.a.getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 2 * mb] = t[i]
+        }
+        self.a.getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 3 * mb] = t[i]
+        }
+
+        self.b.geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 4 * mb] = t[i]
+        }
+        self.b.geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 5 * mb] = t[i]
+        }
+        self.b.getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 6 * mb] = t[i]
+        }
+        self.b.getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 7 * mb] = t[i]
+        }
+
+        self.c.geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 8 * mb] = t[i]
+        }
+        self.c.geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 9 * mb] = t[i]
+        }
+        self.c.getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 10 * mb] = t[i]
+        }
+        self.c.getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 11 * mb] = t[i]
+        }
+    }
+
+    /* output to hex string */
+    pub fn tostring(&mut self) -> String {
+        return format!(
+            "[{},{},{}]",
+            self.a.tostring(),
+            self.b.tostring(),
+            self.c.tostring()
+        );
+    }
+
+    pub fn to_hex(&self) -> String {
+        format!("{} {} {}", self.a.to_hex(), self.b.to_hex(), self.c.to_hex())
+    }
+
+    pub fn from_hex_iter(iter: &mut SplitWhitespace) -> FP12 {
+        FP12 {
+            a: FP4::from_hex_iter(iter),
+            b: FP4::from_hex_iter(iter),
+            c: FP4::from_hex_iter(iter),
+	    stype: DENSE
+        }
+    }
+
+    pub fn from_hex(val: String) -> FP12 {
+        let mut iter = val.split_whitespace();
+        return FP12::from_hex_iter(&mut iter);
+    }
+
+    /* self=self^e */
+    pub fn pow(&self, e: &BIG) -> FP12 {
+        let mut r = FP12::new_copy(self);
+        r.norm();
+        let mut e1 = BIG::new_copy(e);
+        e1.norm();
+        let mut e3 = BIG::new_copy(&e1);
+        e3.pmul(3);
+        e3.norm();
+        let mut w = FP12::new_copy(&r);
+
+        let nb = e3.nbits();
+        for i in (1..nb - 1).rev() {
+            w.usqr();
+            let bt = e3.bit(i) - e1.bit(i);
+            if bt == 1 {
+                w.mul(&r);
+            }
+            if bt == -1 {
+                r.conj();
+                w.mul(&r);
+                r.conj();
+            }
+        }
+
+        w.reduce();
+        return w;
+    }
+
+    /* constant time powering by small integer of max length bts */
+    pub fn pinpow(&mut self, e: i32, bts: i32) {
+        let mut r: [FP12; 2] = [FP12::new_int(1), FP12::new_copy(self)];
+        let mut t = FP12::new();
+
+        for i in (0..bts).rev() {
+            let b: usize = ((e >> i) & 1) as usize;
+            t.copy(&r[b]);
+            r[1 - b].mul(&t);
+            r[b].usqr();
+        }
+        self.copy(&r[0]);
+    }
+
+    pub fn compow(&mut self, e: &BIG, r: &BIG) -> FP4 {
+        let f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
+        let q = BIG::new_ints(&rom::MODULUS);
+
+        let mut g1 = FP12::new_copy(self);
+        let mut g2 = FP12::new_copy(self);
+
+        let mut m = BIG::new_copy(&q);
+        m.rmod(&r);
+
+        let mut a = BIG::new_copy(&e);
+        a.rmod(&mut m);
+
+        let mut b = BIG::new_copy(&e);
+        b.div(&mut m);
+
+        let mut c = g1.trace();
+
+        if b.iszilch() {
+            c = c.xtr_pow(&mut a);
+            return c;
+        }
+
+        g2.frob(&f);
+        let cp = g2.trace();
+        g1.conj();
+        g2.mul(&g1);
+        let cpm1 = g2.trace();
+        g2.mul(&g1);
+        let cpm2 = g2.trace();
+
+        c = c.xtr_pow2(&cp, &cpm1, &cpm2, &mut a, &mut b);
+
+        return c;
+    }
+
+    /* p=q0^u0.q1^u1.q2^u2.q3^u3 */
+    // Bos & Costello https://eprint.iacr.org/2013/458.pdf
+    // Faz-Hernandez & Longa & Sanchez  https://eprint.iacr.org/2013/158.pdf
+    // Side channel attack secure
+    pub fn pow4(q: &[FP12], u: &[BIG]) -> FP12 {
+        let mut g: [FP12; 8] = [
+            FP12::new(),
+            FP12::new(),
+            FP12::new(),
+            FP12::new(),
+            FP12::new(),
+            FP12::new(),
+            FP12::new(),
+            FP12::new(),
+        ];
+
+        let mut r = FP12::new();
+        let mut p = FP12::new();
+        const CT: usize = 1 + big::NLEN * (big::BASEBITS as usize);
+        let mut w: [i8; CT] = [0; CT];
+        let mut s: [i8; CT] = [0; CT];
+
+        let mut mt = BIG::new();
+        let mut t: [BIG; 4] = [
+            BIG::new_copy(&u[0]),
+            BIG::new_copy(&u[1]),
+            BIG::new_copy(&u[2]),
+            BIG::new_copy(&u[3]),
+        ];
+
+        for i in 0..4 {
+            t[i].norm();
+        }
+
+        // precomputation
+        g[0].copy(&q[0]);
+        r.copy(&g[0]);
+        g[1].copy(&r);
+        g[1].mul(&q[1]); // q[0].q[1]
+        g[2].copy(&r);
+        g[2].mul(&q[2]);
+        r.copy(&g[1]); // q[0].q[2]
+        g[3].copy(&r);
+        g[3].mul(&q[2]);
+        r.copy(&g[0]); // q[0].q[1].q[2]
+        g[4].copy(&r);
+        g[4].mul(&q[3]);
+        r.copy(&g[1]); // q[0].q[3]
+        g[5].copy(&r);
+        g[5].mul(&q[3]);
+        r.copy(&g[2]); // q[0].q[1].q[3]
+        g[6].copy(&r);
+        g[6].mul(&q[3]);
+        r.copy(&g[3]); // q[0].q[2].q[3]
+        g[7].copy(&r);
+        g[7].mul(&q[3]); // q[0].q[1].q[2].q[3]
+
+        // Make it odd
+        let pb = 1 - t[0].parity();
+        t[0].inc(pb);
+        t[0].norm();
+
+        // Number of bits
+        mt.zero();
+        for i in 0..4 {
+            mt.or(&t[i]);
+        }
+
+        let nb = 1 + mt.nbits();
+
+        // Sign pivot
+        s[nb - 1] = 1;
+        for i in 0..nb - 1 {
+            t[0].fshr(1);
+            s[i] = (2 * t[0].parity() - 1) as i8;
+            //println!("s={}",s[i]);
+        }
+
+        // Recoded exponent
+        for i in 0..nb {
+            w[i] = 0;
+            let mut k = 1;
+            for j in 1..4 {
+                let bt = s[i] * (t[j].parity() as i8);
+                t[j].fshr(1);
+                t[j].dec((bt >> 1) as isize);
+                t[j].norm();
+                w[i] += bt * (k as i8);
+                k = 2 * k;
+            }
+        }
+
+        // Main loop
+        p.selector(&g, (2 * w[nb - 1] + 1) as i32);
+        for i in (0..nb - 1).rev() {
+            p.usqr();
+            r.selector(&g, (2 * w[i] + s[i]) as i32);
+            p.mul(&r);
+        }
+
+        // apply correction
+        r.copy(&q[0]);
+        r.conj();
+        r.mul(&p);
+        p.cmove(&r, pb);
+        p.reduce();
+        return p;
+    }
+}
diff --git a/src/fp16.rs b/src/fp16.rs
new file mode 100644
index 0000000..c579db4
--- /dev/null
+++ b/src/fp16.rs
@@ -0,0 +1,602 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use super::fp2::FP2;
+use super::fp8::FP8;
+use super::big::BIG;
+//use std::str::SplitWhitespace;
+
+#[derive(Copy, Clone)]
+pub struct FP16 {
+    a: FP8,
+    b: FP8,
+}
+
+impl FP16 {
+    pub fn new() -> FP16 {
+        FP16 {
+            a: FP8::new(),
+            b: FP8::new(),
+        }
+    }
+
+    pub fn new_int(a: isize) -> FP16 {
+        let mut f = FP16::new();
+        f.a.copy(&FP8::new_int(a));
+        f.b.zero();
+        return f;
+    }
+
+    pub fn new_copy(x: &FP16) -> FP16 {
+        let mut f = FP16::new();
+        f.a.copy(&x.a);
+        f.b.copy(&x.b);
+        return f;
+    }
+
+    pub fn new_fp8s(c: &FP8, d: &FP8) -> FP16 {
+        let mut f = FP16::new();
+        f.a.copy(c);
+        f.b.copy(d);
+        return f;
+    }
+
+    pub fn new_fp8(c: &FP8) -> FP16 {
+        let mut f = FP16::new();
+        f.a.copy(c);
+        f.b.zero();
+        return f;
+    }
+
+    pub fn set_fp8s(&mut self,c: &FP8, d: &FP8) {
+        self.a.copy(&c);
+	self.b.copy(&d);
+    }
+
+    pub fn set_fp8(&mut self,c: &FP8) {
+        self.a.copy(&c);
+	self.b.zero();
+    }
+
+    pub fn set_fp8h(&mut self,c: &FP8) {
+        self.b.copy(&c);
+	self.a.zero();
+    }
+
+    /* reduce components mod Modulus */
+    pub fn reduce(&mut self) {
+        self.a.reduce();
+        self.b.reduce();
+    }
+
+    /* normalise components of w */
+    pub fn norm(&mut self) {
+        self.a.norm();
+        self.b.norm();
+    }
+
+    pub fn cmove(&mut self, g: &FP16, d: isize) {
+        self.a.cmove(&g.a, d);
+        self.b.cmove(&g.b, d);
+    }
+
+    /* test self=0 ? */
+    pub fn iszilch(&self) -> bool {
+        return self.a.iszilch() && self.b.iszilch();
+    }
+
+    /* test self=1 ? */
+    pub fn isunity(&self) -> bool {
+        let one = FP8::new_int(1);
+        return self.a.equals(&one) && self.b.iszilch();
+    }
+
+    /* test is w real? That is in a+ib test b is zero */
+    pub fn isreal(&mut self) -> bool {
+        return self.b.iszilch();
+    }
+    /* extract real part a */
+    pub fn real(&self) -> FP8 {
+        let f = FP8::new_copy(&self.a);
+        return f;
+    }
+
+    pub fn geta(&self) -> FP8 {
+        return self.a;
+//        let f = FP8::new_copy(&self.a);
+//        return f;
+    }
+    /* extract imaginary part b */
+    pub fn getb(&self) -> FP8 {
+        return self.b;
+//        let f = FP8::new_copy(&self.b);
+//        return f;
+    }
+
+    /* test self=x */
+    pub fn equals(&self, x: &FP16) -> bool {
+        return self.a.equals(&x.a) && self.b.equals(&x.b);
+    }
+    /* copy self=x */
+    pub fn copy(&mut self, x: &FP16) {
+        self.a.copy(&x.a);
+        self.b.copy(&x.b);
+    }
+
+    /* set self=0 */
+    pub fn zero(&mut self) {
+        self.a.zero();
+        self.b.zero();
+    }
+
+    /* set self=1 */
+    pub fn one(&mut self) {
+        self.a.one();
+        self.b.zero();
+    }
+
+    /* negate self mod Modulus */
+    pub fn neg(&mut self) {
+        self.norm();
+        let mut m = FP8::new_copy(&self.a);
+        let mut t = FP8::new();
+
+        m.add(&self.b);
+        m.neg();
+        t.copy(&m);
+        t.add(&self.b);
+        self.b.copy(&m);
+        self.b.add(&self.a);
+        self.a.copy(&t);
+        self.norm();
+    }
+
+    /* set to a-ib */
+    pub fn conj(&mut self) {
+        self.b.neg();
+        self.norm();
+    }
+
+    /* self=-conjugate(self) */
+    pub fn nconj(&mut self) {
+        self.a.neg();
+        self.norm();
+    }
+
+    /* self+=a */
+    pub fn add(&mut self, x: &FP16) {
+        self.a.add(&x.a);
+        self.b.add(&x.b);
+    }
+
+    pub fn padd(&mut self, x: &FP8) {
+        self.a.add(x);
+    }
+
+    pub fn dbl(&mut self) {
+        self.a.dbl();
+        self.b.dbl();
+    }
+
+    /* self-=a */
+    pub fn sub(&mut self, x: &FP16) {
+        let mut m = FP16::new_copy(x);
+        m.neg();
+        self.add(&m);
+    }
+
+    /* this-=x */
+    pub fn rsub(&mut self, x: &FP16) {
+        self.neg();
+        self.add(x);
+    }
+
+    /* self*=s, where s is an FP8 */
+    pub fn pmul(&mut self, s: &FP8) {
+        self.a.mul(s);
+        self.b.mul(s);
+    }
+
+    /* self*=s, where s is an FP2 */
+    pub fn qmul(&mut self, s: &FP2) {
+        self.a.qmul(s);
+        self.b.qmul(s);
+    }
+
+    /* self*=i, where i is an int */
+    pub fn imul(&mut self, c: isize) {
+        self.a.imul(c);
+        self.b.imul(c);
+    }
+
+    /* self*=self */
+
+    pub fn sqr(&mut self) {
+        let mut t1 = FP8::new_copy(&self.a);
+        let mut t2 = FP8::new_copy(&self.b);
+        let mut t3 = FP8::new_copy(&self.a);
+
+        t3.mul(&self.b);
+        t1.add(&self.b);
+        t2.times_i();
+
+        t2.add(&self.a);
+
+        t1.norm();
+        t2.norm();
+
+        self.a.copy(&t1);
+
+        self.a.mul(&t2);
+
+        t2.copy(&t3);
+        t2.times_i();
+        t2.add(&t3);
+        t2.norm();
+        t2.neg();
+        self.a.add(&t2);
+
+        t3.dbl();
+        self.b.copy(&t3);
+
+        self.norm();
+    }
+
+    /* self*=y */
+    pub fn mul(&mut self, y: &FP16) {
+        let mut t1 = FP8::new_copy(&self.a);
+        let mut t2 = FP8::new_copy(&self.b);
+        let mut t3 = FP8::new();
+        let mut t4 = FP8::new_copy(&self.b);
+
+        t1.mul(&y.a);
+        t2.mul(&y.b);
+        t3.copy(&y.b);
+        t3.add(&y.a);
+        t4.add(&self.a);
+
+        t3.norm();
+        t4.norm();
+
+        t4.mul(&t3);
+
+        t3.copy(&t1);
+        t3.neg();
+        t4.add(&t3);
+        t4.norm();
+
+        t3.copy(&t2);
+        t3.neg();
+        self.b.copy(&t4);
+        self.b.add(&t3);
+
+        t2.times_i();
+        self.a.copy(&t2);
+        self.a.add(&t1);
+
+        self.norm();
+    }
+
+    /* output to hex string */
+    pub fn tostring(&mut self) -> String {
+        return format!("[{},{}]", self.a.tostring(), self.b.tostring());
+    }
+
+    /* self=1/self */
+    pub fn inverse(&mut self) {
+        let mut t1 = FP8::new_copy(&self.a);
+        let mut t2 = FP8::new_copy(&self.b);
+
+        t1.sqr();
+        t2.sqr();
+        t2.times_i();
+        t2.norm();
+        t1.sub(&t2);
+        t1.norm();
+        t1.inverse();
+        self.a.mul(&t1);
+        t1.neg();
+        t1.norm();
+        self.b.mul(&t1);
+    }
+
+    /* self*=i where i = sqrt(-1+sqrt(-1)) */
+    pub fn times_i(&mut self) {
+        let mut s = FP8::new_copy(&self.b);
+        let t = FP8::new_copy(&self.a);
+        s.times_i();
+        self.a.copy(&s);
+        self.b.copy(&t);
+
+        self.norm();
+    }
+
+    pub fn times_i2(&mut self) {
+        self.a.times_i();
+        self.b.times_i();
+    }
+
+    pub fn times_i4(&mut self) {
+        self.a.times_i2();
+        self.b.times_i2();
+    }
+
+    /* self=self^p using Frobenius */
+    pub fn frob(&mut self, f: &FP2) {
+        let mut ff = FP2::new_copy(f);
+        ff.sqr();
+        ff.norm();
+        self.a.frob(&ff);
+        self.b.frob(&ff);
+        self.b.qmul(f);
+        self.b.times_i();
+    }
+
+    /* self=self^e */
+    pub fn pow(&self, e: &BIG) -> FP16 {
+        let mut w = FP16::new_copy(self);
+        w.norm();
+        let mut z = BIG::new_copy(&e);
+        let mut r = FP16::new_int(1);
+        z.norm();
+        loop {
+            let bt = z.parity();
+            z.fshr(1);
+            if bt == 1 {
+                r.mul(&mut w)
+            };
+            if z.iszilch() {
+                break;
+            }
+            w.sqr();
+        }
+        r.reduce();
+        return r;
+    }
+
+    /* XTR xtr_a function */
+    pub fn xtr_a(&mut self, w: &FP16, y: &FP16, z: &FP16) {
+        let mut r = FP16::new_copy(w);
+        let mut t = FP16::new_copy(w);
+        r.sub(y);
+        r.norm();
+        r.pmul(&self.a);
+        t.add(y);
+        t.norm();
+        t.pmul(&self.b);
+        t.times_i();
+
+        self.copy(&r);
+        self.add(&t);
+        self.add(z);
+
+        self.norm();
+    }
+
+    /* XTR xtr_d function */
+    pub fn xtr_d(&mut self) {
+        let mut w = FP16::new_copy(self);
+        self.sqr();
+        w.conj();
+        w.dbl();
+        w.norm();
+        self.sub(&w);
+        self.reduce();
+    }
+
+    /* r=x^n using XTR method on traces of FP24s */
+    pub fn xtr_pow(&self, n: &BIG) -> FP16 {
+        let mut sf = FP16::new_copy(self);
+        sf.norm();
+        let mut a = FP16::new_int(3);
+        let mut b = FP16::new_copy(&sf);
+        let mut c = FP16::new_copy(&b);
+        c.xtr_d();
+        let mut t = FP16::new();
+        let mut r = FP16::new();
+
+        let par = n.parity();
+        let mut v = BIG::new_copy(n);
+        v.norm();
+        v.fshr(1);
+        if par == 0 {
+            v.dec(1);
+            v.norm();
+        }
+
+        let nb = v.nbits();
+        for i in (0..nb).rev() {
+            if v.bit(i) != 1 {
+                t.copy(&b);
+                sf.conj();
+                c.conj();
+                b.xtr_a(&a, &sf, &c);
+                sf.conj();
+                c.copy(&t);
+                c.xtr_d();
+                a.xtr_d();
+            } else {
+                t.copy(&a);
+                t.conj();
+                a.copy(&b);
+                a.xtr_d();
+                b.xtr_a(&c, &sf, &t);
+                c.xtr_d();
+            }
+        }
+        if par == 0 {
+            r.copy(&c)
+        } else {
+            r.copy(&b)
+        }
+        r.reduce();
+        return r;
+    }
+
+    /* r=ck^a.cl^n using XTR double exponentiation method on traces of FP12s. See Stam thesis. */
+    pub fn xtr_pow2(&mut self, ck: &FP16, ckml: &FP16, ckm2l: &FP16, a: &BIG, b: &BIG) -> FP16 {
+        let mut e = BIG::new_copy(a);
+        let mut d = BIG::new_copy(b);
+        let mut w = BIG::new();
+        d.norm();
+        e.norm();
+
+        let mut cu = FP16::new_copy(ck); // can probably be passed in w/o copying
+        let mut cv = FP16::new_copy(self);
+        let mut cumv = FP16::new_copy(ckml);
+        let mut cum2v = FP16::new_copy(ckm2l);
+        let mut r = FP16::new();
+        let mut t = FP16::new();
+
+        let mut f2: usize = 0;
+        while d.parity() == 0 && e.parity() == 0 {
+            d.fshr(1);
+            e.fshr(1);
+            f2 += 1;
+        }
+
+        while BIG::comp(&d, &e) != 0 {
+            if BIG::comp(&d, &e) > 0 {
+                w.copy(&e);
+                w.imul(4);
+                w.norm();
+                if BIG::comp(&d, &w) <= 0 {
+                    w.copy(&d);
+                    d.copy(&e);
+                    e.rsub(&w);
+                    e.norm();
+
+                    t.copy(&cv);
+                    t.xtr_a(&cu, &cumv, &cum2v);
+                    cum2v.copy(&cumv);
+                    cum2v.conj();
+                    cumv.copy(&cv);
+                    cv.copy(&cu);
+                    cu.copy(&t);
+                } else {
+                    if d.parity() == 0 {
+                        d.fshr(1);
+                        r.copy(&cum2v);
+                        r.conj();
+                        t.copy(&cumv);
+                        t.xtr_a(&cu, &cv, &r);
+                        cum2v.copy(&cumv);
+                        cum2v.xtr_d();
+                        cumv.copy(&t);
+                        cu.xtr_d();
+                    } else {
+                        if e.parity() == 1 {
+                            d.sub(&e);
+                            d.norm();
+                            d.fshr(1);
+                            t.copy(&cv);
+                            t.xtr_a(&cu, &cumv, &cum2v);
+                            cu.xtr_d();
+                            cum2v.copy(&cv);
+                            cum2v.xtr_d();
+                            cum2v.conj();
+                            cv.copy(&t);
+                        } else {
+                            w.copy(&d);
+                            d.copy(&e);
+                            d.fshr(1);
+                            e.copy(&w);
+                            t.copy(&cumv);
+                            t.xtr_d();
+                            cumv.copy(&cum2v);
+                            cumv.conj();
+                            cum2v.copy(&t);
+                            cum2v.conj();
+                            t.copy(&cv);
+                            t.xtr_d();
+                            cv.copy(&cu);
+                            cu.copy(&t);
+                        }
+                    }
+                }
+            }
+            if BIG::comp(&d, &e) < 0 {
+                w.copy(&d);
+                w.imul(4);
+                w.norm();
+                if BIG::comp(&e, &w) <= 0 {
+                    e.sub(&d);
+                    e.norm();
+                    t.copy(&cv);
+                    t.xtr_a(&cu, &cumv, &cum2v);
+                    cum2v.copy(&cumv);
+                    cumv.copy(&cu);
+                    cu.copy(&t);
+                } else {
+                    if e.parity() == 0 {
+                        w.copy(&d);
+                        d.copy(&e);
+                        d.fshr(1);
+                        e.copy(&w);
+                        t.copy(&cumv);
+                        t.xtr_d();
+                        cumv.copy(&cum2v);
+                        cumv.conj();
+                        cum2v.copy(&t);
+                        cum2v.conj();
+                        t.copy(&cv);
+                        t.xtr_d();
+                        cv.copy(&cu);
+                        cu.copy(&t);
+                    } else {
+                        if d.parity() == 1 {
+                            w.copy(&e);
+                            e.copy(&d);
+                            w.sub(&d);
+                            w.norm();
+                            d.copy(&w);
+                            d.fshr(1);
+                            t.copy(&cv);
+                            t.xtr_a(&cu, &cumv, &cum2v);
+                            cumv.conj();
+                            cum2v.copy(&cu);
+                            cum2v.xtr_d();
+                            cum2v.conj();
+                            cu.copy(&cv);
+                            cu.xtr_d();
+                            cv.copy(&t);
+                        } else {
+                            d.fshr(1);
+                            r.copy(&cum2v);
+                            r.conj();
+                            t.copy(&cumv);
+                            t.xtr_a(&cu, &cv, &r);
+                            cum2v.copy(&cumv);
+                            cum2v.xtr_d();
+                            cumv.copy(&t);
+                            cu.xtr_d();
+                        }
+                    }
+                }
+            }
+        }
+        r.copy(&cv);
+        r.xtr_a(&cu, &cumv, &cum2v);
+        for _ in 0..f2 {
+            r.xtr_d()
+        }
+        r = r.xtr_pow(&mut d);
+        return r;
+    }
+}
diff --git a/src/fp2.rs b/src/fp2.rs
new file mode 100644
index 0000000..c848f19
--- /dev/null
+++ b/src/fp2.rs
@@ -0,0 +1,407 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use super::fp;
+use super::fp::FP;
+use super::big::BIG;
+use super::dbig::DBIG;
+use super::rom;
+use std::str::SplitWhitespace;
+use std::fmt;
+
+#[derive(Copy, Clone)]
+pub struct FP2 {
+    a: FP,
+    b: FP,
+}
+
+impl PartialEq for FP2 {
+	fn eq(&self, other: &FP2) -> bool {
+		self.equals(other)
+	}
+}
+
+impl fmt::Display for FP2 {
+	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+		write!(f, "FP2: [ {}, {} ]", self.a, self.b)
+	}
+}
+
+impl fmt::Debug for FP2 {
+	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+		write!(f, "FP2: [ {}, {} ]", self.a, self.b)
+	}
+}
+
+impl FP2 {
+    pub fn new() -> FP2 {
+        FP2 {
+            a: FP::new(),
+            b: FP::new(),
+        }
+    }
+
+    pub fn new_int(a: isize) -> FP2 {
+        let mut f = FP2::new();
+        f.a.copy(&FP::new_int(a));
+        f.b.zero();
+        return f;
+    }
+
+    pub fn new_copy(x: &FP2) -> FP2 {
+        let mut f = FP2::new();
+        f.a.copy(&x.a);
+        f.b.copy(&x.b);
+        return f;
+    }
+
+    pub fn new_fps(c: &FP, d: &FP) -> FP2 {
+        let mut f = FP2::new();
+        f.a.copy(c);
+        f.b.copy(d);
+        return f;
+    }
+
+    pub fn new_bigs(c: &BIG, d: &BIG) -> FP2 {
+        let mut f = FP2::new();
+        f.a.copy(&FP::new_big(c));
+        f.b.copy(&FP::new_big(d));
+        return f;
+    }
+
+    pub fn new_fp(c: &FP) -> FP2 {
+        let mut f = FP2::new();
+        f.a.copy(c);
+        f.b.zero();
+        return f;
+    }
+
+    pub fn new_big(c: &BIG) -> FP2 {
+        let mut f = FP2::new();
+        f.a.copy(&FP::new_big(c));
+        f.b.zero();
+        return f;
+    }
+
+    /* reduce components mod Modulus */
+    pub fn reduce(&mut self) {
+        self.a.reduce();
+        self.b.reduce();
+    }
+
+    /* normalise components of w */
+    pub fn norm(&mut self) {
+        self.a.norm();
+        self.b.norm();
+    }
+
+    /* test self=0 ? */
+    pub fn iszilch(&self) -> bool {
+        return self.a.iszilch() && self.b.iszilch();
+    }
+
+    pub fn cmove(&mut self, g: &FP2, d: isize) {
+        self.a.cmove(&g.a, d);
+        self.b.cmove(&g.b, d);
+    }
+
+    /* test self=1 ? */
+    pub fn isunity(&self) -> bool {
+        let one = FP::new_int(1);
+        return self.a.equals(&one) && self.b.iszilch();
+    }
+
+    /* test self=x */
+    pub fn equals(&self, x: &FP2) -> bool {
+        return self.a.equals(&x.a) && self.b.equals(&x.b);
+    }
+
+    /* extract a */
+    pub fn geta(&mut self) -> BIG {
+        return self.a.redc();
+    }
+
+    /* extract b */
+    pub fn getb(&mut self) -> BIG {
+        return self.b.redc();
+    }
+
+    /* copy self=x */
+    pub fn copy(&mut self, x: &FP2) {
+        self.a.copy(&x.a);
+        self.b.copy(&x.b);
+    }
+
+    /* set self=0 */
+    pub fn zero(&mut self) {
+        self.a.zero();
+        self.b.zero();
+    }
+
+    /* set self=1 */
+    pub fn one(&mut self) {
+        self.a.one();
+        self.b.zero();
+    }
+
+    /* negate self mod Modulus */
+    pub fn neg(&mut self) {
+        let mut m = FP::new_copy(&self.a);
+        let mut t = FP::new();
+
+        m.add(&self.b);
+        m.neg();
+        t.copy(&m);
+        t.add(&self.b);
+        self.b.copy(&m);
+        self.b.add(&self.a);
+        self.a.copy(&t);
+    }
+
+    /* set to a-ib */
+    pub fn conj(&mut self) {
+        self.b.neg();
+        self.b.norm();
+    }
+
+    /* self+=a */
+    pub fn add(&mut self, x: &FP2) {
+        self.a.add(&x.a);
+        self.b.add(&x.b);
+    }
+
+    pub fn dbl(&mut self) {
+        self.a.dbl();
+        self.b.dbl();
+    }
+
+    /* self-=a */
+    pub fn sub(&mut self, x: &FP2) {
+        let mut m = FP2::new_copy(x);
+        m.neg();
+        self.add(&m);
+    }
+
+    /* self=a-self */
+    pub fn rsub(&mut self, x: &FP2) {
+        self.neg();
+        self.add(x);
+    }
+
+    /* self*=s, where s is an FP */
+    pub fn pmul(&mut self, s: &FP) {
+        self.a.mul(s);
+        self.b.mul(s);
+    }
+
+    /* self*=i, where i is an int */
+    pub fn imul(&mut self, c: isize) {
+        self.a.imul(c);
+        self.b.imul(c);
+    }
+
+    /* self*=self */
+    pub fn sqr(&mut self) {
+        let mut w1 = FP::new_copy(&self.a);
+        let mut w3 = FP::new_copy(&self.a);
+        let mut mb = FP::new_copy(&self.b);
+
+        w1.add(&self.b);
+
+        w3.add(&self.a);
+        w3.norm();
+        self.b.mul(&w3);
+
+        mb.neg();
+        self.a.add(&mb);
+
+        w1.norm();
+        self.a.norm();
+
+        self.a.mul(&w1);
+    }
+
+    /* this*=y */
+    pub fn mul(&mut self, y: &FP2) {
+        if ((self.a.xes + self.b.xes) as i64) * ((y.a.xes + y.b.xes) as i64) > fp::FEXCESS as i64 {
+            if self.a.xes > 1 {
+                self.a.reduce()
+            }
+            if self.b.xes > 1 {
+                self.b.reduce()
+            }
+        }
+
+        let p = BIG::new_ints(&rom::MODULUS);
+        let mut pr = DBIG::new();
+
+        pr.ucopy(&p);
+
+        let mut c = BIG::new_copy(&(self.a.x));
+        let mut d = BIG::new_copy(&(y.a.x));
+
+        let mut a = BIG::mul(&self.a.x, &y.a.x);
+        let mut b = BIG::mul(&self.b.x, &y.b.x);
+
+        c.add(&self.b.x);
+        c.norm();
+        d.add(&y.b.x);
+        d.norm();
+
+        let mut e = BIG::mul(&c, &d);
+        let mut f = DBIG::new_copy(&a);
+        f.add(&b);
+        b.rsub(&pr);
+
+        a.add(&b);
+        a.norm();
+        e.sub(&f);
+        e.norm();
+
+        self.a.x.copy(&FP::modulo(&mut a));
+        self.a.xes = 3;
+        self.b.x.copy(&FP::modulo(&mut e));
+        self.b.xes = 2;
+    }
+
+    /* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+    /* returns true if this is QR */
+    pub fn sqrt(&mut self) -> bool {
+        if self.iszilch() {
+            return true;
+        }
+        let mut w1 = FP::new_copy(&self.b);
+        let mut w2 = FP::new_copy(&self.a);
+        w1.sqr();
+        w2.sqr();
+        w1.add(&w2);
+        if w1.jacobi() != 1 {
+            self.zero();
+            return false;
+        }
+        w2.copy(&w1.sqrt());
+        w1.copy(&w2);
+        w2.copy(&self.a);
+        w2.add(&w1);
+        w2.norm();
+        w2.div2();
+        if w2.jacobi() != 1 {
+            w2.copy(&self.a);
+            w2.sub(&w1);
+            w2.norm();
+            w2.div2();
+            if w2.jacobi() != 1 {
+                self.zero();
+                return false;
+            }
+        }
+        w1.copy(&w2.sqrt());
+        self.a.copy(&w1);
+        w1.dbl();
+        w1.inverse();
+        self.b.mul(&w1);
+        return true;
+    }
+
+    /* output to hex string */
+    pub fn tostring(&mut self) -> String {
+        return format!("[{},{}]", self.a.tostring(), self.b.tostring());
+    }
+
+    pub fn to_hex(&self) -> String {
+        format!("{} {}", self.a.to_hex(), self.b.to_hex())
+    }
+
+    pub fn from_hex_iter(iter: &mut SplitWhitespace) -> FP2 {
+        FP2 {
+            a: FP::from_hex_iter(iter),
+            b: FP::from_hex_iter(iter)
+        }
+    }
+
+    pub fn from_hex(val: String) -> FP2 {
+        let mut iter = val.split_whitespace();
+        return FP2::from_hex_iter(&mut iter);
+    }
+
+    /* self=1/self */
+    pub fn inverse(&mut self) {
+        self.norm();
+        let mut w1 = FP::new_copy(&self.a);
+        let mut w2 = FP::new_copy(&self.b);
+
+        w1.sqr();
+        w2.sqr();
+        w1.add(&w2);
+        w1.inverse();
+        self.a.mul(&w1);
+        w1.neg();
+        w1.norm();
+        self.b.mul(&w1);
+    }
+
+    /* self/=2 */
+    pub fn div2(&mut self) {
+        self.a.div2();
+        self.b.div2();
+    }
+
+    /* self*=sqrt(-1) */
+    pub fn times_i(&mut self) {
+        let z = FP::new_copy(&self.a);
+        self.a.copy(&self.b);
+        self.a.neg();
+        self.b.copy(&z);
+    }
+
+    /* w*=(1+sqrt(-1)) */
+    /* where X*2-(1+sqrt(-1)) is irreducible for FP4, assumes p=3 mod 8 */
+    pub fn mul_ip(&mut self) {
+        let t = FP2::new_copy(self);
+        let z = FP::new_copy(&self.a);
+        self.a.copy(&self.b);
+        self.a.neg();
+        self.b.copy(&z);
+        self.add(&t);
+    }
+
+    pub fn div_ip2(&mut self) {
+        let mut t = FP2::new();
+        self.norm();
+        t.a.copy(&self.a);
+        t.a.add(&self.b);
+        t.b.copy(&self.b);
+        t.b.sub(&self.a);
+        t.norm();
+        self.copy(&t);
+    }
+
+    /* w/=(1+sqrt(-1)) */
+    pub fn div_ip(&mut self) {
+        let mut t = FP2::new();
+        self.norm();
+        t.a.copy(&self.a);
+        t.a.add(&self.b);
+        t.b.copy(&self.b);
+        t.b.sub(&self.a);
+        t.norm();
+        self.copy(&t);
+        self.div2();
+    }
+}
diff --git a/src/fp24.rs b/src/fp24.rs
new file mode 100644
index 0000000..5f154b3
--- /dev/null
+++ b/src/fp24.rs
@@ -0,0 +1,1268 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use super::big;
+use super::ecp;
+use super::fp2::FP2;
+use super::fp4::FP4;
+use super::fp8::FP8;
+use super::big::BIG;
+use super::rom;
+use types::{SexticTwist};
+//use std::str::SplitWhitespace;
+
+pub const ZERO: usize=0;
+pub const ONE: usize=1;
+pub const SPARSER: usize=2;
+pub const SPARSE: usize=3;
+pub const DENSE: usize=4;
+
+#[derive(Copy, Clone)]
+pub struct FP24 {
+    a: FP8,
+    b: FP8,
+    c: FP8,
+    stype: usize,
+}
+
+impl FP24 {
+    pub fn new() -> FP24 {
+        FP24 {
+            a: FP8::new(),
+            b: FP8::new(),
+            c: FP8::new(),
+            stype: ZERO,
+        }
+    }
+
+    pub fn settype(&mut self,t: usize)  {
+	self.stype = t;
+    }
+
+    pub fn gettype(&self) -> usize {
+        return self.stype;
+    }
+
+    pub fn new_int(a: isize) -> FP24 {
+        let mut f = FP24::new();
+        f.a.copy(&FP8::new_int(a));
+        f.b.zero();
+        f.c.zero();
+	if a == 1 {
+	    f.stype=ONE;
+	} else {
+	    f.stype=SPARSER;
+	}
+        return f;
+    }
+
+    pub fn new_copy(x: &FP24) -> FP24 {
+        let mut f = FP24::new();
+        f.a.copy(&x.a);
+        f.b.copy(&x.b);
+        f.c.copy(&x.c);
+	f.stype=x.stype;
+        return f;
+    }
+
+    pub fn new_fp8s(d: &FP8, e: &FP8, f: &FP8) -> FP24 {
+        let mut g = FP24::new();
+        g.a.copy(d);
+        g.b.copy(e);
+        g.c.copy(f);
+	g.stype=DENSE;
+        return g;
+    }
+
+    pub fn new_fp8(d: &FP8) -> FP24 {
+        let mut g = FP24::new();
+        g.a.copy(d);
+        g.b.zero();
+        g.c.zero();
+	g.stype=SPARSER;
+        return g;
+    }
+
+    /* reduce components mod Modulus */
+    pub fn reduce(&mut self) {
+        self.a.reduce();
+        self.b.reduce();
+        self.c.reduce();
+    }
+
+    /* normalise components of w */
+    pub fn norm(&mut self) {
+        self.a.norm();
+        self.b.norm();
+        self.c.norm();
+    }
+
+    /* test self=0 ? */
+    pub fn iszilch(&self) -> bool {
+        return self.a.iszilch() && self.b.iszilch() && self.c.iszilch();
+    }
+
+    /* Conditional move of g to self dependant on d */
+    pub fn cmove(&mut self, g: &FP24, d: isize) {
+        self.a.cmove(&g.a, d);
+        self.b.cmove(&g.b, d);
+        self.c.cmove(&g.c, d);
+	let mut u=d as usize;
+	u=!(u-1);
+	self.stype^=(self.stype^g.stype)&u;
+    }
+
+    /* return 1 if b==c, no branching */
+    fn teq(b: i32, c: i32) -> isize {
+        let mut x = b ^ c;
+        x -= 1; // if x=0, x now -1
+        return ((x >> 31) & 1) as isize;
+    }
+
+    /* Constant time select from pre-computed table */
+    pub fn selector(&mut self, g: &[FP24], b: i32) {
+        let m = b >> 31;
+        let mut babs = (b ^ m) - m;
+
+        babs = (babs - 1) / 2;
+
+        self.cmove(&g[0], FP24::teq(babs, 0)); // conditional move
+        self.cmove(&g[1], FP24::teq(babs, 1));
+        self.cmove(&g[2], FP24::teq(babs, 2));
+        self.cmove(&g[3], FP24::teq(babs, 3));
+        self.cmove(&g[4], FP24::teq(babs, 4));
+        self.cmove(&g[5], FP24::teq(babs, 5));
+        self.cmove(&g[6], FP24::teq(babs, 6));
+        self.cmove(&g[7], FP24::teq(babs, 7));
+
+        let mut invf = FP24::new_copy(self);
+        invf.conj();
+        self.cmove(&invf, (m & 1) as isize);
+    }
+
+    /* test self=1 ? */
+    pub fn isunity(&self) -> bool {
+        let one = FP8::new_int(1);
+        return self.a.equals(&one) && self.b.iszilch() && self.c.iszilch();
+    }
+
+    /* test self=x */
+    pub fn equals(&self, x: &FP24) -> bool {
+        return self.a.equals(&x.a) && self.b.equals(&x.b) && self.c.equals(&x.c);
+    }
+
+    pub fn geta(&mut self) -> FP8 {
+        return self.a;
+//        let f = FP8::new_copy(&self.a);
+//        return f;
+    }
+
+    pub fn getb(&mut self) -> FP8 {
+        return self.b;
+//	let f = FP8::new_copy(&self.b);
+//        return f;
+    }
+
+    pub fn getc(&mut self) -> FP8 {
+        return self.c;
+//        let f = FP8::new_copy(&self.c);
+//        return f;
+    }
+
+    /* copy self=x */
+    pub fn copy(&mut self, x: &FP24) {
+        self.a.copy(&x.a);
+        self.b.copy(&x.b);
+        self.c.copy(&x.c);
+	self.stype=x.stype;
+    }
+
+    /* set self=1 */
+    pub fn one(&mut self) {
+        self.a.one();
+        self.b.zero();
+        self.c.zero();
+	self.stype=ONE;
+    }
+
+    /* set self=0 */
+    pub fn zero(&mut self) {
+        self.a.zero();
+        self.b.zero();
+        self.c.zero();
+	self.stype=ZERO;
+    }
+
+    /* this=conj(this) */
+    pub fn conj(&mut self) {
+        self.a.conj();
+        self.b.nconj();
+        self.c.conj();
+    }
+
+    /* Granger-Scott Unitary Squaring */
+    pub fn usqr(&mut self) {
+        let mut a = FP8::new_copy(&self.a);
+        let mut b = FP8::new_copy(&self.c);
+        let mut c = FP8::new_copy(&self.b);
+        let mut d = FP8::new();
+
+        self.a.sqr();
+        d.copy(&self.a);
+        d.add(&self.a);
+        self.a.add(&d);
+
+        self.a.norm();
+        a.nconj();
+
+        a.dbl();
+        self.a.add(&a);
+        b.sqr();
+        b.times_i();
+
+        d.copy(&b);
+        d.add(&b);
+        b.add(&d);
+        b.norm();
+
+        c.sqr();
+        d.copy(&c);
+        d.add(&c);
+        c.add(&d);
+        c.norm();
+
+        self.b.conj();
+        self.b.dbl();
+        self.c.nconj();
+
+        self.c.dbl();
+        self.b.add(&b);
+        self.c.add(&c);
+        self.stype=DENSE;
+        self.reduce();
+    }
+
+    /* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
+    pub fn sqr(&mut self) {
+        if self.stype==ONE {
+            return;
+        }
+        let mut a = FP8::new_copy(&self.a);
+        let mut b = FP8::new_copy(&self.b);
+        let mut c = FP8::new_copy(&self.c);
+        let mut d = FP8::new_copy(&self.a);
+
+        a.sqr();
+        b.mul(&self.c);
+        b.dbl();
+        b.norm();
+        c.sqr();
+        d.mul(&self.b);
+        d.dbl();
+
+        self.c.add(&self.a);
+        self.c.add(&self.b);
+        self.c.norm();
+        self.c.sqr();
+
+        self.a.copy(&a);
+        a.add(&b);
+        a.norm();
+        a.add(&c);
+        a.add(&d);
+        a.norm();
+
+        a.neg();
+        b.times_i();
+        c.times_i();
+
+        self.a.add(&b);
+
+        self.b.copy(&c);
+        self.b.add(&d);
+        self.c.add(&a);
+        if self.stype==SPARSER {
+            self.stype=SPARSE;
+        } else {
+            self.stype=DENSE;
+        }
+        self.norm();
+    }
+
+    /* FP24 full multiplication self=self*y */
+    pub fn mul(&mut self, y: &FP24) {
+        let mut z0 = FP8::new_copy(&self.a);
+        let mut z1 = FP8::new();
+        let mut z2 = FP8::new_copy(&mut self.b);
+        let mut z3 = FP8::new();
+        let mut t0 = FP8::new_copy(&self.a);
+        let mut t1 = FP8::new_copy(&y.a);
+
+        z0.mul(&y.a);
+        z2.mul(&y.b);
+
+        t0.add(&self.b);
+        t1.add(&y.b);
+
+        t0.norm();
+        t1.norm();
+
+        z1.copy(&t0);
+        z1.mul(&t1);
+        t0.copy(&self.b);
+        t0.add(&self.c);
+        t1.copy(&y.b);
+        t1.add(&y.c);
+
+        t0.norm();
+        t1.norm();
+
+        z3.copy(&t0);
+        z3.mul(&t1);
+
+        t0.copy(&z0);
+        t0.neg();
+        t1.copy(&z2);
+        t1.neg();
+
+        z1.add(&t0);
+        //z1.norm();
+        self.b.copy(&z1);
+        self.b.add(&t1);
+
+        z3.add(&t1);
+        z2.add(&t0);
+
+        t0.copy(&self.a);
+        t0.add(&self.c);
+        t0.norm();
+        t1.copy(&y.a);
+        t1.add(&y.c);
+        t1.norm();
+        t0.mul(&t1);
+        z2.add(&t0);
+
+        t0.copy(&self.c);
+        t0.mul(&y.c);
+        t1.copy(&t0);
+        t1.neg();
+
+        self.c.copy(&z2);
+        self.c.add(&t1);
+        z3.add(&t1);
+        t0.times_i();
+        self.b.add(&t0);
+        z3.norm();
+
+        z3.times_i();
+        self.a.copy(&z0);
+        self.a.add(&z3);
+        self.stype=DENSE;
+        self.norm();
+    }
+
+/* FP24 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+    pub fn ssmul(&mut self, y: &FP24) {
+        if self.stype==ONE {
+            self.copy(&y);
+            return;
+        }
+        if y.stype==ONE {
+            return;
+        }
+        if y.stype>=SPARSE {
+            let mut z0=FP8::new_copy(&self.a);
+            let mut z1=FP8::new_int(0);
+            let mut z2=FP8::new_int(0);
+            let mut z3=FP8::new_int(0);
+            z0.mul(&y.a);
+
+            if ecp::SEXTIC_TWIST==SexticTwist::M_TYPE {
+                if y.stype==SPARSE || self.stype==SPARSE {
+
+                    let mut ga=FP4::new_int(0);
+		    let mut gb=FP4::new_int(0);
+
+                    gb.copy(&self.b.getb());
+                    gb.mul(&y.b.getb());
+                    ga.zero();
+                    if y.stype!=SPARSE {
+                        ga.copy(&self.b.getb());
+                        ga.mul(&y.b.geta());
+                    }
+                    if self.stype!=SPARSE {
+                        ga.copy(&self.b.geta());
+                        ga.mul(&y.b.getb());
+                    }
+		    z2.set_fp4s(&ga,&gb);
+                    z2.times_i();
+                } else {
+                    z2.copy(&self.b);
+                    z2.mul(&y.b);
+                }
+            } else { 
+               z2.copy(&self.b);
+               z2.mul(&y.b);
+            }
+            let mut t0=FP8::new_copy(&self.a);
+            let mut t1=FP8::new_copy(&y.a);
+            t0.add(&self.b); t0.norm();
+            t1.add(&y.b); t1.norm();
+
+            z1.copy(&t0); z1.mul(&t1);
+            t0.copy(&self.b); t0.add(&self.c); t0.norm();
+            t1.copy(&y.b); t1.add(&y.c); t1.norm();
+
+            z3.copy(&t0); z3.mul(&t1);
+
+            t0.copy(&z0); t0.neg();
+            t1.copy(&z2); t1.neg();
+ 
+            z1.add(&t0);
+            self.b.copy(&z1); self.b.add(&t1);
+
+            z3.add(&t1);
+            z2.add(&t0);
+
+            t0.copy(&self.a); t0.add(&self.c); t0.norm();
+            t1.copy(&y.a); t1.add(&y.c); t1.norm();
+	
+            t0.mul(&t1);
+            z2.add(&t0);
+
+            if ecp::SEXTIC_TWIST==SexticTwist::D_TYPE {
+                if y.stype==SPARSE || self.stype==SPARSE {
+
+                    let mut ga=FP4::new_int(0);
+		    let mut gb=FP4::new_int(0);
+
+                    ga.copy(&self.c.geta());
+                    ga.mul(&y.c.geta());
+                    gb.zero();
+                    if y.stype!=SPARSE {
+                        gb.copy(&self.c.geta());
+                        gb.mul(&y.c.getb());
+                    }
+                    if self.stype!=SPARSE {
+                        gb.copy(&self.c.getb());
+                        gb.mul(&y.c.geta());
+                    }
+		    t0.set_fp4s(&ga,&gb);
+                } else {
+                    t0.copy(&self.c);
+                    t0.mul(&y.c);
+                }
+            } else { 
+                t0.copy(&self.c);
+                t0.mul(&y.c);
+            }
+            t1.copy(&t0); t1.neg();
+
+            self.c.copy(&z2); self.c.add(&t1);
+            z3.add(&t1);
+            t0.times_i();
+            self.b.add(&t0);
+            z3.norm();
+            z3.times_i();
+            self.a.copy(&z0); self.a.add(&z3);
+        } else {
+            if self.stype==SPARSER {
+                self.smul(&y);
+                return;
+            }
+            if ecp::SEXTIC_TWIST==SexticTwist::D_TYPE { // dense by sparser - 13m 
+                let mut z0=FP8::new_copy(&self.a);
+                let mut z2=FP8::new_copy(&self.b);
+                let mut z3=FP8::new_copy(&self.b);
+                let mut t0=FP8::new_int(0);
+                let mut t1=FP8::new_copy(&y.a);
+
+                z0.mul(&y.a);
+                z2.pmul(&y.b.geta());
+                self.b.add(&self.a);
+                t1.padd(&y.b.geta());
+
+                t1.norm();
+                self.b.norm();
+                self.b.mul(&t1);
+                z3.add(&self.c);
+                z3.norm();
+                z3.pmul(&y.b.geta());
+
+                t0.copy(&z0); t0.neg();
+                t1.copy(&z2); t1.neg();
+
+                self.b.add(&t0);
+
+                self.b.add(&t1);
+                z3.add(&t1);
+                z2.add(&t0);
+
+                t0.copy(&self.a); t0.add(&self.c); t0.norm();
+                z3.norm();
+                t0.mul(&y.a);
+                self.c.copy(&z2); self.c.add(&t0);
+
+                z3.times_i();
+                self.a.copy(&z0); self.a.add(&z3);
+            }
+            if ecp::SEXTIC_TWIST==SexticTwist::M_TYPE {
+
+                let mut z0 = FP8::new_copy(&self.a);
+                let mut z1 = FP8::new();
+                let mut z2 = FP8::new();
+                let mut z3 = FP8::new();
+                let mut t0 = FP8::new_copy(&self.a);
+                let mut t1 = FP8::new();
+	
+                z0.mul(&y.a);
+                t0.add(&self.b); t0.norm();
+
+                z1.copy(&t0); z1.mul(&y.a);
+                t0.copy(&self.b); t0.add(&self.c);
+                t0.norm();
+
+                z3.copy(&t0);
+                z3.pmul(&y.c.getb());
+                z3.times_i();
+
+                t0.copy(&z0); t0.neg();
+                z1.add(&t0);
+                self.b.copy(&z1);
+                z2.copy(&t0);
+
+                t0.copy(&self.a); t0.add(&self.c); t0.norm();
+                t1.copy(&y.a); t1.add(&y.c); t1.norm();
+
+                t0.mul(&t1);
+                z2.add(&t0);
+                t0.copy(&self.c);
+			
+                t0.pmul(&y.c.getb());
+                t0.times_i();
+                t1.copy(&t0); t1.neg();
+
+                self.c.copy(&z2); self.c.add(&t1);
+                z3.add(&t1);
+                t0.times_i();
+                self.b.add(&t0);
+                z3.norm();
+                z3.times_i();
+                self.a.copy(&z0); self.a.add(&z3);
+           }	
+        }
+        self.stype=DENSE;
+        self.norm();
+    }
+
+
+    /* Special case of multiplication arises from special form of ATE pairing line function */
+    pub fn smul(&mut self, y: &FP24) {
+        if ecp::SEXTIC_TWIST==SexticTwist::D_TYPE {	
+            let mut w1=FP4::new_copy(&self.a.geta());
+            let mut w2=FP4::new_copy(&self.a.getb());
+            let mut w3=FP4::new_copy(&self.b.geta());
+
+            w1.mul(&y.a.geta());
+            w2.mul(&y.a.getb());
+            w3.mul(&y.b.geta());
+
+            let mut ta=FP4::new_copy(&self.a.geta());
+            let mut tb=FP4::new_copy(&y.a.geta());
+            ta.add(&self.a.getb()); ta.norm();
+            tb.add(&y.a.getb()); tb.norm();
+            let mut tc=FP4::new_copy(&ta);
+            tc.mul(&tb);
+            let mut t=FP4::new_copy(&w1);
+            t.add(&w2);
+            t.neg();
+            tc.add(&t);
+
+            ta.copy(&self.a.geta()); ta.add(&self.b.geta()); ta.norm();
+            tb.copy(&y.a.geta()); tb.add(&y.b.geta()); tb.norm();
+            let mut td=FP4::new_copy(&ta);
+            td.mul(&tb);
+            t.copy(&w1);
+            t.add(&w3);
+            t.neg();
+            td.add(&t);
+
+            ta.copy(&self.a.getb()); ta.add(&self.b.geta()); ta.norm();
+            tb.copy(&y.a.getb()); tb.add(&y.b.geta()); tb.norm();
+            let mut te=FP4::new_copy(&ta);
+            te.mul(&tb);
+            t.copy(&w2);
+            t.add(&w3);
+            t.neg();
+            te.add(&t);
+
+            w2.times_i();
+            w1.add(&w2);
+
+	    self.a.set_fp4s(&w1,&tc);
+	    self.b.set_fp4s(&td,&te);
+	    self.c.set_fp4(&w3);
+
+            self.a.norm();
+            self.b.norm();
+        } else {
+            let mut w1=FP4::new_copy(&self.a.geta());
+            let mut w2=FP4::new_copy(&self.a.getb());
+            let mut w3=FP4::new_copy(&self.c.getb());
+
+            w1.mul(&y.a.geta());
+            w2.mul(&y.a.getb());
+            w3.mul(&y.c.getb());
+
+            let mut ta=FP4::new_copy(&self.a.geta());
+            let mut tb=FP4::new_copy(&y.a.geta());
+            ta.add(&self.a.getb()); ta.norm();
+            tb.add(&y.a.getb()); tb.norm();
+            let mut tc=FP4::new_copy(&ta);
+            tc.mul(&tb);
+            let mut t=FP4::new_copy(&w1);
+            t.add(&w2);
+            t.neg();
+            tc.add(&t);
+
+            ta.copy(&self.a.geta()); ta.add(&self.c.getb()); ta.norm();
+            tb.copy(&y.a.geta()); tb.add(&y.c.getb()); tb.norm();
+            let mut td=FP4::new_copy(&ta);
+            td.mul(&tb);
+            t.copy(&w1);
+            t.add(&w3);
+            t.neg();
+            td.add(&t);
+
+            ta.copy(&self.a.getb()); ta.add(&self.c.getb()); ta.norm();
+            tb.copy(&y.a.getb()); tb.add(&y.c.getb()); tb.norm();
+            let mut te=FP4::new_copy(&ta);
+            te.mul(&tb);
+            t.copy(&w2);
+            t.add(&w3);
+            t.neg();
+            te.add(&t);
+
+            w2.times_i();
+            w1.add(&w2);
+	    self.a.set_fp4s(&w1,&tc);
+
+            w3.times_i();
+            w3.norm();
+	    self.b.set_fp4h(&w3);
+
+            te.norm();
+            te.times_i();
+	    self.c.set_fp4s(&te,&td);
+
+            self.a.norm();
+            self.c.norm();
+	}
+	self.stype=SPARSE;
+    }
+
+    /* self=1/self */
+    pub fn inverse(&mut self) {
+        let mut f0 = FP8::new_copy(&self.a);
+        let mut f1 = FP8::new_copy(&self.b);
+        let mut f2 = FP8::new_copy(&self.a);
+        let mut f3 = FP8::new();
+
+        //self.norm();
+        f0.sqr();
+        f1.mul(&self.c);
+        f1.times_i();
+        f0.sub(&f1);
+        f0.norm();
+
+        f1.copy(&self.c);
+        f1.sqr();
+        f1.times_i();
+        f2.mul(&self.b);
+        f1.sub(&f2);
+        f1.norm();
+
+        f2.copy(&self.b);
+        f2.sqr();
+        f3.copy(&self.a);
+        f3.mul(&self.c);
+        f2.sub(&f3);
+        f2.norm();
+
+        f3.copy(&self.b);
+        f3.mul(&f2);
+        f3.times_i();
+        self.a.mul(&f0);
+        f3.add(&self.a);
+        self.c.mul(&f1);
+        self.c.times_i();
+
+        f3.add(&self.c);
+        f3.norm();
+        f3.inverse();
+        self.a.copy(&f0);
+        self.a.mul(&f3);
+        self.b.copy(&f1);
+        self.b.mul(&f3);
+        self.c.copy(&f2);
+        self.c.mul(&f3);
+        self.stype=DENSE;
+    }
+
+    /* self=self^p using Frobenius */
+    pub fn frob(&mut self, f: &FP2, n: isize) {
+        let mut f2 = FP2::new_copy(f);
+        let mut f3 = FP2::new_copy(f);
+
+        f2.sqr();
+        f3.mul(&f2);
+
+        f3.mul_ip();
+        f3.norm();
+
+        for _i in 0..n {
+            self.a.frob(&f3);
+            self.b.frob(&f3);
+            self.c.frob(&f3);
+
+            self.b.qmul(f);
+            self.b.times_i2();
+            self.c.qmul(&f2);
+            self.c.times_i2();
+            self.c.times_i2();
+        }
+        self.stype=DENSE;
+    }
+
+    /* trace function */
+    pub fn trace(&mut self) -> FP8 {
+        let mut t = FP8::new();
+        t.copy(&self.a);
+        t.imul(3);
+        t.reduce();
+        return t;
+    }
+
+    /* convert from byte array to FP24 */
+    pub fn frombytes(w: &[u8]) -> FP24 {
+        let mut t: [u8; big::MODBYTES as usize] = [0; big::MODBYTES as usize];
+        let mb = big::MODBYTES as usize;
+
+        for i in 0..mb {
+            t[i] = w[i]
+        }
+        let mut a = BIG::frombytes(&t);
+        for i in 0..mb {
+            t[i] = w[i + mb]
+        }
+        let mut b = BIG::frombytes(&t);
+        let mut c = FP2::new_bigs(&a, &b);
+
+        for i in 0..mb {
+            t[i] = w[i + 2 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 3 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        let mut d = FP2::new_bigs(&a, &b);
+
+        let mut ea = FP4::new_fp2s(&c, &d);
+
+        for i in 0..mb {
+            t[i] = w[i + 4 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 5 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        c.copy(&FP2::new_bigs(&a, &b));
+
+        for i in 0..mb {
+            t[i] = w[i + 6 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 7 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        d.copy(&FP2::new_bigs(&a, &b));
+
+        let mut eb = FP4::new_fp2s(&c, &d);
+
+        let e = FP8::new_fp4s(&ea, &eb);
+
+        for i in 0..mb {
+            t[i] = w[i + 8 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 9 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        c.copy(&FP2::new_bigs(&a, &b));
+
+        for i in 0..mb {
+            t[i] = w[i + 10 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 11 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        d.copy(&FP2::new_bigs(&a, &b));
+
+        ea.copy(&FP4::new_fp2s(&c, &d));
+
+        for i in 0..mb {
+            t[i] = w[i + 12 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 13 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        c.copy(&FP2::new_bigs(&a, &b));
+
+        for i in 0..mb {
+            t[i] = w[i + 14 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 15 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        d.copy(&FP2::new_bigs(&a, &b));
+
+        eb.copy(&FP4::new_fp2s(&c, &d));
+
+        let f = FP8::new_fp4s(&ea, &eb);
+
+        for i in 0..mb {
+            t[i] = w[i + 16 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 17 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+
+        c.copy(&FP2::new_bigs(&a, &b));
+
+        for i in 0..mb {
+            t[i] = w[i + 18 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 19 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        d.copy(&FP2::new_bigs(&a, &b));
+
+        ea.copy(&FP4::new_fp2s(&c, &d));
+
+        for i in 0..mb {
+            t[i] = w[i + 20 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 21 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+
+        c.copy(&FP2::new_bigs(&a, &b));
+
+        for i in 0..mb {
+            t[i] = w[i + 22 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 23 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        d.copy(&FP2::new_bigs(&a, &b));
+
+        eb.copy(&FP4::new_fp2s(&c, &d));
+
+        let g = FP8::new_fp4s(&ea, &eb);
+
+        return FP24::new_fp8s(&e, &f, &g);
+    }
+
+    /* convert this to byte array */
+    pub fn tobytes(&mut self, w: &mut [u8]) {
+        let mut t: [u8; big::MODBYTES as usize] = [0; big::MODBYTES as usize];
+        let mb = big::MODBYTES as usize;
+
+        self.a.geta().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i] = t[i]
+        }
+        self.a.geta().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + mb] = t[i]
+        }
+        self.a.geta().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 2 * mb] = t[i]
+        }
+        self.a.geta().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 3 * mb] = t[i]
+        }
+
+        self.a.getb().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 4 * mb] = t[i]
+        }
+        self.a.getb().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 5 * mb] = t[i]
+        }
+        self.a.getb().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 6 * mb] = t[i]
+        }
+        self.a.getb().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 7 * mb] = t[i]
+        }
+
+        self.b.geta().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 8 * mb] = t[i]
+        }
+        self.b.geta().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 9 * mb] = t[i]
+        }
+        self.b.geta().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 10 * mb] = t[i]
+        }
+        self.b.geta().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 11 * mb] = t[i]
+        }
+
+        self.b.getb().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 12 * mb] = t[i]
+        }
+        self.b.getb().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 13 * mb] = t[i]
+        }
+        self.b.getb().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 14 * mb] = t[i]
+        }
+        self.b.getb().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 15 * mb] = t[i]
+        }
+
+        self.c.geta().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 16 * mb] = t[i]
+        }
+        self.c.geta().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 17 * mb] = t[i]
+        }
+        self.c.geta().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 18 * mb] = t[i]
+        }
+        self.c.geta().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 19 * mb] = t[i]
+        }
+
+        self.c.getb().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 20 * mb] = t[i]
+        }
+        self.c.getb().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 21 * mb] = t[i]
+        }
+        self.c.getb().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 22 * mb] = t[i]
+        }
+        self.c.getb().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 23 * mb] = t[i]
+        }
+    }
+
+    /* output to hex string */
+    pub fn tostring(&mut self) -> String {
+        return format!(
+            "[{},{},{}]",
+            self.a.tostring(),
+            self.b.tostring(),
+            self.c.tostring()
+        );
+    }
+
+    /* self=self^e */
+    pub fn pow(&self, e: &BIG) -> FP24 {
+        let mut r = FP24::new_copy(self);
+        r.norm();
+        let mut e1 = BIG::new_copy(e);
+        e1.norm();
+        let mut e3 = BIG::new_copy(&e1);
+        e3.pmul(3);
+        e3.norm();
+        let mut w = FP24::new_copy(&r);
+
+        let nb = e3.nbits();
+        for i in (1..nb - 1).rev() {
+            w.usqr();
+            let bt = e3.bit(i) - e1.bit(i);
+            if bt == 1 {
+                w.mul(&r);
+            }
+            if bt == -1 {
+                r.conj();
+                w.mul(&r);
+                r.conj();
+            }
+        }
+
+        w.reduce();
+        return w;
+    }
+
+    /* constant time powering by small integer of max length bts */
+    pub fn pinpow(&mut self, e: i32, bts: i32) {
+        let mut r: [FP24; 2] = [FP24::new_int(1), FP24::new_copy(self)];
+        let mut t = FP24::new();
+
+        for i in (0..bts).rev() {
+            let b: usize = ((e >> i) & 1) as usize;
+            t.copy(&r[b]);
+            r[1 - b].mul(&t);
+            r[b].usqr();
+        }
+        self.copy(&r[0]);
+    }
+
+    pub fn compow(&mut self, e: &BIG, r: &BIG) -> FP8 {
+        let f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
+        let q = BIG::new_ints(&rom::MODULUS);
+
+        let mut g1 = FP24::new_copy(self);
+        let mut g2 = FP24::new_copy(self);
+
+        let mut m = BIG::new_copy(&q);
+        m.rmod(&r);
+
+        let mut a = BIG::new_copy(&e);
+        a.rmod(&mut m);
+
+        let mut b = BIG::new_copy(&e);
+        b.div(&mut m);
+
+        let mut c = g1.trace();
+
+        if b.iszilch() {
+            c = c.xtr_pow(&mut a);
+            return c;
+        }
+
+        g2.frob(&f, 1);
+        let cp = g2.trace();
+        g1.conj();
+        g2.mul(&g1);
+        let cpm1 = g2.trace();
+        g2.mul(&g1);
+        let cpm2 = g2.trace();
+
+        c = c.xtr_pow2(&cp, &cpm1, &cpm2, &mut a, &mut b);
+
+        return c;
+    }
+
+    /* p=q0^u0.q1^u1.q2^u2.q3^u3... */
+    // Bos & Costello https://eprint.iacr.org/2013/458.pdf
+    // Faz-Hernandez & Longa & Sanchez  https://eprint.iacr.org/2013/158.pdf
+    // Side channel attack secure
+    pub fn pow8(q: &[FP24], u: &[BIG]) -> FP24 {
+        let mut g1: [FP24; 8] = [
+            FP24::new(),
+            FP24::new(),
+            FP24::new(),
+            FP24::new(),
+            FP24::new(),
+            FP24::new(),
+            FP24::new(),
+            FP24::new(),
+        ];
+        let mut g2: [FP24; 8] = [
+            FP24::new(),
+            FP24::new(),
+            FP24::new(),
+            FP24::new(),
+            FP24::new(),
+            FP24::new(),
+            FP24::new(),
+            FP24::new(),
+        ];
+
+        let mut r = FP24::new();
+        let mut p = FP24::new();
+        const CT: usize = 1 + big::NLEN * (big::BASEBITS as usize);
+        let mut w1: [i8; CT] = [0; CT];
+        let mut s1: [i8; CT] = [0; CT];
+        let mut w2: [i8; CT] = [0; CT];
+        let mut s2: [i8; CT] = [0; CT];
+
+        let mut mt = BIG::new();
+        let mut t: [BIG; 8] = [
+            BIG::new_copy(&u[0]),
+            BIG::new_copy(&u[1]),
+            BIG::new_copy(&u[2]),
+            BIG::new_copy(&u[3]),
+            BIG::new_copy(&u[4]),
+            BIG::new_copy(&u[5]),
+            BIG::new_copy(&u[6]),
+            BIG::new_copy(&u[7]),
+        ];
+
+        for i in 0..8 {
+            t[i].norm();
+        }
+
+        // precomputation
+        g1[0].copy(&q[0]);
+        r.copy(&g1[0]);
+        g1[1].copy(&r);
+        g1[1].mul(&q[1]); // q[0].q[1]
+        g1[2].copy(&r);
+        g1[2].mul(&q[2]);
+        r.copy(&g1[1]); // q[0].q[2]
+        g1[3].copy(&r);
+        g1[3].mul(&q[2]);
+        r.copy(&g1[0]); // q[0].q[1].q[2]
+        g1[4].copy(&r);
+        g1[4].mul(&q[3]);
+        r.copy(&g1[1]); // q[0].q[3]
+        g1[5].copy(&r);
+        g1[5].mul(&q[3]);
+        r.copy(&g1[2]); // q[0].q[1].q[3]
+        g1[6].copy(&r);
+        g1[6].mul(&q[3]);
+        r.copy(&g1[3]); // q[0].q[2].q[3]
+        g1[7].copy(&r);
+        g1[7].mul(&q[3]); // q[0].q[1].q[2].q[3]
+
+        // Use Frobenius
+        let f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
+        for i in 0..8 {
+            g2[i].copy(&g1[i]);
+            g2[i].frob(&f, 4);
+        }
+
+        // Make it odd
+        let pb1 = 1 - t[0].parity();
+        t[0].inc(pb1);
+        t[0].norm();
+
+        let pb2 = 1 - t[4].parity();
+        t[4].inc(pb2);
+        t[4].norm();
+
+        // Number of bits
+        mt.zero();
+        for i in 0..8 {
+            mt.or(&t[i]);
+        }
+
+        let nb = 1 + mt.nbits();
+
+        // Sign pivot
+
+        s1[nb - 1] = 1;
+        s2[nb - 1] = 1;
+        for i in 0..nb - 1 {
+            t[0].fshr(1);
+            s1[i] = (2 * t[0].parity() - 1) as i8;
+            t[4].fshr(1);
+            s2[i] = (2 * t[4].parity() - 1) as i8;
+        }
+
+        // Recoded exponent
+        for i in 0..nb {
+            w1[i] = 0;
+            let mut k = 1;
+            for j in 1..4 {
+                let bt = s1[i] * (t[j].parity() as i8);
+                t[j].fshr(1);
+                t[j].dec((bt >> 1) as isize);
+                t[j].norm();
+                w1[i] += bt * (k as i8);
+                k = 2 * k;
+            }
+
+            w2[i] = 0;
+            k = 1;
+            for j in 5..8 {
+                let bt = s2[i] * (t[j].parity() as i8);
+                t[j].fshr(1);
+                t[j].dec((bt >> 1) as isize);
+                t[j].norm();
+                w2[i] += bt * (k as i8);
+                k = 2 * k;
+            }
+        }
+
+        // Main loop
+        p.selector(&g1, (2 * w1[nb - 1] + 1) as i32);
+        r.selector(&g2, (2 * w2[nb - 1] + 1) as i32);
+        p.mul(&r);
+        for i in (0..nb - 1).rev() {
+            p.usqr();
+            r.selector(&g1, (2 * w1[i] + s1[i]) as i32);
+            p.mul(&r);
+            r.selector(&g2, (2 * w2[i] + s2[i]) as i32);
+            p.mul(&r);
+        }
+
+        // apply correction
+        r.copy(&q[0]);
+        r.conj();
+        r.mul(&p);
+        p.cmove(&r, pb1);
+
+        r.copy(&q[4]);
+        r.conj();
+        r.mul(&p);
+        p.cmove(&r, pb2);
+
+        p.reduce();
+        return p;
+    }
+}
diff --git a/src/fp4.rs b/src/fp4.rs
new file mode 100644
index 0000000..1db4b73
--- /dev/null
+++ b/src/fp4.rs
@@ -0,0 +1,697 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use super::fp::FP;
+use super::fp2::FP2;
+use super::big::BIG;
+use std::str::SplitWhitespace;
+
+#[derive(Copy, Clone)]
+pub struct FP4 {
+    a: FP2,
+    b: FP2,
+}
+
+impl PartialEq for FP4 {
+	fn eq(&self, other: &FP4) -> bool {
+		self.equals(other)
+	}
+}
+
+impl FP4 {
+    pub fn new() -> FP4 {
+        FP4 {
+            a: FP2::new(),
+            b: FP2::new(),
+        }
+    }
+
+    pub fn new_int(a: isize) -> FP4 {
+        let mut f = FP4::new();
+        f.a.copy(&FP2::new_int(a));
+        f.b.zero();
+        return f;
+    }
+
+    pub fn new_copy(x: &FP4) -> FP4 {
+        let mut f = FP4::new();
+        f.a.copy(&x.a);
+        f.b.copy(&x.b);
+        return f;
+    }
+
+    pub fn new_fp2s(c: &FP2, d: &FP2) -> FP4 {
+        let mut f = FP4::new();
+        f.a.copy(c);
+        f.b.copy(d);
+        return f;
+    }
+
+    pub fn new_fp2(c: &FP2) -> FP4 {
+        let mut f = FP4::new();
+        f.a.copy(c);
+        f.b.zero();
+        return f;
+    }
+	
+    pub fn set_fp2s(&mut self,c: &FP2, d: &FP2) {
+        self.a.copy(&c);
+	self.b.copy(&d);
+    }
+
+    pub fn set_fp2(&mut self,c: &FP2) {
+        self.a.copy(&c);
+	self.b.zero();
+    }
+
+    pub fn set_fp2h(&mut self,c: &FP2) {
+        self.b.copy(&c);
+	self.a.zero();
+    }
+
+    /* reduce components mod Modulus */
+    pub fn reduce(&mut self) {
+        self.a.reduce();
+        self.b.reduce();
+    }
+
+    /* normalise components of w */
+    pub fn norm(&mut self) {
+        self.a.norm();
+        self.b.norm();
+    }
+
+    pub fn cmove(&mut self, g: &FP4, d: isize) {
+        self.a.cmove(&g.a, d);
+        self.b.cmove(&g.b, d);
+    }
+
+    /* test self=0 ? */
+    pub fn iszilch(&self) -> bool {
+        return self.a.iszilch() && self.b.iszilch();
+    }
+
+    /* test self=1 ? */
+    pub fn isunity(&self) -> bool {
+        let one = FP2::new_int(1);
+        return self.a.equals(&one) && self.b.iszilch();
+    }
+
+    /* test is w real? That is in a+ib test b is zero */
+    pub fn isreal(&mut self) -> bool {
+        return self.b.iszilch();
+    }
+    /* extract real part a */
+    pub fn real(&self) -> FP2 {
+        let f = FP2::new_copy(&self.a);
+        return f;
+    }
+
+    pub fn geta(&self) -> FP2 {
+        return self.a;
+//        let f = FP2::new_copy(&self.a);
+//        return f;
+    }
+    /* extract imaginary part b */
+    pub fn getb(&self) -> FP2 {
+        return self.b;
+//        let f = FP2::new_copy(&self.b);
+//        return f;
+    }
+
+    /* test self=x */
+    pub fn equals(&self, x: &FP4) -> bool {
+        return self.a.equals(&x.a) && self.b.equals(&x.b);
+    }
+    /* copy self=x */
+    pub fn copy(&mut self, x: &FP4) {
+        self.a.copy(&x.a);
+        self.b.copy(&x.b);
+    }
+
+    /* set self=0 */
+    pub fn zero(&mut self) {
+        self.a.zero();
+        self.b.zero();
+    }
+
+    /* set self=1 */
+    pub fn one(&mut self) {
+        self.a.one();
+        self.b.zero();
+    }
+
+    /* negate self mod Modulus */
+    pub fn neg(&mut self) {
+        self.norm();
+        let mut m = FP2::new_copy(&self.a);
+        let mut t = FP2::new();
+
+        m.add(&self.b);
+        m.neg();
+        t.copy(&m);
+        t.add(&self.b);
+        self.b.copy(&m);
+        self.b.add(&self.a);
+        self.a.copy(&t);
+        self.norm();
+    }
+
+    /* set to a-ib */
+    pub fn conj(&mut self) {
+        self.b.neg();
+        self.norm();
+    }
+
+    /* self=-conjugate(self) */
+    pub fn nconj(&mut self) {
+        self.a.neg();
+        self.norm();
+    }
+
+    /* self+=a */
+    pub fn add(&mut self, x: &FP4) {
+        self.a.add(&x.a);
+        self.b.add(&x.b);
+    }
+
+    pub fn padd(&mut self, x: &FP2) {
+        self.a.add(x);
+    }
+
+    pub fn dbl(&mut self) {
+        self.a.dbl();
+        self.b.dbl();
+    }
+
+    /* self-=a */
+    pub fn sub(&mut self, x: &FP4) {
+        let mut m = FP4::new_copy(x);
+        m.neg();
+        self.add(&m);
+    }
+
+    /* self-=a */
+    pub fn rsub(&mut self, x: &FP4) {
+        self.neg();
+        self.add(x);
+    }
+
+    /* self*=s, where s is an FP2 */
+    pub fn pmul(&mut self, s: &FP2) {
+        self.a.mul(s);
+        self.b.mul(s);
+    }
+
+    /* self*=s, where s is an FP */
+    pub fn qmul(&mut self, s: &FP) {
+        self.a.pmul(s);
+        self.b.pmul(s);
+    }
+
+    /* self*=i, where i is an int */
+    pub fn imul(&mut self, c: isize) {
+        self.a.imul(c);
+        self.b.imul(c);
+    }
+
+    /* self*=self */
+
+    pub fn sqr(&mut self) {
+        let mut t1 = FP2::new_copy(&self.a);
+        let mut t2 = FP2::new_copy(&self.b);
+        let mut t3 = FP2::new_copy(&self.a);
+
+        t3.mul(&self.b);
+        t1.add(&self.b);
+        t2.mul_ip();
+
+        t2.add(&self.a);
+
+        t1.norm();
+        t2.norm();
+
+        self.a.copy(&t1);
+
+        self.a.mul(&t2);
+
+        t2.copy(&t3);
+        t2.mul_ip();
+        t2.add(&t3);
+        t2.norm();
+        t2.neg();
+        self.a.add(&t2);
+
+        t3.dbl();
+        self.b.copy(&t3);
+
+        self.norm();
+    }
+
+    /* self*=y */
+    pub fn mul(&mut self, y: &FP4) {
+        //self.norm();
+
+        let mut t1 = FP2::new_copy(&self.a);
+        let mut t2 = FP2::new_copy(&self.b);
+        let mut t3 = FP2::new();
+        let mut t4 = FP2::new_copy(&self.b);
+
+        t1.mul(&y.a);
+        t2.mul(&y.b);
+        t3.copy(&y.b);
+        t3.add(&y.a);
+        t4.add(&self.a);
+
+        t3.norm();
+        t4.norm();
+
+        t4.mul(&t3);
+
+        t3.copy(&t1);
+        t3.neg();
+        t4.add(&t3);
+        t4.norm();
+
+        t3.copy(&t2);
+        t3.neg();
+        self.b.copy(&t4);
+        self.b.add(&t3);
+
+        t2.mul_ip();
+        self.a.copy(&t2);
+        self.a.add(&t1);
+
+        self.norm();
+    }
+
+    /* output to hex string */
+    pub fn tostring(&mut self) -> String {
+        return format!("[{},{}]", self.a.tostring(), self.b.tostring());
+    }
+
+    pub fn to_hex(&self) -> String {
+        format!("{} {}", self.a.to_hex(), self.b.to_hex())
+    }
+
+    pub fn from_hex_iter(iter: &mut SplitWhitespace) -> FP4 {
+        FP4 {
+            a: FP2::from_hex_iter(iter),
+            b: FP2::from_hex_iter(iter)
+        }
+    }
+
+    pub fn from_hex(val: String) -> FP4 {
+        let mut iter = val.split_whitespace();
+        return FP4::from_hex_iter(&mut iter);
+    }
+
+    /* self=1/self */
+    pub fn inverse(&mut self) {
+        //self.norm();
+
+        let mut t1 = FP2::new_copy(&self.a);
+        let mut t2 = FP2::new_copy(&self.b);
+
+        t1.sqr();
+        t2.sqr();
+        t2.mul_ip();
+        t2.norm();
+        t1.sub(&t2);
+        t1.inverse();
+        self.a.mul(&t1);
+        t1.neg();
+        t1.norm();
+        self.b.mul(&t1);
+    }
+
+    /* self*=i where i = sqrt(-1+sqrt(-1)) */
+    pub fn times_i(&mut self) {
+        let mut s = FP2::new_copy(&self.b);
+        let mut t = FP2::new_copy(&self.b);
+        s.times_i();
+        t.add(&s);
+        self.b.copy(&self.a);
+        self.a.copy(&t);
+        self.norm();
+    }
+
+    /* self=self^p using Frobenius */
+    pub fn frob(&mut self, f: &FP2) {
+        self.a.conj();
+        self.b.conj();
+        self.b.mul(f);
+    }
+
+    /* self=self^e */
+    pub fn pow(&self, e: &BIG) -> FP4 {
+        let mut w = FP4::new_copy(self);
+        w.norm();
+        let mut z = BIG::new_copy(&e);
+        let mut r = FP4::new_int(1);
+        z.norm();
+        loop {
+            let bt = z.parity();
+            z.fshr(1);
+            if bt == 1 {
+                r.mul(&mut w)
+            };
+            if z.iszilch() {
+                break;
+            }
+            w.sqr();
+        }
+        r.reduce();
+        return r;
+    }
+
+    /* XTR xtr_a function */
+    pub fn xtr_a(&mut self, w: &FP4, y: &FP4, z: &FP4) {
+        let mut r = FP4::new_copy(w);
+        let mut t = FP4::new_copy(w);
+        r.sub(y);
+        r.norm();
+        r.pmul(&self.a);
+        t.add(y);
+        t.norm();
+        t.pmul(&self.b);
+        t.times_i();
+
+        self.copy(&r);
+        self.add(&t);
+        self.add(z);
+
+        self.norm();
+    }
+
+    /* XTR xtr_d function */
+    pub fn xtr_d(&mut self) {
+        let mut w = FP4::new_copy(self);
+        self.sqr();
+        w.conj();
+        w.dbl();
+        w.norm();
+        self.sub(&w);
+        self.reduce();
+    }
+
+    /* r=x^n using XTR method on traces of FP12s */
+    pub fn xtr_pow(&self, n: &BIG) -> FP4 {
+        let mut sf = FP4::new_copy(self);
+        sf.norm();
+        let mut a = FP4::new_int(3);
+        let mut b = FP4::new_copy(&sf);
+        let mut c = FP4::new_copy(&b);
+        c.xtr_d();
+        let mut t = FP4::new();
+        let mut r = FP4::new();
+
+        let par = n.parity();
+        let mut v = BIG::new_copy(n);
+        v.norm();
+        v.fshr(1);
+        if par == 0 {
+            v.dec(1);
+            v.norm();
+        }
+
+        let nb = v.nbits();
+        for i in (0..nb).rev() {
+            if v.bit(i) != 1 {
+                t.copy(&b);
+                sf.conj();
+                c.conj();
+                b.xtr_a(&a, &sf, &c);
+                sf.conj();
+                c.copy(&t);
+                c.xtr_d();
+                a.xtr_d();
+            } else {
+                t.copy(&a);
+                t.conj();
+                a.copy(&b);
+                a.xtr_d();
+                b.xtr_a(&c, &sf, &t);
+                c.xtr_d();
+            }
+        }
+        if par == 0 {
+            r.copy(&c)
+        } else {
+            r.copy(&b)
+        }
+        r.reduce();
+        return r;
+    }
+
+    /* r=ck^a.cl^n using XTR double exponentiation method on traces of FP12s. See Stam thesis. */
+    pub fn xtr_pow2(&mut self, ck: &FP4, ckml: &FP4, ckm2l: &FP4, a: &BIG, b: &BIG) -> FP4 {
+        let mut e = BIG::new_copy(a);
+        let mut d = BIG::new_copy(b);
+        let mut w = BIG::new();
+        e.norm();
+        d.norm();
+
+        let mut cu = FP4::new_copy(ck); // can probably be passed in w/o copying
+        let mut cv = FP4::new_copy(self);
+        let mut cumv = FP4::new_copy(ckml);
+        let mut cum2v = FP4::new_copy(ckm2l);
+        let mut r = FP4::new();
+        let mut t = FP4::new();
+
+        let mut f2: usize = 0;
+        while d.parity() == 0 && e.parity() == 0 {
+            d.fshr(1);
+            e.fshr(1);
+            f2 += 1;
+        }
+
+        while BIG::comp(&d, &e) != 0 {
+            if BIG::comp(&d, &e) > 0 {
+                w.copy(&e);
+                w.imul(4);
+                w.norm();
+                if BIG::comp(&d, &w) <= 0 {
+                    w.copy(&d);
+                    d.copy(&e);
+                    e.rsub(&w);
+                    e.norm();
+
+                    t.copy(&cv);
+                    t.xtr_a(&cu, &cumv, &cum2v);
+                    cum2v.copy(&cumv);
+                    cum2v.conj();
+                    cumv.copy(&cv);
+                    cv.copy(&cu);
+                    cu.copy(&t);
+                } else {
+                    if d.parity() == 0 {
+                        d.fshr(1);
+                        r.copy(&cum2v);
+                        r.conj();
+                        t.copy(&cumv);
+                        t.xtr_a(&cu, &cv, &r);
+                        cum2v.copy(&cumv);
+                        cum2v.xtr_d();
+                        cumv.copy(&t);
+                        cu.xtr_d();
+                    } else {
+                        if e.parity() == 1 {
+                            d.sub(&e);
+                            d.norm();
+                            d.fshr(1);
+                            t.copy(&cv);
+                            t.xtr_a(&cu, &cumv, &cum2v);
+                            cu.xtr_d();
+                            cum2v.copy(&cv);
+                            cum2v.xtr_d();
+                            cum2v.conj();
+                            cv.copy(&t);
+                        } else {
+                            w.copy(&d);
+                            d.copy(&e);
+                            d.fshr(1);
+                            e.copy(&w);
+                            t.copy(&cumv);
+                            t.xtr_d();
+                            cumv.copy(&cum2v);
+                            cumv.conj();
+                            cum2v.copy(&t);
+                            cum2v.conj();
+                            t.copy(&cv);
+                            t.xtr_d();
+                            cv.copy(&cu);
+                            cu.copy(&t);
+                        }
+                    }
+                }
+            }
+            if BIG::comp(&d, &e) < 0 {
+                w.copy(&d);
+                w.imul(4);
+                w.norm();
+                if BIG::comp(&e, &w) <= 0 {
+                    e.sub(&d);
+                    e.norm();
+                    t.copy(&cv);
+                    t.xtr_a(&cu, &cumv, &cum2v);
+                    cum2v.copy(&cumv);
+                    cumv.copy(&cu);
+                    cu.copy(&t);
+                } else {
+                    if e.parity() == 0 {
+                        w.copy(&d);
+                        d.copy(&e);
+                        d.fshr(1);
+                        e.copy(&w);
+                        t.copy(&cumv);
+                        t.xtr_d();
+                        cumv.copy(&cum2v);
+                        cumv.conj();
+                        cum2v.copy(&t);
+                        cum2v.conj();
+                        t.copy(&cv);
+                        t.xtr_d();
+                        cv.copy(&cu);
+                        cu.copy(&t);
+                    } else {
+                        if d.parity() == 1 {
+                            w.copy(&e);
+                            e.copy(&d);
+                            w.sub(&d);
+                            w.norm();
+                            d.copy(&w);
+                            d.fshr(1);
+                            t.copy(&cv);
+                            t.xtr_a(&cu, &cumv, &cum2v);
+                            cumv.conj();
+                            cum2v.copy(&cu);
+                            cum2v.xtr_d();
+                            cum2v.conj();
+                            cu.copy(&cv);
+                            cu.xtr_d();
+                            cv.copy(&t);
+                        } else {
+                            d.fshr(1);
+                            r.copy(&cum2v);
+                            r.conj();
+                            t.copy(&cumv);
+                            t.xtr_a(&cu, &cv, &r);
+                            cum2v.copy(&cumv);
+                            cum2v.xtr_d();
+                            cumv.copy(&t);
+                            cu.xtr_d();
+                        }
+                    }
+                }
+            }
+        }
+        r.copy(&cv);
+        r.xtr_a(&cu, &cumv, &cum2v);
+        for _ in 0..f2 {
+            r.xtr_d()
+        }
+        r = r.xtr_pow(&mut d);
+        return r;
+    }
+
+    /* this/=2 */
+    pub fn div2(&mut self) {
+        self.a.div2();
+        self.b.div2();
+    }
+
+    pub fn div_i(&mut self) {
+        let mut u = FP2::new_copy(&self.a);
+        let v = FP2::new_copy(&self.b);
+        u.div_ip();
+        self.a.copy(&v);
+        self.b.copy(&u);
+    }
+
+    pub fn div_2i(&mut self) {
+        let mut u = FP2::new_copy(&self.a);
+        let mut v = FP2::new_copy(&self.b);
+        u.div_ip2();
+        v.dbl();
+        v.norm();
+        self.a.copy(&v);
+        self.b.copy(&u);
+    }
+
+    /* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+    /* returns true if this is QR */
+    pub fn sqrt(&mut self) -> bool {
+        if self.iszilch() {
+            return true;
+        }
+
+        let mut a = FP2::new_copy(&self.a);
+        let mut s = FP2::new_copy(&self.b);
+        let mut t = FP2::new_copy(&self.a);
+
+        if s.iszilch() {
+            if t.sqrt() {
+                self.a.copy(&t);
+                self.b.zero();
+            } else {
+                t.div_ip();
+                t.sqrt();
+                self.b.copy(&t);
+                self.a.zero();
+            }
+            return true;
+        }
+        s.sqr();
+        a.sqr();
+        s.mul_ip();
+        s.norm();
+        a.sub(&s);
+
+        s.copy(&a);
+        if !s.sqrt() {
+            return false;
+        }
+
+        a.copy(&t);
+        a.add(&s);
+        a.norm();
+        a.div2();
+
+        if !a.sqrt() {
+            a.copy(&t);
+            a.sub(&s);
+            a.norm();
+            a.div2();
+            if !a.sqrt() {
+                return false;
+            }
+        }
+        t.copy(&self.b);
+        s.copy(&a);
+        s.add(&a);
+        s.inverse();
+
+        t.mul(&s);
+        self.a.copy(&a);
+        self.b.copy(&t);
+
+        return true;
+    }
+}
diff --git a/src/fp48.rs b/src/fp48.rs
new file mode 100644
index 0000000..c72ee4c
--- /dev/null
+++ b/src/fp48.rs
@@ -0,0 +1,1610 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use super::big;
+use super::ecp;
+use super::fp2::FP2;
+use super::fp4::FP4;
+use super::fp8::FP8;
+use super::fp16::FP16;
+use super::big::BIG;
+use super::rom;
+use types::SexticTwist;
+//use std::str::SplitWhitespace;
+
+pub const ZERO: usize=0;
+pub const ONE: usize=1;
+pub const SPARSER: usize=2;
+pub const SPARSE: usize=3;
+pub const DENSE: usize=4;
+
+#[derive(Copy, Clone)]
+pub struct FP48 {
+    a: FP16,
+    b: FP16,
+    c: FP16,
+    stype: usize,
+}
+
+impl FP48 {
+    pub fn new() -> FP48 {
+        FP48 {
+            a: FP16::new(),
+            b: FP16::new(),
+            c: FP16::new(),
+	    stype: ZERO,
+        }
+    }
+
+    pub fn settype(&mut self,t: usize)  {
+	self.stype = t;
+    }
+
+    pub fn gettype(&self) -> usize {
+        return self.stype;
+    }
+
+    pub fn new_int(a: isize) -> FP48 {
+        let mut f = FP48::new();
+        f.a.copy(&FP16::new_int(a));
+        f.b.zero();
+        f.c.zero();
+	if a == 1 {
+	    f.stype=ONE;
+	} else {
+	    f.stype=SPARSER;
+	}
+        return f;
+    }
+
+    pub fn new_copy(x: &FP48) -> FP48 {
+        let mut f = FP48::new();
+        f.a.copy(&x.a);
+        f.b.copy(&x.b);
+        f.c.copy(&x.c);
+	f.stype=x.stype;
+        return f;
+    }
+
+    pub fn new_fp16s(d: &FP16, e: &FP16, f: &FP16) -> FP48 {
+        let mut g = FP48::new();
+        g.a.copy(d);
+        g.b.copy(e);
+        g.c.copy(f);
+	g.stype=DENSE;
+        return g;
+    }
+
+    pub fn new_fp16(d: &FP16) -> FP48 {
+        let mut g = FP48::new();
+        g.a.copy(d);
+        g.b.zero();
+        g.c.zero();
+	g.stype=SPARSER;
+        return g;
+    }
+
+    /* reduce components mod Modulus */
+    pub fn reduce(&mut self) {
+        self.a.reduce();
+        self.b.reduce();
+        self.c.reduce();
+    }
+
+    /* normalise components of w */
+    pub fn norm(&mut self) {
+        self.a.norm();
+        self.b.norm();
+        self.c.norm();
+    }
+
+    /* test self=0 ? */
+    pub fn iszilch(&self) -> bool {
+        return self.a.iszilch() && self.b.iszilch() && self.c.iszilch();
+    }
+
+    /* Conditional move of g to self dependant on d */
+    pub fn cmove(&mut self, g: &FP48, d: isize) {
+        self.a.cmove(&g.a, d);
+        self.b.cmove(&g.b, d);
+        self.c.cmove(&g.c, d);
+	let mut u=d as usize;
+	u=!(u-1);
+	self.stype^=(self.stype^g.stype)&u;
+    }
+
+    /* return 1 if b==c, no branching */
+    fn teq(b: i32, c: i32) -> isize {
+        let mut x = b ^ c;
+        x -= 1; // if x=0, x now -1
+        return ((x >> 31) & 1) as isize;
+    }
+
+    /* Constant time select from pre-computed table */
+    pub fn selector(&mut self, g: &[FP48], b: i32) {
+        let m = b >> 31;
+        let mut babs = (b ^ m) - m;
+
+        babs = (babs - 1) / 2;
+
+        self.cmove(&g[0], FP48::teq(babs, 0)); // conditional move
+        self.cmove(&g[1], FP48::teq(babs, 1));
+        self.cmove(&g[2], FP48::teq(babs, 2));
+        self.cmove(&g[3], FP48::teq(babs, 3));
+        self.cmove(&g[4], FP48::teq(babs, 4));
+        self.cmove(&g[5], FP48::teq(babs, 5));
+        self.cmove(&g[6], FP48::teq(babs, 6));
+        self.cmove(&g[7], FP48::teq(babs, 7));
+
+        let mut invf = FP48::new_copy(self);
+        invf.conj();
+        self.cmove(&invf, (m & 1) as isize);
+    }
+
+    /* test self=1 ? */
+    pub fn isunity(&self) -> bool {
+        let one = FP16::new_int(1);
+        return self.a.equals(&one) && self.b.iszilch() && self.c.iszilch();
+    }
+
+    /* test self=x */
+    pub fn equals(&self, x: &FP48) -> bool {
+        return self.a.equals(&x.a) && self.b.equals(&x.b) && self.c.equals(&x.c);
+    }
+
+    pub fn geta(&mut self) -> FP16 {
+        return self.a;
+//        let f = FP16::new_copy(&self.a);
+//        return f;
+    }
+
+    pub fn getb(&mut self) -> FP16 {
+        return self.b;
+//        let f = FP16::new_copy(&self.b);
+//        return f;
+    }
+
+    pub fn getc(&mut self) -> FP16 {
+        return self.c;
+//        let f = FP16::new_copy(&self.c);
+//        return f;
+    }
+
+    /* copy self=x */
+    pub fn copy(&mut self, x: &FP48) {
+        self.a.copy(&x.a);
+        self.b.copy(&x.b);
+        self.c.copy(&x.c);
+	self.stype=x.stype;
+    }
+
+    /* set self=1 */
+    pub fn one(&mut self) {
+        self.a.one();
+        self.b.zero();
+        self.c.zero();
+	self.stype=ONE;
+    }
+
+    /* set self=0 */
+    pub fn zero(&mut self) {
+        self.a.zero();
+        self.b.zero();
+        self.c.zero();
+	self.stype=ZERO;
+    }
+
+    /* this=conj(this) */
+    pub fn conj(&mut self) {
+        self.a.conj();
+        self.b.nconj();
+        self.c.conj();
+    }
+
+    /* Granger-Scott Unitary Squaring */
+    pub fn usqr(&mut self) {
+        let mut a = FP16::new_copy(&self.a);
+        let mut b = FP16::new_copy(&self.c);
+        let mut c = FP16::new_copy(&self.b);
+        let mut d = FP16::new();
+
+        self.a.sqr();
+        d.copy(&self.a);
+        d.add(&self.a);
+        self.a.add(&d);
+
+        self.a.norm();
+        a.nconj();
+
+        a.dbl();
+        self.a.add(&a);
+        b.sqr();
+        b.times_i();
+
+        d.copy(&b);
+        d.add(&b);
+        b.add(&d);
+        b.norm();
+
+        c.sqr();
+        d.copy(&c);
+        d.add(&c);
+        c.add(&d);
+        c.norm();
+
+        self.b.conj();
+        self.b.dbl();
+        self.c.nconj();
+
+        self.c.dbl();
+        self.b.add(&b);
+        self.c.add(&c);
+        self.stype=DENSE;
+        self.reduce();
+    }
+
+    /* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
+    pub fn sqr(&mut self) {
+        if self.stype==ONE {
+            return;
+        }
+        let mut a = FP16::new_copy(&self.a);
+        let mut b = FP16::new_copy(&self.b);
+        let mut c = FP16::new_copy(&self.c);
+        let mut d = FP16::new_copy(&self.a);
+
+        a.sqr();
+        b.mul(&self.c);
+        b.dbl();
+        b.norm();
+        c.sqr();
+        d.mul(&self.b);
+        d.dbl();
+
+        self.c.add(&self.a);
+        self.c.add(&self.b);
+        self.c.norm();
+        self.c.sqr();
+
+        self.a.copy(&a);
+        a.add(&b);
+        a.norm();
+        a.add(&c);
+        a.add(&d);
+        a.norm();
+
+        a.neg();
+        b.times_i();
+        c.times_i();
+
+        self.a.add(&b);
+
+        self.b.copy(&c);
+        self.b.add(&d);
+        self.c.add(&a);
+        if self.stype==SPARSER {
+            self.stype=SPARSE;
+        } else {
+            self.stype=DENSE;
+        }
+        self.norm();
+    }
+
+    /* FP48 full multiplication self=self*y */
+    pub fn mul(&mut self, y: &FP48) {
+        let mut z0 = FP16::new_copy(&self.a);
+        let mut z1 = FP16::new();
+        let mut z2 = FP16::new_copy(&mut self.b);
+        let mut z3 = FP16::new();
+        let mut t0 = FP16::new_copy(&self.a);
+        let mut t1 = FP16::new_copy(&y.a);
+
+        z0.mul(&y.a);
+        z2.mul(&y.b);
+
+        t0.add(&self.b);
+        t1.add(&y.b);
+
+        t0.norm();
+        t1.norm();
+
+        z1.copy(&t0);
+        z1.mul(&t1);
+        t0.copy(&self.b);
+        t0.add(&self.c);
+        t1.copy(&y.b);
+        t1.add(&y.c);
+
+        t0.norm();
+        t1.norm();
+
+        z3.copy(&t0);
+        z3.mul(&t1);
+
+        t0.copy(&z0);
+        t0.neg();
+        t1.copy(&z2);
+        t1.neg();
+
+        z1.add(&t0);
+        self.b.copy(&z1);
+        self.b.add(&t1);
+
+        z3.add(&t1);
+        z2.add(&t0);
+
+        t0.copy(&self.a);
+        t0.add(&self.c);
+        t0.norm();
+        t1.copy(&y.a);
+        t1.add(&y.c);
+        t1.norm();
+        t0.mul(&t1);
+        z2.add(&t0);
+
+        t0.copy(&self.c);
+        t0.mul(&y.c);
+        t1.copy(&t0);
+        t1.neg();
+
+        self.c.copy(&z2);
+        self.c.add(&t1);
+        z3.add(&t1);
+        t0.times_i();
+        self.b.add(&t0);
+        z3.norm();
+
+        z3.times_i();
+        self.a.copy(&z0);
+        self.a.add(&z3);
+        self.stype=DENSE;
+        self.norm();
+    }
+
+/* FP48 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+    pub fn ssmul(&mut self, y: &FP48) {
+        if self.stype==ONE {
+            self.copy(&y);
+            return;
+        }
+        if y.stype==ONE {
+            return;
+        }
+        if y.stype>=SPARSE {
+            let mut z0=FP16::new_copy(&self.a);
+            let mut z1=FP16::new_int(0);
+            let mut z2=FP16::new_int(0);
+            let mut z3=FP16::new_int(0);
+            z0.mul(&y.a);
+
+            if ecp::SEXTIC_TWIST==SexticTwist::M_TYPE {
+                if y.stype==SPARSE || self.stype==SPARSE {
+
+                    let mut ga=FP8::new_int(0);
+		    let mut gb=FP8::new_int(0);
+
+                    gb.copy(&self.b.getb());
+                    gb.mul(&y.b.getb());
+                    ga.zero();
+                    if y.stype!=SPARSE {
+                        ga.copy(&self.b.getb());
+                        ga.mul(&y.b.geta());
+                    }
+                    if self.stype!=SPARSE {
+                        ga.copy(&self.b.geta());
+                        ga.mul(&y.b.getb());
+                    }
+		    z2.set_fp8s(&ga,&gb);
+                    z2.times_i();
+                } else {
+                    z2.copy(&self.b);
+                    z2.mul(&y.b);
+                }
+            } else { 
+               z2.copy(&self.b);
+               z2.mul(&y.b);
+            }
+            let mut t0=FP16::new_copy(&self.a);
+            let mut t1=FP16::new_copy(&y.a);
+            t0.add(&self.b); t0.norm();
+            t1.add(&y.b); t1.norm();
+
+            z1.copy(&t0); z1.mul(&t1);
+            t0.copy(&self.b); t0.add(&self.c); t0.norm();
+            t1.copy(&y.b); t1.add(&y.c); t1.norm();
+
+            z3.copy(&t0); z3.mul(&t1);
+
+            t0.copy(&z0); t0.neg();
+            t1.copy(&z2); t1.neg();
+ 
+            z1.add(&t0);
+            self.b.copy(&z1); self.b.add(&t1);
+
+            z3.add(&t1);
+            z2.add(&t0);
+
+            t0.copy(&self.a); t0.add(&self.c); t0.norm();
+            t1.copy(&y.a); t1.add(&y.c); t1.norm();
+	
+            t0.mul(&t1);
+            z2.add(&t0);
+
+            if ecp::SEXTIC_TWIST==SexticTwist::D_TYPE {
+                if y.stype==SPARSE || self.stype==SPARSE {
+
+                    let mut ga=FP8::new_int(0);
+		    let mut gb=FP8::new_int(0);
+
+                    ga.copy(&self.c.geta());
+                    ga.mul(&y.c.geta());
+                    gb.zero();
+                    if y.stype!=SPARSE {
+                        gb.copy(&self.c.geta());
+                        gb.mul(&y.c.getb());
+                    }
+                    if self.stype!=SPARSE {
+                        gb.copy(&self.c.getb());
+                        gb.mul(&y.c.geta());
+                    }
+		    t0.set_fp8s(&ga,&gb);
+                } else {
+                    t0.copy(&self.c);
+                    t0.mul(&y.c);
+                }
+            } else { 
+                t0.copy(&self.c);
+                t0.mul(&y.c);
+            }
+            t1.copy(&t0); t1.neg();
+
+            self.c.copy(&z2); self.c.add(&t1);
+            z3.add(&t1);
+            t0.times_i();
+            self.b.add(&t0);
+            z3.norm();
+            z3.times_i();
+            self.a.copy(&z0); self.a.add(&z3);
+        } else {
+            if self.stype==SPARSER {
+                self.smul(&y);
+                return;
+            }
+            if ecp::SEXTIC_TWIST==SexticTwist::D_TYPE { // dense by sparser - 13m 
+                let mut z0=FP16::new_copy(&self.a);
+                let mut z2=FP16::new_copy(&self.b);
+                let mut z3=FP16::new_copy(&self.b);
+                let mut t0=FP16::new_int(0);
+                let mut t1=FP16::new_copy(&y.a);
+
+                z0.mul(&y.a);
+                z2.pmul(&y.b.geta());
+                self.b.add(&self.a);
+                t1.padd(&y.b.geta());
+
+                t1.norm();
+                self.b.norm();
+                self.b.mul(&t1);
+                z3.add(&self.c);
+                z3.norm();
+                z3.pmul(&y.b.geta());
+
+                t0.copy(&z0); t0.neg();
+                t1.copy(&z2); t1.neg();
+
+                self.b.add(&t0);
+
+                self.b.add(&t1);
+                z3.add(&t1);
+                z2.add(&t0);
+
+                t0.copy(&self.a); t0.add(&self.c); t0.norm();
+                z3.norm();
+                t0.mul(&y.a);
+                self.c.copy(&z2); self.c.add(&t0);
+
+                z3.times_i();
+                self.a.copy(&z0); self.a.add(&z3);
+            }
+            if ecp::SEXTIC_TWIST==SexticTwist::M_TYPE {
+
+                let mut z0 = FP16::new_copy(&self.a);
+                let mut z1 = FP16::new();
+                let mut z2 = FP16::new();
+                let mut z3 = FP16::new();
+                let mut t0 = FP16::new_copy(&self.a);
+                let mut t1 = FP16::new();
+	
+                z0.mul(&y.a);
+                t0.add(&self.b); t0.norm();
+
+                z1.copy(&t0); z1.mul(&y.a);
+                t0.copy(&self.b); t0.add(&self.c);
+                t0.norm();
+
+                z3.copy(&t0);
+                z3.pmul(&y.c.getb());
+                z3.times_i();
+
+                t0.copy(&z0); t0.neg();
+                z1.add(&t0);
+                self.b.copy(&z1);
+                z2.copy(&t0);
+
+                t0.copy(&self.a); t0.add(&self.c); t0.norm();
+                t1.copy(&y.a); t1.add(&y.c); t1.norm();
+
+                t0.mul(&t1);
+                z2.add(&t0);
+                t0.copy(&self.c);
+			
+                t0.pmul(&y.c.getb());
+                t0.times_i();
+                t1.copy(&t0); t1.neg();
+
+                self.c.copy(&z2); self.c.add(&t1);
+                z3.add(&t1);
+                t0.times_i();
+                self.b.add(&t0);
+                z3.norm();
+                z3.times_i();
+                self.a.copy(&z0); self.a.add(&z3);
+           }	
+        }
+        self.stype=DENSE;
+        self.norm();
+    }
+
+
+    /* Special case of multiplication arises from special form of ATE pairing line function */
+    pub fn smul(&mut self, y: &FP48) {
+        if ecp::SEXTIC_TWIST==SexticTwist::D_TYPE {	
+            let mut w1=FP8::new_copy(&self.a.geta());
+            let mut w2=FP8::new_copy(&self.a.getb());
+            let mut w3=FP8::new_copy(&self.b.geta());
+
+            w1.mul(&y.a.geta());
+            w2.mul(&y.a.getb());
+            w3.mul(&y.b.geta());
+
+            let mut ta=FP8::new_copy(&self.a.geta());
+            let mut tb=FP8::new_copy(&y.a.geta());
+            ta.add(&self.a.getb()); ta.norm();
+            tb.add(&y.a.getb()); tb.norm();
+            let mut tc=FP8::new_copy(&ta);
+            tc.mul(&tb);
+            let mut t=FP8::new_copy(&w1);
+            t.add(&w2);
+            t.neg();
+            tc.add(&t);
+
+            ta.copy(&self.a.geta()); ta.add(&self.b.geta()); ta.norm();
+            tb.copy(&y.a.geta()); tb.add(&y.b.geta()); tb.norm();
+            let mut td=FP8::new_copy(&ta);
+            td.mul(&tb);
+            t.copy(&w1);
+            t.add(&w3);
+            t.neg();
+            td.add(&t);
+
+            ta.copy(&self.a.getb()); ta.add(&self.b.geta()); ta.norm();
+            tb.copy(&y.a.getb()); tb.add(&y.b.geta()); tb.norm();
+            let mut te=FP8::new_copy(&ta);
+            te.mul(&tb);
+            t.copy(&w2);
+            t.add(&w3);
+            t.neg();
+            te.add(&t);
+
+            w2.times_i();
+            w1.add(&w2);
+
+	    self.a.set_fp8s(&w1,&tc);
+	    self.b.set_fp8s(&td,&te);
+	    self.c.set_fp8(&w3);
+
+            self.a.norm();
+            self.b.norm();
+        } else {
+            let mut w1=FP8::new_copy(&self.a.geta());
+            let mut w2=FP8::new_copy(&self.a.getb());
+            let mut w3=FP8::new_copy(&self.c.getb());
+
+            w1.mul(&y.a.geta());
+            w2.mul(&y.a.getb());
+            w3.mul(&y.c.getb());
+
+            let mut ta=FP8::new_copy(&self.a.geta());
+            let mut tb=FP8::new_copy(&y.a.geta());
+            ta.add(&self.a.getb()); ta.norm();
+            tb.add(&y.a.getb()); tb.norm();
+            let mut tc=FP8::new_copy(&ta);
+            tc.mul(&tb);
+            let mut t=FP8::new_copy(&w1);
+            t.add(&w2);
+            t.neg();
+            tc.add(&t);
+
+            ta.copy(&self.a.geta()); ta.add(&self.c.getb()); ta.norm();
+            tb.copy(&y.a.geta()); tb.add(&y.c.getb()); tb.norm();
+            let mut td=FP8::new_copy(&ta);
+            td.mul(&tb);
+            t.copy(&w1);
+            t.add(&w3);
+            t.neg();
+            td.add(&t);
+
+            ta.copy(&self.a.getb()); ta.add(&self.c.getb()); ta.norm();
+            tb.copy(&y.a.getb()); tb.add(&y.c.getb()); tb.norm();
+            let mut te=FP8::new_copy(&ta);
+            te.mul(&tb);
+            t.copy(&w2);
+            t.add(&w3);
+            t.neg();
+            te.add(&t);
+
+            w2.times_i();
+            w1.add(&w2);
+	    self.a.set_fp8s(&w1,&tc);
+
+            w3.times_i();
+            w3.norm();
+	    self.b.set_fp8h(&w3);
+
+            te.norm();
+            te.times_i();
+	    self.c.set_fp8s(&te,&td);
+
+            self.a.norm();
+            self.c.norm();
+	}
+	self.stype=SPARSE;
+    }
+
+    /* self=1/self */
+    pub fn inverse(&mut self) {
+        let mut f0 = FP16::new_copy(&self.a);
+        let mut f1 = FP16::new_copy(&self.b);
+        let mut f2 = FP16::new_copy(&self.a);
+        let mut f3 = FP16::new();
+
+        //self.norm();
+        f0.sqr();
+        f1.mul(&self.c);
+        f1.times_i();
+        f0.sub(&f1);
+        f0.norm();
+
+        f1.copy(&self.c);
+        f1.sqr();
+        f1.times_i();
+        f2.mul(&self.b);
+        f1.sub(&f2);
+        f1.norm();
+
+        f2.copy(&self.b);
+        f2.sqr();
+        f3.copy(&self.a);
+        f3.mul(&self.c);
+        f2.sub(&f3);
+        f2.norm();
+
+        f3.copy(&self.b);
+        f3.mul(&f2);
+        f3.times_i();
+        self.a.mul(&f0);
+        f3.add(&self.a);
+        self.c.mul(&f1);
+        self.c.times_i();
+
+        f3.add(&self.c);
+        f3.norm();
+        f3.inverse();
+        self.a.copy(&f0);
+        self.a.mul(&f3);
+        self.b.copy(&f1);
+        self.b.mul(&f3);
+        self.c.copy(&f2);
+        self.c.mul(&f3);
+        self.stype=DENSE;
+    }
+
+    /* self=self^p using Frobenius */
+    pub fn frob(&mut self, f: &FP2, n: isize) {
+        let mut f2 = FP2::new_copy(f);
+        let mut f3 = FP2::new_copy(f);
+
+        f2.sqr();
+        f3.mul(&f2);
+
+        f3.mul_ip();
+        f3.norm();
+        f3.mul_ip();
+        f3.norm();
+
+        for _i in 0..n {
+            self.a.frob(&f3);
+            self.b.frob(&f3);
+            self.c.frob(&f3);
+
+            self.b.qmul(f);
+            self.b.times_i4();
+            self.b.times_i2();
+            self.c.qmul(&f2);
+            self.c.times_i4();
+            self.c.times_i4();
+            self.c.times_i4();
+        }
+        self.stype=DENSE;
+    }
+
+    /* trace function */
+    pub fn trace(&mut self) -> FP16 {
+        let mut t = FP16::new();
+        t.copy(&self.a);
+        t.imul(3);
+        t.reduce();
+        return t;
+    }
+
+    /* convert from byte array to FP48 */
+    pub fn frombytes(w: &[u8]) -> FP48 {
+        let mut t: [u8; big::MODBYTES as usize] = [0; big::MODBYTES as usize];
+        let mb = big::MODBYTES as usize;
+
+        for i in 0..mb {
+            t[i] = w[i]
+        }
+        let mut a = BIG::frombytes(&t);
+        for i in 0..mb {
+            t[i] = w[i + mb]
+        }
+        let mut b = BIG::frombytes(&t);
+        let mut c = FP2::new_bigs(&a, &b);
+
+        for i in 0..mb {
+            t[i] = w[i + 2 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 3 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        let mut d = FP2::new_bigs(&a, &b);
+
+        let mut ea = FP4::new_fp2s(&c, &d);
+
+        for i in 0..mb {
+            t[i] = w[i + 4 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 5 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        c.copy(&FP2::new_bigs(&a, &b));
+
+        for i in 0..mb {
+            t[i] = w[i + 6 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 7 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        d.copy(&FP2::new_bigs(&a, &b));
+
+        let mut eb = FP4::new_fp2s(&c, &d);
+
+        let mut ea8 = FP8::new_fp4s(&ea, &eb);
+
+        for i in 0..mb {
+            t[i] = w[i + 8 * mb]
+        }
+        let mut a = BIG::frombytes(&t);
+        for i in 0..mb {
+            t[i] = w[i + 9 * mb]
+        }
+        let mut b = BIG::frombytes(&t);
+        let mut c = FP2::new_bigs(&a, &b);
+
+        for i in 0..mb {
+            t[i] = w[i + 10 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 11 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        let mut d = FP2::new_bigs(&a, &b);
+
+        ea.copy(&FP4::new_fp2s(&c, &d));
+
+        for i in 0..mb {
+            t[i] = w[i + 12 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 13 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        c.copy(&FP2::new_bigs(&a, &b));
+
+        for i in 0..mb {
+            t[i] = w[i + 14 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 15 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        d.copy(&FP2::new_bigs(&a, &b));
+
+        eb.copy(&FP4::new_fp2s(&c, &d));
+
+        let mut eb8 = FP8::new_fp4s(&ea, &eb);
+
+        let e = FP16::new_fp8s(&ea8, &eb8);
+
+        for i in 0..mb {
+            t[i] = w[i + 16 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 17 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        c.copy(&FP2::new_bigs(&a, &b));
+
+        for i in 0..mb {
+            t[i] = w[i + 18 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 19 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        d.copy(&FP2::new_bigs(&a, &b));
+
+        ea.copy(&FP4::new_fp2s(&c, &d));
+
+        for i in 0..mb {
+            t[i] = w[i + 20 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 21 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        c.copy(&FP2::new_bigs(&a, &b));
+
+        for i in 0..mb {
+            t[i] = w[i + 22 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 23 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        d.copy(&FP2::new_bigs(&a, &b));
+
+        eb.copy(&FP4::new_fp2s(&c, &d));
+
+        ea8.copy(&FP8::new_fp4s(&ea, &eb));
+
+        for i in 0..mb {
+            t[i] = w[i + 24 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 25 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        c.copy(&FP2::new_bigs(&a, &b));
+
+        for i in 0..mb {
+            t[i] = w[i + 26 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 27 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        d.copy(&FP2::new_bigs(&a, &b));
+
+        ea.copy(&FP4::new_fp2s(&c, &d));
+
+        for i in 0..mb {
+            t[i] = w[i + 28 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 29 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        c.copy(&FP2::new_bigs(&a, &b));
+
+        for i in 0..mb {
+            t[i] = w[i + 30 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 31 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        d.copy(&FP2::new_bigs(&a, &b));
+
+        eb.copy(&FP4::new_fp2s(&c, &d));
+
+        eb8.copy(&FP8::new_fp4s(&ea, &eb));
+
+        let f = FP16::new_fp8s(&ea8, &eb8);
+
+        for i in 0..mb {
+            t[i] = w[i + 32 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 33 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+
+        c.copy(&FP2::new_bigs(&a, &b));
+
+        for i in 0..mb {
+            t[i] = w[i + 34 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 35 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        d.copy(&FP2::new_bigs(&a, &b));
+
+        ea.copy(&FP4::new_fp2s(&c, &d));
+
+        for i in 0..mb {
+            t[i] = w[i + 36 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 37 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+
+        c.copy(&FP2::new_bigs(&a, &b));
+
+        for i in 0..mb {
+            t[i] = w[i + 38 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 39 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        d.copy(&FP2::new_bigs(&a, &b));
+
+        eb.copy(&FP4::new_fp2s(&c, &d));
+
+        ea8.copy(&FP8::new_fp4s(&ea, &eb));
+
+        for i in 0..mb {
+            t[i] = w[i + 40 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 41 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+
+        c.copy(&FP2::new_bigs(&a, &b));
+
+        for i in 0..mb {
+            t[i] = w[i + 42 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 43 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        d.copy(&FP2::new_bigs(&a, &b));
+
+        ea.copy(&FP4::new_fp2s(&c, &d));
+
+        for i in 0..mb {
+            t[i] = w[i + 44 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 45 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+
+        c.copy(&FP2::new_bigs(&a, &b));
+
+        for i in 0..mb {
+            t[i] = w[i + 46 * mb]
+        }
+        a.copy(&BIG::frombytes(&t));
+        for i in 0..mb {
+            t[i] = w[i + 47 * mb]
+        }
+        b.copy(&BIG::frombytes(&t));
+        d.copy(&FP2::new_bigs(&a, &b));
+
+        eb.copy(&FP4::new_fp2s(&c, &d));
+
+        eb8.copy(&FP8::new_fp4s(&ea, &eb));
+
+        let g = FP16::new_fp8s(&ea8, &eb8);
+
+        return FP48::new_fp16s(&e, &f, &g);
+    }
+
+    /* convert this to byte array */
+    pub fn tobytes(&mut self, w: &mut [u8]) {
+        let mut t: [u8; big::MODBYTES as usize] = [0; big::MODBYTES as usize];
+        let mb = big::MODBYTES as usize;
+
+        self.a.geta().geta().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i] = t[i]
+        }
+        self.a.geta().geta().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + mb] = t[i]
+        }
+        self.a.geta().geta().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 2 * mb] = t[i]
+        }
+        self.a.geta().geta().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 3 * mb] = t[i]
+        }
+
+        self.a.geta().getb().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 4 * mb] = t[i]
+        }
+        self.a.geta().getb().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 5 * mb] = t[i]
+        }
+        self.a.geta().getb().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 6 * mb] = t[i]
+        }
+        self.a.geta().getb().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 7 * mb] = t[i]
+        }
+
+        self.a.getb().geta().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 8 * mb] = t[i]
+        }
+        self.a.getb().geta().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 9 * mb] = t[i]
+        }
+        self.a.getb().geta().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 10 * mb] = t[i]
+        }
+        self.a.getb().geta().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 11 * mb] = t[i]
+        }
+
+        self.a.getb().getb().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 12 * mb] = t[i]
+        }
+        self.a.getb().getb().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 13 * mb] = t[i]
+        }
+        self.a.getb().getb().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 14 * mb] = t[i]
+        }
+        self.a.getb().getb().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 15 * mb] = t[i]
+        }
+
+        self.b.geta().geta().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 16 * mb] = t[i]
+        }
+        self.b.geta().geta().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 17 * mb] = t[i]
+        }
+        self.b.geta().geta().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 18 * mb] = t[i]
+        }
+        self.b.geta().geta().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 19 * mb] = t[i]
+        }
+
+        self.b.geta().getb().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 20 * mb] = t[i]
+        }
+        self.b.geta().getb().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 21 * mb] = t[i]
+        }
+        self.b.geta().getb().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 22 * mb] = t[i]
+        }
+        self.b.geta().getb().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 23 * mb] = t[i]
+        }
+
+        self.b.getb().geta().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 24 * mb] = t[i]
+        }
+        self.b.getb().geta().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 25 * mb] = t[i]
+        }
+        self.b.getb().geta().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 26 * mb] = t[i]
+        }
+        self.b.getb().geta().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 27 * mb] = t[i]
+        }
+
+        self.b.getb().getb().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 28 * mb] = t[i]
+        }
+        self.b.getb().getb().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 29 * mb] = t[i]
+        }
+        self.b.getb().getb().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 30 * mb] = t[i]
+        }
+        self.b.getb().getb().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 31 * mb] = t[i]
+        }
+
+        self.c.geta().geta().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 32 * mb] = t[i]
+        }
+        self.c.geta().geta().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 33 * mb] = t[i]
+        }
+        self.c.geta().geta().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 34 * mb] = t[i]
+        }
+        self.c.geta().geta().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 35 * mb] = t[i]
+        }
+
+        self.c.geta().getb().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 36 * mb] = t[i]
+        }
+        self.c.geta().getb().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 37 * mb] = t[i]
+        }
+        self.c.geta().getb().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 38 * mb] = t[i]
+        }
+        self.c.geta().getb().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 39 * mb] = t[i]
+        }
+
+        self.c.getb().geta().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 40 * mb] = t[i]
+        }
+        self.c.getb().geta().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 41 * mb] = t[i]
+        }
+        self.c.getb().geta().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 42 * mb] = t[i]
+        }
+        self.c.getb().geta().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 43 * mb] = t[i]
+        }
+
+        self.c.getb().getb().geta().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 44 * mb] = t[i]
+        }
+        self.c.getb().getb().geta().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 45 * mb] = t[i]
+        }
+        self.c.getb().getb().getb().geta().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 46 * mb] = t[i]
+        }
+        self.c.getb().getb().getb().getb().tobytes(&mut t);
+        for i in 0..mb {
+            w[i + 47 * mb] = t[i]
+        }
+    }
+
+    /* output to hex string */
+    pub fn tostring(&mut self) -> String {
+        return format!(
+            "[{},{},{}]",
+            self.a.tostring(),
+            self.b.tostring(),
+            self.c.tostring()
+        );
+    }
+
+    /* self=self^e */
+    pub fn pow(&self, e: &BIG) -> FP48 {
+        let mut r = FP48::new_copy(self);
+        r.norm();
+        let mut e1 = BIG::new_copy(e);
+        e1.norm();
+        let mut e3 = BIG::new_copy(&e1);
+        e3.pmul(3);
+        e3.norm();
+        let mut w = FP48::new_copy(&r);
+
+        let nb = e3.nbits();
+        for i in (1..nb - 1).rev() {
+            w.usqr();
+            let bt = e3.bit(i) - e1.bit(i);
+            if bt == 1 {
+                w.mul(&r);
+            }
+            if bt == -1 {
+                r.conj();
+                w.mul(&r);
+                r.conj();
+            }
+        }
+
+        w.reduce();
+        return w;
+    }
+
+    /* constant time powering by small integer of max length bts */
+    pub fn pinpow(&mut self, e: i32, bts: i32) {
+        let mut r: [FP48; 2] = [FP48::new_int(1), FP48::new_copy(self)];
+        let mut t = FP48::new();
+
+        for i in (0..bts).rev() {
+            let b: usize = ((e >> i) & 1) as usize;
+            t.copy(&r[b]);
+            r[1 - b].mul(&t);
+            r[b].usqr();
+        }
+        self.copy(&r[0]);
+    }
+
+    pub fn compow(&mut self, e: &BIG, r: &BIG) -> FP16 {
+        let f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
+        let q = BIG::new_ints(&rom::MODULUS);
+
+        let mut g1 = FP48::new_copy(self);
+        let mut g2 = FP48::new_copy(self);
+
+        let mut m = BIG::new_copy(&q);
+        m.rmod(&r);
+
+        let mut a = BIG::new_copy(&e);
+        a.rmod(&mut m);
+
+        let mut b = BIG::new_copy(&e);
+        b.div(&mut m);
+
+        let mut c = g1.trace();
+
+        if b.iszilch() {
+            c = c.xtr_pow(&mut a);
+            return c;
+        }
+
+        g2.frob(&f, 1);
+        let cp = g2.trace();
+        g1.conj();
+        g2.mul(&g1);
+        let cpm1 = g2.trace();
+        g2.mul(&g1);
+        let cpm2 = g2.trace();
+
+        c = c.xtr_pow2(&cp, &cpm1, &cpm2, &mut a, &mut b);
+
+        return c;
+    }
+
+    /* p=q0^u0.q1^u1.q2^u2.q3^u3... */
+    // Bos & Costello https://eprint.iacr.org/2013/458.pdf
+    // Faz-Hernandez & Longa & Sanchez  https://eprint.iacr.org/2013/158.pdf
+    // Side channel attack secure
+    pub fn pow16(q: &[FP48], u: &[BIG]) -> FP48 {
+        let mut g1: [FP48; 8] = [
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+        ];
+        let mut g2: [FP48; 8] = [
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+        ];
+        let mut g3: [FP48; 8] = [
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+        ];
+        let mut g4: [FP48; 8] = [
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+        ];
+
+        let mut r = FP48::new();
+        let mut p = FP48::new();
+        const CT: usize = 1 + big::NLEN * (big::BASEBITS as usize);
+        let mut w1: [i8; CT] = [0; CT];
+        let mut s1: [i8; CT] = [0; CT];
+        let mut w2: [i8; CT] = [0; CT];
+        let mut s2: [i8; CT] = [0; CT];
+        let mut w3: [i8; CT] = [0; CT];
+        let mut s3: [i8; CT] = [0; CT];
+        let mut w4: [i8; CT] = [0; CT];
+        let mut s4: [i8; CT] = [0; CT];
+
+        let mut mt = BIG::new();
+        let mut t: [BIG; 16] = [
+            BIG::new_copy(&u[0]),
+            BIG::new_copy(&u[1]),
+            BIG::new_copy(&u[2]),
+            BIG::new_copy(&u[3]),
+            BIG::new_copy(&u[4]),
+            BIG::new_copy(&u[5]),
+            BIG::new_copy(&u[6]),
+            BIG::new_copy(&u[7]),
+            BIG::new_copy(&u[8]),
+            BIG::new_copy(&u[9]),
+            BIG::new_copy(&u[10]),
+            BIG::new_copy(&u[11]),
+            BIG::new_copy(&u[12]),
+            BIG::new_copy(&u[13]),
+            BIG::new_copy(&u[14]),
+            BIG::new_copy(&u[15]),
+        ];
+
+        for i in 0..16 {
+            t[i].norm();
+        }
+
+        // precomputation
+        g1[0].copy(&q[0]);
+        r.copy(&g1[0]);
+        g1[1].copy(&r);
+        g1[1].mul(&q[1]); // q[0].q[1]
+        g1[2].copy(&r);
+        g1[2].mul(&q[2]);
+        r.copy(&g1[1]); // q[0].q[2]
+        g1[3].copy(&r);
+        g1[3].mul(&q[2]);
+        r.copy(&g1[0]); // q[0].q[1].q[2]
+        g1[4].copy(&r);
+        g1[4].mul(&q[3]);
+        r.copy(&g1[1]); // q[0].q[3]
+        g1[5].copy(&r);
+        g1[5].mul(&q[3]);
+        r.copy(&g1[2]); // q[0].q[1].q[3]
+        g1[6].copy(&r);
+        g1[6].mul(&q[3]);
+        r.copy(&g1[3]); // q[0].q[2].q[3]
+        g1[7].copy(&r);
+        g1[7].mul(&q[3]); // q[0].q[1].q[2].q[3]
+
+        // Use Frobenius
+        let f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
+        for i in 0..8 {
+            g2[i].copy(&g1[i]);
+            g2[i].frob(&f, 4);
+            g3[i].copy(&g2[i]);
+            g3[i].frob(&f, 4);
+            g4[i].copy(&g3[i]);
+            g4[i].frob(&f, 4);
+        }
+
+        // Make it odd
+        let pb1 = 1 - t[0].parity();
+        t[0].inc(pb1);
+        t[0].norm();
+
+        let pb2 = 1 - t[4].parity();
+        t[4].inc(pb2);
+        t[4].norm();
+
+        let pb3 = 1 - t[8].parity();
+        t[8].inc(pb3);
+        t[8].norm();
+
+        let pb4 = 1 - t[12].parity();
+        t[12].inc(pb4);
+        t[12].norm();
+
+        // Number of bits
+        mt.zero();
+        for i in 0..16 {
+            mt.or(&t[i]);
+        }
+
+        let nb = 1 + mt.nbits();
+
+        // Sign pivot
+
+        s1[nb - 1] = 1;
+        s2[nb - 1] = 1;
+        s3[nb - 1] = 1;
+        s4[nb - 1] = 1;
+        for i in 0..nb - 1 {
+            t[0].fshr(1);
+            s1[i] = (2 * t[0].parity() - 1) as i8;
+            t[4].fshr(1);
+            s2[i] = (2 * t[4].parity() - 1) as i8;
+            t[8].fshr(1);
+            s3[i] = (2 * t[8].parity() - 1) as i8;
+            t[12].fshr(1);
+            s4[i] = (2 * t[12].parity() - 1) as i8;
+        }
+
+        // Recoded exponent
+        for i in 0..nb {
+            w1[i] = 0;
+            let mut k = 1;
+            for j in 1..4 {
+                let bt = s1[i] * (t[j].parity() as i8);
+                t[j].fshr(1);
+                t[j].dec((bt >> 1) as isize);
+                t[j].norm();
+                w1[i] += bt * (k as i8);
+                k = 2 * k;
+            }
+
+            w2[i] = 0;
+            k = 1;
+            for j in 5..8 {
+                let bt = s2[i] * (t[j].parity() as i8);
+                t[j].fshr(1);
+                t[j].dec((bt >> 1) as isize);
+                t[j].norm();
+                w2[i] += bt * (k as i8);
+                k = 2 * k;
+            }
+
+            w3[i] = 0;
+            k = 1;
+            for j in 9..12 {
+                let bt = s3[i] * (t[j].parity() as i8);
+                t[j].fshr(1);
+                t[j].dec((bt >> 1) as isize);
+                t[j].norm();
+                w3[i] += bt * (k as i8);
+                k = 2 * k;
+            }
+
+            w4[i] = 0;
+            k = 1;
+            for j in 13..16 {
+                let bt = s4[i] * (t[j].parity() as i8);
+                t[j].fshr(1);
+                t[j].dec((bt >> 1) as isize);
+                t[j].norm();
+                w4[i] += bt * (k as i8);
+                k = 2 * k;
+            }
+        }
+
+        // Main loop
+        p.selector(&g1, (2 * w1[nb - 1] + 1) as i32);
+        r.selector(&g2, (2 * w2[nb - 1] + 1) as i32);
+        p.mul(&r);
+        r.selector(&g3, (2 * w3[nb - 1] + 1) as i32);
+        p.mul(&r);
+        r.selector(&g4, (2 * w4[nb - 1] + 1) as i32);
+        p.mul(&r);
+        for i in (0..nb - 1).rev() {
+            p.usqr();
+            r.selector(&g1, (2 * w1[i] + s1[i]) as i32);
+            p.mul(&r);
+            r.selector(&g2, (2 * w2[i] + s2[i]) as i32);
+            p.mul(&r);
+            r.selector(&g3, (2 * w3[i] + s3[i]) as i32);
+            p.mul(&r);
+            r.selector(&g4, (2 * w4[i] + s4[i]) as i32);
+            p.mul(&r);
+        }
+
+        // apply correction
+        r.copy(&q[0]);
+        r.conj();
+        r.mul(&p);
+        p.cmove(&r, pb1);
+
+        r.copy(&q[4]);
+        r.conj();
+        r.mul(&p);
+        p.cmove(&r, pb2);
+
+        r.copy(&q[8]);
+        r.conj();
+        r.mul(&p);
+        p.cmove(&r, pb3);
+
+        r.copy(&q[12]);
+        r.conj();
+        r.mul(&p);
+        p.cmove(&r, pb4);
+
+        p.reduce();
+        return p;
+    }
+}
diff --git a/src/fp8.rs b/src/fp8.rs
new file mode 100644
index 0000000..dfc84d1
--- /dev/null
+++ b/src/fp8.rs
@@ -0,0 +1,701 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use super::fp::FP;
+use super::fp2::FP2;
+use super::fp4::FP4;
+use super::big::BIG;
+//use std::str::SplitWhitespace;
+
+#[derive(Copy, Clone)]
+pub struct FP8 {
+    a: FP4,
+    b: FP4,
+}
+
+impl FP8 {
+    pub fn new() -> FP8 {
+        FP8 {
+            a: FP4::new(),
+            b: FP4::new(),
+        }
+    }
+
+    pub fn new_int(a: isize) -> FP8 {
+        let mut f = FP8::new();
+        f.a.copy(&FP4::new_int(a));
+        f.b.zero();
+        return f;
+    }
+
+    pub fn new_copy(x: &FP8) -> FP8 {
+        let mut f = FP8::new();
+        f.a.copy(&x.a);
+        f.b.copy(&x.b);
+        return f;
+    }
+
+    pub fn new_fp4s(c: &FP4, d: &FP4) -> FP8 {
+        let mut f = FP8::new();
+        f.a.copy(c);
+        f.b.copy(d);
+        return f;
+    }
+
+    pub fn new_fp4(c: &FP4) -> FP8 {
+        let mut f = FP8::new();
+        f.a.copy(c);
+        f.b.zero();
+        return f;
+    }
+
+    pub fn set_fp4s(&mut self,c: &FP4, d: &FP4) {
+        self.a.copy(&c);
+	self.b.copy(&d);
+    }
+
+    pub fn set_fp4(&mut self,c: &FP4) {
+        self.a.copy(&c);
+	self.b.zero();
+    }
+
+    pub fn set_fp4h(&mut self,c: &FP4) {
+        self.b.copy(&c);
+	self.a.zero();
+    }
+
+
+    /* reduce components mod Modulus */
+    pub fn reduce(&mut self) {
+        self.a.reduce();
+        self.b.reduce();
+    }
+
+    /* normalise components of w */
+    pub fn norm(&mut self) {
+        self.a.norm();
+        self.b.norm();
+    }
+
+    pub fn cmove(&mut self, g: &FP8, d: isize) {
+        self.a.cmove(&g.a, d);
+        self.b.cmove(&g.b, d);
+    }
+
+    /* test self=0 ? */
+    pub fn iszilch(&self) -> bool {
+        return self.a.iszilch() && self.b.iszilch();
+    }
+
+    /* test self=1 ? */
+    pub fn isunity(&self) -> bool {
+        let one = FP4::new_int(1);
+        return self.a.equals(&one) && self.b.iszilch();
+    }
+
+    /* test is w real? That is in a+ib test b is zero */
+    pub fn isreal(&mut self) -> bool {
+        return self.b.iszilch();
+    }
+    /* extract real part a */
+    pub fn real(&self) -> FP4 {
+        let f = FP4::new_copy(&self.a);
+        return f;
+    }
+
+    pub fn geta(&self) -> FP4 {
+        return self.a;
+//        let f = FP4::new_copy(&self.a);
+//        return f;
+    }
+    /* extract imaginary part b */
+    pub fn getb(&self) -> FP4 {
+        return self.b;
+//        let f = FP4::new_copy(&self.b);
+//        return f;
+    }
+
+    /* test self=x */
+    pub fn equals(&self, x: &FP8) -> bool {
+        return self.a.equals(&x.a) && self.b.equals(&x.b);
+    }
+    /* copy self=x */
+    pub fn copy(&mut self, x: &FP8) {
+        self.a.copy(&x.a);
+        self.b.copy(&x.b);
+    }
+
+    /* set self=0 */
+    pub fn zero(&mut self) {
+        self.a.zero();
+        self.b.zero();
+    }
+
+    /* set self=1 */
+    pub fn one(&mut self) {
+        self.a.one();
+        self.b.zero();
+    }
+
+    /* negate self mod Modulus */
+    pub fn neg(&mut self) {
+        self.norm();
+        let mut m = FP4::new_copy(&self.a);
+        let mut t = FP4::new();
+
+        m.add(&self.b);
+        m.neg();
+
+        t.copy(&m);
+        t.add(&self.b);
+        self.b.copy(&m);
+        self.b.add(&self.a);
+        self.a.copy(&t);
+        self.norm();
+    }
+
+    /* set to a-ib */
+    pub fn conj(&mut self) {
+        self.b.neg();
+        self.norm();
+    }
+
+    /* self=-conjugate(self) */
+    pub fn nconj(&mut self) {
+        self.a.neg();
+        self.norm();
+    }
+
+    /* self+=a */
+    pub fn add(&mut self, x: &FP8) {
+        self.a.add(&x.a);
+        self.b.add(&x.b);
+    }
+
+    pub fn padd(&mut self, x: &FP4) {
+        self.a.add(x);
+    }
+
+    pub fn dbl(&mut self) {
+        self.a.dbl();
+        self.b.dbl();
+    }
+
+    /* self-=a */
+    pub fn sub(&mut self, x: &FP8) {
+        let mut m = FP8::new_copy(x);
+        m.neg();
+        self.add(&m);
+    }
+
+    /* this-=x */
+    pub fn rsub(&mut self, x: &FP8) {
+        self.neg();
+        self.add(x);
+    }
+
+    /* self*=s, where s is an FP4 */
+    pub fn pmul(&mut self, s: &FP4) {
+        self.a.mul(s);
+        self.b.mul(s);
+    }
+
+    /* self*=s, where s is an FP2 */
+    pub fn qmul(&mut self, s: &FP2) {
+        self.a.pmul(s);
+        self.b.pmul(s);
+    }
+
+    /* self*=s, where s is an FP */
+    pub fn tmul(&mut self, s: &FP) {
+        self.a.qmul(s);
+        self.b.qmul(s);
+    }
+
+    /* self*=i, where i is an int */
+    pub fn imul(&mut self, c: isize) {
+        self.a.imul(c);
+        self.b.imul(c);
+    }
+
+    /* self*=self */
+
+    pub fn sqr(&mut self) {
+        let mut t1 = FP4::new_copy(&self.a);
+        let mut t2 = FP4::new_copy(&self.b);
+        let mut t3 = FP4::new_copy(&self.a);
+
+        t3.mul(&self.b);
+        t1.add(&self.b);
+        t2.times_i();
+
+        t2.add(&self.a);
+
+        t1.norm();
+        t2.norm();
+
+        self.a.copy(&t1);
+
+        self.a.mul(&t2);
+
+        t2.copy(&t3);
+        t2.times_i();
+        t2.add(&t3);
+        t2.norm();
+        t2.neg();
+        self.a.add(&t2);
+
+        t3.dbl();
+        self.b.copy(&t3);
+
+        self.norm();
+    }
+
+    /* self*=y */
+    pub fn mul(&mut self, y: &FP8) {
+        //self.norm();
+
+        let mut t1 = FP4::new_copy(&self.a);
+        let mut t2 = FP4::new_copy(&self.b);
+        let mut t3 = FP4::new();
+        let mut t4 = FP4::new_copy(&self.b);
+
+        t1.mul(&y.a);
+        t2.mul(&y.b);
+        t3.copy(&y.b);
+        t3.add(&y.a);
+        t4.add(&self.a);
+
+        t3.norm();
+        t4.norm();
+
+        t4.mul(&t3);
+
+        t3.copy(&t1);
+        t3.neg();
+        t4.add(&t3);
+        t4.norm();
+
+        t3.copy(&t2);
+        t3.neg();
+        self.b.copy(&t4);
+        self.b.add(&t3);
+
+        t2.times_i();
+        self.a.copy(&t2);
+        self.a.add(&t1);
+
+        self.norm();
+    }
+
+    /* output to hex string */
+    pub fn tostring(&mut self) -> String {
+        return format!("[{},{}]", self.a.tostring(), self.b.tostring());
+    }
+
+    /* self=1/self */
+    pub fn inverse(&mut self) {
+        //self.norm();
+
+        let mut t1 = FP4::new_copy(&self.a);
+        let mut t2 = FP4::new_copy(&self.b);
+
+        t1.sqr();
+        t2.sqr();
+        t2.times_i();
+        t2.norm();
+        t1.sub(&t2);
+        t1.norm();
+        t1.inverse();
+        self.a.mul(&t1);
+        t1.neg();
+        t1.norm();
+        self.b.mul(&t1);
+    }
+
+    /* self*=i where i = sqrt(-1+sqrt(-1)) */
+    pub fn times_i(&mut self) {
+        let mut s = FP4::new_copy(&self.b);
+        let t = FP4::new_copy(&self.a);
+        s.times_i();
+        self.a.copy(&s);
+        self.b.copy(&t);
+
+        self.norm();
+    }
+
+    pub fn times_i2(&mut self) {
+        self.a.times_i();
+        self.b.times_i();
+    }
+
+    /* self=self^p using Frobenius */
+    pub fn frob(&mut self, f: &FP2) {
+        let mut ff = FP2::new_copy(f);
+        ff.sqr();
+        ff.mul_ip();
+        ff.norm();
+        self.a.frob(&ff);
+        self.b.frob(&ff);
+        self.b.pmul(f);
+        self.b.times_i();
+    }
+
+    /* self=self^e */
+    pub fn pow(&self, e: &BIG) -> FP8 {
+        let mut w = FP8::new_copy(self);
+        w.norm();
+        let mut z = BIG::new_copy(&e);
+        let mut r = FP8::new_int(1);
+        z.norm();
+        loop {
+            let bt = z.parity();
+            z.fshr(1);
+            if bt == 1 {
+                r.mul(&mut w)
+            };
+            if z.iszilch() {
+                break;
+            }
+            w.sqr();
+        }
+        r.reduce();
+        return r;
+    }
+
+    /* XTR xtr_a function */
+    pub fn xtr_a(&mut self, w: &FP8, y: &FP8, z: &FP8) {
+        let mut r = FP8::new_copy(w);
+        let mut t = FP8::new_copy(w);
+
+        r.sub(y);
+        r.norm();
+        r.pmul(&self.a);
+        t.add(y);
+        t.norm();
+        t.pmul(&self.b);
+        t.times_i();
+
+        self.copy(&r);
+        self.add(&t);
+        self.add(z);
+
+        self.norm();
+    }
+
+    /* XTR xtr_d function */
+    pub fn xtr_d(&mut self) {
+        let mut w = FP8::new_copy(self);
+        self.sqr();
+        w.conj();
+        w.dbl();
+        w.norm();
+        self.sub(&w);
+        self.reduce();
+    }
+
+    /* r=x^n using XTR method on traces of FP24s */
+    pub fn xtr_pow(&self, n: &BIG) -> FP8 {
+        let mut sf = FP8::new_copy(self);
+        sf.norm();
+        let mut a = FP8::new_int(3);
+        let mut b = FP8::new_copy(&sf);
+        let mut c = FP8::new_copy(&b);
+        c.xtr_d();
+        let mut t = FP8::new();
+        let mut r = FP8::new();
+
+        let par = n.parity();
+        let mut v = BIG::new_copy(n);
+        v.norm();
+        v.fshr(1);
+        if par == 0 {
+            v.dec(1);
+            v.norm();
+        }
+
+        let nb = v.nbits();
+        for i in (0..nb).rev() {
+            if v.bit(i) != 1 {
+                t.copy(&b);
+                sf.conj();
+                c.conj();
+                b.xtr_a(&a, &sf, &c);
+                sf.conj();
+                c.copy(&t);
+                c.xtr_d();
+                a.xtr_d();
+            } else {
+                t.copy(&a);
+                t.conj();
+                a.copy(&b);
+                a.xtr_d();
+                b.xtr_a(&c, &sf, &t);
+                c.xtr_d();
+            }
+        }
+        if par == 0 {
+            r.copy(&c)
+        } else {
+            r.copy(&b)
+        }
+        r.reduce();
+        return r;
+    }
+
+    /* r=ck^a.cl^n using XTR double exponentiation method on traces of FP12s. See Stam thesis. */
+    pub fn xtr_pow2(&mut self, ck: &FP8, ckml: &FP8, ckm2l: &FP8, a: &BIG, b: &BIG) -> FP8 {
+        let mut e = BIG::new_copy(a);
+        let mut d = BIG::new_copy(b);
+        let mut w = BIG::new();
+        e.norm();
+        d.norm();
+
+        let mut cu = FP8::new_copy(ck); // can probably be passed in w/o copying
+        let mut cv = FP8::new_copy(self);
+        let mut cumv = FP8::new_copy(ckml);
+        let mut cum2v = FP8::new_copy(ckm2l);
+        let mut r = FP8::new();
+        let mut t = FP8::new();
+
+        let mut f2: usize = 0;
+        while d.parity() == 0 && e.parity() == 0 {
+            d.fshr(1);
+            e.fshr(1);
+            f2 += 1;
+        }
+
+        while BIG::comp(&d, &e) != 0 {
+            if BIG::comp(&d, &e) > 0 {
+                w.copy(&e);
+                w.imul(4);
+                w.norm();
+                if BIG::comp(&d, &w) <= 0 {
+                    w.copy(&d);
+                    d.copy(&e);
+                    e.rsub(&w);
+                    e.norm();
+
+                    t.copy(&cv);
+                    t.xtr_a(&cu, &cumv, &cum2v);
+                    cum2v.copy(&cumv);
+                    cum2v.conj();
+                    cumv.copy(&cv);
+                    cv.copy(&cu);
+                    cu.copy(&t);
+                } else {
+                    if d.parity() == 0 {
+                        d.fshr(1);
+                        r.copy(&cum2v);
+                        r.conj();
+                        t.copy(&cumv);
+                        t.xtr_a(&cu, &cv, &r);
+                        cum2v.copy(&cumv);
+                        cum2v.xtr_d();
+                        cumv.copy(&t);
+                        cu.xtr_d();
+                    } else {
+                        if e.parity() == 1 {
+                            d.sub(&e);
+                            d.norm();
+                            d.fshr(1);
+                            t.copy(&cv);
+                            t.xtr_a(&cu, &cumv, &cum2v);
+                            cu.xtr_d();
+                            cum2v.copy(&cv);
+                            cum2v.xtr_d();
+                            cum2v.conj();
+                            cv.copy(&t);
+                        } else {
+                            w.copy(&d);
+                            d.copy(&e);
+                            d.fshr(1);
+                            e.copy(&w);
+                            t.copy(&cumv);
+                            t.xtr_d();
+                            cumv.copy(&cum2v);
+                            cumv.conj();
+                            cum2v.copy(&t);
+                            cum2v.conj();
+                            t.copy(&cv);
+                            t.xtr_d();
+                            cv.copy(&cu);
+                            cu.copy(&t);
+                        }
+                    }
+                }
+            }
+            if BIG::comp(&d, &e) < 0 {
+                w.copy(&d);
+                w.imul(4);
+                w.norm();
+                if BIG::comp(&e, &w) <= 0 {
+                    e.sub(&d);
+                    e.norm();
+                    t.copy(&cv);
+                    t.xtr_a(&cu, &cumv, &cum2v);
+                    cum2v.copy(&cumv);
+                    cumv.copy(&cu);
+                    cu.copy(&t);
+                } else {
+                    if e.parity() == 0 {
+                        w.copy(&d);
+                        d.copy(&e);
+                        d.fshr(1);
+                        e.copy(&w);
+                        t.copy(&cumv);
+                        t.xtr_d();
+                        cumv.copy(&cum2v);
+                        cumv.conj();
+                        cum2v.copy(&t);
+                        cum2v.conj();
+                        t.copy(&cv);
+                        t.xtr_d();
+                        cv.copy(&cu);
+                        cu.copy(&t);
+                    } else {
+                        if d.parity() == 1 {
+                            w.copy(&e);
+                            e.copy(&d);
+                            w.sub(&d);
+                            w.norm();
+                            d.copy(&w);
+                            d.fshr(1);
+                            t.copy(&cv);
+                            t.xtr_a(&cu, &cumv, &cum2v);
+                            cumv.conj();
+                            cum2v.copy(&cu);
+                            cum2v.xtr_d();
+                            cum2v.conj();
+                            cu.copy(&cv);
+                            cu.xtr_d();
+                            cv.copy(&t);
+                        } else {
+                            d.fshr(1);
+                            r.copy(&cum2v);
+                            r.conj();
+                            t.copy(&cumv);
+                            t.xtr_a(&cu, &cv, &r);
+                            cum2v.copy(&cumv);
+                            cum2v.xtr_d();
+                            cumv.copy(&t);
+                            cu.xtr_d();
+                        }
+                    }
+                }
+            }
+        }
+        r.copy(&cv);
+        r.xtr_a(&cu, &cumv, &cum2v);
+        for _ in 0..f2 {
+            r.xtr_d()
+        }
+        r = r.xtr_pow(&mut d);
+        return r;
+    }
+
+    /* this/=2 */
+    pub fn div2(&mut self) {
+        self.a.div2();
+        self.b.div2();
+    }
+
+    pub fn div_i(&mut self) {
+        let mut u = FP4::new_copy(&self.a);
+        let v = FP4::new_copy(&self.b);
+        u.div_i();
+        self.a.copy(&v);
+        self.b.copy(&u);
+    }
+
+    pub fn div_i2(&mut self) {
+        self.a.div_i();
+        self.b.div_i();
+    }
+
+    pub fn div_2i(&mut self) {
+        let mut u = FP4::new_copy(&self.a);
+        let mut v = FP4::new_copy(&self.b);
+        u.div_2i();
+        v.dbl();
+        v.norm();
+        self.a.copy(&v);
+        self.b.copy(&u);
+    }
+
+    /* sqrt(a+ib) = sqrt(a+sqrt(a*a-n*b*b)/2)+ib/(2*sqrt(a+sqrt(a*a-n*b*b)/2)) */
+    /* returns true if this is QR */
+    pub fn sqrt(&mut self) -> bool {
+        if self.iszilch() {
+            return true;
+        }
+
+        let mut a = FP4::new_copy(&self.a);
+        let mut s = FP4::new_copy(&self.b);
+        let mut t = FP4::new_copy(&self.a);
+
+        if s.iszilch() {
+            if t.sqrt() {
+                self.a.copy(&t);
+                self.b.zero();
+            } else {
+                t.div_i();
+                t.sqrt();
+                self.b.copy(&t);
+                self.a.zero();
+            }
+            return true;
+        }
+        s.sqr();
+        a.sqr();
+        s.times_i();
+        s.norm();
+        a.sub(&s);
+
+        s.copy(&a);
+        if !s.sqrt() {
+            return false;
+        }
+
+        a.copy(&t);
+        a.add(&s);
+        a.norm();
+        a.div2();
+
+        if !a.sqrt() {
+            a.copy(&t);
+            a.sub(&s);
+            a.norm();
+            a.div2();
+            if !a.sqrt() {
+                return false;
+            }
+        }
+        t.copy(&self.b);
+        s.copy(&a);
+        s.add(&a);
+        s.inverse();
+
+        t.mul(&s);
+        self.a.copy(&a);
+        self.b.copy(&t);
+
+        return true;
+    }
+}
diff --git a/src/gcm.rs b/src/gcm.rs
new file mode 100644
index 0000000..b2c293f
--- /dev/null
+++ b/src/gcm.rs
@@ -0,0 +1,481 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+const GCM_NB: usize = 4;
+const GCM_ACCEPTING_HEADER: usize = 0;
+const GCM_ACCEPTING_CIPHER: usize = 1;
+const GCM_NOT_ACCEPTING_MORE: usize = 2;
+const GCM_FINISHED: usize = 3;
+const GCM_ENCRYPTING: usize = 0;
+const GCM_DECRYPTING: usize = 1;
+
+use crate::aes;
+use crate::aes::AES;
+
+pub struct GCM {
+    table: [[u32; 4]; 128],
+    statex: [u8; 16],
+    y_0: [u8; 16],
+    //  counter: usize,
+    lena: [u32; 2],
+    lenc: [u32; 2],
+    status: usize,
+    a: AES,
+}
+
+impl GCM {
+    fn pack(b: [u8; 4]) -> u32 {
+        /* pack bytes into a 32-bit Word */
+        return ((((b[0]) & 0xff) as u32) << 24)
+            | ((((b[1]) & 0xff) as u32) << 16)
+            | ((((b[2]) & 0xff) as u32) << 8)
+            | (((b[3]) & 0xff) as u32);
+    }
+
+    fn unpack(a: u32) -> [u8; 4] {
+        /* unpack bytes from a word */
+        let b: [u8; 4] = [
+            ((a >> 24) & 0xff) as u8,
+            ((a >> 16) & 0xff) as u8,
+            ((a >> 8) & 0xff) as u8,
+            (a & 0xff) as u8,
+        ];
+        return b;
+    }
+
+    fn precompute(&mut self, h: &[u8]) {
+        let mut b: [u8; 4] = [0; 4];
+        let mut j = 0;
+        for i in 0..GCM_NB {
+            b[0] = h[j];
+            b[1] = h[j + 1];
+            b[2] = h[j + 2];
+            b[3] = h[j + 3];
+            self.table[0][i] = GCM::pack(b);
+            j += 4;
+        }
+        for i in 1..128 {
+            let mut c: u32 = 0;
+            for j in 0..GCM_NB {
+                self.table[i][j] = c | (self.table[i - 1][j]) >> 1;
+                c = self.table[i - 1][j] << 31;
+            }
+            if c != 0 {
+                self.table[i][0] ^= 0xE1000000
+            } /* irreducible polynomial */
+        }
+    }
+
+    fn gf2mul(&mut self) {
+        /* gf2m mul - Z=H*X mod 2^128 */
+        let mut p: [u32; 4] = [0; 4];
+
+        for i in 0..4 {
+            p[i] = 0
+        }
+        let mut j: usize = 8;
+        let mut m = 0;
+        for i in 0..128 {
+            j -= 1;
+            let mut c = ((self.statex[m] >> j) & 1) as u32;
+            c = (!c) + 1;
+            for k in 0..GCM_NB {
+                p[k] ^= self.table[i][k] & c
+            }
+            if j == 0 {
+                j = 8;
+                m += 1;
+                if m == 16 {
+                    break;
+                }
+            }
+        }
+        j = 0;
+        for i in 0..GCM_NB {
+            let b = GCM::unpack(p[i]);
+            self.statex[j] = b[0];
+            self.statex[j + 1] = b[1];
+            self.statex[j + 2] = b[2];
+            self.statex[j + 3] = b[3];
+            j += 4;
+        }
+    }
+
+    fn wrap(&mut self) {
+        /* Finish off GHASH */
+        let mut f: [u32; 4] = [0; 4];
+        let mut el: [u8; 16] = [0; 16];
+
+        /* convert lengths from bytes to bits */
+        f[0] = (self.lena[0] << 3) | (self.lena[1] & 0xE0000000) >> 29;
+        f[1] = self.lena[1] << 3;
+        f[2] = (self.lenc[0] << 3) | (self.lenc[1] & 0xE0000000) >> 29;
+        f[3] = self.lenc[1] << 3;
+        let mut j = 0;
+        for i in 0..GCM_NB {
+            let b = GCM::unpack(f[i]);
+            el[j] = b[0];
+            el[j + 1] = b[1];
+            el[j + 2] = b[2];
+            el[j + 3] = b[3];
+            j += 4;
+        }
+        for i in 0..16 {
+            self.statex[i] ^= el[i]
+        }
+        self.gf2mul();
+    }
+
+    fn ghash(&mut self, plain: &[u8], len: usize) -> bool {
+        if self.status == GCM_ACCEPTING_HEADER {
+            self.status = GCM_ACCEPTING_CIPHER
+        }
+        if self.status != GCM_ACCEPTING_CIPHER {
+            return false;
+        }
+
+        let mut j = 0;
+        while j < len {
+            for i in 0..16 {
+                if j >= len {
+                    break;
+                }
+                self.statex[i] ^= plain[j];
+                j += 1;
+                self.lenc[1] += 1;
+                if self.lenc[1] == 0 {
+                    self.lenc[0] += 1
+                }
+            }
+            self.gf2mul();
+        }
+        if len % 16 != 0 {
+            self.status = GCM_NOT_ACCEPTING_MORE
+        }
+        return true;
+    }
+
+    /* Initialize GCM mode */
+    pub fn init(&mut self, nk: usize, key: &[u8], niv: usize, iv: &[u8]) {
+        /* iv size niv is usually 12 bytes (96 bits). AES key size nk can be 16,24 or 32 bytes */
+        let mut h: [u8; 16] = [0; 16];
+
+        for i in 0..16 {
+            h[i] = 0;
+            self.statex[i] = 0
+        }
+
+        self.a = AES::new();
+
+        self.a.init(aes::ECB, nk, key, None);
+        self.a.ecb_encrypt(&mut h); /* E(K,0) */
+        self.precompute(&h);
+
+        self.lena[0] = 0;
+        self.lenc[0] = 0;
+        self.lena[1] = 0;
+        self.lenc[1] = 0;
+        if niv == 12 {
+            for i in 0..12 {
+                self.a.f[i] = iv[i]
+            }
+            let b = GCM::unpack(1);
+            self.a.f[12] = b[0];
+            self.a.f[13] = b[1];
+            self.a.f[14] = b[2];
+            self.a.f[15] = b[3]; /* initialise IV */
+            for i in 0..16 {
+                self.y_0[i] = self.a.f[i]
+            }
+        } else {
+            self.status = GCM_ACCEPTING_CIPHER;
+            self.ghash(iv, niv); /* GHASH(H,0,IV) */
+            self.wrap();
+            for i in 0..16 {
+                self.a.f[i] = self.statex[i];
+                self.y_0[i] = self.a.f[i];
+                self.statex[i] = 0
+            }
+            self.lena[0] = 0;
+            self.lenc[0] = 0;
+            self.lena[1] = 0;
+            self.lenc[1] = 0;
+        }
+        self.status = GCM_ACCEPTING_HEADER;
+    }
+
+    pub fn new() -> GCM {
+        GCM {
+            table: [[0; 4]; 128],
+            statex: [0; 16],
+            y_0: [0; 16],
+            //counter:0,
+            lena: [0; 2],
+            lenc: [0; 2],
+            status: 0,
+            a: AES::new(),
+        }
+    }
+
+    /* Add Header data - included but not encrypted */
+    pub fn add_header(&mut self, header: &[u8], len: usize) -> bool {
+        /* Add some header. Won't be encrypted, but will be authenticated. len is length of header */
+        if self.status != GCM_ACCEPTING_HEADER {
+            return false;
+        }
+        let mut j = 0;
+        while j < len {
+            for i in 0..16 {
+                if j >= len {
+                    break;
+                }
+                self.statex[i] ^= header[j];
+                j += 1;
+                self.lena[1] += 1;
+                if self.lena[1] == 0 {
+                    self.lena[0] += 1
+                }
+            }
+            self.gf2mul();
+        }
+        if len % 16 != 0 {
+            self.status = GCM_ACCEPTING_CIPHER
+        }
+        return true;
+    }
+
+    /* Add Plaintext - included and encrypted */
+    pub fn add_plain(&mut self, cipher: &mut [u8], plain: &[u8], len: usize) -> bool {
+        let mut cb: [u8; 16] = [0; 16];
+        let mut b: [u8; 4] = [0; 4];
+
+        let mut counter: u32;
+        if self.status == GCM_ACCEPTING_HEADER {
+            self.status = GCM_ACCEPTING_CIPHER
+        }
+        if self.status != GCM_ACCEPTING_CIPHER {
+            return false;
+        }
+
+        let mut j = 0;
+        while j < len {
+            b[0] = self.a.f[12];
+            b[1] = self.a.f[13];
+            b[2] = self.a.f[14];
+            b[3] = self.a.f[15];
+            counter = GCM::pack(b);
+            counter += 1;
+            b = GCM::unpack(counter);
+            self.a.f[12] = b[0];
+            self.a.f[13] = b[1];
+            self.a.f[14] = b[2];
+            self.a.f[15] = b[3]; /* increment counter */
+            for i in 0..16 {
+                cb[i] = self.a.f[i]
+            }
+            self.a.ecb_encrypt(&mut cb); /* encrypt it  */
+
+            for i in 0..16 {
+                if j >= len {
+                    break;
+                }
+                cipher[j] = plain[j] ^ cb[i];
+                self.statex[i] ^= cipher[j];
+                j += 1;
+                self.lenc[1] += 1;
+                if self.lenc[1] == 0 {
+                    self.lenc[0] += 1
+                }
+            }
+            self.gf2mul()
+        }
+        if len % 16 != 0 {
+            self.status = GCM_NOT_ACCEPTING_MORE
+        }
+        return true;
+    }
+
+    /* Add Ciphertext - decrypts to plaintext */
+    pub fn add_cipher(&mut self, plain: &mut [u8], cipher: &[u8], len: usize) -> bool {
+        let mut cb: [u8; 16] = [0; 16];
+        let mut b: [u8; 4] = [0; 4];
+
+        let mut counter: u32;
+
+        if self.status == GCM_ACCEPTING_HEADER {
+            self.status = GCM_ACCEPTING_CIPHER
+        }
+        if self.status != GCM_ACCEPTING_CIPHER {
+            return false;
+        }
+
+        let mut j = 0;
+        while j < len {
+            b[0] = self.a.f[12];
+            b[1] = self.a.f[13];
+            b[2] = self.a.f[14];
+            b[3] = self.a.f[15];
+            counter = GCM::pack(b);
+            counter += 1;
+            b = GCM::unpack(counter);
+            self.a.f[12] = b[0];
+            self.a.f[13] = b[1];
+            self.a.f[14] = b[2];
+            self.a.f[15] = b[3]; /* increment counter */
+            for i in 0..16 {
+                cb[i] = self.a.f[i]
+            }
+            self.a.ecb_encrypt(&mut cb); /* encrypt it  */
+            for i in 0..16 {
+                if j >= len {
+                    break;
+                }
+                let oc = cipher[j];
+                plain[j] = cipher[j] ^ cb[i];
+                self.statex[i] ^= oc;
+                j += 1;
+                self.lenc[1] += 1;
+                if self.lenc[1] == 0 {
+                    self.lenc[0] += 1
+                }
+            }
+            self.gf2mul()
+        }
+        if len % 16 != 0 {
+            self.status = GCM_NOT_ACCEPTING_MORE
+        }
+        return true;
+    }
+
+    /* Finish and extract Tag */
+    pub fn finish(&mut self, extract: bool) -> [u8; 16] {
+        /* Finish off GHASH and extract tag (MAC) */
+        let mut tag: [u8; 16] = [0; 16];
+
+        self.wrap();
+        /* extract tag */
+        if extract {
+            self.a.ecb_encrypt(&mut (self.y_0)); /* E(K,Y0) */
+            for i in 0..16 {
+                self.y_0[i] ^= self.statex[i]
+            }
+            for i in 0..16 {
+                tag[i] = self.y_0[i];
+                self.y_0[i] = 0;
+                self.statex[i] = 0
+            }
+        }
+        self.status = GCM_FINISHED;
+        self.a.end();
+        return tag;
+    }
+
+    pub fn hex2bytes(hex: &[u8], bin: &mut [u8]) {
+        let len = hex.len();
+
+        for i in 0..len / 2 {
+            let mut v: u8;
+            let mut c = hex[2 * i];
+            if c >= b'0' && c <= b'9' {
+                v = c - b'0';
+            } else if c >= b'A' && c <= b'F' {
+                v = c - b'A' + 10;
+            } else if c >= b'a' && c <= b'f' {
+                v = c - b'a' + 10;
+            } else {
+                v = 0;
+            }
+            v <<= 4;
+            c = hex[2 * i + 1];
+            if c >= b'0' && c <= b'9' {
+                v += c - b'0';
+            } else if c >= b'A' && c <= b'F' {
+                v += c - b'A' + 10;
+            } else if c >= b'a' && c <= b'f' {
+                v += c - b'a' + 10;
+            } else {
+                v = 0;
+            }
+            bin[i] = v;
+        }
+    }
+}
+/*
+fn main()
+{
+    let kt=b"feffe9928665731c6d6a8f9467308308";
+    let mt=b"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39";
+    let ht=b"feedfacedeadbeeffeedfacedeadbeefabaddad2";
+    let nt=b"9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b";
+// Tag should be 619cc5aefffe0bfa462af43c1699d050
+
+    let mut gcm=GCM::new();
+
+    let len=mt.len()/2;
+    let lenh=ht.len()/2;
+    let lenk=kt.len()/2;
+    let leniv=nt.len()/2;
+
+    //let mut t:[u8;16]=[0;16]; // Tag
+    let mut k:[u8;16]=[0;16];   // AES Key
+    let mut h:[u8;64]=[0;64];   // Header - to be included in Authentication, but not encrypted
+    let mut n:[u8;100]=[0;100]; // IV - Initialisation vector
+    let mut m:[u8;100]=[0;100]; // Plaintext to be encrypted/authenticated
+    let mut c:[u8;100]=[0;100]; // Ciphertext
+    let mut p:[u8;100]=[0;100]; // Recovered Plaintext
+
+    GCM::hex2bytes(mt,&mut m);
+    GCM::hex2bytes(ht,&mut h);
+    GCM::hex2bytes(kt,&mut k);
+    GCM::hex2bytes(nt,&mut n);
+
+     println!("Plaintext=");
+    for i in 0..len {print!("{:02x}",m[i])}
+    println!("");
+
+    gcm.init(lenk,&k,leniv,&n);
+
+    gcm.add_header(&h,lenh);
+    gcm.add_plain(&mut c,&m,len);
+    let mut t=gcm.finish(true);
+
+     println!("Ciphertext=");
+    for i in 0..len {print!("{:02x}",c[i])}
+    println!("");
+
+     println!("Tag=");
+    for i in 0..16 {print!("{:02x}",t[i])}
+    println!("");
+
+    gcm.init(lenk,&k,leniv,&n);
+
+    gcm.add_header(&h,lenh);
+    gcm.add_cipher(&mut p,&c,len);
+    t=gcm.finish(true);
+
+     println!("Plaintext=");
+    for i in 0..len {print!("{:02x}",p[i])}
+    println!("");
+
+    println!("Tag=");
+    for i in 0..16 {print!("{:02x}",t[i])}
+    println!("");
+
+}
+*/
diff --git a/src/hash256.rs b/src/hash256.rs
new file mode 100644
index 0000000..e8d6260
--- /dev/null
+++ b/src/hash256.rs
@@ -0,0 +1,216 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+const HASH256_H0: u32 = 0x6A09E667;
+const HASH256_H1: u32 = 0xBB67AE85;
+const HASH256_H2: u32 = 0x3C6EF372;
+const HASH256_H3: u32 = 0xA54FF53A;
+const HASH256_H4: u32 = 0x510E527F;
+const HASH256_H5: u32 = 0x9B05688C;
+const HASH256_H6: u32 = 0x1F83D9AB;
+const HASH256_H7: u32 = 0x5BE0CD19;
+
+const HASH256_K: [u32; 64] = [
+    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
+];
+
+pub struct HASH256 {
+    length: [u32; 2],
+    h: [u32; 8],
+    w: [u32; 64],
+}
+
+impl HASH256 {
+    fn s(n: u32, x: u32) -> u32 {
+        return ((x) >> n) | ((x) << (32 - n));
+    }
+    fn r(n: u32, x: u32) -> u32 {
+        return (x) >> n;
+    }
+
+    fn ch(x: u32, y: u32, z: u32) -> u32 {
+        return (x & y) ^ (!(x) & z);
+    }
+
+    fn maj(x: u32, y: u32, z: u32) -> u32 {
+        return (x & y) ^ (x & z) ^ (y & z);
+    }
+    fn sig0(x: u32) -> u32 {
+        return HASH256::s(2, x) ^ HASH256::s(13, x) ^ HASH256::s(22, x);
+    }
+
+    fn sig1(x: u32) -> u32 {
+        return HASH256::s(6, x) ^ HASH256::s(11, x) ^ HASH256::s(25, x);
+    }
+
+    fn theta0(x: u32) -> u32 {
+        return HASH256::s(7, x) ^ HASH256::s(18, x) ^ HASH256::r(3, x);
+    }
+
+    fn theta1(x: u32) -> u32 {
+        return HASH256::s(17, x) ^ HASH256::s(19, x) ^ HASH256::r(10, x);
+    }
+
+    fn transform(&mut self) {
+        /* basic transformation step */
+        for j in 16..64 {
+            self.w[j] = HASH256::theta1(self.w[j - 2])
+                .wrapping_add(self.w[j - 7])
+                .wrapping_add(HASH256::theta0(self.w[j - 15]))
+                .wrapping_add(self.w[j - 16]);
+        }
+        let mut a = self.h[0];
+        let mut b = self.h[1];
+        let mut c = self.h[2];
+        let mut d = self.h[3];
+        let mut e = self.h[4];
+        let mut f = self.h[5];
+        let mut g = self.h[6];
+        let mut hh = self.h[7];
+        for j in 0..64 {
+            /* 64 times - mush it up */
+            let t1 = hh
+                .wrapping_add(HASH256::sig1(e))
+                .wrapping_add(HASH256::ch(e, f, g))
+                .wrapping_add(HASH256_K[j])
+                .wrapping_add(self.w[j]);
+            let t2 = HASH256::sig0(a).wrapping_add(HASH256::maj(a, b, c));
+            hh = g;
+            g = f;
+            f = e;
+            e = d.wrapping_add(t1);
+            d = c;
+            c = b;
+            b = a;
+            a = t1.wrapping_add(t2);
+        }
+        self.h[0] = self.h[0].wrapping_add(a);
+        self.h[1] = self.h[1].wrapping_add(b);
+        self.h[2] = self.h[2].wrapping_add(c);
+        self.h[3] = self.h[3].wrapping_add(d);
+        self.h[4] = self.h[4].wrapping_add(e);
+        self.h[5] = self.h[5].wrapping_add(f);
+        self.h[6] = self.h[6].wrapping_add(g);
+        self.h[7] = self.h[7].wrapping_add(hh);
+    }
+
+    /* Initialise Hash function */
+    pub fn init(&mut self) {
+        /* initialise */
+        for i in 0..64 {
+            self.w[i] = 0
+        }
+        self.length[0] = 0;
+        self.length[1] = 0;
+        self.h[0] = HASH256_H0;
+        self.h[1] = HASH256_H1;
+        self.h[2] = HASH256_H2;
+        self.h[3] = HASH256_H3;
+        self.h[4] = HASH256_H4;
+        self.h[5] = HASH256_H5;
+        self.h[6] = HASH256_H6;
+        self.h[7] = HASH256_H7;
+    }
+
+    pub fn new() -> HASH256 {
+        let mut nh = HASH256 {
+            length: [0; 2],
+            h: [0; 8],
+            w: [0; 64],
+        };
+        nh.init();
+        return nh;
+    }
+
+    /* process a single byte */
+    pub fn process(&mut self, byt: u8) {
+        /* process the next message byte */
+        let cnt = ((self.length[0] / 32) % 16) as usize;
+        self.w[cnt] <<= 8;
+        self.w[cnt] |= (byt & 0xFF) as u32;
+        self.length[0] += 8;
+        if self.length[0] == 0 {
+            self.length[1] += 1;
+            self.length[0] = 0
+        }
+        if (self.length[0] % 512) == 0 {
+            self.transform()
+        }
+    }
+
+    /* process an array of bytes */
+
+    pub fn process_array(&mut self, b: &[u8]) {
+        for i in 0..b.len() {
+            self.process(b[i])
+        }
+    }
+
+    /* process a 32-bit integer */
+    pub fn process_num(&mut self, n: i32) {
+        self.process(((n >> 24) & 0xff) as u8);
+        self.process(((n >> 16) & 0xff) as u8);
+        self.process(((n >> 8) & 0xff) as u8);
+        self.process((n & 0xff) as u8);
+    }
+
+    /* Generate 32-byte Hash */
+    pub fn hash(&mut self) -> [u8; 32] {
+        /* pad message and finish - supply digest */
+        let mut digest: [u8; 32] = [0; 32];
+        let len0 = self.length[0];
+        let len1 = self.length[1];
+        self.process(0x80);
+        while (self.length[0] % 512) != 448 {
+            self.process(0)
+        }
+        self.w[14] = len1;
+        self.w[15] = len0;
+        self.transform();
+        for i in 0..32 {
+            /* convert to bytes */
+            digest[i] = ((self.h[i / 4] >> (8 * (3 - i % 4))) & 0xff) as u8;
+        }
+        self.init();
+        return digest;
+    }
+}
+
+//248d6a61 d20638b8 e5c02693 0c3e6039 a33ce459 64ff2167 f6ecedd4 19db06c1
+/*
+fn main() {
+    let s = String::from("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
+    let test = s.into_bytes();
+    let mut sh=HASH256::new();
+
+    for i in 0..test.len(){
+        sh.process(test[i]);
+    }
+
+    let digest=sh.hash();
+    for i in 0..32 {print!("{:02x}",digest[i])}
+}
+*/
diff --git a/src/hash384.rs b/src/hash384.rs
new file mode 100644
index 0000000..2e3729e
--- /dev/null
+++ b/src/hash384.rs
@@ -0,0 +1,288 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+const HASH384_H0: u64 = 0xcbbb9d5dc1059ed8;
+const HASH384_H1: u64 = 0x629a292a367cd507;
+const HASH384_H2: u64 = 0x9159015a3070dd17;
+const HASH384_H3: u64 = 0x152fecd8f70e5939;
+const HASH384_H4: u64 = 0x67332667ffc00b31;
+const HASH384_H5: u64 = 0x8eb44a8768581511;
+const HASH384_H6: u64 = 0xdb0c2e0d64f98fa7;
+const HASH384_H7: u64 = 0x47b5481dbefa4fa4;
+
+const HASH384_K: [u64; 80] = [
+    0x428a2f98d728ae22,
+    0x7137449123ef65cd,
+    0xb5c0fbcfec4d3b2f,
+    0xe9b5dba58189dbbc,
+    0x3956c25bf348b538,
+    0x59f111f1b605d019,
+    0x923f82a4af194f9b,
+    0xab1c5ed5da6d8118,
+    0xd807aa98a3030242,
+    0x12835b0145706fbe,
+    0x243185be4ee4b28c,
+    0x550c7dc3d5ffb4e2,
+    0x72be5d74f27b896f,
+    0x80deb1fe3b1696b1,
+    0x9bdc06a725c71235,
+    0xc19bf174cf692694,
+    0xe49b69c19ef14ad2,
+    0xefbe4786384f25e3,
+    0x0fc19dc68b8cd5b5,
+    0x240ca1cc77ac9c65,
+    0x2de92c6f592b0275,
+    0x4a7484aa6ea6e483,
+    0x5cb0a9dcbd41fbd4,
+    0x76f988da831153b5,
+    0x983e5152ee66dfab,
+    0xa831c66d2db43210,
+    0xb00327c898fb213f,
+    0xbf597fc7beef0ee4,
+    0xc6e00bf33da88fc2,
+    0xd5a79147930aa725,
+    0x06ca6351e003826f,
+    0x142929670a0e6e70,
+    0x27b70a8546d22ffc,
+    0x2e1b21385c26c926,
+    0x4d2c6dfc5ac42aed,
+    0x53380d139d95b3df,
+    0x650a73548baf63de,
+    0x766a0abb3c77b2a8,
+    0x81c2c92e47edaee6,
+    0x92722c851482353b,
+    0xa2bfe8a14cf10364,
+    0xa81a664bbc423001,
+    0xc24b8b70d0f89791,
+    0xc76c51a30654be30,
+    0xd192e819d6ef5218,
+    0xd69906245565a910,
+    0xf40e35855771202a,
+    0x106aa07032bbd1b8,
+    0x19a4c116b8d2d0c8,
+    0x1e376c085141ab53,
+    0x2748774cdf8eeb99,
+    0x34b0bcb5e19b48a8,
+    0x391c0cb3c5c95a63,
+    0x4ed8aa4ae3418acb,
+    0x5b9cca4f7763e373,
+    0x682e6ff3d6b2b8a3,
+    0x748f82ee5defb2fc,
+    0x78a5636f43172f60,
+    0x84c87814a1f0ab72,
+    0x8cc702081a6439ec,
+    0x90befffa23631e28,
+    0xa4506cebde82bde9,
+    0xbef9a3f7b2c67915,
+    0xc67178f2e372532b,
+    0xca273eceea26619c,
+    0xd186b8c721c0c207,
+    0xeada7dd6cde0eb1e,
+    0xf57d4f7fee6ed178,
+    0x06f067aa72176fba,
+    0x0a637dc5a2c898a6,
+    0x113f9804bef90dae,
+    0x1b710b35131c471b,
+    0x28db77f523047d84,
+    0x32caab7b40c72493,
+    0x3c9ebe0a15c9bebc,
+    0x431d67c49c100d4c,
+    0x4cc5d4becb3e42b6,
+    0x597f299cfc657e2a,
+    0x5fcb6fab3ad6faec,
+    0x6c44198c4a475817,
+];
+
+pub struct HASH384 {
+    length: [u64; 2],
+    h: [u64; 8],
+    w: [u64; 80],
+}
+
+impl HASH384 {
+    fn s(n: u64, x: u64) -> u64 {
+        return ((x) >> n) | ((x) << (64 - n));
+    }
+    fn r(n: u64, x: u64) -> u64 {
+        return (x) >> n;
+    }
+
+    fn ch(x: u64, y: u64, z: u64) -> u64 {
+        return (x & y) ^ (!(x) & z);
+    }
+
+    fn maj(x: u64, y: u64, z: u64) -> u64 {
+        return (x & y) ^ (x & z) ^ (y & z);
+    }
+
+    fn sig0(x: u64) -> u64 {
+        return HASH384::s(28, x) ^ HASH384::s(34, x) ^ HASH384::s(39, x);
+    }
+
+    fn sig1(x: u64) -> u64 {
+        return HASH384::s(14, x) ^ HASH384::s(18, x) ^ HASH384::s(41, x);
+    }
+
+    fn theta0(x: u64) -> u64 {
+        return HASH384::s(1, x) ^ HASH384::s(8, x) ^ HASH384::r(7, x);
+    }
+
+    fn theta1(x: u64) -> u64 {
+        return HASH384::s(19, x) ^ HASH384::s(61, x) ^ HASH384::r(6, x);
+    }
+
+    fn transform(&mut self) {
+        /* basic transformation step */
+        for j in 16..80 {
+            self.w[j] = HASH384::theta1(self.w[j - 2])
+                .wrapping_add(self.w[j - 7])
+                .wrapping_add(HASH384::theta0(self.w[j - 15]))
+                .wrapping_add(self.w[j - 16]);
+        }
+        let mut a = self.h[0];
+        let mut b = self.h[1];
+        let mut c = self.h[2];
+        let mut d = self.h[3];
+        let mut e = self.h[4];
+        let mut f = self.h[5];
+        let mut g = self.h[6];
+        let mut hh = self.h[7];
+        for j in 0..80 {
+            /* 64 times - mush it up */
+            let t1 = hh
+                .wrapping_add(HASH384::sig1(e))
+                .wrapping_add(HASH384::ch(e, f, g))
+                .wrapping_add(HASH384_K[j])
+                .wrapping_add(self.w[j]);
+            let t2 = HASH384::sig0(a).wrapping_add(HASH384::maj(a, b, c));
+            hh = g;
+            g = f;
+            f = e;
+            e = d.wrapping_add(t1);
+            d = c;
+            c = b;
+            b = a;
+            a = t1.wrapping_add(t2);
+        }
+        self.h[0] = self.h[0].wrapping_add(a);
+        self.h[1] = self.h[1].wrapping_add(b);
+        self.h[2] = self.h[2].wrapping_add(c);
+        self.h[3] = self.h[3].wrapping_add(d);
+        self.h[4] = self.h[4].wrapping_add(e);
+        self.h[5] = self.h[5].wrapping_add(f);
+        self.h[6] = self.h[6].wrapping_add(g);
+        self.h[7] = self.h[7].wrapping_add(hh);
+    }
+
+    /* Initialise Hash function */
+    pub fn init(&mut self) {
+        /* initialise */
+        for i in 0..64 {
+            self.w[i] = 0
+        }
+        self.length[0] = 0;
+        self.length[1] = 0;
+        self.h[0] = HASH384_H0;
+        self.h[1] = HASH384_H1;
+        self.h[2] = HASH384_H2;
+        self.h[3] = HASH384_H3;
+        self.h[4] = HASH384_H4;
+        self.h[5] = HASH384_H5;
+        self.h[6] = HASH384_H6;
+        self.h[7] = HASH384_H7;
+    }
+
+    pub fn new() -> HASH384 {
+        let mut nh = HASH384 {
+            length: [0; 2],
+            h: [0; 8],
+            w: [0; 80],
+        };
+        nh.init();
+        return nh;
+    }
+
+    /* process a single byte */
+    pub fn process(&mut self, byt: u8) {
+        /* process the next message byte */
+        let cnt = ((self.length[0] / 64) % 16) as usize;
+        self.w[cnt] <<= 8;
+        self.w[cnt] |= (byt & 0xFF) as u64;
+        self.length[0] += 8;
+        if self.length[0] == 0 {
+            self.length[1] += 1;
+            self.length[0] = 0
+        }
+        if (self.length[0] % 1024) == 0 {
+            self.transform()
+        }
+    }
+
+    /* process an array of bytes */
+
+    pub fn process_array(&mut self, b: &[u8]) {
+        for i in 0..b.len() {
+            self.process(b[i])
+        }
+    }
+
+    /* process a 32-bit integer */
+    pub fn process_num(&mut self, n: i32) {
+        self.process(((n >> 24) & 0xff) as u8);
+        self.process(((n >> 16) & 0xff) as u8);
+        self.process(((n >> 8) & 0xff) as u8);
+        self.process((n & 0xff) as u8);
+    }
+
+    /* Generate 48-byte Hash */
+    pub fn hash(&mut self) -> [u8; 48] {
+        /* pad message and finish - supply digest */
+        let mut digest: [u8; 48] = [0; 48];
+        let len0 = self.length[0];
+        let len1 = self.length[1];
+        self.process(0x80);
+        while (self.length[0] % 1024) != 896 {
+            self.process(0)
+        }
+        self.w[14] = len1;
+        self.w[15] = len0;
+        self.transform();
+        for i in 0..48 {
+            /* convert to bytes */
+            digest[i] = ((self.h[i / 8] >> (8 * (7 - i % 8))) & 0xff) as u8;
+        }
+        self.init();
+        return digest;
+    }
+}
+
+//09330c33f71147e8 3d192fc782cd1b47 53111b173b3b05d2 2fa08086e3b0f712 fcc7c71a557e2db9 66c3e9fa91746039
+/*
+fn main() {
+    let s = String::from("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
+    let test = s.into_bytes();
+    let mut sh=HASH384::new();
+
+    for i in 0..test.len(){
+        sh.process(test[i]);
+    }
+
+    let digest=sh.hash();
+    for i in 0..48 {print!("{:02x}",digest[i])}
+} */
diff --git a/src/hash512.rs b/src/hash512.rs
new file mode 100644
index 0000000..4eb0689
--- /dev/null
+++ b/src/hash512.rs
@@ -0,0 +1,288 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+const HASH512_H0: u64 = 0x6a09e667f3bcc908;
+const HASH512_H1: u64 = 0xbb67ae8584caa73b;
+const HASH512_H2: u64 = 0x3c6ef372fe94f82b;
+const HASH512_H3: u64 = 0xa54ff53a5f1d36f1;
+const HASH512_H4: u64 = 0x510e527fade682d1;
+const HASH512_H5: u64 = 0x9b05688c2b3e6c1f;
+const HASH512_H6: u64 = 0x1f83d9abfb41bd6b;
+const HASH512_H7: u64 = 0x5be0cd19137e2179;
+
+const HASH512_K: [u64; 80] = [
+    0x428a2f98d728ae22,
+    0x7137449123ef65cd,
+    0xb5c0fbcfec4d3b2f,
+    0xe9b5dba58189dbbc,
+    0x3956c25bf348b538,
+    0x59f111f1b605d019,
+    0x923f82a4af194f9b,
+    0xab1c5ed5da6d8118,
+    0xd807aa98a3030242,
+    0x12835b0145706fbe,
+    0x243185be4ee4b28c,
+    0x550c7dc3d5ffb4e2,
+    0x72be5d74f27b896f,
+    0x80deb1fe3b1696b1,
+    0x9bdc06a725c71235,
+    0xc19bf174cf692694,
+    0xe49b69c19ef14ad2,
+    0xefbe4786384f25e3,
+    0x0fc19dc68b8cd5b5,
+    0x240ca1cc77ac9c65,
+    0x2de92c6f592b0275,
+    0x4a7484aa6ea6e483,
+    0x5cb0a9dcbd41fbd4,
+    0x76f988da831153b5,
+    0x983e5152ee66dfab,
+    0xa831c66d2db43210,
+    0xb00327c898fb213f,
+    0xbf597fc7beef0ee4,
+    0xc6e00bf33da88fc2,
+    0xd5a79147930aa725,
+    0x06ca6351e003826f,
+    0x142929670a0e6e70,
+    0x27b70a8546d22ffc,
+    0x2e1b21385c26c926,
+    0x4d2c6dfc5ac42aed,
+    0x53380d139d95b3df,
+    0x650a73548baf63de,
+    0x766a0abb3c77b2a8,
+    0x81c2c92e47edaee6,
+    0x92722c851482353b,
+    0xa2bfe8a14cf10364,
+    0xa81a664bbc423001,
+    0xc24b8b70d0f89791,
+    0xc76c51a30654be30,
+    0xd192e819d6ef5218,
+    0xd69906245565a910,
+    0xf40e35855771202a,
+    0x106aa07032bbd1b8,
+    0x19a4c116b8d2d0c8,
+    0x1e376c085141ab53,
+    0x2748774cdf8eeb99,
+    0x34b0bcb5e19b48a8,
+    0x391c0cb3c5c95a63,
+    0x4ed8aa4ae3418acb,
+    0x5b9cca4f7763e373,
+    0x682e6ff3d6b2b8a3,
+    0x748f82ee5defb2fc,
+    0x78a5636f43172f60,
+    0x84c87814a1f0ab72,
+    0x8cc702081a6439ec,
+    0x90befffa23631e28,
+    0xa4506cebde82bde9,
+    0xbef9a3f7b2c67915,
+    0xc67178f2e372532b,
+    0xca273eceea26619c,
+    0xd186b8c721c0c207,
+    0xeada7dd6cde0eb1e,
+    0xf57d4f7fee6ed178,
+    0x06f067aa72176fba,
+    0x0a637dc5a2c898a6,
+    0x113f9804bef90dae,
+    0x1b710b35131c471b,
+    0x28db77f523047d84,
+    0x32caab7b40c72493,
+    0x3c9ebe0a15c9bebc,
+    0x431d67c49c100d4c,
+    0x4cc5d4becb3e42b6,
+    0x597f299cfc657e2a,
+    0x5fcb6fab3ad6faec,
+    0x6c44198c4a475817,
+];
+
+pub struct HASH512 {
+    length: [u64; 2],
+    h: [u64; 8],
+    w: [u64; 80],
+}
+
+impl HASH512 {
+    fn s(n: u64, x: u64) -> u64 {
+        return ((x) >> n) | ((x) << (64 - n));
+    }
+    fn r(n: u64, x: u64) -> u64 {
+        return (x) >> n;
+    }
+
+    fn ch(x: u64, y: u64, z: u64) -> u64 {
+        return (x & y) ^ (!(x) & z);
+    }
+
+    fn maj(x: u64, y: u64, z: u64) -> u64 {
+        return (x & y) ^ (x & z) ^ (y & z);
+    }
+
+    fn sig0(x: u64) -> u64 {
+        return HASH512::s(28, x) ^ HASH512::s(34, x) ^ HASH512::s(39, x);
+    }
+
+    fn sig1(x: u64) -> u64 {
+        return HASH512::s(14, x) ^ HASH512::s(18, x) ^ HASH512::s(41, x);
+    }
+
+    fn theta0(x: u64) -> u64 {
+        return HASH512::s(1, x) ^ HASH512::s(8, x) ^ HASH512::r(7, x);
+    }
+
+    fn theta1(x: u64) -> u64 {
+        return HASH512::s(19, x) ^ HASH512::s(61, x) ^ HASH512::r(6, x);
+    }
+
+    fn transform(&mut self) {
+        /* basic transformation step */
+        for j in 16..80 {
+            self.w[j] = HASH512::theta1(self.w[j - 2])
+                .wrapping_add(self.w[j - 7])
+                .wrapping_add(HASH512::theta0(self.w[j - 15]))
+                .wrapping_add(self.w[j - 16]);
+        }
+        let mut a = self.h[0];
+        let mut b = self.h[1];
+        let mut c = self.h[2];
+        let mut d = self.h[3];
+        let mut e = self.h[4];
+        let mut f = self.h[5];
+        let mut g = self.h[6];
+        let mut hh = self.h[7];
+        for j in 0..80 {
+            /* 64 times - mush it up */
+            let t1 = hh
+                .wrapping_add(HASH512::sig1(e))
+                .wrapping_add(HASH512::ch(e, f, g))
+                .wrapping_add(HASH512_K[j])
+                .wrapping_add(self.w[j]);
+            let t2 = HASH512::sig0(a).wrapping_add(HASH512::maj(a, b, c));
+            hh = g;
+            g = f;
+            f = e;
+            e = d.wrapping_add(t1);
+            d = c;
+            c = b;
+            b = a;
+            a = t1.wrapping_add(t2);
+        }
+        self.h[0] = self.h[0].wrapping_add(a);
+        self.h[1] = self.h[1].wrapping_add(b);
+        self.h[2] = self.h[2].wrapping_add(c);
+        self.h[3] = self.h[3].wrapping_add(d);
+        self.h[4] = self.h[4].wrapping_add(e);
+        self.h[5] = self.h[5].wrapping_add(f);
+        self.h[6] = self.h[6].wrapping_add(g);
+        self.h[7] = self.h[7].wrapping_add(hh);
+    }
+
+    /* Initialise Hash function */
+    pub fn init(&mut self) {
+        /* initialise */
+        for i in 0..64 {
+            self.w[i] = 0
+        }
+        self.length[0] = 0;
+        self.length[1] = 0;
+        self.h[0] = HASH512_H0;
+        self.h[1] = HASH512_H1;
+        self.h[2] = HASH512_H2;
+        self.h[3] = HASH512_H3;
+        self.h[4] = HASH512_H4;
+        self.h[5] = HASH512_H5;
+        self.h[6] = HASH512_H6;
+        self.h[7] = HASH512_H7;
+    }
+
+    pub fn new() -> HASH512 {
+        let mut nh = HASH512 {
+            length: [0; 2],
+            h: [0; 8],
+            w: [0; 80],
+        };
+        nh.init();
+        return nh;
+    }
+
+    /* process a single byte */
+    pub fn process(&mut self, byt: u8) {
+        /* process the next message byte */
+        let cnt = ((self.length[0] / 64) % 16) as usize;
+        self.w[cnt] <<= 8;
+        self.w[cnt] |= (byt & 0xFF) as u64;
+        self.length[0] += 8;
+        if self.length[0] == 0 {
+            self.length[1] += 1;
+            self.length[0] = 0
+        }
+        if (self.length[0] % 1024) == 0 {
+            self.transform()
+        }
+    }
+
+    /* process an array of bytes */
+
+    pub fn process_array(&mut self, b: &[u8]) {
+        for i in 0..b.len() {
+            self.process(b[i])
+        }
+    }
+
+    /* process a 32-bit integer */
+    pub fn process_num(&mut self, n: i32) {
+        self.process(((n >> 24) & 0xff) as u8);
+        self.process(((n >> 16) & 0xff) as u8);
+        self.process(((n >> 8) & 0xff) as u8);
+        self.process((n & 0xff) as u8);
+    }
+
+    /* Generate 64-byte Hash */
+    pub fn hash(&mut self) -> [u8; 64] {
+        /* pad message and finish - supply digest */
+        let mut digest: [u8; 64] = [0; 64];
+        let len0 = self.length[0];
+        let len1 = self.length[1];
+        self.process(0x80);
+        while (self.length[0] % 1024) != 896 {
+            self.process(0)
+        }
+        self.w[14] = len1;
+        self.w[15] = len0;
+        self.transform();
+        for i in 0..64 {
+            /* convert to bytes */
+            digest[i] = ((self.h[i / 8] >> (8 * (7 - i % 8))) & 0xff) as u8;
+        }
+        self.init();
+        return digest;
+    }
+}
+
+//8e959b75dae313da 8cf4f72814fc143f 8f7779c6eb9f7fa1 7299aeadb6889018 501d289e4900f7e4 331b99dec4b5433a c7d329eeb6dd2654 5e96e55b874be909
+/*
+fn main() {
+    let s = String::from("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
+    let test = s.into_bytes();
+    let mut sh=HASH512::new();
+
+    for i in 0..test.len(){
+        sh.process(test[i]);
+    }
+
+    let digest=sh.hash();
+    for i in 0..64 {print!("{:02x}",digest[i])}
+} */
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..108057b
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,561 @@
+pub mod aes;
+pub mod gcm;
+pub mod hash256;
+pub mod hash384;
+pub mod hash512;
+pub mod rand;
+pub mod sha3;
+pub mod nhs;
+pub mod types;
+#[cfg(target_pointer_width = "32")]
+#[path = "arch/arch32.rs"]
+pub mod arch;
+#[cfg(target_pointer_width = "64")]
+#[path = "arch/arch64.rs"]
+pub mod arch;
+
+#[cfg(feature = "bls48")]
+#[path = "./"]
+pub mod bls48 {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_bls48_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_bls48_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecp8;
+    pub mod fp2;
+    pub mod fp4;
+    pub mod fp8;
+    pub mod fp16;
+    pub mod fp48;
+    pub mod pair256;
+    pub mod mpin256;
+    pub mod bls256;
+}
+
+#[cfg(feature = "bls461")]
+#[path = "./"]
+pub mod bls461 {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_bls461_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_bls461_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecp2;
+    pub mod fp2;
+    pub mod fp4;
+    pub mod fp12;
+    pub mod pair;
+    pub mod mpin;
+    pub mod bls;
+}
+
+#[cfg(feature = "bls383")]
+#[path = "./"]
+pub mod bls383 {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_bls383_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_bls383_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecp2;
+    pub mod fp2;
+    pub mod fp4;
+    pub mod fp12;
+    pub mod pair;
+    pub mod mpin;
+    pub mod bls;
+}
+
+#[cfg(feature = "bls381")]
+#[path = "./"]
+pub mod bls381 {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_bls381_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_bls381_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecp2;
+    pub mod fp2;
+    pub mod fp4;
+    pub mod fp12;
+    pub mod pair;
+    pub mod mpin;
+    pub mod bls;
+}
+
+#[cfg(feature = "fp512bn")]
+#[path = "./"]
+pub mod fp512bn {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_fp512bn_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_fp512bn_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecp2;
+    pub mod fp2;
+    pub mod fp4;
+    pub mod fp12;
+    pub mod pair;
+    pub mod mpin;
+    pub mod bls;
+}
+
+#[cfg(feature = "fp256bn")]
+#[path = "./"]
+pub mod fp256bn {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_fp256bn_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_fp256bn_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecp2;
+    pub mod fp2;
+    pub mod fp4;
+    pub mod fp12;
+    pub mod pair;
+    pub mod mpin;
+    pub mod bls;
+}
+
+#[cfg(feature = "bls24")]
+#[path = "./"]
+pub mod bls24 {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_bls24_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_bls24_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod fp2;
+    pub mod fp4;
+    pub mod fp8;
+    pub mod fp24;
+    pub mod ecp;
+    pub mod ecp4;
+    pub mod pair192;
+    pub mod mpin192;
+    pub mod bls192;
+}
+
+#[cfg(feature = "anssi")]
+#[path = "./"]
+pub mod anssi {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_anssi_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_anssi_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecdh;
+}
+
+#[cfg(feature = "brainpool")]
+#[path = "./"]
+pub mod brainpool {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_brainpool_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_brainpool_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecdh;
+}
+
+#[cfg(feature = "goldilocks")]
+#[path = "./"]
+pub mod goldilocks {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_goldilocks_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_goldilocks_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecdh;
+}
+
+#[cfg(feature = "hifive")]
+#[path = "./"]
+pub mod hifive {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_hifive_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_hifive_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecdh;
+}
+
+#[cfg(feature = "nist256")]
+#[path = "./"]
+pub mod nist256 {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_nist256_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_nist256_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecdh;
+}
+
+#[cfg(feature = "nist384")]
+#[path = "./"]
+pub mod nist384 {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_nist384_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_nist384_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecdh;
+}
+
+#[cfg(feature = "nist521")]
+#[path = "./"]
+pub mod nist521 {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_nist521_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_nist521_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecdh;
+}
+
+#[cfg(feature = "nums256e")]
+#[path = "./"]
+pub mod nums256e {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_nums256e_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_nums256e_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecdh;
+}
+
+#[cfg(feature = "nums256w")]
+#[path = "./"]
+pub mod nums256w {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_nums256w_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_nums256w_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecdh;
+}
+
+#[cfg(feature = "nums384e")]
+#[path = "./"]
+pub mod nums384e {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_nums384e_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_nums384e_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecdh;
+}
+
+#[cfg(feature = "nums384w")]
+#[path = "./"]
+pub mod nums384w {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_nums384w_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_nums384w_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecdh;
+}
+
+#[cfg(feature = "nums512w")]
+#[path = "./"]
+pub mod nums512w {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_nums512w_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_nums512w_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecdh;
+}
+
+#[cfg(feature = "nums512e")]
+#[path = "./"]
+pub mod nums512e {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_nums512e_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_nums512e_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecdh;
+}
+
+#[cfg(feature = "secp256k1")]
+#[path = "./"]
+pub mod secp256k1 {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_secp256k1_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_secp256k1_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecdh;
+}
+
+#[cfg(feature = "c25519")]
+#[path = "./"]
+pub mod c25519 {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_c25519_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_c25519_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecdh;
+}
+
+#[cfg(feature = "c41417")]
+#[path = "./"]
+pub mod c41417 {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_c41417_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_c41417_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecdh;
+}
+
+#[cfg(feature = "ed25519")]
+#[path = "./"]
+pub mod ed25519 {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_ed25519_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_ed25519_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecdh;
+}
+
+#[cfg(feature = "bn254CX")]
+#[path = "./"]
+pub mod bn254CX {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_bn254CX_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_bn254CX_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecdh;
+    pub mod ecp2;
+    pub mod fp2;
+    pub mod fp4;
+    pub mod fp12;
+    pub mod pair;
+    pub mod mpin;
+    pub mod bls;
+}
+
+#[cfg(feature = "bn254")]
+#[path = "./"]
+pub mod bn254 {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_bn254_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_bn254_64.rs"]
+    pub mod rom;
+
+    pub mod big;
+    pub mod dbig;
+    pub mod fp;
+    pub mod ecp;
+    pub mod ecdh;
+    pub mod ecp2;
+    pub mod fp2;
+    pub mod fp4;
+    pub mod fp12;
+    pub mod pair;
+    pub mod mpin;
+    pub mod bls;
+}
+
+#[cfg(feature = "rsa2048")]
+#[path = "./"]
+pub mod rsa2048 {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_rsa2048_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_rsa2048_64.rs"]
+    pub mod rom;
+    pub mod big;
+    pub mod dbig;
+    pub mod ff;
+    pub mod rsa;
+}
+
+#[cfg(feature = "rsa3072")]
+#[path = "./"]
+pub mod rsa3072 {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_rsa3072_32.rs"]
+    pub mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_rsa3072_64.rs"]
+    pub mod rom;
+    pub mod big;
+    pub mod dbig;
+    pub mod ff;
+    pub mod rsa;
+}
+
+#[cfg(feature = "rsa4096")]
+#[path = "./"]
+pub mod rsa4096 {
+    #[cfg(target_pointer_width = "32")]
+    #[path = "roms/rom_rsa4096_32.rs"]
+    mod rom;
+    #[cfg(target_pointer_width = "64")]
+    #[path = "roms/rom_rsa4096_64.rs"]
+    mod rom;
+    pub mod big;
+    pub mod dbig;
+    pub mod ff;
+    pub mod rsa;
+}
\ No newline at end of file
diff --git a/src/mpin.rs b/src/mpin.rs
new file mode 100644
index 0000000..6d7e9ca
--- /dev/null
+++ b/src/mpin.rs
@@ -0,0 +1,945 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use std::time::SystemTime;
+use std::time::UNIX_EPOCH;
+
+use super::ecp;
+use super::ecp::ECP;
+use super::ecp2::ECP2;
+use super::fp4::FP4;
+use super::fp12::FP12;
+use super::big::BIG;
+use super::pair;
+use super::big;
+use super::rom;
+
+use rand::RAND;
+use hash256::HASH256;
+use hash384::HASH384;
+use hash512::HASH512;
+
+
+/* MPIN API Functions */
+
+/* Configure mode of operation */
+
+pub const EFS: usize = big::MODBYTES as usize;
+pub const EGS: usize = big::MODBYTES as usize;
+pub const BAD_PARAMS: isize = -11;
+pub const INVALID_POINT: isize = -14;
+pub const WRONG_ORDER: isize = -18;
+pub const BAD_PIN: isize = -19;
+pub const SHA256: usize = 32;
+pub const SHA384: usize = 48;
+pub const SHA512: usize = 64;
+
+/* Configure your PIN here */
+
+pub const MAXPIN: i32 = 10000; /* PIN less than this */
+pub const PBLEN: i32 = 14; /* Number of bits in PIN */
+pub const TS: usize = 10; /* 10 for 4 digit PIN, 14 for 6-digit PIN - 2^TS/TS approx = sqrt(MAXPIN) */
+pub const TRAP: usize = 200; /* 200 for 4 digit PIN, 2000 for 6-digit PIN  - approx 2*sqrt(MAXPIN) */
+
+#[allow(non_snake_case)]
+fn hash(sha: usize, c: &mut FP4, U: &mut ECP, r: &mut [u8]) -> bool {
+    let mut w: [u8; EFS] = [0; EFS];
+    let mut t: [u8; 6 * EFS] = [0; 6 * EFS];
+
+    c.geta().geta().tobytes(&mut w);
+    for i in 0..EFS {
+        t[i] = w[i]
+    }
+    c.geta().getb().tobytes(&mut w);
+    for i in EFS..2 * EFS {
+        t[i] = w[i - EFS]
+    }
+    c.getb().geta().tobytes(&mut w);
+    for i in 2 * EFS..3 * EFS {
+        t[i] = w[i - 2 * EFS]
+    }
+    c.getb().getb().tobytes(&mut w);
+    for i in 3 * EFS..4 * EFS {
+        t[i] = w[i - 3 * EFS]
+    }
+
+    U.getx().tobytes(&mut w);
+    for i in 4 * EFS..5 * EFS {
+        t[i] = w[i - 4 * EFS]
+    }
+    U.gety().tobytes(&mut w);
+    for i in 5 * EFS..6 * EFS {
+        t[i] = w[i - 5 * EFS]
+    }
+
+    if sha == SHA256 {
+        let mut h = HASH256::new();
+        h.process_array(&t);
+        let sh = h.hash();
+        for i in 0..ecp::AESKEY {
+            r[i] = sh[i]
+        }
+        return true;
+    }
+    if sha == SHA384 {
+        let mut h = HASH384::new();
+        h.process_array(&t);
+        let sh = h.hash();
+        for i in 0..ecp::AESKEY {
+            r[i] = sh[i]
+        }
+        return true;
+    }
+    if sha == SHA512 {
+        let mut h = HASH512::new();
+        h.process_array(&t);
+        let sh = h.hash();
+        for i in 0..ecp::AESKEY {
+            r[i] = sh[i]
+        }
+        return true;
+    }
+    return false;
+}
+
+/* Hash number (optional) and string to point on curve */
+
+fn hashit(sha: usize, n: usize, id: &[u8], w: &mut [u8]) -> bool {
+    let mut r: [u8; 64] = [0; 64];
+    let mut didit = false;
+    if sha == SHA256 {
+        let mut h = HASH256::new();
+        if n > 0 {
+            h.process_num(n as i32)
+        }
+        h.process_array(id);
+        let hs = h.hash();
+        for i in 0..sha {
+            r[i] = hs[i];
+        }
+        didit = true;
+    }
+    if sha == SHA384 {
+        let mut h = HASH384::new();
+        if n > 0 {
+            h.process_num(n as i32)
+        }
+        h.process_array(id);
+        let hs = h.hash();
+        for i in 0..sha {
+            r[i] = hs[i];
+        }
+        didit = true;
+    }
+    if sha == SHA512 {
+        let mut h = HASH512::new();
+        if n > 0 {
+            h.process_num(n as i32)
+        }
+        h.process_array(id);
+        let hs = h.hash();
+        for i in 0..sha {
+            r[i] = hs[i];
+        }
+        didit = true;
+    }
+    if !didit {
+        return false;
+    }
+
+    let rm = big::MODBYTES as usize;
+
+    if sha > rm {
+        for i in 0..rm {
+            w[i] = r[i]
+        }
+    } else {
+        for i in 0..sha {
+            w[i + rm - sha] = r[i]
+        }
+        for i in 0..(rm - sha) {
+            w[i] = 0
+        }
+    }
+
+    return true;
+}
+
+/* return time in slots since epoch */
+pub fn today() -> usize {
+    return (SystemTime::now()
+        .duration_since(UNIX_EPOCH)
+        .unwrap()
+        .as_secs()
+        / (60 * 1440)) as usize;
+}
+
+/* these next two functions help to implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* maps a random u to a point on the curve */
+#[allow(non_snake_case)]
+fn emap(u: &BIG, cb: isize) -> ECP {
+    let mut P: ECP;
+    let mut x = BIG::new_copy(u);
+    let mut p = BIG::new_ints(&rom::MODULUS);
+    x.rmod(&mut p);
+    loop {
+        P = ECP::new_bigint(&x, cb);
+        if !P.is_infinity() {
+            break;
+        }
+        x.inc(1);
+        x.norm();
+    }
+    return P;
+}
+
+/* returns u derived from P. Random value in range 1 to return value should then be added to u */
+#[allow(non_snake_case)]
+fn unmap(u: &mut BIG, P: &mut ECP) -> isize {
+    let s = P.gets();
+    let mut R: ECP;
+    let mut r = 0;
+    let x = P.getx();
+    u.copy(&x);
+    loop {
+        u.dec(1);
+        u.norm();
+        r += 1;
+        R = ECP::new_bigint(u, s);
+        if !R.is_infinity() {
+            break;
+        }
+    }
+    return r as isize;
+}
+
+pub fn hash_id(sha: usize, id: &[u8], w: &mut [u8]) -> bool {
+    return hashit(sha, 0, id, w);
+}
+
+/* these next two functions implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* Elliptic curve point E in format (0x04,x,y} is converted to form {0x0-,u,v} */
+/* Note that u and v are indistinguisible from random strings */
+#[allow(non_snake_case)]
+pub fn encoding(rng: &mut RAND, e: &mut [u8]) -> isize {
+    let mut t: [u8; EFS] = [0; EFS];
+
+    for i in 0..EFS {
+        t[i] = e[i + 1]
+    }
+    let mut u = BIG::frombytes(&t);
+    for i in 0..EFS {
+        t[i] = e[i + EFS + 1]
+    }
+    let mut v = BIG::frombytes(&t);
+
+    let mut P = ECP::new_bigs(&u, &v);
+    if P.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let p = BIG::new_ints(&rom::MODULUS);
+    u = BIG::randomnum(&p, rng);
+
+    let mut su = rng.getbyte() as isize;
+    su %= 2;
+
+    let mut W = emap(&mut u, su);
+    P.sub(&mut W);
+    let sv = P.gets();
+    let rn = unmap(&mut v, &mut P);
+    let mut m = rng.getbyte() as isize;
+    m %= rn;
+    v.inc(m + 1);
+    e[0] = (su + 2 * sv) as u8;
+    u.tobytes(&mut t);
+    for i in 0..EFS {
+        e[i + 1] = t[i]
+    }
+    v.tobytes(&mut t);
+    for i in 0..EFS {
+        e[i + EFS + 1] = t[i]
+    }
+
+    return 0;
+}
+
+#[allow(non_snake_case)]
+pub fn decoding(d: &mut [u8]) -> isize {
+    let mut t: [u8; EFS] = [0; EFS];
+
+    if (d[0] & 0x04) != 0 {
+        return INVALID_POINT;
+    }
+
+    for i in 0..EFS {
+        t[i] = d[i + 1]
+    }
+    let mut u = BIG::frombytes(&t);
+    for i in 0..EFS {
+        t[i] = d[i + EFS + 1]
+    }
+    let mut v = BIG::frombytes(&t);
+
+    let su = (d[0] & 1) as isize;
+    let sv = ((d[0] >> 1) & 1) as isize;
+    let mut W = emap(&mut u, su);
+    let mut P = emap(&mut v, sv);
+    P.add(&mut W);
+    u = P.getx();
+    v = P.gety();
+    d[0] = 0x04;
+    u.tobytes(&mut t);
+    for i in 0..EFS {
+        d[i + 1] = t[i]
+    }
+    v.tobytes(&mut t);
+    for i in 0..EFS {
+        d[i + EFS + 1] = t[i]
+    }
+
+    return 0;
+}
+
+/* R=R1+R2 in group G1 */
+#[allow(non_snake_case)]
+pub fn recombine_g1(r1: &[u8], r2: &[u8], r: &mut [u8]) -> isize {
+    let mut P = ECP::frombytes(&r1);
+    let mut Q = ECP::frombytes(&r2);
+
+    if P.is_infinity() || Q.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    P.add(&mut Q);
+
+    P.tobytes(r, false);
+    return 0;
+}
+
+/* W=W1+W2 in group G2 */
+#[allow(non_snake_case)]
+pub fn recombine_g2(w1: &[u8], w2: &[u8], w: &mut [u8]) -> isize {
+    let mut P = ECP2::frombytes(&w1);
+    let mut Q = ECP2::frombytes(&w2);
+
+    if P.is_infinity() || Q.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    P.add(&mut Q);
+
+    P.tobytes(w);
+    return 0;
+}
+
+/* create random secret S */
+pub fn random_generate(rng: &mut RAND, s: &mut [u8]) -> isize {
+    let r = BIG::new_ints(&rom::CURVE_ORDER);
+    let mut sc = BIG::randomnum(&r, rng);
+    sc.tobytes(s);
+    return 0;
+}
+
+/* Extract Server Secret SST=S*Q where Q is fixed generator in G2 and S is master secret */
+#[allow(non_snake_case)]
+pub fn get_server_secret(s: &[u8], sst: &mut [u8]) -> isize {
+    let mut Q = ECP2::generator();
+
+    let mut sc = BIG::frombytes(s);
+    Q = pair::g2mul(&mut Q, &mut sc);
+    Q.tobytes(sst);
+    return 0;
+}
+
+/*
+ W=x*H(G);
+ if RNG == NULL then X is passed in
+ if RNG != NULL the X is passed out
+ if type=0 W=x*G where G is point on the curve, else W=x*M(G), where M(G) is mapping of octet G to point on the curve
+*/
+#[allow(non_snake_case)]
+pub fn get_g1_multiple(
+    rng: Option<&mut RAND>,
+    typ: usize,
+    x: &mut [u8],
+    g: &[u8],
+    w: &mut [u8],
+) -> isize {
+    let mut sx: BIG;
+    let r = BIG::new_ints(&rom::CURVE_ORDER);
+
+    if let Some(rd) = rng {
+        sx = BIG::randomnum(&r, rd);
+        sx.tobytes(x);
+    } else {
+        sx = BIG::frombytes(x);
+    }
+    let mut P: ECP;
+
+    if typ == 0 {
+        P = ECP::frombytes(g);
+        if P.is_infinity() {
+            return INVALID_POINT;
+        }
+    } else {
+        P = ECP::mapit(g)
+    }
+
+    pair::g1mul(&mut P, &mut sx).tobytes(w, false);
+    return 0;
+}
+
+/* Client secret CST=S*H(CID) where CID is client ID and S is master secret */
+/* CID is hashed externally */
+pub fn get_client_secret(s: &mut [u8], cid: &[u8], cst: &mut [u8]) -> isize {
+    return get_g1_multiple(None, 1, s, cid, cst);
+}
+
+/* Extract PIN from TOKEN for identity CID */
+#[allow(non_snake_case)]
+pub fn extract_pin(sha: usize, cid: &[u8], pin: i32, token: &mut [u8]) -> isize {
+    return extract_factor(sha, cid, pin % MAXPIN, PBLEN, token);
+}
+
+/* Extract factor from TOKEN for identity CID */
+#[allow(non_snake_case)]
+pub fn extract_factor(
+    sha: usize,
+    cid: &[u8],
+    factor: i32,
+    facbits: i32,
+    token: &mut [u8],
+) -> isize {
+    let mut P = ECP::frombytes(&token);
+    const RM: usize = big::MODBYTES as usize;
+    let mut h: [u8; RM] = [0; RM];
+    if P.is_infinity() {
+        return INVALID_POINT;
+    }
+    hashit(sha, 0, cid, &mut h);
+    let mut R = ECP::mapit(&h);
+
+    R = R.pinmul(factor, facbits);
+    P.sub(&mut R);
+
+    P.tobytes(token, false);
+
+    return 0;
+}
+
+/* Restore factor to TOKEN for identity CID */
+#[allow(non_snake_case)]
+pub fn restore_factor(
+    sha: usize,
+    cid: &[u8],
+    factor: i32,
+    facbits: i32,
+    token: &mut [u8],
+) -> isize {
+    let mut P = ECP::frombytes(&token);
+    const RM: usize = big::MODBYTES as usize;
+    let mut h: [u8; RM] = [0; RM];
+    if P.is_infinity() {
+        return INVALID_POINT;
+    }
+    hashit(sha, 0, cid, &mut h);
+    let mut R = ECP::mapit(&h);
+
+    R = R.pinmul(factor, facbits);
+    P.add(&mut R);
+
+    P.tobytes(token, false);
+
+    return 0;
+}
+
+/* Functions to support M-Pin Full */
+#[allow(non_snake_case)]
+pub fn precompute(token: &[u8], cid: &[u8], g1: &mut [u8], g2: &mut [u8]) -> isize {
+    let T = ECP::frombytes(&token);
+    if T.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let P = ECP::mapit(&cid);
+
+    let Q = ECP2::generator();
+
+    let mut g = pair::ate(&Q, &T);
+    g = pair::fexp(&g);
+    g.tobytes(g1);
+
+    g = pair::ate(&Q, &P);
+    g = pair::fexp(&g);
+    g.tobytes(g2);
+
+    return 0;
+}
+
+/* Time Permit CTT=S*(date|H(CID)) where S is master secret */
+#[allow(non_snake_case)]
+pub fn get_client_permit(sha: usize, date: usize, s: &[u8], cid: &[u8], ctt: &mut [u8]) -> isize {
+    const RM: usize = big::MODBYTES as usize;
+    let mut h: [u8; RM] = [0; RM];
+    hashit(sha, date, cid, &mut h);
+    let mut P = ECP::mapit(&h);
+
+    let mut sc = BIG::frombytes(s);
+    pair::g1mul(&mut P, &mut sc).tobytes(ctt, false);
+    return 0;
+}
+
+/* Implement step 1 on client side of MPin protocol */
+#[allow(non_snake_case)]
+pub fn client_1(
+    sha: usize,
+    date: usize,
+    client_id: &[u8],
+    rng: Option<&mut RAND>,
+    x: &mut [u8],
+    pin: usize,
+    token: &[u8],
+    sec: &mut [u8],
+    xid: Option<&mut [u8]>,
+    xcid: Option<&mut [u8]>,
+    permit: Option<&[u8]>,
+) -> isize {
+    let r = BIG::new_ints(&rom::CURVE_ORDER);
+
+    let mut sx: BIG;
+
+    if let Some(rd) = rng {
+        sx = BIG::randomnum(&r, rd);
+        sx.tobytes(x);
+    } else {
+        sx = BIG::frombytes(x);
+    }
+
+    const RM: usize = big::MODBYTES as usize;
+    let mut h: [u8; RM] = [0; RM];
+
+    hashit(sha, 0, &client_id, &mut h);
+    let mut P = ECP::mapit(&h);
+
+    let mut T = ECP::frombytes(&token);
+    if T.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut W = P.pinmul((pin as i32) % MAXPIN, PBLEN);
+    T.add(&mut W);
+    if date != 0 {
+        if let Some(rpermit) = permit {
+            W = ECP::frombytes(&rpermit);
+        }
+        if W.is_infinity() {
+            return INVALID_POINT;
+        }
+        T.add(&mut W);
+        let mut h2: [u8; RM] = [0; RM];
+        hashit(sha, date, &h, &mut h2);
+        W = ECP::mapit(&h2);
+        if let Some(mut rxid) = xid {
+            P = pair::g1mul(&mut P, &mut sx);
+            P.tobytes(&mut rxid, false);
+            W = pair::g1mul(&mut W, &mut sx);
+            P.add(&mut W);
+        } else {
+            P.add(&mut W);
+            P = pair::g1mul(&mut P, &mut sx);
+        }
+        if let Some(mut rxcid) = xcid {
+            P.tobytes(&mut rxcid, false)
+        }
+    } else {
+        if let Some(mut rxid) = xid {
+            P = pair::g1mul(&mut P, &mut sx);
+            P.tobytes(&mut rxid, false);
+        }
+    }
+
+    T.tobytes(sec, false);
+    return 0;
+}
+
+/* Outputs H(CID) and H(T|H(CID)) for time permits. If no time permits set HID=HTID */
+#[allow(non_snake_case)]
+pub fn server_1(sha: usize, date: usize, cid: &[u8], hid: &mut [u8], htid: Option<&mut [u8]>) {
+    const RM: usize = big::MODBYTES as usize;
+    let mut h: [u8; RM] = [0; RM];
+
+    hashit(sha, 0, cid, &mut h);
+
+    let mut P = ECP::mapit(&h);
+
+    P.tobytes(hid, false);
+    if date != 0 {
+        let mut h2: [u8; RM] = [0; RM];
+        hashit(sha, date, &h, &mut h2);
+        let mut R = ECP::mapit(&h2);
+        P.add(&mut R);
+        if let Some(rhtid) = htid {
+            P.tobytes(rhtid, false);
+        }
+    }
+}
+
+/* Implement step 2 on client side of MPin protocol */
+#[allow(non_snake_case)]
+pub fn client_2(x: &[u8], y: &[u8], sec: &mut [u8]) -> isize {
+    let mut r = BIG::new_ints(&rom::CURVE_ORDER);
+    let mut P = ECP::frombytes(sec);
+    if P.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut px = BIG::frombytes(x);
+    let py = BIG::frombytes(y);
+    px.add(&py);
+    px.rmod(&mut r);
+
+    P = pair::g1mul(&mut P, &mut px);
+    P.neg();
+    P.tobytes(sec, false);
+
+    return 0;
+}
+
+/* return time since epoch */
+pub fn get_time() -> usize {
+    return (SystemTime::now()
+        .duration_since(UNIX_EPOCH)
+        .unwrap()
+        .as_secs()) as usize;
+}
+
+/* Generate Y = H(epoch, xCID/xID) */
+pub fn get_y(sha: usize, timevalue: usize, xcid: &[u8], y: &mut [u8]) {
+    const RM: usize = big::MODBYTES as usize;
+    let mut h: [u8; RM] = [0; RM];
+
+    hashit(sha, timevalue, xcid, &mut h);
+
+    let mut sy = BIG::frombytes(&h);
+    let mut q = BIG::new_ints(&rom::CURVE_ORDER);
+    sy.rmod(&mut q);
+    sy.tobytes(y);
+}
+
+/* Implement step 2 of MPin protocol on server side */
+#[allow(non_snake_case)]
+pub fn server_2(
+    date: usize,
+    hid: &[u8],
+    htid: Option<&[u8]>,
+    y: &[u8],
+    sst: &[u8],
+    xid: Option<&[u8]>,
+    xcid: Option<&[u8]>,
+    msec: &[u8],
+    e: Option<&mut [u8]>,
+    f: Option<&mut [u8]>,
+) -> isize {
+    let Q = ECP2::generator();
+
+    let sQ = ECP2::frombytes(&sst);
+    if sQ.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut R: ECP;
+    if date != 0 {
+        if let Some(rxcid) = xcid {
+            R = ECP::frombytes(&rxcid);
+        } else {
+            return BAD_PARAMS;
+        }
+    } else {
+        if let Some(rxid) = xid {
+            R = ECP::frombytes(&rxid)
+        } else {
+            return BAD_PARAMS;
+        }
+    }
+    if R.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut sy = BIG::frombytes(&y);
+    let mut P: ECP;
+    if date != 0 {
+        if let Some(rhtid) = htid {
+            P = ECP::frombytes(&rhtid)
+        } else {
+            return BAD_PARAMS;
+        }
+    } else {
+        P = ECP::frombytes(&hid);
+    }
+
+    if P.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    P = pair::g1mul(&mut P, &mut sy);
+    P.add(&mut R);
+    R = ECP::frombytes(&msec);
+    if R.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut g: FP12;
+
+    g = pair::ate2(&Q, &R, &sQ, &P);
+    g = pair::fexp(&g);
+
+    if !g.isunity() {
+        if let Some(rxid) = xid {
+            if let Some(re) = e {
+                if let Some(rf) = f {
+                    g.tobytes(re);
+                    if date != 0 {
+                        P = ECP::frombytes(&hid);
+                        if P.is_infinity() {
+                            return INVALID_POINT;
+                        }
+                        R = ECP::frombytes(&rxid);
+                        if R.is_infinity() {
+                            return INVALID_POINT;
+                        }
+                        P = pair::g1mul(&mut P, &mut sy);
+                        P.add(&mut R); //P.affine();
+                    }
+                    g = pair::ate(&Q, &P);
+                    g = pair::fexp(&g);
+                    g.tobytes(rf);
+                }
+            }
+        }
+
+        return BAD_PIN;
+    }
+
+    return 0;
+}
+
+/* Pollards kangaroos used to return PIN error */
+pub fn kangaroo(e: &[u8], f: &[u8]) -> isize {
+    let mut ge = FP12::frombytes(e);
+    let mut gf = FP12::frombytes(f);
+    let mut distance: [isize; TS] = [0; TS];
+    let mut t = FP12::new_copy(&gf);
+
+    let mut table: [FP12; TS] = [FP12::new(); TS];
+    let mut s: isize = 1;
+    for m in 0..TS {
+        distance[m] = s;
+        table[m] = FP12::new_copy(&t);
+        s *= 2;
+        t.usqr();
+    }
+    t.one();
+    let mut dn: isize = 0;
+    let mut i: usize;
+    for _ in 0..TRAP {
+        i = (t.geta().geta().geta().lastbits(20) % (TS as isize)) as usize;
+        t.mul(&mut table[i]);
+        dn += distance[i];
+    }
+    gf.copy(&t);
+    gf.conj();
+    let mut steps: usize = 0;
+    let mut dm: isize = 0;
+    let mut res: isize = 0;
+    while dm - dn < MAXPIN as isize {
+        steps += 1;
+        if steps > 4 * TRAP {
+            break;
+        }
+        i = (ge.geta().geta().geta().lastbits(20) % (TS as isize)) as usize;
+        ge.mul(&mut table[i]);
+        dm += distance[i];
+        if ge.equals(&mut t) {
+            res = dm - dn;
+            break;
+        }
+        if ge.equals(&mut gf) {
+            res = dn - dm;
+            break;
+        }
+    }
+    if steps > 4 * TRAP || dm - dn >= MAXPIN as isize {
+        res = 0
+    } // Trap Failed  - probable invalid token
+    return res;
+}
+
+/* Hash the M-Pin transcript - new */
+
+pub fn hash_all(
+    sha: usize,
+    hid: &[u8],
+    xid: &[u8],
+    xcid: Option<&[u8]>,
+    sec: &[u8],
+    y: &[u8],
+    r: &[u8],
+    w: &[u8],
+    h: &mut [u8],
+) -> bool {
+    let mut tlen: usize = 0;
+    const RM: usize = big::MODBYTES as usize;
+    let mut t: [u8; 10 * RM + 4] = [0; 10 * RM + 4];
+
+    for i in 0..hid.len() {
+        t[i] = hid[i]
+    }
+    tlen += hid.len();
+
+    if let Some(rxcid) = xcid {
+        for i in 0..rxcid.len() {
+            t[i + tlen] = rxcid[i]
+        }
+        tlen += rxcid.len();
+    } else {
+        for i in 0..xid.len() {
+            t[i + tlen] = xid[i]
+        }
+        tlen += xid.len();
+    }
+
+    for i in 0..sec.len() {
+        t[i + tlen] = sec[i]
+    }
+    tlen += sec.len();
+    for i in 0..y.len() {
+        t[i + tlen] = y[i]
+    }
+    tlen += y.len();
+    for i in 0..r.len() {
+        t[i + tlen] = r[i]
+    }
+    tlen += r.len();
+    for i in 0..w.len() {
+        t[i + tlen] = w[i]
+    }
+    tlen += w.len();
+    if tlen != 10 * RM + 4 {
+        return false;
+    }
+
+    return hashit(sha, 0, &t, h);
+}
+
+/* calculate common key on client side */
+/* wCID = w.(A+AT) */
+#[allow(non_snake_case)]
+pub fn client_key(
+    sha: usize,
+    g1: &[u8],
+    g2: &[u8],
+    pin: usize,
+    r: &[u8],
+    x: &[u8],
+    h: &[u8],
+    wcid: &[u8],
+    ck: &mut [u8],
+) -> isize {
+    let mut g1 = FP12::frombytes(&g1);
+    let mut g2 = FP12::frombytes(&g2);
+    let mut z = BIG::frombytes(&r);
+    let mut x = BIG::frombytes(&x);
+    let h = BIG::frombytes(&h);
+
+    let mut W = ECP::frombytes(&wcid);
+    if W.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    W = pair::g1mul(&mut W, &mut x);
+
+    let mut r = BIG::new_ints(&rom::CURVE_ORDER);
+
+    z.add(&h); //new
+    z.rmod(&mut r);
+
+    g2.pinpow(pin as i32, PBLEN);
+    g1.mul(&mut g2);
+
+    let mut c = g1.compow(&z, &mut r);
+
+    hash(sha, &mut c, &mut W, ck);
+
+    return 0;
+}
+
+/* calculate common key on server side */
+/* Z=r.A - no time permits involved */
+#[allow(non_snake_case)]
+pub fn server_key(
+    sha: usize,
+    z: &[u8],
+    sst: &[u8],
+    w: &[u8],
+    h: &[u8],
+    hid: &[u8],
+    xid: &[u8],
+    xcid: Option<&[u8]>,
+    sk: &mut [u8],
+) -> isize {
+    let sQ = ECP2::frombytes(&sst);
+    if sQ.is_infinity() {
+        return INVALID_POINT;
+    }
+    let mut R = ECP::frombytes(&z);
+    if R.is_infinity() {
+        return INVALID_POINT;
+    }
+    let mut A = ECP::frombytes(&hid);
+    if A.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut U = ECP::new();
+    if let Some(rxcid) = xcid {
+        U.copy(&ECP::frombytes(&rxcid));
+    } else {
+        U.copy(&ECP::frombytes(&xid));
+    }
+
+    if U.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut w = BIG::frombytes(&w);
+    let mut h = BIG::frombytes(&h);
+    A = pair::g1mul(&mut A, &mut h); // new
+    R.add(&mut A);
+
+    U = pair::g1mul(&mut U, &mut w);
+    let mut g = pair::ate(&sQ, &R);
+    g = pair::fexp(&g);
+
+    let mut c = g.trace();
+
+    hash(sha, &mut c, &mut U, sk);
+
+    return 0;
+}
diff --git a/src/mpin192.rs b/src/mpin192.rs
new file mode 100644
index 0000000..276a560
--- /dev/null
+++ b/src/mpin192.rs
@@ -0,0 +1,960 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use std::time::SystemTime;
+use std::time::UNIX_EPOCH;
+
+use super::ecp;
+use super::ecp::ECP;
+use super::ecp4::ECP4;
+use super::fp8::FP8;
+use super::fp24::FP24;
+use super::big::BIG;
+use super::pair192;
+use super::big;
+use super::rom;
+
+use rand::RAND;
+use hash256::HASH256;
+use hash384::HASH384;
+use hash512::HASH512;
+
+
+/* MPIN API Functions */
+
+/* Configure mode of operation */
+
+pub const EFS: usize = big::MODBYTES as usize;
+pub const EGS: usize = big::MODBYTES as usize;
+pub const BAD_PARAMS: isize = -11;
+pub const INVALID_POINT: isize = -14;
+pub const WRONG_ORDER: isize = -18;
+pub const BAD_PIN: isize = -19;
+pub const SHA256: usize = 32;
+pub const SHA384: usize = 48;
+pub const SHA512: usize = 64;
+
+/* Configure your PIN here */
+
+pub const MAXPIN: i32 = 10000; /* PIN less than this */
+pub const PBLEN: i32 = 14; /* Number of bits in PIN */
+pub const TS: usize = 10; /* 10 for 4 digit PIN, 14 for 6-digit PIN - 2^TS/TS approx = sqrt(MAXPIN) */
+pub const TRAP: usize = 200; /* 200 for 4 digit PIN, 2000 for 6-digit PIN  - approx 2*sqrt(MAXPIN) */
+
+#[allow(non_snake_case)]
+fn hash(sha: usize, c: &mut FP8, U: &mut ECP, r: &mut [u8]) -> bool {
+    let mut w: [u8; EFS] = [0; EFS];
+    let mut t: [u8; 10 * EFS] = [0; 10 * EFS];
+
+    c.geta().geta().geta().tobytes(&mut w);
+    for i in 0..EFS {
+        t[i] = w[i]
+    }
+    c.geta().geta().getb().tobytes(&mut w);
+    for i in EFS..2 * EFS {
+        t[i] = w[i - EFS]
+    }
+    c.geta().getb().geta().tobytes(&mut w);
+    for i in 2 * EFS..3 * EFS {
+        t[i] = w[i - 2 * EFS]
+    }
+    c.geta().getb().getb().tobytes(&mut w);
+    for i in 3 * EFS..4 * EFS {
+        t[i] = w[i - 3 * EFS]
+    }
+    c.getb().geta().geta().tobytes(&mut w);
+    for i in 4 * EFS..5 * EFS {
+        t[i] = w[i - 4 * EFS]
+    }
+    c.getb().geta().getb().tobytes(&mut w);
+    for i in 5 * EFS..6 * EFS {
+        t[i] = w[i - 5 * EFS]
+    }
+    c.getb().getb().geta().tobytes(&mut w);
+    for i in 6 * EFS..7 * EFS {
+        t[i] = w[i - 6 * EFS]
+    }
+    c.getb().getb().getb().tobytes(&mut w);
+    for i in 7 * EFS..8 * EFS {
+        t[i] = w[i - 7 * EFS]
+    }
+
+    U.getx().tobytes(&mut w);
+    for i in 8 * EFS..9 * EFS {
+        t[i] = w[i - 8 * EFS]
+    }
+    U.gety().tobytes(&mut w);
+    for i in 9 * EFS..10 * EFS {
+        t[i] = w[i - 9 * EFS]
+    }
+
+    if sha == SHA256 {
+        let mut h = HASH256::new();
+        h.process_array(&t);
+        let sh = h.hash();
+        for i in 0..ecp::AESKEY {
+            r[i] = sh[i]
+        }
+        return true;
+    }
+    if sha == SHA384 {
+        let mut h = HASH384::new();
+        h.process_array(&t);
+        let sh = h.hash();
+        for i in 0..ecp::AESKEY {
+            r[i] = sh[i]
+        }
+        return true;
+    }
+    if sha == SHA512 {
+        let mut h = HASH512::new();
+        h.process_array(&t);
+        let sh = h.hash();
+        for i in 0..ecp::AESKEY {
+            r[i] = sh[i]
+        }
+        return true;
+    }
+    return false;
+}
+
+/* Hash number (optional) and string to point on curve */
+
+fn hashit(sha: usize, n: usize, id: &[u8], w: &mut [u8]) -> bool {
+    let mut r: [u8; 64] = [0; 64];
+    let mut didit = false;
+    if sha == SHA256 {
+        let mut h = HASH256::new();
+        if n > 0 {
+            h.process_num(n as i32)
+        }
+        h.process_array(id);
+        let hs = h.hash();
+        for i in 0..sha {
+            r[i] = hs[i];
+        }
+        didit = true;
+    }
+    if sha == SHA384 {
+        let mut h = HASH384::new();
+        if n > 0 {
+            h.process_num(n as i32)
+        }
+        h.process_array(id);
+        let hs = h.hash();
+        for i in 0..sha {
+            r[i] = hs[i];
+        }
+        didit = true;
+    }
+    if sha == SHA512 {
+        let mut h = HASH512::new();
+        if n > 0 {
+            h.process_num(n as i32)
+        }
+        h.process_array(id);
+        let hs = h.hash();
+        for i in 0..sha {
+            r[i] = hs[i];
+        }
+        didit = true;
+    }
+    if !didit {
+        return false;
+    }
+
+    let rm = big::MODBYTES as usize;
+
+    if sha > rm {
+        for i in 0..rm {
+            w[i] = r[i]
+        }
+    } else {
+        for i in 0..sha {
+            w[i + rm - sha] = r[i]
+        }
+        for i in 0..(rm - sha) {
+            w[i] = 0
+        }
+    }
+
+    return true;
+}
+
+/* return time in slots since epoch */
+pub fn today() -> usize {
+    return (SystemTime::now()
+        .duration_since(UNIX_EPOCH)
+        .unwrap()
+        .as_secs()
+        / (60 * 1440)) as usize;
+}
+
+/* these next two functions help to implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* maps a random u to a point on the curve */
+#[allow(non_snake_case)]
+fn emap(u: &BIG, cb: isize) -> ECP {
+    let mut P: ECP;
+    let mut x = BIG::new_copy(u);
+    let mut p = BIG::new_ints(&rom::MODULUS);
+    x.rmod(&mut p);
+    loop {
+        P = ECP::new_bigint(&x, cb);
+        if !P.is_infinity() {
+            break;
+        }
+        x.inc(1);
+        x.norm();
+    }
+    return P;
+}
+
+/* returns u derived from P. Random value in range 1 to return value should then be added to u */
+#[allow(non_snake_case)]
+fn unmap(u: &mut BIG, P: &mut ECP) -> isize {
+    let s = P.gets();
+    let mut R: ECP;
+    let mut r = 0;
+    let x = P.getx();
+    u.copy(&x);
+    loop {
+        u.dec(1);
+        u.norm();
+        r += 1;
+        R = ECP::new_bigint(u, s);
+        if !R.is_infinity() {
+            break;
+        }
+    }
+    return r as isize;
+}
+
+pub fn hash_id(sha: usize, id: &[u8], w: &mut [u8]) -> bool {
+    return hashit(sha, 0, id, w);
+}
+
+/* these next two functions implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* Elliptic curve point E in format (0x04,x,y} is converted to form {0x0-,u,v} */
+/* Note that u and v are indistinguisible from random strings */
+#[allow(non_snake_case)]
+pub fn encoding(rng: &mut RAND, e: &mut [u8]) -> isize {
+    let mut t: [u8; EFS] = [0; EFS];
+
+    for i in 0..EFS {
+        t[i] = e[i + 1]
+    }
+    let mut u = BIG::frombytes(&t);
+    for i in 0..EFS {
+        t[i] = e[i + EFS + 1]
+    }
+    let mut v = BIG::frombytes(&t);
+
+    let mut P = ECP::new_bigs(&u, &v);
+    if P.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let p = BIG::new_ints(&rom::MODULUS);
+    u = BIG::randomnum(&p, rng);
+
+    let mut su = rng.getbyte() as isize;
+    su %= 2;
+
+    let mut W = emap(&mut u, su);
+    P.sub(&mut W);
+    let sv = P.gets();
+    let rn = unmap(&mut v, &mut P);
+    let mut m = rng.getbyte() as isize;
+    m %= rn;
+    v.inc(m + 1);
+    e[0] = (su + 2 * sv) as u8;
+    u.tobytes(&mut t);
+    for i in 0..EFS {
+        e[i + 1] = t[i]
+    }
+    v.tobytes(&mut t);
+    for i in 0..EFS {
+        e[i + EFS + 1] = t[i]
+    }
+
+    return 0;
+}
+
+#[allow(non_snake_case)]
+pub fn decoding(d: &mut [u8]) -> isize {
+    let mut t: [u8; EFS] = [0; EFS];
+
+    if (d[0] & 0x04) != 0 {
+        return INVALID_POINT;
+    }
+
+    for i in 0..EFS {
+        t[i] = d[i + 1]
+    }
+    let mut u = BIG::frombytes(&t);
+    for i in 0..EFS {
+        t[i] = d[i + EFS + 1]
+    }
+    let mut v = BIG::frombytes(&t);
+
+    let su = (d[0] & 1) as isize;
+    let sv = ((d[0] >> 1) & 1) as isize;
+    let mut W = emap(&mut u, su);
+    let mut P = emap(&mut v, sv);
+    P.add(&mut W);
+    u = P.getx();
+    v = P.gety();
+    d[0] = 0x04;
+    u.tobytes(&mut t);
+    for i in 0..EFS {
+        d[i + 1] = t[i]
+    }
+    v.tobytes(&mut t);
+    for i in 0..EFS {
+        d[i + EFS + 1] = t[i]
+    }
+
+    return 0;
+}
+
+/* R=R1+R2 in group G1 */
+#[allow(non_snake_case)]
+pub fn recombine_g1(r1: &[u8], r2: &[u8], r: &mut [u8]) -> isize {
+    let mut P = ECP::frombytes(&r1);
+    let mut Q = ECP::frombytes(&r2);
+
+    if P.is_infinity() || Q.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    P.add(&mut Q);
+
+    P.tobytes(r, false);
+    return 0;
+}
+
+/* W=W1+W2 in group G2 */
+#[allow(non_snake_case)]
+pub fn recombine_g2(w1: &[u8], w2: &[u8], w: &mut [u8]) -> isize {
+    let mut P = ECP4::frombytes(&w1);
+    let mut Q = ECP4::frombytes(&w2);
+
+    if P.is_infinity() || Q.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    P.add(&mut Q);
+
+    P.tobytes(w);
+    return 0;
+}
+
+/* create random secret S */
+pub fn random_generate(rng: &mut RAND, s: &mut [u8]) -> isize {
+    let r = BIG::new_ints(&rom::CURVE_ORDER);
+    let mut sc = BIG::randomnum(&r, rng);
+    sc.tobytes(s);
+    return 0;
+}
+
+/* Extract Server Secret SST=S*Q where Q is fixed generator in G2 and S is master secret */
+#[allow(non_snake_case)]
+pub fn get_server_secret(s: &[u8], sst: &mut [u8]) -> isize {
+    let mut Q = ECP4::generator();
+    let mut sc = BIG::frombytes(s);
+    Q = pair192::g2mul(&mut Q, &mut sc);
+    Q.tobytes(sst);
+    return 0;
+}
+
+/*
+ W=x*H(G);
+ if RNG == NULL then X is passed in
+ if RNG != NULL the X is passed out
+ if type=0 W=x*G where G is point on the curve, else W=x*M(G), where M(G) is mapping of octet G to point on the curve
+*/
+#[allow(non_snake_case)]
+pub fn get_g1_multiple(
+    rng: Option<&mut RAND>,
+    typ: usize,
+    x: &mut [u8],
+    g: &[u8],
+    w: &mut [u8],
+) -> isize {
+    let mut sx: BIG;
+    let r = BIG::new_ints(&rom::CURVE_ORDER);
+
+    if let Some(rd) = rng {
+        sx = BIG::randomnum(&r, rd);
+        sx.tobytes(x);
+    } else {
+        sx = BIG::frombytes(x);
+    }
+    let mut P: ECP;
+
+    if typ == 0 {
+        P = ECP::frombytes(g);
+        if P.is_infinity() {
+            return INVALID_POINT;
+        }
+    } else {
+        P = ECP::mapit(g)
+    }
+
+    pair192::g1mul(&mut P, &mut sx).tobytes(w, false);
+    return 0;
+}
+
+/* Client secret CST=S*H(CID) where CID is client ID and S is master secret */
+/* CID is hashed externally */
+pub fn get_client_secret(s: &mut [u8], cid: &[u8], cst: &mut [u8]) -> isize {
+    return get_g1_multiple(None, 1, s, cid, cst);
+}
+
+/* Extract PIN from TOKEN for identity CID */
+#[allow(non_snake_case)]
+pub fn extract_pin(sha: usize, cid: &[u8], pin: i32, token: &mut [u8]) -> isize {
+    return extract_factor(sha, cid, pin % MAXPIN, PBLEN, token);
+}
+
+/* Extract factor from TOKEN for identity CID */
+#[allow(non_snake_case)]
+pub fn extract_factor(
+    sha: usize,
+    cid: &[u8],
+    factor: i32,
+    facbits: i32,
+    token: &mut [u8],
+) -> isize {
+    let mut P = ECP::frombytes(&token);
+    const RM: usize = big::MODBYTES as usize;
+    let mut h: [u8; RM] = [0; RM];
+    if P.is_infinity() {
+        return INVALID_POINT;
+    }
+    hashit(sha, 0, cid, &mut h);
+    let mut R = ECP::mapit(&h);
+
+    R = R.pinmul(factor, facbits);
+    P.sub(&mut R);
+
+    P.tobytes(token, false);
+
+    return 0;
+}
+
+/* Restore factor to TOKEN for identity CID */
+#[allow(non_snake_case)]
+pub fn restore_factor(
+    sha: usize,
+    cid: &[u8],
+    factor: i32,
+    facbits: i32,
+    token: &mut [u8],
+) -> isize {
+    let mut P = ECP::frombytes(&token);
+    const RM: usize = big::MODBYTES as usize;
+    let mut h: [u8; RM] = [0; RM];
+    if P.is_infinity() {
+        return INVALID_POINT;
+    }
+    hashit(sha, 0, cid, &mut h);
+    let mut R = ECP::mapit(&h);
+
+    R = R.pinmul(factor, facbits);
+    P.add(&mut R);
+
+    P.tobytes(token, false);
+
+    return 0;
+}
+
+/* Functions to support M-Pin Full */
+#[allow(non_snake_case)]
+pub fn precompute(token: &[u8], cid: &[u8], g1: &mut [u8], g2: &mut [u8]) -> isize {
+    let T = ECP::frombytes(&token);
+    if T.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let P = ECP::mapit(&cid);
+
+    let Q = ECP4::generator();
+
+    let mut g = pair192::ate(&Q, &T);
+    g = pair192::fexp(&g);
+    g.tobytes(g1);
+
+    g = pair192::ate(&Q, &P);
+    g = pair192::fexp(&g);
+    g.tobytes(g2);
+
+    return 0;
+}
+
+/* Time Permit CTT=S*(date|H(CID)) where S is master secret */
+#[allow(non_snake_case)]
+pub fn get_client_permit(sha: usize, date: usize, s: &[u8], cid: &[u8], ctt: &mut [u8]) -> isize {
+    const RM: usize = big::MODBYTES as usize;
+    let mut h: [u8; RM] = [0; RM];
+    hashit(sha, date, cid, &mut h);
+    let mut P = ECP::mapit(&h);
+
+    let mut sc = BIG::frombytes(s);
+    pair192::g1mul(&mut P, &mut sc).tobytes(ctt, false);
+    return 0;
+}
+
+/* Implement step 1 on client side of MPin protocol */
+#[allow(non_snake_case)]
+pub fn client_1(
+    sha: usize,
+    date: usize,
+    client_id: &[u8],
+    rng: Option<&mut RAND>,
+    x: &mut [u8],
+    pin: usize,
+    token: &[u8],
+    sec: &mut [u8],
+    xid: Option<&mut [u8]>,
+    xcid: Option<&mut [u8]>,
+    permit: Option<&[u8]>,
+) -> isize {
+    let r = BIG::new_ints(&rom::CURVE_ORDER);
+
+    let mut sx: BIG;
+
+    if let Some(rd) = rng {
+        sx = BIG::randomnum(&r, rd);
+        sx.tobytes(x);
+    } else {
+        sx = BIG::frombytes(x);
+    }
+
+    const RM: usize = big::MODBYTES as usize;
+    let mut h: [u8; RM] = [0; RM];
+
+    hashit(sha, 0, &client_id, &mut h);
+    let mut P = ECP::mapit(&h);
+
+    let mut T = ECP::frombytes(&token);
+    if T.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut W = P.pinmul((pin as i32) % MAXPIN, PBLEN);
+    T.add(&mut W);
+    if date != 0 {
+        if let Some(rpermit) = permit {
+            W = ECP::frombytes(&rpermit);
+        }
+        if W.is_infinity() {
+            return INVALID_POINT;
+        }
+        T.add(&mut W);
+        let mut h2: [u8; RM] = [0; RM];
+        hashit(sha, date, &h, &mut h2);
+        W = ECP::mapit(&h2);
+        if let Some(mut rxid) = xid {
+            P = pair192::g1mul(&mut P, &mut sx);
+            P.tobytes(&mut rxid, false);
+            W = pair192::g1mul(&mut W, &mut sx);
+            P.add(&mut W);
+        } else {
+            P.add(&mut W);
+            P = pair192::g1mul(&mut P, &mut sx);
+        }
+        if let Some(mut rxcid) = xcid {
+            P.tobytes(&mut rxcid, false)
+        }
+    } else {
+        if let Some(mut rxid) = xid {
+            P = pair192::g1mul(&mut P, &mut sx);
+            P.tobytes(&mut rxid, false);
+        }
+    }
+
+    T.tobytes(sec, false);
+    return 0;
+}
+
+/* Outputs H(CID) and H(T|H(CID)) for time permits. If no time permits set HID=HTID */
+#[allow(non_snake_case)]
+pub fn server_1(sha: usize, date: usize, cid: &[u8], hid: &mut [u8], htid: Option<&mut [u8]>) {
+    const RM: usize = big::MODBYTES as usize;
+    let mut h: [u8; RM] = [0; RM];
+
+    hashit(sha, 0, cid, &mut h);
+
+    let mut P = ECP::mapit(&h);
+
+    P.tobytes(hid, false);
+    if date != 0 {
+        let mut h2: [u8; RM] = [0; RM];
+        hashit(sha, date, &h, &mut h2);
+        let mut R = ECP::mapit(&h2);
+        P.add(&mut R);
+        if let Some(rhtid) = htid {
+            P.tobytes(rhtid, false);
+        }
+    }
+}
+
+/* Implement step 2 on client side of MPin protocol */
+#[allow(non_snake_case)]
+pub fn client_2(x: &[u8], y: &[u8], sec: &mut [u8]) -> isize {
+    let mut r = BIG::new_ints(&rom::CURVE_ORDER);
+    let mut P = ECP::frombytes(sec);
+    if P.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut px = BIG::frombytes(x);
+    let py = BIG::frombytes(y);
+    px.add(&py);
+    px.rmod(&mut r);
+
+    P = pair192::g1mul(&mut P, &mut px);
+    P.neg();
+    P.tobytes(sec, false);
+
+    return 0;
+}
+
+/* return time since epoch */
+pub fn get_time() -> usize {
+    return (SystemTime::now()
+        .duration_since(UNIX_EPOCH)
+        .unwrap()
+        .as_secs()) as usize;
+}
+
+/* Generate Y = H(epoch, xCID/xID) */
+pub fn get_y(sha: usize, timevalue: usize, xcid: &[u8], y: &mut [u8]) {
+    const RM: usize = big::MODBYTES as usize;
+    let mut h: [u8; RM] = [0; RM];
+
+    hashit(sha, timevalue, xcid, &mut h);
+
+    let mut sy = BIG::frombytes(&h);
+    let mut q = BIG::new_ints(&rom::CURVE_ORDER);
+    sy.rmod(&mut q);
+    sy.tobytes(y);
+}
+
+/* Implement step 2 of MPin protocol on server side */
+#[allow(non_snake_case)]
+pub fn server_2(
+    date: usize,
+    hid: &[u8],
+    htid: Option<&[u8]>,
+    y: &[u8],
+    sst: &[u8],
+    xid: Option<&[u8]>,
+    xcid: Option<&[u8]>,
+    msec: &[u8],
+    e: Option<&mut [u8]>,
+    f: Option<&mut [u8]>,
+) -> isize {
+    let Q = ECP4::generator();
+
+    let sQ = ECP4::frombytes(&sst);
+    if sQ.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut R: ECP;
+    if date != 0 {
+        if let Some(rxcid) = xcid {
+            R = ECP::frombytes(&rxcid);
+        } else {
+            return BAD_PARAMS;
+        }
+    } else {
+        if let Some(rxid) = xid {
+            R = ECP::frombytes(&rxid)
+        } else {
+            return BAD_PARAMS;
+        }
+    }
+    if R.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut sy = BIG::frombytes(&y);
+    let mut P: ECP;
+    if date != 0 {
+        if let Some(rhtid) = htid {
+            P = ECP::frombytes(&rhtid)
+        } else {
+            return BAD_PARAMS;
+        }
+    } else {
+        P = ECP::frombytes(&hid);
+    }
+
+    if P.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    P = pair192::g1mul(&mut P, &mut sy);
+    P.add(&mut R);
+    R = ECP::frombytes(&msec);
+    if R.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut g: FP24;
+
+    g = pair192::ate2(&Q, &R, &sQ, &P);
+    g = pair192::fexp(&g);
+
+    if !g.isunity() {
+        if let Some(rxid) = xid {
+            if let Some(re) = e {
+                if let Some(rf) = f {
+                    g.tobytes(re);
+                    if date != 0 {
+                        P = ECP::frombytes(&hid);
+                        if P.is_infinity() {
+                            return INVALID_POINT;
+                        }
+                        R = ECP::frombytes(&rxid);
+                        if R.is_infinity() {
+                            return INVALID_POINT;
+                        }
+                        P = pair192::g1mul(&mut P, &mut sy);
+                        P.add(&mut R); //P.affine();
+                    }
+                    g = pair192::ate(&Q, &P);
+                    g = pair192::fexp(&g);
+                    g.tobytes(rf);
+                }
+            }
+        }
+
+        return BAD_PIN;
+    }
+
+    return 0;
+}
+
+/* Pollards kangaroos used to return PIN error */
+pub fn kangaroo(e: &[u8], f: &[u8]) -> isize {
+    let mut ge = FP24::frombytes(e);
+    let mut gf = FP24::frombytes(f);
+    let mut distance: [isize; TS] = [0; TS];
+    let mut t = FP24::new_copy(&gf);
+
+    let mut table: [FP24; TS] = [FP24::new(); TS];
+    let mut s: isize = 1;
+    for m in 0..TS {
+        distance[m] = s;
+        table[m] = FP24::new_copy(&t);
+        s *= 2;
+        t.usqr();
+    }
+    t.one();
+    let mut dn: isize = 0;
+    let mut i: usize;
+    for _ in 0..TRAP {
+        i = (t.geta().geta().geta().geta().lastbits(20) % (TS as isize)) as usize;
+        t.mul(&mut table[i]);
+        dn += distance[i];
+    }
+    gf.copy(&t);
+    gf.conj();
+    let mut steps: usize = 0;
+    let mut dm: isize = 0;
+    let mut res: isize = 0;
+    while dm - dn < MAXPIN as isize {
+        steps += 1;
+        if steps > 4 * TRAP {
+            break;
+        }
+        i = (ge.geta().geta().geta().geta().lastbits(20) % (TS as isize)) as usize;
+        ge.mul(&mut table[i]);
+        dm += distance[i];
+        if ge.equals(&mut t) {
+            res = dm - dn;
+            break;
+        }
+        if ge.equals(&mut gf) {
+            res = dn - dm;
+            break;
+        }
+    }
+    if steps > 4 * TRAP || dm - dn >= MAXPIN as isize {
+        res = 0
+    } // Trap Failed  - probable invalid token
+    return res;
+}
+
+/* Hash the M-Pin transcript - new */
+
+pub fn hash_all(
+    sha: usize,
+    hid: &[u8],
+    xid: &[u8],
+    xcid: Option<&[u8]>,
+    sec: &[u8],
+    y: &[u8],
+    r: &[u8],
+    w: &[u8],
+    h: &mut [u8],
+) -> bool {
+    let mut tlen: usize = 0;
+    const RM: usize = big::MODBYTES as usize;
+    let mut t: [u8; 10 * RM + 4] = [0; 10 * RM + 4];
+
+    for i in 0..hid.len() {
+        t[i] = hid[i]
+    }
+    tlen += hid.len();
+
+    if let Some(rxcid) = xcid {
+        for i in 0..rxcid.len() {
+            t[i + tlen] = rxcid[i]
+        }
+        tlen += rxcid.len();
+    } else {
+        for i in 0..xid.len() {
+            t[i + tlen] = xid[i]
+        }
+        tlen += xid.len();
+    }
+
+    for i in 0..sec.len() {
+        t[i + tlen] = sec[i]
+    }
+    tlen += sec.len();
+    for i in 0..y.len() {
+        t[i + tlen] = y[i]
+    }
+    tlen += y.len();
+    for i in 0..r.len() {
+        t[i + tlen] = r[i]
+    }
+    tlen += r.len();
+    for i in 0..w.len() {
+        t[i + tlen] = w[i]
+    }
+    tlen += w.len();
+    if tlen != 10 * RM + 4 {
+        return false;
+    }
+
+    return hashit(sha, 0, &t, h);
+}
+
+/* calculate common key on client side */
+/* wCID = w.(A+AT) */
+#[allow(non_snake_case)]
+pub fn client_key(
+    sha: usize,
+    g1: &[u8],
+    g2: &[u8],
+    pin: usize,
+    r: &[u8],
+    x: &[u8],
+    h: &[u8],
+    wcid: &[u8],
+    ck: &mut [u8],
+) -> isize {
+    let mut g1 = FP24::frombytes(&g1);
+    let mut g2 = FP24::frombytes(&g2);
+    let mut z = BIG::frombytes(&r);
+    let mut x = BIG::frombytes(&x);
+    let h = BIG::frombytes(&h);
+
+    let mut W = ECP::frombytes(&wcid);
+    if W.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    W = pair192::g1mul(&mut W, &mut x);
+
+    let mut r = BIG::new_ints(&rom::CURVE_ORDER);
+
+    z.add(&h); //new
+    z.rmod(&mut r);
+
+    g2.pinpow(pin as i32, PBLEN);
+    g1.mul(&mut g2);
+
+    let mut c = g1.compow(&z, &mut r);
+
+    hash(sha, &mut c, &mut W, ck);
+
+    return 0;
+}
+
+/* calculate common key on server side */
+/* Z=r.A - no time permits involved */
+#[allow(non_snake_case)]
+pub fn server_key(
+    sha: usize,
+    z: &[u8],
+    sst: &[u8],
+    w: &[u8],
+    h: &[u8],
+    hid: &[u8],
+    xid: &[u8],
+    xcid: Option<&[u8]>,
+    sk: &mut [u8],
+) -> isize {
+    let sQ = ECP4::frombytes(&sst);
+    if sQ.is_infinity() {
+        return INVALID_POINT;
+    }
+    let mut R = ECP::frombytes(&z);
+    if R.is_infinity() {
+        return INVALID_POINT;
+    }
+    let mut A = ECP::frombytes(&hid);
+    if A.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut U = ECP::new();
+    if let Some(rxcid) = xcid {
+        U.copy(&ECP::frombytes(&rxcid));
+    } else {
+        U.copy(&ECP::frombytes(&xid));
+    }
+
+    if U.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut w = BIG::frombytes(&w);
+    let mut h = BIG::frombytes(&h);
+    A = pair192::g1mul(&mut A, &mut h); // new
+    R.add(&mut A);
+
+    U = pair192::g1mul(&mut U, &mut w);
+    let mut g = pair192::ate(&sQ, &R);
+    g = pair192::fexp(&g);
+
+    let mut c = g.trace();
+
+    hash(sha, &mut c, &mut U, sk);
+
+    return 0;
+}
diff --git a/src/mpin256.rs b/src/mpin256.rs
new file mode 100644
index 0000000..b4928e9
--- /dev/null
+++ b/src/mpin256.rs
@@ -0,0 +1,993 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use std::time::SystemTime;
+use std::time::UNIX_EPOCH;
+
+use super::ecp;
+use super::ecp::ECP;
+use super::ecp8::ECP8;
+use super::fp16::FP16;
+use super::fp48::FP48;
+use super::big::BIG;
+use super::pair256;
+use super::big;
+use super::rom;
+
+use rand::RAND;
+use hash256::HASH256;
+use hash384::HASH384;
+use hash512::HASH512;
+
+/* MPIN API Functions */
+
+/* Configure mode of operation */
+
+pub const EFS: usize = big::MODBYTES as usize;
+pub const EGS: usize = big::MODBYTES as usize;
+pub const BAD_PARAMS: isize = -11;
+pub const INVALID_POINT: isize = -14;
+pub const WRONG_ORDER: isize = -18;
+pub const BAD_PIN: isize = -19;
+pub const SHA256: usize = 32;
+pub const SHA384: usize = 48;
+pub const SHA512: usize = 64;
+
+/* Configure your PIN here */
+
+pub const MAXPIN: i32 = 10000; /* PIN less than this */
+pub const PBLEN: i32 = 14; /* Number of bits in PIN */
+pub const TS: usize = 10; /* 10 for 4 digit PIN, 14 for 6-digit PIN - 2^TS/TS approx = sqrt(MAXPIN) */
+pub const TRAP: usize = 200; /* 200 for 4 digit PIN, 2000 for 6-digit PIN  - approx 2*sqrt(MAXPIN) */
+
+#[allow(non_snake_case)]
+fn hash(sha: usize, c: &mut FP16, U: &mut ECP, r: &mut [u8]) -> bool {
+    let mut w: [u8; EFS] = [0; EFS];
+    let mut t: [u8; 18 * EFS] = [0; 18 * EFS];
+
+    c.geta().geta().geta().geta().tobytes(&mut w);
+    for i in 0..EFS {
+        t[i] = w[i]
+    }
+    c.geta().geta().geta().getb().tobytes(&mut w);
+    for i in EFS..2 * EFS {
+        t[i] = w[i - EFS]
+    }
+    c.geta().geta().getb().geta().tobytes(&mut w);
+    for i in 2 * EFS..3 * EFS {
+        t[i] = w[i - 2 * EFS]
+    }
+    c.geta().geta().getb().getb().tobytes(&mut w);
+    for i in 3 * EFS..4 * EFS {
+        t[i] = w[i - 3 * EFS]
+    }
+    c.geta().getb().geta().geta().tobytes(&mut w);
+    for i in 4 * EFS..5 * EFS {
+        t[i] = w[i - 4 * EFS]
+    }
+    c.geta().getb().geta().getb().tobytes(&mut w);
+    for i in 5 * EFS..6 * EFS {
+        t[i] = w[i - 5 * EFS]
+    }
+    c.geta().getb().getb().geta().tobytes(&mut w);
+    for i in 6 * EFS..7 * EFS {
+        t[i] = w[i - 6 * EFS]
+    }
+    c.geta().getb().getb().getb().tobytes(&mut w);
+    for i in 7 * EFS..8 * EFS {
+        t[i] = w[i - 7 * EFS]
+    }
+
+    c.getb().geta().geta().geta().tobytes(&mut w);
+    for i in 8 * EFS..9 * EFS {
+        t[i] = w[i - 8 * EFS]
+    }
+    c.getb().geta().geta().getb().tobytes(&mut w);
+    for i in 9 * EFS..10 * EFS {
+        t[i] = w[i - 9 * EFS]
+    }
+    c.getb().geta().getb().geta().tobytes(&mut w);
+    for i in 10 * EFS..11 * EFS {
+        t[i] = w[i - 10 * EFS]
+    }
+    c.getb().geta().getb().getb().tobytes(&mut w);
+    for i in 11 * EFS..12 * EFS {
+        t[i] = w[i - 11 * EFS]
+    }
+    c.getb().getb().geta().geta().tobytes(&mut w);
+    for i in 12 * EFS..13 * EFS {
+        t[i] = w[i - 12 * EFS]
+    }
+    c.getb().getb().geta().getb().tobytes(&mut w);
+    for i in 13 * EFS..14 * EFS {
+        t[i] = w[i - 13 * EFS]
+    }
+    c.getb().getb().getb().geta().tobytes(&mut w);
+    for i in 14 * EFS..15 * EFS {
+        t[i] = w[i - 14 * EFS]
+    }
+    c.getb().getb().getb().getb().tobytes(&mut w);
+    for i in 15 * EFS..16 * EFS {
+        t[i] = w[i - 15 * EFS]
+    }
+
+    U.getx().tobytes(&mut w);
+    for i in 16 * EFS..17 * EFS {
+        t[i] = w[i - 16 * EFS]
+    }
+    U.gety().tobytes(&mut w);
+    for i in 17 * EFS..18 * EFS {
+        t[i] = w[i - 17 * EFS]
+    }
+
+    if sha == SHA256 {
+        let mut h = HASH256::new();
+        h.process_array(&t);
+        let sh = h.hash();
+        for i in 0..ecp::AESKEY {
+            r[i] = sh[i]
+        }
+        return true;
+    }
+    if sha == SHA384 {
+        let mut h = HASH384::new();
+        h.process_array(&t);
+        let sh = h.hash();
+        for i in 0..ecp::AESKEY {
+            r[i] = sh[i]
+        }
+        return true;
+    }
+    if sha == SHA512 {
+        let mut h = HASH512::new();
+        h.process_array(&t);
+        let sh = h.hash();
+        for i in 0..ecp::AESKEY {
+            r[i] = sh[i]
+        }
+        return true;
+    }
+    return false;
+}
+
+/* Hash number (optional) and string to point on curve */
+
+fn hashit(sha: usize, n: usize, id: &[u8], w: &mut [u8]) -> bool {
+    let mut r: [u8; 64] = [0; 64];
+    let mut didit = false;
+    if sha == SHA256 {
+        let mut h = HASH256::new();
+        if n > 0 {
+            h.process_num(n as i32)
+        }
+        h.process_array(id);
+        let hs = h.hash();
+        for i in 0..sha {
+            r[i] = hs[i];
+        }
+        didit = true;
+    }
+    if sha == SHA384 {
+        let mut h = HASH384::new();
+        if n > 0 {
+            h.process_num(n as i32)
+        }
+        h.process_array(id);
+        let hs = h.hash();
+        for i in 0..sha {
+            r[i] = hs[i];
+        }
+        didit = true;
+    }
+    if sha == SHA512 {
+        let mut h = HASH512::new();
+        if n > 0 {
+            h.process_num(n as i32)
+        }
+        h.process_array(id);
+        let hs = h.hash();
+        for i in 0..sha {
+            r[i] = hs[i];
+        }
+        didit = true;
+    }
+    if !didit {
+        return false;
+    }
+
+    let rm = big::MODBYTES as usize;
+
+    if sha > rm {
+        for i in 0..rm {
+            w[i] = r[i]
+        }
+    } else {
+        for i in 0..sha {
+            w[i + rm - sha] = r[i]
+        }
+        for i in 0..(rm - sha) {
+            w[i] = 0
+        }
+    }
+
+    return true;
+}
+
+/* return time in slots since epoch */
+pub fn today() -> usize {
+    return (SystemTime::now()
+        .duration_since(UNIX_EPOCH)
+        .unwrap()
+        .as_secs()
+        / (60 * 1440)) as usize;
+}
+
+/* these next two functions help to implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* maps a random u to a point on the curve */
+#[allow(non_snake_case)]
+fn emap(u: &BIG, cb: isize) -> ECP {
+    let mut P: ECP;
+    let mut x = BIG::new_copy(u);
+    let mut p = BIG::new_ints(&rom::MODULUS);
+    x.rmod(&mut p);
+    loop {
+        P = ECP::new_bigint(&x, cb);
+        if !P.is_infinity() {
+            break;
+        }
+        x.inc(1);
+        x.norm();
+    }
+    return P;
+}
+
+/* returns u derived from P. Random value in range 1 to return value should then be added to u */
+#[allow(non_snake_case)]
+fn unmap(u: &mut BIG, P: &mut ECP) -> isize {
+    let s = P.gets();
+    let mut R: ECP;
+    let mut r = 0;
+    let x = P.getx();
+    u.copy(&x);
+    loop {
+        u.dec(1);
+        u.norm();
+        r += 1;
+        R = ECP::new_bigint(u, s);
+        if !R.is_infinity() {
+            break;
+        }
+    }
+    return r as isize;
+}
+
+pub fn hash_id(sha: usize, id: &[u8], w: &mut [u8]) -> bool {
+    return hashit(sha, 0, id, w);
+}
+
+/* these next two functions implement elligator squared - http://eprint.iacr.org/2014/043 */
+/* Elliptic curve point E in format (0x04,x,y} is converted to form {0x0-,u,v} */
+/* Note that u and v are indistinguisible from random strings */
+#[allow(non_snake_case)]
+pub fn encoding(rng: &mut RAND, e: &mut [u8]) -> isize {
+    let mut t: [u8; EFS] = [0; EFS];
+
+    for i in 0..EFS {
+        t[i] = e[i + 1]
+    }
+    let mut u = BIG::frombytes(&t);
+    for i in 0..EFS {
+        t[i] = e[i + EFS + 1]
+    }
+    let mut v = BIG::frombytes(&t);
+
+    let mut P = ECP::new_bigs(&u, &v);
+    if P.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let p = BIG::new_ints(&rom::MODULUS);
+    u = BIG::randomnum(&p, rng);
+
+    let mut su = rng.getbyte() as isize;
+    su %= 2;
+
+    let mut W = emap(&mut u, su);
+    P.sub(&mut W);
+    let sv = P.gets();
+    let rn = unmap(&mut v, &mut P);
+    let mut m = rng.getbyte() as isize;
+    m %= rn;
+    v.inc(m + 1);
+    e[0] = (su + 2 * sv) as u8;
+    u.tobytes(&mut t);
+    for i in 0..EFS {
+        e[i + 1] = t[i]
+    }
+    v.tobytes(&mut t);
+    for i in 0..EFS {
+        e[i + EFS + 1] = t[i]
+    }
+
+    return 0;
+}
+
+#[allow(non_snake_case)]
+pub fn decoding(d: &mut [u8]) -> isize {
+    let mut t: [u8; EFS] = [0; EFS];
+
+    if (d[0] & 0x04) != 0 {
+        return INVALID_POINT;
+    }
+
+    for i in 0..EFS {
+        t[i] = d[i + 1]
+    }
+    let mut u = BIG::frombytes(&t);
+    for i in 0..EFS {
+        t[i] = d[i + EFS + 1]
+    }
+    let mut v = BIG::frombytes(&t);
+
+    let su = (d[0] & 1) as isize;
+    let sv = ((d[0] >> 1) & 1) as isize;
+    let mut W = emap(&mut u, su);
+    let mut P = emap(&mut v, sv);
+    P.add(&mut W);
+    u = P.getx();
+    v = P.gety();
+    d[0] = 0x04;
+    u.tobytes(&mut t);
+    for i in 0..EFS {
+        d[i + 1] = t[i]
+    }
+    v.tobytes(&mut t);
+    for i in 0..EFS {
+        d[i + EFS + 1] = t[i]
+    }
+
+    return 0;
+}
+
+/* R=R1+R2 in group G1 */
+#[allow(non_snake_case)]
+pub fn recombine_g1(r1: &[u8], r2: &[u8], r: &mut [u8]) -> isize {
+    let mut P = ECP::frombytes(&r1);
+    let mut Q = ECP::frombytes(&r2);
+
+    if P.is_infinity() || Q.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    P.add(&mut Q);
+
+    P.tobytes(r, false);
+    return 0;
+}
+
+/* W=W1+W2 in group G2 */
+#[allow(non_snake_case)]
+pub fn recombine_g2(w1: &[u8], w2: &[u8], w: &mut [u8]) -> isize {
+    let mut P = ECP8::frombytes(&w1);
+    let mut Q = ECP8::frombytes(&w2);
+
+    if P.is_infinity() || Q.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    P.add(&mut Q);
+
+    P.tobytes(w);
+    return 0;
+}
+
+/* create random secret S */
+pub fn random_generate(rng: &mut RAND, s: &mut [u8]) -> isize {
+    let r = BIG::new_ints(&rom::CURVE_ORDER);
+    let mut sc = BIG::randomnum(&r, rng);
+    sc.tobytes(s);
+    return 0;
+}
+
+/* Extract Server Secret SST=S*Q where Q is fixed generator in G2 and S is master secret */
+#[allow(non_snake_case)]
+pub fn get_server_secret(s: &[u8], sst: &mut [u8]) -> isize {
+    let mut Q = ECP8::generator();
+
+    let mut sc = BIG::frombytes(s);
+    Q = pair256::g2mul(&mut Q, &mut sc);
+    Q.tobytes(sst);
+    return 0;
+}
+
+/*
+ W=x*H(G);
+ if RNG == NULL then X is passed in
+ if RNG != NULL the X is passed out
+ if type=0 W=x*G where G is point on the curve, else W=x*M(G), where M(G) is mapping of octet G to point on the curve
+*/
+#[allow(non_snake_case)]
+pub fn get_g1_multiple(
+    rng: Option<&mut RAND>,
+    typ: usize,
+    x: &mut [u8],
+    g: &[u8],
+    w: &mut [u8],
+) -> isize {
+    let mut sx: BIG;
+    let r = BIG::new_ints(&rom::CURVE_ORDER);
+
+    if let Some(rd) = rng {
+        sx = BIG::randomnum(&r, rd);
+        sx.tobytes(x);
+    } else {
+        sx = BIG::frombytes(x);
+    }
+    let mut P: ECP;
+
+    if typ == 0 {
+        P = ECP::frombytes(g);
+        if P.is_infinity() {
+            return INVALID_POINT;
+        }
+    } else {
+        P = ECP::mapit(g)
+    }
+
+    pair256::g1mul(&mut P, &mut sx).tobytes(w, false);
+    return 0;
+}
+
+/* Client secret CST=S*H(CID) where CID is client ID and S is master secret */
+/* CID is hashed externally */
+pub fn get_client_secret(s: &mut [u8], cid: &[u8], cst: &mut [u8]) -> isize {
+    return get_g1_multiple(None, 1, s, cid, cst);
+}
+
+/* Extract PIN from TOKEN for identity CID */
+#[allow(non_snake_case)]
+pub fn extract_pin(sha: usize, cid: &[u8], pin: i32, token: &mut [u8]) -> isize {
+    return extract_factor(sha, cid, pin % MAXPIN, PBLEN, token);
+}
+
+/* Extract factor from TOKEN for identity CID */
+#[allow(non_snake_case)]
+pub fn extract_factor(
+    sha: usize,
+    cid: &[u8],
+    factor: i32,
+    facbits: i32,
+    token: &mut [u8],
+) -> isize {
+    let mut P = ECP::frombytes(&token);
+    const RM: usize = big::MODBYTES as usize;
+    let mut h: [u8; RM] = [0; RM];
+    if P.is_infinity() {
+        return INVALID_POINT;
+    }
+    hashit(sha, 0, cid, &mut h);
+    let mut R = ECP::mapit(&h);
+
+    R = R.pinmul(factor, facbits);
+    P.sub(&mut R);
+
+    P.tobytes(token, false);
+
+    return 0;
+}
+
+/* Restore factor to TOKEN for identity CID */
+#[allow(non_snake_case)]
+pub fn restore_factor(
+    sha: usize,
+    cid: &[u8],
+    factor: i32,
+    facbits: i32,
+    token: &mut [u8],
+) -> isize {
+    let mut P = ECP::frombytes(&token);
+    const RM: usize = big::MODBYTES as usize;
+    let mut h: [u8; RM] = [0; RM];
+    if P.is_infinity() {
+        return INVALID_POINT;
+    }
+    hashit(sha, 0, cid, &mut h);
+    let mut R = ECP::mapit(&h);
+
+    R = R.pinmul(factor, facbits);
+    P.add(&mut R);
+
+    P.tobytes(token, false);
+
+    return 0;
+}
+
+/* Functions to support M-Pin Full */
+#[allow(non_snake_case)]
+pub fn precompute(token: &[u8], cid: &[u8], g1: &mut [u8], g2: &mut [u8]) -> isize {
+    let T = ECP::frombytes(&token);
+    if T.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let P = ECP::mapit(&cid);
+
+    let Q = ECP8::generator();
+
+    let mut g = pair256::ate(&Q, &T);
+    g = pair256::fexp(&g);
+    g.tobytes(g1);
+
+    g = pair256::ate(&Q, &P);
+    g = pair256::fexp(&g);
+    g.tobytes(g2);
+
+    return 0;
+}
+
+/* Time Permit CTT=S*(date|H(CID)) where S is master secret */
+#[allow(non_snake_case)]
+pub fn get_client_permit(sha: usize, date: usize, s: &[u8], cid: &[u8], ctt: &mut [u8]) -> isize {
+    const RM: usize = big::MODBYTES as usize;
+    let mut h: [u8; RM] = [0; RM];
+    hashit(sha, date, cid, &mut h);
+    let mut P = ECP::mapit(&h);
+
+    let mut sc = BIG::frombytes(s);
+    pair256::g1mul(&mut P, &mut sc).tobytes(ctt, false);
+    return 0;
+}
+
+/* Implement step 1 on client side of MPin protocol */
+#[allow(non_snake_case)]
+pub fn client_1(
+    sha: usize,
+    date: usize,
+    client_id: &[u8],
+    rng: Option<&mut RAND>,
+    x: &mut [u8],
+    pin: usize,
+    token: &[u8],
+    sec: &mut [u8],
+    xid: Option<&mut [u8]>,
+    xcid: Option<&mut [u8]>,
+    permit: Option<&[u8]>,
+) -> isize {
+    let r = BIG::new_ints(&rom::CURVE_ORDER);
+
+    let mut sx: BIG;
+
+    if let Some(rd) = rng {
+        sx = BIG::randomnum(&r, rd);
+        sx.tobytes(x);
+    } else {
+        sx = BIG::frombytes(x);
+    }
+
+    const RM: usize = big::MODBYTES as usize;
+    let mut h: [u8; RM] = [0; RM];
+
+    hashit(sha, 0, &client_id, &mut h);
+    let mut P = ECP::mapit(&h);
+
+    let mut T = ECP::frombytes(&token);
+    if T.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut W = P.pinmul((pin as i32) % MAXPIN, PBLEN);
+    T.add(&mut W);
+    if date != 0 {
+        if let Some(rpermit) = permit {
+            W = ECP::frombytes(&rpermit);
+        }
+        if W.is_infinity() {
+            return INVALID_POINT;
+        }
+        T.add(&mut W);
+        let mut h2: [u8; RM] = [0; RM];
+        hashit(sha, date, &h, &mut h2);
+        W = ECP::mapit(&h2);
+        if let Some(mut rxid) = xid {
+            P = pair256::g1mul(&mut P, &mut sx);
+            P.tobytes(&mut rxid, false);
+            W = pair256::g1mul(&mut W, &mut sx);
+            P.add(&mut W);
+        } else {
+            P.add(&mut W);
+            P = pair256::g1mul(&mut P, &mut sx);
+        }
+        if let Some(mut rxcid) = xcid {
+            P.tobytes(&mut rxcid, false)
+        }
+    } else {
+        if let Some(mut rxid) = xid {
+            P = pair256::g1mul(&mut P, &mut sx);
+            P.tobytes(&mut rxid, false);
+        }
+    }
+
+    T.tobytes(sec, false);
+    return 0;
+}
+
+/* Outputs H(CID) and H(T|H(CID)) for time permits. If no time permits set HID=HTID */
+#[allow(non_snake_case)]
+pub fn server_1(sha: usize, date: usize, cid: &[u8], hid: &mut [u8], htid: Option<&mut [u8]>) {
+    const RM: usize = big::MODBYTES as usize;
+    let mut h: [u8; RM] = [0; RM];
+
+    hashit(sha, 0, cid, &mut h);
+
+    let mut P = ECP::mapit(&h);
+
+    P.tobytes(hid, false);
+    if date != 0 {
+        let mut h2: [u8; RM] = [0; RM];
+        hashit(sha, date, &h, &mut h2);
+        let mut R = ECP::mapit(&h2);
+        P.add(&mut R);
+        if let Some(rhtid) = htid {
+            P.tobytes(rhtid, false);
+        }
+    }
+}
+
+/* Implement step 2 on client side of MPin protocol */
+#[allow(non_snake_case)]
+pub fn client_2(x: &[u8], y: &[u8], sec: &mut [u8]) -> isize {
+    let mut r = BIG::new_ints(&rom::CURVE_ORDER);
+    let mut P = ECP::frombytes(sec);
+    if P.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut px = BIG::frombytes(x);
+    let py = BIG::frombytes(y);
+    px.add(&py);
+    px.rmod(&mut r);
+
+    P = pair256::g1mul(&mut P, &mut px);
+    P.neg();
+    P.tobytes(sec, false);
+
+    return 0;
+}
+
+/* return time since epoch */
+pub fn get_time() -> usize {
+    return (SystemTime::now()
+        .duration_since(UNIX_EPOCH)
+        .unwrap()
+        .as_secs()) as usize;
+}
+
+/* Generate Y = H(epoch, xCID/xID) */
+pub fn get_y(sha: usize, timevalue: usize, xcid: &[u8], y: &mut [u8]) {
+    const RM: usize = big::MODBYTES as usize;
+    let mut h: [u8; RM] = [0; RM];
+
+    hashit(sha, timevalue, xcid, &mut h);
+
+    let mut sy = BIG::frombytes(&h);
+    let mut q = BIG::new_ints(&rom::CURVE_ORDER);
+    sy.rmod(&mut q);
+    sy.tobytes(y);
+}
+
+/* Implement step 2 of MPin protocol on server side */
+#[allow(non_snake_case)]
+pub fn server_2(
+    date: usize,
+    hid: &[u8],
+    htid: Option<&[u8]>,
+    y: &[u8],
+    sst: &[u8],
+    xid: Option<&[u8]>,
+    xcid: Option<&[u8]>,
+    msec: &[u8],
+    e: Option<&mut [u8]>,
+    f: Option<&mut [u8]>,
+) -> isize {
+    let Q = ECP8::generator();
+
+    let sQ = ECP8::frombytes(&sst);
+    if sQ.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut R: ECP;
+    if date != 0 {
+        if let Some(rxcid) = xcid {
+            R = ECP::frombytes(&rxcid);
+        } else {
+            return BAD_PARAMS;
+        }
+    } else {
+        if let Some(rxid) = xid {
+            R = ECP::frombytes(&rxid)
+        } else {
+            return BAD_PARAMS;
+        }
+    }
+    if R.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut sy = BIG::frombytes(&y);
+    let mut P: ECP;
+    if date != 0 {
+        if let Some(rhtid) = htid {
+            P = ECP::frombytes(&rhtid)
+        } else {
+            return BAD_PARAMS;
+        }
+    } else {
+        P = ECP::frombytes(&hid);
+    }
+
+    if P.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    P = pair256::g1mul(&mut P, &mut sy);
+    P.add(&mut R);
+    R = ECP::frombytes(&msec);
+    if R.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut g: FP48;
+
+    g = pair256::ate2(&Q, &R, &sQ, &P);
+    g = pair256::fexp(&g);
+
+    if !g.isunity() {
+        if let Some(rxid) = xid {
+            if let Some(re) = e {
+                if let Some(rf) = f {
+                    g.tobytes(re);
+                    if date != 0 {
+                        P = ECP::frombytes(&hid);
+                        if P.is_infinity() {
+                            return INVALID_POINT;
+                        }
+                        R = ECP::frombytes(&rxid);
+                        if R.is_infinity() {
+                            return INVALID_POINT;
+                        }
+                        P = pair256::g1mul(&mut P, &mut sy);
+                        P.add(&mut R); //P.affine();
+                    }
+                    g = pair256::ate(&Q, &P);
+                    g = pair256::fexp(&g);
+                    g.tobytes(rf);
+                }
+            }
+        }
+
+        return BAD_PIN;
+    }
+
+    return 0;
+}
+
+/* Pollards kangaroos used to return PIN error */
+pub fn kangaroo(e: &[u8], f: &[u8]) -> isize {
+    let mut ge = FP48::frombytes(e);
+    let mut gf = FP48::frombytes(f);
+    let mut distance: [isize; TS] = [0; TS];
+    let mut t = FP48::new_copy(&gf);
+
+    let mut table: [FP48; TS] = [FP48::new(); TS];
+    let mut s: isize = 1;
+    for m in 0..TS {
+        distance[m] = s;
+        table[m] = FP48::new_copy(&t);
+        s *= 2;
+        t.usqr();
+    }
+    t.one();
+    let mut dn: isize = 0;
+    let mut i: usize;
+    for _ in 0..TRAP {
+        i = (t.geta().geta().geta().geta().geta().lastbits(20) % (TS as isize)) as usize;
+        t.mul(&mut table[i]);
+        dn += distance[i];
+    }
+    gf.copy(&t);
+    gf.conj();
+    let mut steps: usize = 0;
+    let mut dm: isize = 0;
+    let mut res: isize = 0;
+    while dm - dn < MAXPIN as isize {
+        steps += 1;
+        if steps > 4 * TRAP {
+            break;
+        }
+        i = (ge.geta().geta().geta().geta().geta().lastbits(20) % (TS as isize)) as usize;
+        ge.mul(&mut table[i]);
+        dm += distance[i];
+        if ge.equals(&mut t) {
+            res = dm - dn;
+            break;
+        }
+        if ge.equals(&mut gf) {
+            res = dn - dm;
+            break;
+        }
+    }
+    if steps > 4 * TRAP || dm - dn >= MAXPIN as isize {
+        res = 0
+    } // Trap Failed  - probable invalid token
+    return res;
+}
+
+/* Hash the M-Pin transcript - new */
+
+pub fn hash_all(
+    sha: usize,
+    hid: &[u8],
+    xid: &[u8],
+    xcid: Option<&[u8]>,
+    sec: &[u8],
+    y: &[u8],
+    r: &[u8],
+    w: &[u8],
+    h: &mut [u8],
+) -> bool {
+    let mut tlen: usize = 0;
+    const RM: usize = big::MODBYTES as usize;
+    let mut t: [u8; 10 * RM + 4] = [0; 10 * RM + 4];
+
+    for i in 0..hid.len() {
+        t[i] = hid[i]
+    }
+    tlen += hid.len();
+
+    if let Some(rxcid) = xcid {
+        for i in 0..rxcid.len() {
+            t[i + tlen] = rxcid[i]
+        }
+        tlen += rxcid.len();
+    } else {
+        for i in 0..xid.len() {
+            t[i + tlen] = xid[i]
+        }
+        tlen += xid.len();
+    }
+
+    for i in 0..sec.len() {
+        t[i + tlen] = sec[i]
+    }
+    tlen += sec.len();
+    for i in 0..y.len() {
+        t[i + tlen] = y[i]
+    }
+    tlen += y.len();
+    for i in 0..r.len() {
+        t[i + tlen] = r[i]
+    }
+    tlen += r.len();
+    for i in 0..w.len() {
+        t[i + tlen] = w[i]
+    }
+    tlen += w.len();
+    if tlen != 10 * RM + 4 {
+        return false;
+    }
+
+    return hashit(sha, 0, &t, h);
+}
+
+/* calculate common key on client side */
+/* wCID = w.(A+AT) */
+#[allow(non_snake_case)]
+pub fn client_key(
+    sha: usize,
+    g1: &[u8],
+    g2: &[u8],
+    pin: usize,
+    r: &[u8],
+    x: &[u8],
+    h: &[u8],
+    wcid: &[u8],
+    ck: &mut [u8],
+) -> isize {
+    let mut g1 = FP48::frombytes(&g1);
+    let mut g2 = FP48::frombytes(&g2);
+    let mut z = BIG::frombytes(&r);
+    let mut x = BIG::frombytes(&x);
+    let h = BIG::frombytes(&h);
+
+    let mut W = ECP::frombytes(&wcid);
+    if W.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    W = pair256::g1mul(&mut W, &mut x);
+
+    let mut r = BIG::new_ints(&rom::CURVE_ORDER);
+
+    z.add(&h); //new
+    z.rmod(&mut r);
+
+    g2.pinpow(pin as i32, PBLEN);
+    g1.mul(&mut g2);
+
+    let mut c = g1.compow(&z, &mut r);
+
+    hash(sha, &mut c, &mut W, ck);
+
+    return 0;
+}
+
+/* calculate common key on server side */
+/* Z=r.A - no time permits involved */
+#[allow(non_snake_case)]
+pub fn server_key(
+    sha: usize,
+    z: &[u8],
+    sst: &[u8],
+    w: &[u8],
+    h: &[u8],
+    hid: &[u8],
+    xid: &[u8],
+    xcid: Option<&[u8]>,
+    sk: &mut [u8],
+) -> isize {
+    let sQ = ECP8::frombytes(&sst);
+    if sQ.is_infinity() {
+        return INVALID_POINT;
+    }
+    let mut R = ECP::frombytes(&z);
+    if R.is_infinity() {
+        return INVALID_POINT;
+    }
+    let mut A = ECP::frombytes(&hid);
+    if A.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut U = ECP::new();
+    if let Some(rxcid) = xcid {
+        U.copy(&ECP::frombytes(&rxcid));
+    } else {
+        U.copy(&ECP::frombytes(&xid));
+    }
+
+    if U.is_infinity() {
+        return INVALID_POINT;
+    }
+
+    let mut w = BIG::frombytes(&w);
+    let mut h = BIG::frombytes(&h);
+    A = pair256::g1mul(&mut A, &mut h); // new
+    R.add(&mut A);
+
+    U = pair256::g1mul(&mut U, &mut w);
+    let mut g = pair256::ate(&sQ, &R);
+    g = pair256::fexp(&g);
+
+    let mut c = g.trace();
+
+    hash(sha, &mut c, &mut U, sk);
+
+    return 0;
+}
diff --git a/src/nhs.rs b/src/nhs.rs
new file mode 100644
index 0000000..7f2f605
--- /dev/null
+++ b/src/nhs.rs
@@ -0,0 +1,705 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* NewHope Simple API high-level functions  */
+
+use crate::rand::RAND;
+use crate::sha3;
+use crate::sha3::SHA3;
+
+const PRIME: i32 = 0x3001; // q in Hex
+const LGN: usize = 10; // Degree n=2^LGN
+const ND: u32 = 0xF7002FFF; // 1/(R-q) mod R
+const ONE: i32 = 0x2AC8; // R mod q
+const R2MODP: u64 = 0x1620; // R^2 mod q
+
+const DEGREE: usize = (1 << LGN);
+const WL: usize = 32;
+
+const INV: i32 = 0xeab;
+const INVPR: i32 = 0x2c2a;
+
+const ROOTS: [i32; 1024] = [
+    0x2ac8, 0x2baf, 0x299b, 0x685, 0x2f04, 0x158d, 0x2d49, 0x24b5, 0x1edc, 0xab3, 0x2a95, 0x24d,
+    0x3cb, 0x6a8, 0x12f9, 0x15ba, 0x1861, 0x2a89, 0x1c5c, 0xbe6, 0xc1e, 0x2024, 0x207, 0x19ce,
+    0x2710, 0x1744, 0x18bc, 0x2cd7, 0x396, 0x18d5, 0x1c45, 0xc4, 0x21a6, 0xe03, 0x2b3c, 0x2d91,
+    0xc5d, 0x432, 0x1fbc, 0xcae, 0x2512, 0x2979, 0x3b2, 0x714, 0xb2e, 0x1a97, 0x1a03, 0x1bcd,
+    0x2216, 0x2701, 0xa, 0x263c, 0x1179, 0x200c, 0x2d08, 0x1c34, 0x291, 0x2c99, 0x2a5a, 0x723,
+    0xb1d, 0x1ccc, 0x1fb6, 0x2f58, 0x2bfe, 0x1cda, 0x2a0, 0x5f1, 0x2de, 0x1fc7, 0x1ea8, 0x1719,
+    0x2fa7, 0x27ec, 0x20ff, 0x12c0, 0x1ac1, 0x2232, 0x2f9b, 0xd3e, 0x2aed, 0x15f0, 0x11e8, 0xed0,
+    0x26a, 0x1de5, 0xa3f, 0xf43, 0xebf, 0x204e, 0xac7, 0x2d9c, 0x5ea, 0x25d1, 0xb6, 0x49c, 0x995,
+    0x2555, 0x26e2, 0x100, 0x1878, 0x5aa, 0x2e10, 0x271c, 0xcb, 0x1b4c, 0x2fb8, 0x25b7, 0x1543,
+    0x2c7b, 0x241a, 0x2223, 0x20ca, 0x24ed, 0x137, 0x1b65, 0x1dc2, 0x7c7, 0x2ec3, 0xd0c, 0x1169,
+    0x1c7a, 0x1ea1, 0xf89, 0x2199, 0x291d, 0x1088, 0x2046, 0x256d, 0x2bc7, 0x2e9b, 0x41f, 0x1b55,
+    0x2b38, 0xd0, 0x2e6a, 0x1755, 0x6bc, 0x2724, 0x3ba, 0x222e, 0x2c5c, 0x2da5, 0x213c, 0x10fe,
+    0x169a, 0x1552, 0x5d3, 0x300, 0x1b5d, 0x1342, 0x2004, 0x256f, 0x2039, 0x667, 0x23b5, 0x1123,
+    0xdb, 0x2da0, 0xe1e, 0x2f54, 0x2767, 0x154a, 0x40a, 0x11d3, 0x2821, 0xc09, 0x974, 0x694, 0xfbf,
+    0x27ba, 0x132, 0x83f, 0x2d06, 0x10e, 0x183f, 0x29ae, 0x28c3, 0x2dc9, 0x1144, 0x2c70, 0x2a4a,
+    0xf3c, 0x1e32, 0x1171, 0x1e43, 0xdd4, 0x2ddf, 0x28d2, 0xfac, 0x3c4, 0x2f19, 0x10a6, 0x2f7,
+    0xe1d, 0x828, 0x138f, 0x1332, 0xfab, 0xcf6, 0x13f8, 0x24a0, 0x112d, 0x2717, 0x6e7, 0x1044,
+    0x36e, 0xfe8, 0x6a, 0xba7, 0x1d69, 0x29ec, 0x23b2, 0xaee, 0x16df, 0x1068, 0x1a7e, 0x253f,
+    0x24c, 0xb33, 0x2683, 0x15ce, 0x1ad3, 0x1a36, 0xc96, 0xaea, 0x260a, 0xce, 0x28b1, 0xe4f,
+    0x2b11, 0x5f8, 0x1fc4, 0xe77, 0x2366, 0x11f9, 0x153c, 0x24eb, 0x20cd, 0x1398, 0x22, 0x2b97,
+    0x249b, 0x8eb, 0x12b2, 0x2fe3, 0x29c1, 0x1b00, 0x2663, 0xeaa, 0x2e06, 0xe0, 0x1569, 0x10f5,
+    0x284e, 0xa38, 0x201d, 0x1c53, 0x1681, 0x1f6f, 0x2f95, 0x2fe8, 0xacb, 0x1680, 0x17fd, 0x2c39,
+    0x165a, 0x10bb, 0x29d8, 0x2622, 0x1196, 0x884, 0x2a79, 0x140e, 0x2d80, 0x6fa, 0x11b2, 0x26c4,
+    0x355, 0x1054, 0x29e9, 0x23ed, 0xbe3, 0x24fa, 0x1fb3, 0x10ac, 0x2919, 0x2584, 0x10a4, 0xe85,
+    0x650, 0x1893, 0x1dc1, 0xd8e, 0x12dc, 0x2d42, 0x284d, 0xfff, 0x250f, 0xacd, 0x13c3, 0x6cc,
+    0x1a79, 0x1221, 0x2614, 0x270a, 0x1ea, 0x155, 0x2818, 0x222c, 0x2e5b, 0x25d8, 0x1dbf, 0x191c,
+    0xb0f, 0xdac, 0x1082, 0x12ef, 0x11b6, 0xfa8, 0x2b72, 0x159d, 0x209e, 0x31b, 0x2c7c, 0x14f7,
+    0xe09, 0x1bb2, 0x1ec7, 0x2404, 0x20ae, 0x6ad, 0xed6, 0x2b70, 0x1c7b, 0x18d1, 0x2732, 0x12da,
+    0xd56, 0x5c1, 0x1648, 0x18b7, 0x1605, 0x1bc4, 0x280, 0x2ece, 0xc, 0x1aae, 0x1c4, 0x1cdb,
+    0x22d6, 0x21d8, 0x257c, 0x51f, 0x211b, 0xff, 0x2ee0, 0x2585, 0xe1, 0x2c35, 0x26db, 0x2971,
+    0x2208, 0x17e1, 0x21be, 0x135e, 0x28d6, 0x2891, 0x1689, 0x2138, 0xb86, 0x2e3a, 0x1204, 0x2d10,
+    0x2324, 0xf3f, 0x2508, 0x33d, 0xcb2, 0x292a, 0xe27, 0x2e64, 0x29f8, 0x2d46, 0x9b7, 0x20eb,
+    0x1b7c, 0x9eb, 0x2b2a, 0x58c, 0x27d0, 0x121b, 0x272e, 0x29f6, 0x2dbd, 0x2697, 0x2aac, 0xd6f,
+    0x1c67, 0x2c5b, 0x108d, 0x363, 0x249d, 0x2d5e, 0x2fd, 0x2cb2, 0x1f8f, 0x20a4, 0xa19, 0x2ac9,
+    0x19b1, 0x1581, 0x17a2, 0x29eb, 0x1b72, 0x13b0, 0xee4, 0xa8f, 0x2315, 0x5e6, 0x951, 0x2e29,
+    0xdad, 0x1f2b, 0x224e, 0x37f, 0x1a72, 0xa91, 0x1407, 0x2df9, 0x3ad, 0x23f7, 0x1a24, 0x1d2a,
+    0x234b, 0x1df3, 0x1143, 0x7ff, 0x1a6d, 0x2774, 0x2690, 0x2ab5, 0x586, 0x2781, 0x2009, 0x2fdd,
+    0x2881, 0x399, 0x2fb6, 0x144, 0x137f, 0xfa0, 0x2e4c, 0x1c7f, 0x2fac, 0xb09, 0x1264, 0x127b,
+    0x198c, 0x2b40, 0x230, 0x1cf4, 0x180b, 0xb58, 0x144a, 0x2aec, 0xfb, 0x2602, 0x14ee, 0x783,
+    0x1098, 0x23d8, 0x203, 0xe9, 0x108a, 0x14b8, 0xeec, 0xc58, 0x1248, 0x243c, 0x28aa, 0x6bf,
+    0x27c4, 0x276e, 0x19b8, 0x1d11, 0x2e16, 0x472, 0x1464, 0x24b9, 0x662, 0x1097, 0x2067, 0x20d6,
+    0x171c, 0x4, 0x682, 0x17bb, 0x1186, 0x4f2, 0x3ff, 0x2a43, 0x1dc7, 0x1ae5, 0x8cc, 0x2e7c,
+    0x2ef8, 0x2ae0, 0x2904, 0xed4, 0x6c5, 0x14ae, 0xb72, 0x11c3, 0x337, 0x2da3, 0x2916, 0x6d8,
+    0x1cf9, 0x10ee, 0x1800, 0x1ae4, 0xa0d, 0x101b, 0x1a8d, 0x2e98, 0x24cd, 0x813, 0x1aa4, 0x9b9,
+    0x680, 0x2349, 0x24d1, 0x20f8, 0xe31, 0x249f, 0x216b, 0x12d9, 0x1d21, 0x19db, 0x191a, 0x1dd0,
+    0x5df, 0x55c, 0x2b86, 0x213, 0xe9e, 0x1ef1, 0x268a, 0x1d5e, 0x1e20, 0x28c1, 0x1379, 0x249,
+    0x19de, 0x18b, 0x1e41, 0x2a1e, 0x2612, 0x297, 0x2e96, 0x2102, 0x46, 0x1b9f, 0x1a4d, 0x2050,
+    0x1b32, 0x568, 0x11f7, 0x1829, 0x870, 0x1f4, 0x1dca, 0x990, 0x1df6, 0x2b62, 0x13ec, 0x9f2,
+    0x1260, 0x2997, 0x1412, 0x1e6d, 0x1694, 0x11ac, 0x2d8b, 0x276f, 0x26f5, 0x233e, 0x2b44, 0x2f5a,
+    0x2d37, 0x2cb1, 0xc75, 0x98d, 0x1d56, 0x7ae, 0x10e6, 0x113f, 0x17b8, 0xad3, 0x737, 0x221e,
+    0x1b70, 0x1f3e, 0x2966, 0x18b2, 0x4fa, 0x2044, 0x1312, 0x154e, 0x2029, 0x700, 0x1b45, 0x27a6,
+    0x226a, 0x21bf, 0x58d, 0x2f11, 0x2e02, 0x17fc, 0x4d2, 0x1757, 0xcb1, 0x2ef1, 0x2582, 0x1276,
+    0x881, 0x2fc0, 0x104a, 0x670, 0x274f, 0x2b53, 0x19dd, 0x752, 0x1663, 0xcbd, 0x2b2b, 0x2fc6,
+    0x13b6, 0x21e6, 0x15f6, 0x126b, 0x2637, 0x1cd9, 0x2f50, 0xe82, 0x5b0, 0x24e0, 0x1350, 0x2f24,
+    0x21f7, 0x1a16, 0x2f3e, 0x167e, 0x1f7d, 0x28a0, 0x16f0, 0xe33, 0x53b, 0x28c5, 0x1500, 0x2f88,
+    0x26cc, 0x2018, 0x1604, 0x218b, 0x2cd1, 0x9ee, 0x17f3, 0x5fd, 0x1f5a, 0x2d0, 0x2b46, 0x23cc,
+    0x503, 0x1c46, 0x1cc3, 0x28e2, 0x243e, 0x122b, 0x2e0c, 0xe37, 0x2611, 0x85e, 0x9b8, 0x1b24,
+    0x762, 0x19b6, 0x3bc, 0x2d50, 0x2079, 0x18da, 0x170a, 0x800, 0xaa2, 0x135a, 0x1a15, 0x13d1,
+    0xca, 0x2113, 0x2db9, 0xdb2, 0x1a5c, 0x29a9, 0x1488, 0x14c1, 0x2c9, 0x917, 0x28e7, 0x265c,
+    0xdab, 0x2ab9, 0x2bc6, 0x105b, 0x1839, 0x219c, 0x50, 0x11da, 0x1802, 0xf56, 0x2e6, 0x2190,
+    0xddb, 0x56e, 0x9d9, 0x1c81, 0x1016, 0x12d6, 0x296f, 0x14b4, 0x1014, 0x1e64, 0x1d90, 0x89f,
+    0x2bc2, 0x2777, 0x2819, 0x1c65, 0x1a41, 0x5a2, 0x2cd2, 0x427, 0xd71, 0x29c8, 0x1e58, 0x53f,
+    0x7c5, 0x1dcd, 0x4a1, 0x1268, 0x2597, 0x2926, 0xee, 0x111b, 0x1038, 0xe6c, 0x22dc, 0x2f2f,
+    0x441, 0x2cfd, 0x1cb0, 0x6a4, 0x2224, 0x620, 0x5dc, 0x16b1, 0x2a1d, 0x1787, 0x20c7, 0x641,
+    0xd84, 0x1c05, 0x2d0d, 0x2f52, 0x1b8c, 0xd7d, 0x17e8, 0x1589, 0xc73, 0x151b, 0x4e2, 0x1ae9,
+    0x1b18, 0xb9b, 0x949, 0x2c60, 0x1e7a, 0xd5, 0x1bdc, 0x1f57, 0x1753, 0x124a, 0x559, 0xb76,
+    0x2334, 0x12d1, 0x1de1, 0x14b2, 0x2faa, 0x1697, 0x147a, 0x5a1, 0x2c30, 0x1c02, 0x1043, 0x2ee1,
+    0x2402, 0x1cc8, 0x2a16, 0xff7, 0x1364, 0x1b9a, 0x2a53, 0x2f94, 0x294c, 0x1ee5, 0x1a87, 0x2141,
+    0xd66, 0x953, 0x28a3, 0x2f30, 0x2477, 0x18e3, 0x1035, 0x1fc1, 0x1d68, 0x2fb3, 0x138c, 0x2487,
+    0x1bf8, 0xd96, 0x1018, 0x748, 0x244e, 0x15bd, 0x175e, 0x2be, 0x23d, 0x1da, 0x176d, 0xc17,
+    0x24be, 0x2ebb, 0x7d8, 0x100a, 0x759, 0x1db4, 0x2259, 0x23f4, 0x2d59, 0x2847, 0xbf5, 0x1cfe,
+    0xa20, 0x258, 0x1180, 0x279c, 0x54, 0x2abf, 0xc5c, 0x9f9, 0x3d5, 0x2ce4, 0x165f, 0x23d9,
+    0x27b9, 0x6f9, 0x281a, 0x169e, 0x627, 0x156d, 0x1ff8, 0x211, 0x2e34, 0x1724, 0x2c2e, 0x2790,
+    0x2dd5, 0x2bf2, 0xdbc, 0x2884, 0x20a9, 0x2390, 0x1e1a, 0x1b6a, 0x5f7, 0xab7, 0x1333, 0x16ab,
+    0x28dd, 0x20, 0x30f, 0x24b6, 0x5c2, 0x1ce4, 0x1400, 0x2669, 0x60, 0x156c, 0xe20, 0x26d4,
+    0x26ab, 0x1ebb, 0x223d, 0x5b4, 0x2025, 0x1e1c, 0xaae, 0x2e08, 0x6cd, 0x1677, 0x13d9, 0x17b5,
+    0x1046, 0x1d8c, 0x14eb, 0x18d8, 0x1ce5, 0x2478, 0x16ae, 0xb79, 0x23d4, 0x684, 0x156b, 0x567,
+    0x1a, 0x29ce, 0x83a, 0x19e8, 0x58e, 0x294a, 0x1136, 0x2319, 0x2fba, 0x1a29, 0x1d, 0x1879,
+    0x291b, 0x19f6, 0x2c2f, 0x21c9, 0x19bb, 0xbbc, 0x26f9, 0xc22, 0x708, 0x11a1, 0x18d3, 0x7f8,
+    0x28f8, 0x2427, 0x1deb, 0xaed, 0x26aa, 0x2482, 0x203b, 0x2f05, 0x2b82, 0x192f, 0x2df4, 0x8dc,
+    0x2877, 0xd5e, 0x240e, 0x775, 0x2dae, 0x1d3e, 0x20ba, 0x215b, 0x22d1, 0xeba, 0xf50, 0xaa8,
+    0x184a, 0x1f67, 0x2e04, 0xc6e, 0x6dd, 0x1a09, 0x27f, 0x494, 0x1426, 0xae3, 0xe15, 0x65f,
+    0x13c4, 0x105, 0x872, 0x2667, 0x1ff6, 0xd9f, 0x2ca1, 0x2f39, 0x2657, 0x23fd, 0x2405, 0xb73,
+    0x2294, 0x1f1e, 0x2eba, 0x110a, 0x2cae, 0x141f, 0x22cd, 0x25d6, 0x11c1, 0x1c, 0x2d8e, 0x161a,
+    0x1aa8, 0x229e, 0x1bf9, 0x7cf, 0x106d, 0x2c40, 0xd93, 0x255e, 0x28c2, 0xc1a, 0x2f17, 0x7ca,
+    0x2f63, 0xbf,
+];
+const IROOTS: [i32; 1024] = [
+    0x2ac8, 0x452, 0x297c, 0x666, 0xb4c, 0x2b8, 0x1a74, 0xfd, 0x1a47, 0x1d08, 0x2959, 0x2c36,
+    0x2db4, 0x56c, 0x254e, 0x1125, 0x2f3d, 0x13bc, 0x172c, 0x2c6b, 0x32a, 0x1745, 0x18bd, 0x8f1,
+    0x1633, 0x2dfa, 0xfdd, 0x23e3, 0x241b, 0x13a5, 0x578, 0x17a0, 0xa9, 0x104b, 0x1335, 0x24e4,
+    0x28de, 0x5a7, 0x368, 0x2d70, 0x13cd, 0x2f9, 0xff5, 0x1e88, 0x9c5, 0x2ff7, 0x900, 0xdeb,
+    0x1434, 0x15fe, 0x156a, 0x24d3, 0x28ed, 0x2c4f, 0x688, 0xaef, 0x2353, 0x1045, 0x2bcf, 0x23a4,
+    0x270, 0x4c5, 0x21fe, 0xe5b, 0xfbb, 0x1f79, 0x6e4, 0xe68, 0x2078, 0x1160, 0x1387, 0x1e98,
+    0x22f5, 0x13e, 0x283a, 0x123f, 0x149c, 0x2eca, 0xb14, 0xf37, 0xdde, 0xbe7, 0x386, 0x1abe,
+    0xa4a, 0x49, 0x14b5, 0x2f36, 0x8e5, 0x1f1, 0x2a57, 0x1789, 0x2f01, 0x91f, 0xaac, 0x266c,
+    0x2b65, 0x2f4b, 0xa30, 0x2a17, 0x265, 0x253a, 0xfb3, 0x2142, 0x20be, 0x25c2, 0x121c, 0x2d97,
+    0x2131, 0x1e19, 0x1a11, 0x514, 0x22c3, 0x66, 0xdcf, 0x1540, 0x1d41, 0xf02, 0x815, 0x5a, 0x18e8,
+    0x1159, 0x103a, 0x2d23, 0x2a10, 0x2d61, 0x1327, 0x403, 0x25c9, 0x7b3, 0x1f0c, 0x1a98, 0x2f21,
+    0x1fb, 0x2157, 0x99e, 0x1501, 0x640, 0x1e, 0x1d4f, 0x2716, 0xb66, 0x46a, 0x2fdf, 0x1c69, 0xf34,
+    0xb16, 0x1ac5, 0x1e08, 0xc9b, 0x218a, 0x103d, 0x2a09, 0x4f0, 0x21b2, 0x750, 0x2f33, 0x9f7,
+    0x2517, 0x236b, 0x15cb, 0x152e, 0x1a33, 0x97e, 0x24ce, 0x2db5, 0xac2, 0x1583, 0x1f99, 0x1922,
+    0x2513, 0xc4f, 0x615, 0x1298, 0x245a, 0x2f97, 0x2019, 0x2c93, 0x1fbd, 0x291a, 0x8ea, 0x1ed4,
+    0xb61, 0x1c09, 0x230b, 0x2056, 0x1ccf, 0x1c72, 0x27d9, 0x21e4, 0x2d0a, 0x1f5b, 0xe8, 0x2c3d,
+    0x2055, 0x72f, 0x222, 0x222d, 0x11be, 0x1e90, 0x11cf, 0x20c5, 0x5b7, 0x391, 0x1ebd, 0x238,
+    0x73e, 0x653, 0x17c2, 0x2ef3, 0x2fb, 0x27c2, 0x2ecf, 0x847, 0x2042, 0x296d, 0x268d, 0x23f8,
+    0x7e0, 0x1e2e, 0x2bf7, 0x1ab7, 0x89a, 0xad, 0x21e3, 0x261, 0x2f26, 0x1ede, 0xc4c, 0x299a,
+    0xfc8, 0xa92, 0xffd, 0x1cbf, 0x14a4, 0x2d01, 0x2a2e, 0x1aaf, 0x1967, 0x1f03, 0xec5, 0x25c,
+    0x3a5, 0xdd3, 0x2c47, 0x8dd, 0x2945, 0x18ac, 0x197, 0x2f31, 0x4c9, 0x14ac, 0x2be2, 0x166,
+    0x43a, 0xa94, 0x1b53, 0x293c, 0x212d, 0x6fd, 0x521, 0x109, 0x185, 0x2735, 0x151c, 0x123a,
+    0x5be, 0x2c02, 0x2b0f, 0x1e7b, 0x1846, 0x297f, 0x2ffd, 0x18e5, 0xf2b, 0xf9a, 0x1f6a, 0x299f,
+    0xb48, 0x1b9d, 0x2b8f, 0x1eb, 0x12f0, 0x1649, 0x893, 0x83d, 0x2942, 0x757, 0xbc5, 0x1db9,
+    0x23a9, 0x2115, 0x1b49, 0x1f77, 0x2f18, 0x2dfe, 0xc29, 0x1f69, 0x287e, 0x1b13, 0x9ff, 0x2f06,
+    0x515, 0x1bb7, 0x24a9, 0x17f6, 0x130d, 0x2dd1, 0x4c1, 0x1675, 0x1d86, 0x1d9d, 0x24f8, 0x55,
+    0x1382, 0x1b5, 0x2061, 0x1c82, 0x2ebd, 0x4b, 0x2c68, 0x780, 0x24, 0xff8, 0x880, 0x2a7b, 0x54c,
+    0x971, 0x88d, 0x1594, 0x2802, 0x1ebe, 0x120e, 0xcb6, 0x12d7, 0x15dd, 0xc0a, 0x2c54, 0x208,
+    0x1bfa, 0x2570, 0x158f, 0x2c82, 0xdb3, 0x10d6, 0x2254, 0x1d8, 0x26b0, 0x2a1b, 0xcec, 0x2572,
+    0x211d, 0x1c51, 0x148f, 0x616, 0x185f, 0x1a80, 0x1650, 0x538, 0x25e8, 0xf5d, 0x1072, 0x34f,
+    0x2d04, 0x2a3, 0xb64, 0x2c9e, 0x1f74, 0x3a6, 0x139a, 0x2292, 0x555, 0x96a, 0x244, 0x60b, 0x8d3,
+    0x1de6, 0x831, 0x2a75, 0x4d7, 0x2616, 0x1485, 0xf16, 0x264a, 0x2bb, 0x609, 0x19d, 0x21da,
+    0x6d7, 0x234f, 0x2cc4, 0xaf9, 0x20c2, 0xcdd, 0x2f1, 0x1dfd, 0x1c7, 0x247b, 0xec9, 0x1978,
+    0x770, 0x72b, 0x1ca3, 0xe43, 0x1820, 0xdf9, 0x690, 0x926, 0x3cc, 0x2f20, 0xa7c, 0x121, 0x2f02,
+    0xee6, 0x2ae2, 0xa85, 0xe29, 0xd2b, 0x1326, 0x2e3d, 0x1553, 0x2ff5, 0x133, 0x2d81, 0x143d,
+    0x19fc, 0x174a, 0x19b9, 0x2a40, 0x22ab, 0x1d27, 0x8cf, 0x1730, 0x1386, 0x491, 0x212b, 0x2954,
+    0xf53, 0xbfd, 0x113a, 0x144f, 0x21f8, 0x1b0a, 0x385, 0x2ce6, 0xf63, 0x1a64, 0x48f, 0x2059,
+    0x1e4b, 0x1d12, 0x1f7f, 0x2255, 0x24f2, 0x16e5, 0x1242, 0xa29, 0x1a6, 0xdd5, 0x7e9, 0x2eac,
+    0x2e17, 0x8f7, 0x9ed, 0x1de0, 0x1588, 0x2935, 0x1c3e, 0x2534, 0xaf2, 0x2002, 0x7b4, 0x2bf,
+    0x1d25, 0x2273, 0x1240, 0x176e, 0x29b1, 0x217c, 0x1f5d, 0xa7d, 0x6e8, 0x1f55, 0x104e, 0xb07,
+    0x241e, 0xc14, 0x618, 0x1fad, 0x2cac, 0x93d, 0x1e4f, 0x2907, 0x281, 0x1bf3, 0x588, 0x277d,
+    0x1e6b, 0x9df, 0x629, 0x1f46, 0x19a7, 0x3c8, 0x1804, 0x1981, 0x2536, 0x19, 0x6c, 0x1092,
+    0x1980, 0x13ae, 0xfe4, 0x2f42, 0x9e, 0x2837, 0xea, 0x23e7, 0x73f, 0xaa3, 0x226e, 0x3c1, 0x1f94,
+    0x2832, 0x1408, 0xd63, 0x1559, 0x19e7, 0x273, 0x2fe5, 0x1e40, 0xa2b, 0xd34, 0x1be2, 0x353,
+    0x1ef7, 0x147, 0x10e3, 0xd6d, 0x248e, 0xbfc, 0xc04, 0x9aa, 0xc8, 0x360, 0x2262, 0x100b, 0x99a,
+    0x278f, 0x2efc, 0x1c3d, 0x29a2, 0x21ec, 0x251e, 0x1bdb, 0x2b6d, 0x2d82, 0x15f8, 0x2924, 0x2393,
+    0x1fd, 0x109a, 0x17b7, 0x2559, 0x20b1, 0x2147, 0xd30, 0xea6, 0xf47, 0x12c3, 0x253, 0x288c,
+    0xbf3, 0x22a3, 0x78a, 0x2725, 0x20d, 0x16d2, 0x47f, 0xfc, 0xfc6, 0xb7f, 0x957, 0x2514, 0x1216,
+    0xbda, 0x709, 0x2809, 0x172e, 0x1e60, 0x28f9, 0x23df, 0x908, 0x2445, 0x1646, 0xe38, 0x3d2,
+    0x160b, 0x6e6, 0x1788, 0x2fe4, 0x15d8, 0x47, 0xce8, 0x1ecb, 0x6b7, 0x2a73, 0x1619, 0x27c7,
+    0x633, 0x2fe7, 0x2a9a, 0x1a96, 0x297d, 0xc2d, 0x2488, 0x1953, 0xb89, 0x131c, 0x1729, 0x1b16,
+    0x1275, 0x1fbb, 0x184c, 0x1c28, 0x198a, 0x2934, 0x1f9, 0x2553, 0x11e5, 0xfdc, 0x2a4d, 0xdc4,
+    0x1146, 0x956, 0x92d, 0x21e1, 0x1a95, 0x2fa1, 0x998, 0x1c01, 0x131d, 0x2a3f, 0xb4b, 0x2cf2,
+    0x2fe1, 0x724, 0x1956, 0x1cce, 0x254a, 0x2a0a, 0x1497, 0x11e7, 0xc71, 0xf58, 0x77d, 0x2245,
+    0x40f, 0x22c, 0x871, 0x3d3, 0x18dd, 0x1cd, 0x2df0, 0x1009, 0x1a94, 0x29da, 0x1963, 0x7e7,
+    0x2908, 0x848, 0xc28, 0x19a2, 0x31d, 0x2c2c, 0x2608, 0x23a5, 0x542, 0x2fad, 0x865, 0x1e81,
+    0x2da9, 0x25e1, 0x1303, 0x240c, 0x7ba, 0x2a8, 0xc0d, 0xda8, 0x124d, 0x28a8, 0x1ff7, 0x2829,
+    0x146, 0xb43, 0x23ea, 0x1894, 0x2e27, 0x2dc4, 0x2d43, 0x18a3, 0x1a44, 0xbb3, 0x28b9, 0x1fe9,
+    0x226b, 0x1409, 0xb7a, 0x1c75, 0x4e, 0x1299, 0x1040, 0x1fcc, 0x171e, 0xb8a, 0xd1, 0x75e,
+    0x26ae, 0x229b, 0xec0, 0x157a, 0x111c, 0x6b5, 0x6d, 0x5ae, 0x1467, 0x1c9d, 0x200a, 0x5eb,
+    0x1339, 0xbff, 0x120, 0x1fbe, 0x13ff, 0x3d1, 0x2a60, 0x1b87, 0x196a, 0x57, 0x1b4f, 0x1220,
+    0x1d30, 0xccd, 0x248b, 0x2aa8, 0x1db7, 0x18ae, 0x10aa, 0x1425, 0x2f2c, 0x1187, 0x3a1, 0x26b8,
+    0x2466, 0x14e9, 0x1518, 0x2b1f, 0x1ae6, 0x238e, 0x1a78, 0x1819, 0x2284, 0x1475, 0xaf, 0x2f4,
+    0x13fc, 0x227d, 0x29c0, 0xf3a, 0x187a, 0x5e4, 0x1950, 0x2a25, 0x29e1, 0xddd, 0x295d, 0x1351,
+    0x304, 0x2bc0, 0xd2, 0xd25, 0x2195, 0x1fc9, 0x1ee6, 0x2f13, 0x6db, 0xa6a, 0x1d99, 0x2b60,
+    0x1234, 0x283c, 0x2ac2, 0x11a9, 0x639, 0x2290, 0x2bda, 0x32f, 0x2a5f, 0x15c0, 0x139c, 0x7e8,
+    0x88a, 0x43f, 0x2762, 0x1271, 0x119d, 0x1fed, 0x1b4d, 0x692, 0x1d2b, 0x1feb, 0x1380, 0x2628,
+    0x2a93, 0x2226, 0xe71, 0x2d1b, 0x20ab, 0x17ff, 0x1e27, 0x2fb1, 0xe65, 0x17c8, 0x1fa6, 0x43b,
+    0x548, 0x2256, 0x9a5, 0x71a, 0x26ea, 0x2d38, 0x1b40, 0x1b79, 0x658, 0x15a5, 0x224f, 0x248,
+    0xeee, 0x2f37, 0x1c30, 0x15ec, 0x1ca7, 0x255f, 0x2801, 0x18f7, 0x1727, 0xf88, 0x2b1, 0x2c45,
+    0x164b, 0x289f, 0x14dd, 0x2649, 0x27a3, 0x9f0, 0x21ca, 0x1f5, 0x1dd6, 0xbc3, 0x71f, 0x133e,
+    0x13bb, 0x2afe, 0xc35, 0x4bb, 0x2d31, 0x10a7, 0x2a04, 0x180e, 0x2613, 0x330, 0xe76, 0x19fd,
+    0xfe9, 0x935, 0x79, 0x1b01, 0x73c, 0x2ac6, 0x21ce, 0x1911, 0x761, 0x1084, 0x1983, 0xc3, 0x15eb,
+    0xe0a, 0xdd, 0x1cb1, 0xb21, 0x2a51, 0x217f, 0xb1, 0x1328, 0x9ca, 0x1d96, 0x1a0b, 0xe1b, 0x1c4b,
+    0x3b, 0x4d6, 0x2344, 0x199e, 0x28af, 0x1624, 0x4ae, 0x8b2, 0x2991, 0x1fb7, 0x41, 0x2780,
+    0x1d8b, 0xa7f, 0x110, 0x2350, 0x18aa, 0x2b2f, 0x1805, 0x1ff, 0xf0, 0x2a74, 0xe42, 0xd97, 0x85b,
+    0x14bc, 0x2901, 0xfd8, 0x1ab3, 0x1cef, 0xfbd, 0x2b07, 0x174f, 0x69b, 0x10c3, 0x1491, 0xde3,
+    0x28ca, 0x252e, 0x1849, 0x1ec2, 0x1f1b, 0x2853, 0x12ab, 0x2674, 0x238c, 0x350, 0x2ca, 0xa7,
+    0x4bd, 0xcc3, 0x90c, 0x892, 0x276, 0x1e55, 0x196d, 0x1194, 0x1bef, 0x66a, 0x1da1, 0x260f,
+    0x1c15, 0x49f, 0x120b, 0x2671, 0x1237, 0x2e0d, 0x2791, 0x17d8, 0x1e0a, 0x2a99, 0x14cf, 0xfb1,
+    0x15b4, 0x1462, 0x2fbb, 0xeff, 0x16b, 0x2d6a, 0x9ef, 0x5e3, 0x11c0, 0x2e76, 0x1623, 0x2db8,
+    0x1c88, 0x740, 0x11e1, 0x12a3, 0x977, 0x1110, 0x2163, 0x2dee, 0x47b, 0x2aa5, 0x2a22, 0x1231,
+    0x16e7, 0x1626, 0x12e0, 0x1d28, 0xe96, 0xb62, 0x21d0, 0xf09, 0xb30, 0xcb8, 0x2981, 0x2648,
+    0x155d, 0x27ee, 0xb34, 0x169, 0x1574, 0x1fe6, 0x25f4, 0x151d, 0x1801, 0x1f13, 0x1308, 0x2929,
+    0x6eb, 0x25e, 0x2cca, 0x1e3e, 0x248f,
+];
+
+fn round(a: i32, b: i32) -> i32 {
+    return (a + b / 2) / b;
+}
+
+/* Constant time absolute value */
+fn nabs(x: i32) -> i32 {
+    let mask = x >> 31;
+    return (x + mask) ^ mask;
+}
+
+/* Montgomery stuff */
+
+fn redc(t: u64) -> i32 {
+    let m = (t as u32).wrapping_mul(ND);
+    return (((m as u64) * (PRIME as u64) + t) >> WL) as i32;
+}
+
+fn nres(x: i32) -> i32 {
+    return redc((x as u64) * R2MODP);
+}
+
+fn modmul(a: i32, b: i32) -> i32 {
+    return redc((a as u64) * (b as u64));
+}
+
+/* Cooley-Tukey NTT */
+fn ntt(x: &mut [i32]) {
+    let mut t = DEGREE / 2;
+    let q = PRIME;
+
+    /* Convert to Montgomery form */
+    for j in 0..DEGREE {
+        x[j] = nres(x[j])
+    }
+    let mut m = 1;
+    while m < DEGREE {
+        let mut k = 0;
+        for i in 0..m {
+            let s = ROOTS[m + i];
+            for j in k..k + t {
+                let u = x[j];
+                let v = modmul(x[j + t], s);
+                x[j] = u + v;
+                x[j + t] = u + 2 * q - v;
+            }
+            k += 2 * t;
+        }
+        t /= 2;
+        m *= 2;
+    }
+}
+
+/* Gentleman-Sande INTT */
+
+fn intt(x: &mut [i32]) {
+    let mut t = 1;
+    let q = PRIME;
+    let mut m = DEGREE / 2;
+    while m > 1 {
+        let mut k = 0;
+        for i in 0..m {
+            let s = IROOTS[m + i];
+            for j in k..k + t {
+                let u = x[j];
+                let v = x[j + t];
+                x[j] = u + v;
+                let w = u + (DEGREE as i32) * q - v;
+                x[j + t] = modmul(w, s);
+            }
+            k += 2 * t;
+        }
+        t *= 2;
+        m /= 2;
+    }
+
+    /* Last iteration merged with n^-1 */
+    t = DEGREE / 2;
+    for j in 0..t {
+        let u = x[j];
+        let v = x[j + t];
+        let w = u + (DEGREE as i32) * q - v;
+        x[j + t] = modmul(w, INVPR);
+        x[j] = modmul(u + v, INV);
+    }
+    /* convert back from Montgomery to "normal" form */
+    for j in 0..DEGREE {
+        x[j] = redc(x[j] as u64);
+        x[j] -= q;
+        x[j] += (x[j] >> (WL - 1)) & q;
+    }
+}
+
+/* See https://eprint.iacr.org/2016/1157.pdf */
+
+fn encode(key: &[u8], poly: &mut [i32]) {
+    let q2 = PRIME / 2;
+    let mut j = 0;
+    let mut i = 0;
+    while i < 256 {
+        let mut kj = key[j];
+        j += 1;
+        for _ in 0..8 {
+            let b = (kj & 1) as i32;
+            poly[i] = b * q2;
+            poly[i + 256] = b * q2;
+            poly[i + 512] = b * q2;
+            poly[i + 768] = b * q2;
+            kj >>= 1;
+            i += 1;
+        }
+    }
+}
+
+fn decode(poly: &[i32], key: &mut [u8]) {
+    let q2 = PRIME / 2;
+    for i in 0..32 {
+        key[i] = 0;
+    }
+
+    let mut i = 0;
+    let mut j = 0;
+    while i < 256 {
+        for _ in 0..8 {
+            let t = nabs(poly[i] - q2)
+                + nabs(poly[i + 256] - q2)
+                + nabs(poly[i + 512] - q2)
+                + nabs(poly[i + 768] - q2);
+            let mut b = t - PRIME;
+            b = (b >> 31) & 1;
+            key[j] = (key[j] >> 1) + ((b << 7) as u8);
+            i += 1;
+        }
+        j += 1;
+    }
+}
+
+/* convert 32-byte seed to random polynomial */
+
+fn parse(seed: &[u8], poly: &mut [i32]) {
+    let mut hash: [u8; 4 * DEGREE] = [0; 4 * DEGREE];
+    let mut sh = SHA3::new(sha3::SHAKE128);
+    for i in 0..32 {
+        sh.process(seed[i])
+    }
+    sh.shake(&mut hash, 4 * DEGREE);
+
+    let mut j = 0;
+    for i in 0..DEGREE {
+        let mut n = (hash[j] & 0x7f) as i32;
+        n <<= 8;
+        n += (hash[j + 1]) as i32;
+        n <<= 8;
+        n += (hash[j + 2]) as i32;
+        n <<= 8;
+        n += (hash[j + 3]) as i32;
+        j += 4;
+        poly[i] = nres(n);
+        //poly[i]=modmul(n,ONE); // reduce 31-bit random number mod q
+    }
+}
+
+/* Compress 14 bits polynomial coefficients into byte array */
+/* 7 bytes is 3x14 */
+
+fn nhs_pack(poly: &[i32], array: &mut [u8]) {
+    let mut j = 0;
+    let mut i = 0;
+    while i < DEGREE {
+        let a = poly[i];
+        let b = poly[i + 1];
+        let c = poly[i + 2];
+        let d = poly[i + 3];
+        i += 4;
+        array[j] = (a & 0xff) as u8;
+        array[j + 1] = (((a >> 8) | (b << 6)) & 0xff) as u8;
+        array[j + 2] = ((b >> 2) & 0xff) as u8;
+        array[j + 3] = (((b >> 10) | (c << 4)) & 0xff) as u8;
+        array[j + 4] = ((c >> 4) & 0xff) as u8;
+        array[j + 5] = (((c >> 12) | (d << 2)) & 0xff) as u8;
+        array[j + 6] = (d >> 6) as u8;
+        j += 7;
+    }
+}
+
+fn nhs_unpack(array: &[u8], poly: &mut [i32]) {
+    let mut j = 0;
+    let mut i = 0;
+    while i < DEGREE {
+        let a = ((array[j]) & 0xff) as i32;
+        let b = ((array[j + 1]) & 0xff) as i32;
+        let c = ((array[j + 2]) & 0xff) as i32;
+        let d = ((array[j + 3]) & 0xff) as i32;
+        let e = ((array[j + 4]) & 0xff) as i32;
+        let f = ((array[j + 5]) & 0xff) as i32;
+        let g = ((array[j + 6]) & 0xff) as i32;
+        j += 7;
+        poly[i] = a | ((b & 0x3f) << 8);
+        poly[i + 1] = (b >> 6) | (c << 2) | ((d & 0xf) << 10);
+        poly[i + 2] = (d >> 4) | (e << 4) | ((f & 3) << 12);
+        poly[i + 3] = (f >> 2) | (g << 6);
+        i += 4;
+    }
+}
+
+/* See https://eprint.iacr.org/2016/1157.pdf */
+
+fn compress(poly: &[i32], array: &mut [u8]) {
+    let mut col = 0 as i32;
+    let mut j = 0;
+    let mut i = 0;
+    while i < DEGREE {
+        for _ in 0..8 {
+            let b = round(poly[i] * 8, PRIME) & 7;
+            col = (col << 3) + b;
+            i += 1;
+        }
+        array[j] = (col & 0xff) as u8;
+        array[j + 1] = ((col >> 8) & 0xff) as u8;
+        array[j + 2] = ((col >> 16) & 0xff) as u8;
+        j += 3;
+        col = 0;
+    }
+}
+
+fn decompress(array: &[u8], poly: &mut [i32]) {
+    let mut j = 0;
+    let mut i = 0;
+    while i < DEGREE {
+        let mut col = (array[j + 2] as i32) & 0xff;
+        col = (col << 8) + ((array[j + 1] as i32) & 0xff);
+        col = (col << 8) + ((array[j] as i32) & 0xff);
+        j += 3;
+        for _ in 0..8 {
+            let b = (col & 0xe00000) >> 21;
+            col <<= 3;
+            poly[i] = round(b * PRIME, 8);
+            i += 1;
+        }
+    }
+}
+
+/* generate centered binomial distribution */
+
+fn error(rng: &mut RAND, poly: &mut [i32]) {
+    for i in 0..DEGREE {
+        let mut n1 = ((rng.getbyte() as i32) & 0xff) + (((rng.getbyte() as i32) & 0xff) << 8);
+        let mut n2 = ((rng.getbyte() as i32) & 0xff) + (((rng.getbyte() as i32) & 0xff) << 8);
+        let mut r = 0 as i32;
+        for _ in 0..16 {
+            r += (n1 & 1) - (n2 & 1);
+            n1 >>= 1;
+            n2 >>= 1;
+        }
+        poly[i] = r + PRIME;
+    }
+}
+
+fn redc_it(p: &mut [i32]) {
+    for i in 0..DEGREE {
+        p[i] = redc(p[i] as u64);
+    }
+}
+
+fn nres_it(p: &mut [i32]) {
+    for i in 0..DEGREE {
+        p[i] = nres(p[i]);
+    }
+}
+
+fn poly_mul(p1: &mut [i32], p3: &[i32]) {
+    for i in 0..DEGREE {
+        p1[i] = modmul(p1[i], p3[i]);
+    }
+}
+
+fn poly_add(p1: &mut [i32], p3: &[i32]) {
+    for i in 0..DEGREE {
+        p1[i] = p1[i] + p3[i];
+    }
+}
+
+fn poly_rsub(p1: &mut [i32], p2: &[i32]) {
+    for i in 0..DEGREE {
+        p1[i] = p2[i] + PRIME - p1[i];
+    }
+}
+
+/* reduces inputs < 2q */
+fn poly_soft_reduce(poly: &mut [i32]) {
+    for i in 0..DEGREE {
+        let e = poly[i] - PRIME;
+        poly[i] = e + ((e >> (WL - 1)) & PRIME);
+    }
+}
+
+/* fully reduces modulo q */
+fn poly_hard_reduce(poly: &mut [i32]) {
+    for i in 0..DEGREE {
+        let mut e = modmul(poly[i], ONE);
+        e = e - PRIME;
+        poly[i] = e + ((e >> (WL - 1)) & PRIME);
+    }
+}
+
+/* API files */
+
+pub fn server_1(mut rng: &mut RAND, sb: &mut [u8], ss: &mut [u8]) {
+    let mut seed: [u8; 32] = [0; 32];
+    let mut array: [u8; 1792] = [0; 1792];
+    let mut s: [i32; DEGREE] = [0; DEGREE];
+    let mut e: [i32; DEGREE] = [0; DEGREE];
+    let mut b: [i32; DEGREE] = [0; DEGREE];
+
+    for i in 0..32 {
+        seed[i] = rng.getbyte();
+    }
+
+    parse(&seed, &mut b);
+
+    error(&mut rng, &mut e);
+    error(&mut rng, &mut s);
+
+    ntt(&mut s);
+    ntt(&mut e);
+    poly_mul(&mut b, &s);
+    poly_add(&mut b, &e);
+    poly_hard_reduce(&mut b);
+
+    redc_it(&mut b);
+    nhs_pack(&b, &mut array);
+
+    for i in 0..32 {
+        sb[i] = seed[i];
+    }
+
+    for i in 0..1792 {
+        sb[i + 32] = array[i];
+    }
+
+    poly_hard_reduce(&mut s);
+    nhs_pack(&s, &mut array);
+
+    for i in 0..1792 {
+        ss[i] = array[i];
+    }
+}
+
+pub fn client(mut rng: &mut RAND, sb: &[u8], uc: &mut [u8], okey: &mut [u8]) {
+    let mut sh = SHA3::new(sha3::HASH256);
+
+    let mut seed: [u8; 32] = [0; 32];
+    let mut array: [u8; 1792] = [0; 1792];
+    let mut key: [u8; 32] = [0; 32];
+    let mut cc: [u8; 384] = [0; 384];
+
+    let mut sd: [i32; DEGREE] = [0; DEGREE];
+    let mut ed: [i32; DEGREE] = [0; DEGREE];
+    let mut u: [i32; DEGREE] = [0; DEGREE];
+    let mut k: [i32; DEGREE] = [0; DEGREE];
+    let mut c: [i32; DEGREE] = [0; DEGREE];
+
+    error(&mut rng, &mut sd);
+    error(&mut rng, &mut ed);
+
+    ntt(&mut sd);
+    ntt(&mut ed);
+
+    for i in 0..32 {
+        seed[i] = sb[i];
+    }
+
+    for i in 0..1792 {
+        array[i] = sb[i + 32];
+    }
+
+    parse(&seed, &mut u);
+
+    poly_mul(&mut u, &sd);
+    poly_add(&mut u, &ed);
+    poly_hard_reduce(&mut u);
+
+    for i in 0..32 {
+        key[i] = rng.getbyte();
+    }
+
+    for i in 0..32 {
+        sh.process(key[i]);
+    }
+    sh.hash(&mut key);
+
+    encode(&key, &mut k);
+
+    nhs_unpack(&array, &mut c);
+    nres_it(&mut c);
+
+    poly_mul(&mut c, &sd);
+    intt(&mut c);
+    error(&mut rng, &mut ed);
+    poly_add(&mut c, &ed);
+    poly_add(&mut c, &k);
+
+    compress(&c, &mut cc);
+
+    sh = SHA3::new(sha3::HASH256);
+    for i in 0..32 {
+        sh.process(key[i]);
+    }
+    sh.hash(&mut key);
+
+    for i in 0..32 {
+        okey[i] = key[i];
+    }
+
+    redc_it(&mut u);
+    nhs_pack(&u, &mut array);
+
+    for i in 0..1792 {
+        uc[i] = array[i];
+    }
+
+    for i in 0..384 {
+        uc[i + 1792] = cc[i];
+    }
+}
+
+pub fn server_2(ss: &[u8], uc: &[u8], okey: &mut [u8]) {
+    let mut sh = SHA3::new(sha3::HASH256);
+
+    let mut s: [i32; DEGREE] = [0; DEGREE];
+    let mut k: [i32; DEGREE] = [0; DEGREE];
+    let mut c: [i32; DEGREE] = [0; DEGREE];
+
+    let mut array: [u8; 1792] = [0; 1792];
+    let mut key: [u8; 32] = [0; 32];
+    let mut cc: [u8; 384] = [0; 384];
+
+    for i in 0..1792 {
+        array[i] = uc[i];
+    }
+
+    nhs_unpack(&array, &mut k);
+    nres_it(&mut k);
+
+    for i in 0..384 {
+        cc[i] = uc[i + 1792];
+    }
+
+    decompress(&cc, &mut c);
+
+    for i in 0..1792 {
+        array[i] = ss[i];
+    }
+
+    nhs_unpack(&array, &mut s);
+
+    poly_mul(&mut k, &s);
+    intt(&mut k);
+    poly_rsub(&mut k, &c);
+    poly_soft_reduce(&mut k);
+
+    decode(&k, &mut key);
+
+    for i in 0..32 {
+        sh.process(key[i]);
+    }
+    sh.hash(&mut key);
+
+    for i in 0..32 {
+        okey[i] = key[i];
+    }
+}
+
+/*
+fn main() {
+    let x=3;
+    let y=redc(x as u64);
+    let z=redc((y as u64)*(R2MODP));
+    println!("{:02x}",z);
+
+    let mut a:[i32;1024]=[0;1024];
+    for i in 0..1024 {a[i]=i as i32}
+
+    ntt(&mut a);
+
+    for i in 0..1024 {a[i]=modmul(a[i],ONE)}
+
+    intt(&mut a);
+
+    println!("{:02x}",a[7]);
+
+}
+*/
diff --git a/src/pair.rs b/src/pair.rs
new file mode 100644
index 0000000..2050296
--- /dev/null
+++ b/src/pair.rs
@@ -0,0 +1,755 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+
+use super::fp::FP;
+use super::ecp::ECP;
+use super::fp2::FP2;
+use super::ecp2::ECP2;
+use super::fp4::FP4;
+use super::fp12;
+use super::fp12::FP12;
+use super::big::BIG;
+use super::dbig::DBIG;
+use super::ecp;
+use super::rom;
+use types::{SexticTwist, CurvePairingType, SignOfX};
+
+#[allow(non_snake_case)]
+fn linedbl(A: &mut ECP2, qx: &FP, qy: &FP) -> FP12 {
+    let mut a = FP4::new();
+    let mut b = FP4::new();
+    let mut c = FP4::new();
+
+    let mut xx = FP2::new_copy(&A.getpx()); //X
+    let mut yy = FP2::new_copy(&A.getpy()); //Y
+    let mut zz = FP2::new_copy(&A.getpz()); //Z
+    let mut yz = FP2::new_copy(&yy); //Y
+    yz.mul(&zz); //YZ
+    xx.sqr(); //X^2
+    yy.sqr(); //Y^2
+    zz.sqr(); //Z^2
+
+    yz.imul(4);
+    yz.neg();
+    yz.norm(); //-2YZ
+    yz.pmul(qy); //-2YZ.Ys
+
+    xx.imul(6); //3X^2
+    xx.pmul(qx); //3X^2.Xs
+
+    let sb = 3 * rom::CURVE_B_I;
+    zz.imul(sb);
+    if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+        zz.div_ip2();
+    }
+    if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+        zz.mul_ip();
+        zz.dbl();
+        yz.mul_ip();
+        yz.norm();
+    }
+
+    zz.norm(); // 3b.Z^2
+
+    yy.dbl();
+    zz.sub(&yy);
+    zz.norm(); // 3b.Z^2-Y^2
+
+    a.copy(&FP4::new_fp2s(&yz, &zz)); // -2YZ.Ys | 3b.Z^2-Y^2 | 3X^2.Xs
+    if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+        b.copy(&FP4::new_fp2(&xx)); // L(0,1) | L(0,0) | L(1,0)
+    }
+    if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+        c.copy(&FP4::new_fp2(&xx));
+        c.times_i();
+    }
+    A.dbl();
+    let mut res= FP12::new_fp4s(&a, &b, &c);
+    res.settype(fp12::SPARSER);
+    return res;
+}
+
+#[allow(non_snake_case)]
+fn lineadd(A: &mut ECP2, B: &ECP2, qx: &FP, qy: &FP) -> FP12 {
+    let mut a = FP4::new();
+    let mut b = FP4::new();
+    let mut c = FP4::new();
+
+    let mut x1 = FP2::new_copy(&A.getpx()); // X1
+    let mut y1 = FP2::new_copy(&A.getpy()); // Y1
+    let mut t1 = FP2::new_copy(&A.getpz()); // Z1
+    let mut t2 = FP2::new_copy(&A.getpz()); // Z1
+
+    t1.mul(&B.getpy()); // T1=Z1.Y2
+    t2.mul(&B.getpx()); // T2=Z1.X2
+
+    x1.sub(&t2);
+    x1.norm(); // X1=X1-Z1.X2
+    y1.sub(&t1);
+    y1.norm(); // Y1=Y1-Z1.Y2
+
+    t1.copy(&x1); // T1=X1-Z1.X2
+    x1.pmul(qy); // X1=(X1-Z1.X2).Ys
+    if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+        x1.mul_ip();
+        x1.norm();
+    }
+
+    t1.mul(&B.getpy()); // T1=(X1-Z1.X2).Y2
+
+    t2.copy(&y1); // T2=Y1-Z1.Y2
+    t2.mul(&B.getpx()); // T2=(Y1-Z1.Y2).X2
+    t2.sub(&t1);
+    t2.norm(); // T2=(Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2
+    y1.pmul(qx);
+    y1.neg();
+    y1.norm(); // Y1=-(Y1-Z1.Y2).Xs
+
+    a.copy(&FP4::new_fp2s(&x1, &t2)); // (X1-Z1.X2).Ys  |  (Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2  | - (Y1-Z1.Y2).Xs
+    if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+        b.copy(&FP4::new_fp2(&y1));
+    }
+    if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+        c.copy(&FP4::new_fp2(&y1));
+        c.times_i();
+    }
+
+    A.add(B);
+    let mut res= FP12::new_fp4s(&a, &b, &c);
+    res.settype(fp12::SPARSER);
+    return res;
+}
+
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+#[allow(non_snake_case)]
+fn lbits(n3: &mut BIG,n: &mut BIG) -> usize {
+    n.copy(&BIG::new_ints(&rom::CURVE_BNX));
+    if ecp::CURVE_PAIRING_TYPE==CurvePairingType::BN {
+        n.pmul(6);
+        if ecp::SIGN_OF_X==SignOfX::POSITIVEX {
+            n.inc(2);
+        } else {
+            n.dec(2);
+        }
+    }
+    n.norm();
+    n3.copy(&n);
+    n3.pmul(3);
+    n3.norm();
+    return n3.nbits();
+}
+
+/* prepare for multi-pairing */
+pub fn initmp() -> [FP12; rom::ATE_BITS] {
+    let r: [FP12; rom::ATE_BITS] = [FP12::new_int(1); rom::ATE_BITS];
+    return r
+}
+
+/* basic Miller loop */
+pub fn miller(r:&[FP12]) -> FP12 {
+    let mut res=FP12::new_int(1);
+    for i in (1..rom::ATE_BITS).rev() {
+        res.sqr();
+        res.ssmul(&r[i]);
+    }
+
+    if ecp::SIGN_OF_X==SignOfX::NEGATIVEX {
+        res.conj();
+    }
+    res.ssmul(&r[0]);
+    return res;
+}
+
+/* Accumulate another set of line functions for n-pairing */
+#[allow(non_snake_case)]
+pub fn another(r:&mut [FP12],P1: &ECP2,Q1: &ECP) {
+    let mut f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
+    let mut n = BIG::new();
+    let mut n3 = BIG::new();
+    let mut K = ECP2::new();
+
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+    let mut P = ECP2::new();
+    P.copy(P1);
+    P.affine();
+    let mut Q = ECP::new();
+    Q.copy(Q1);
+    Q.affine();
+
+    if ecp::CURVE_PAIRING_TYPE==CurvePairingType::BN {
+        if ecp::SEXTIC_TWIST==SexticTwist::M_TYPE {
+            f.inverse();
+            f.norm();
+        }
+    }
+
+    let qx = FP::new_copy(&Q.getpx());
+    let qy = FP::new_copy(&Q.getpy());
+    let mut A = ECP2::new();
+
+    A.copy(&P);
+    let mut NP = ECP2::new();
+    NP.copy(&P);
+    NP.neg();
+
+    let nb=lbits(&mut n3,&mut n);
+
+    for i in (1..nb-1).rev() {
+        let mut lv=linedbl(&mut A,&qx,&qy);
+
+	let bt=n3.bit(i)-n.bit(i);
+        if bt==1 {
+            let lv2=lineadd(&mut A,&P,&qx,&qy);
+            lv.smul(&lv2);
+        }
+        if bt==-1 {
+            let lv2=lineadd(&mut A,&NP,&qx,&qy);
+            lv.smul(&lv2);
+        }
+        r[i].ssmul(&lv);
+    }
+
+/* R-ate fixup required for BN curves */
+    if ecp::CURVE_PAIRING_TYPE==CurvePairingType::BN {
+        if ecp::SIGN_OF_X==SignOfX::NEGATIVEX {
+            A.neg();
+        }
+        K.copy(&P);
+        K.frob(&f);
+        let mut lv=lineadd(&mut A,&K,&qx,&qy);
+        K.frob(&f);
+        K.neg();
+        let lv2=lineadd(&mut A,&K,&qx,&qy);
+        lv.smul(&lv2);
+	r[0].ssmul(&lv);
+    } 
+}
+
+#[allow(non_snake_case)]
+/* Optimal R-ate pairing */
+pub fn ate(P1: &ECP2, Q1: &ECP) -> FP12 {
+    let mut f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
+    let mut n = BIG::new();
+    let mut n3 = BIG::new();
+    let mut K = ECP2::new();
+
+    if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BN {
+        if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+            f.inverse();
+            f.norm();
+        }
+    } 
+    let mut P = ECP2::new();
+    P.copy(P1);
+    P.affine();
+    let mut Q = ECP::new();
+    Q.copy(Q1);
+    Q.affine();
+
+    let qx = FP::new_copy(&Q.getpx());
+    let qy = FP::new_copy(&Q.getpy());
+
+    let mut A = ECP2::new();
+    let mut r = FP12::new_int(1);
+
+    A.copy(&P);
+    let mut NP = ECP2::new();
+    NP.copy(&P);
+    NP.neg();
+
+    let nb=lbits(&mut n3,&mut n);
+
+    for i in (1..nb - 1).rev() {
+        r.sqr();
+        let mut lv = linedbl(&mut A, &qx, &qy);
+        let bt = n3.bit(i) - n.bit(i);
+        if bt == 1 {
+            let lv2 = lineadd(&mut A, &P, &qx, &qy);
+            lv.smul(&lv2);
+        }
+        if bt == -1 {
+            let lv2 = lineadd(&mut A, &NP, &qx, &qy);
+            lv.smul(&lv2);
+        }
+        r.ssmul(&lv);
+    }
+
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        r.conj();
+    }
+
+    /* R-ate fixup required for BN curves */
+
+    if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BN {
+        if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+            A.neg();
+        }
+
+        K.copy(&P);
+        K.frob(&f);
+
+        let mut lv = lineadd(&mut A, &K, &qx, &qy);
+        K.frob(&f);
+        K.neg();
+        let lv2 = lineadd(&mut A, &K, &qx, &qy);
+	lv.smul(&lv2);
+        r.ssmul(&lv);
+    }
+
+    return r;
+}
+
+#[allow(non_snake_case)]
+/* Optimal R-ate double pairing e(P,Q).e(R,S) */
+pub fn ate2(P1: &ECP2, Q1: &ECP, R1: &ECP2, S1: &ECP) -> FP12 {
+    let mut f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
+    let mut n = BIG::new();
+    let mut n3 = BIG::new();
+    let mut K = ECP2::new();
+
+    if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BN {
+        if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+            f.inverse();
+            f.norm();
+        }
+    } 
+
+    let mut P = ECP2::new();
+    P.copy(P1);
+    P.affine();
+    let mut Q = ECP::new();
+    Q.copy(Q1);
+    Q.affine();
+    let mut R = ECP2::new();
+    R.copy(R1);
+    R.affine();
+    let mut S = ECP::new();
+    S.copy(S1);
+    S.affine();
+
+    let qx = FP::new_copy(&Q.getpx());
+    let qy = FP::new_copy(&Q.getpy());
+
+    let sx = FP::new_copy(&S.getpx());
+    let sy = FP::new_copy(&S.getpy());
+
+    let mut A = ECP2::new();
+    let mut B = ECP2::new();
+    let mut r = FP12::new_int(1);
+
+    A.copy(&P);
+    B.copy(&R);
+
+    let mut NP = ECP2::new();
+    NP.copy(&P);
+    NP.neg();
+    let mut NR = ECP2::new();
+    NR.copy(&R);
+    NR.neg();
+
+    let nb=lbits(&mut n3,&mut n);
+
+    for i in (1..nb - 1).rev() {
+        r.sqr();
+        let mut lv = linedbl(&mut A, &qx, &qy);
+        let lv2 = linedbl(&mut B, &sx, &sy);
+	lv.smul(&lv2);
+        r.ssmul(&lv);
+        let bt = n3.bit(i) - n.bit(i);
+        if bt == 1 {
+            lv = lineadd(&mut A, &P, &qx, &qy);
+            let lv2 = lineadd(&mut B, &R, &sx, &sy);
+	    lv.smul(&lv2);
+            r.ssmul(&lv);
+        }
+        if bt == -1 {
+            lv = lineadd(&mut A, &NP, &qx, &qy);
+            let lv2 = lineadd(&mut B, &NR, &sx, &sy);
+	    lv.smul(&lv2);
+            r.ssmul(&lv);
+        }
+    }
+
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        r.conj();
+    }
+
+    /* R-ate fixup */
+    if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BN {
+        if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+            A.neg();
+            B.neg();
+        }
+        K.copy(&P);
+        K.frob(&f);
+
+        let mut lv = lineadd(&mut A, &K, &qx, &qy);
+        K.frob(&f);
+        K.neg();
+        let mut lv2 = lineadd(&mut A, &K, &qx, &qy);
+	lv.smul(&lv2);
+        r.ssmul(&lv);
+
+        K.copy(&R);
+        K.frob(&f);
+
+        lv = lineadd(&mut B, &K, &sx, &sy);
+        K.frob(&f);
+        K.neg();
+        lv2 = lineadd(&mut B, &K, &sx, &sy);
+	lv.smul(&lv2);
+        r.ssmul(&lv);
+
+    }
+
+    return r;
+}
+
+/* final exponentiation - keep separate for multi-pairings and to avoid thrashing stack */
+pub fn fexp(m: &FP12) -> FP12 {
+    let f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
+    let mut x = BIG::new_ints(&rom::CURVE_BNX);
+    let mut r = FP12::new_copy(m);
+
+    /* Easy part of final exp */
+    let mut lv = FP12::new_copy(&r);
+    lv.inverse();
+    r.conj();
+
+    r.mul(&lv);
+    lv.copy(&r);
+    r.frob(&f);
+    r.frob(&f);
+    r.mul(&lv);
+//    if r.isunity() {
+//	r.zero();
+//	return r;
+//    }
+
+    /* Hard part of final exp */
+    if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BN {
+        lv.copy(&r);
+        lv.frob(&f);
+        let mut x0 = FP12::new_copy(&lv);
+        x0.frob(&f);
+        lv.mul(&r);
+        x0.mul(&lv);
+        x0.frob(&f);
+        let mut x1 = FP12::new_copy(&r);
+        x1.conj();
+        let mut x4 = r.pow(&mut x);
+        if ecp::SIGN_OF_X == SignOfX::POSITIVEX {
+            x4.conj();
+        }
+
+        let mut x3 = FP12::new_copy(&x4);
+        x3.frob(&f);
+
+        let mut x2 = x4.pow(&mut x);
+        if ecp::SIGN_OF_X == SignOfX::POSITIVEX {
+            x2.conj();
+        }
+        let mut x5 = FP12::new_copy(&x2);
+        x5.conj();
+        lv = x2.pow(&mut x);
+        if ecp::SIGN_OF_X == SignOfX::POSITIVEX {
+            lv.conj();
+        }
+        x2.frob(&f);
+        r.copy(&x2);
+        r.conj();
+
+        x4.mul(&r);
+        x2.frob(&f);
+
+        r.copy(&lv);
+        r.frob(&f);
+        lv.mul(&r);
+
+        lv.usqr();
+        lv.mul(&x4);
+        lv.mul(&x5);
+        r.copy(&x3);
+        r.mul(&x5);
+        r.mul(&lv);
+        lv.mul(&x2);
+        r.usqr();
+        r.mul(&lv);
+        r.usqr();
+        lv.copy(&r);
+        lv.mul(&x1);
+        r.mul(&x0);
+        lv.usqr();
+        r.mul(&lv);
+        r.reduce();
+    } else {
+        // Ghamman & Fouotsa Method
+
+        let mut y0 = FP12::new_copy(&r);
+        y0.usqr();
+        let mut y1 = y0.pow(&mut x);
+        if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+            y1.conj();
+        }
+        x.fshr(1);
+        let mut y2 = y1.pow(&mut x);
+        if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+            y2.conj();
+        }
+        x.fshl(1);
+        let mut y3 = FP12::new_copy(&r);
+        y3.conj();
+        y1.mul(&y3);
+
+        y1.conj();
+        y1.mul(&y2);
+
+        y2 = y1.pow(&mut x);
+        if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+            y2.conj();
+        }
+        y3 = y2.pow(&mut x);
+        if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+            y3.conj();
+        }
+        y1.conj();
+        y3.mul(&y1);
+
+        y1.conj();
+        y1.frob(&f);
+        y1.frob(&f);
+        y1.frob(&f);
+        y2.frob(&f);
+        y2.frob(&f);
+        y1.mul(&y2);
+
+        y2 = y3.pow(&mut x);
+        if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+            y2.conj();
+        }
+        y2.mul(&y0);
+        y2.mul(&r);
+
+        y1.mul(&y2);
+        y2.copy(&y3);
+        y2.frob(&f);
+        y1.mul(&y2);
+        r.copy(&y1);
+        r.reduce();
+    }
+    return r;
+}
+
+#[allow(non_snake_case)]
+/* GLV method */
+fn glv(e: &BIG) -> [BIG; 2] {
+    let mut u: [BIG; 2] = [BIG::new(), BIG::new()];
+    if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BN {
+        let mut t = BIG::new();
+        let q = BIG::new_ints(&rom::CURVE_ORDER);
+        let mut v: [BIG; 2] = [BIG::new(), BIG::new()];
+
+        for i in 0..2 {
+            t.copy(&BIG::new_ints(&rom::CURVE_W[i])); // why not just t=new BIG(ROM.CURVE_W[i]);
+            let mut d: DBIG = BIG::mul(&t, e);
+            v[i].copy(&d.div(&q));
+        }
+        u[0].copy(&e);
+        for i in 0..2 {
+            for j in 0..2 {
+                t = BIG::new_ints(&rom::CURVE_SB[j][i]);
+                t = BIG::modmul(&mut v[j], &mut t, &q);
+                u[i].add(&q);
+                u[i].sub(&t);
+                u[i].rmod(&q);
+            }
+        }
+    } else {
+        let q = BIG::new_ints(&rom::CURVE_ORDER);
+        let x = BIG::new_ints(&rom::CURVE_BNX);
+        let x2 = BIG::smul(&x, &x);
+        u[0].copy(&e);
+        u[0].rmod(&x2);
+        u[1].copy(&e);
+        u[1].div(&x2);
+        u[1].rsub(&q);
+    }
+    return u;
+}
+
+#[allow(non_snake_case)]
+/* Galbraith & Scott Method */
+pub fn gs(e: &BIG) -> [BIG; 4] {
+    let mut u: [BIG; 4] = [BIG::new(), BIG::new(), BIG::new(), BIG::new()];
+    if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BN {
+        let mut t = BIG::new();
+        let q = BIG::new_ints(&rom::CURVE_ORDER);
+
+        let mut v: [BIG; 4] = [BIG::new(), BIG::new(), BIG::new(), BIG::new()];
+        for i in 0..4 {
+            t.copy(&BIG::new_ints(&rom::CURVE_WB[i]));
+            let mut d: DBIG = BIG::mul(&t, e);
+            v[i].copy(&d.div(&q));
+        }
+        u[0].copy(&e);
+        for i in 0..4 {
+            for j in 0..4 {
+                t = BIG::new_ints(&rom::CURVE_BB[j][i]);
+                t = BIG::modmul(&mut v[j], &mut t, &q);
+                u[i].add(&q);
+                u[i].sub(&t);
+                u[i].rmod(&q);
+            }
+        }
+    } else {
+        let q = BIG::new_ints(&rom::CURVE_ORDER);
+        let x = BIG::new_ints(&rom::CURVE_BNX);
+        let mut w = BIG::new_copy(&e);
+        for i in 0..3 {
+            u[i].copy(&w);
+            u[i].rmod(&x);
+            w.div(&x);
+        }
+        u[3].copy(&w);
+        if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+            let mut t = BIG::new();
+            t.copy(&BIG::modneg(&mut u[1], &q));
+            u[1].copy(&t);
+            t.copy(&BIG::modneg(&mut u[3], &q));
+            u[3].copy(&t);
+        }
+    }
+    return u;
+}
+
+#[allow(non_snake_case)]
+/* Multiply P by e in group G1 */
+pub fn g1mul(P: &ECP, e: &mut BIG) -> ECP {
+    let mut R = ECP::new();
+    if rom::USE_GLV {
+        R.copy(P);
+        let mut Q = ECP::new();
+        Q.copy(P);
+        Q.affine();
+        let q = BIG::new_ints(&rom::CURVE_ORDER);
+        let mut cru = FP::new_big(&BIG::new_ints(&rom::CURVE_CRU));
+        let mut u = glv(e);
+        Q.mulx(&mut cru);
+
+        let mut np = u[0].nbits();
+        let mut t: BIG = BIG::modneg(&mut u[0], &q);
+        let mut nn = t.nbits();
+        if nn < np {
+            u[0].copy(&t);
+            R.neg();
+        }
+
+        np = u[1].nbits();
+        t = BIG::modneg(&mut u[1], &q);
+        nn = t.nbits();
+        if nn < np {
+            u[1].copy(&t);
+            Q.neg();
+        }
+        u[0].norm();
+        u[1].norm();
+        R = R.mul2(&u[0], &mut Q, &u[1]);
+    } else {
+        R = P.mul(e);
+    }
+    return R;
+}
+
+#[allow(non_snake_case)]
+/* Multiply P by e in group G2 */
+pub fn g2mul(P: &ECP2, e: &BIG) -> ECP2 {
+    let mut R = ECP2::new();
+    if rom::USE_GS_G2 {
+        let mut Q: [ECP2; 4] = [ECP2::new(), ECP2::new(), ECP2::new(), ECP2::new()];
+        let mut f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
+        let q = BIG::new_ints(&rom::CURVE_ORDER);
+        let mut u = gs(e);
+        let mut T = ECP2::new();
+
+        if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+            f.inverse();
+            f.norm();
+        }
+
+        let mut t = BIG::new();
+        Q[0].copy(&P);
+        for i in 1..4 {
+            T.copy(&Q[i - 1]);
+            Q[i].copy(&T);
+            Q[i].frob(&f);
+        }
+        for i in 0..4 {
+            let np = u[i].nbits();
+            t.copy(&BIG::modneg(&mut u[i], &q));
+            let nn = t.nbits();
+            if nn < np {
+                u[i].copy(&t);
+                Q[i].neg();
+            }
+            u[i].norm();
+        }
+
+        R.copy(&ECP2::mul4(&mut Q, &u));
+    } else {
+        R.copy(&P.mul(e));
+    }
+    return R;
+}
+
+/* f=f^e */
+/* Note that this method requires a lot of RAM! Better to use compressed XTR method, see FP4.java */
+pub fn gtpow(d: &FP12, e: &BIG) -> FP12 {
+    let mut r = FP12::new();
+    if rom::USE_GS_GT {
+        let mut g: [FP12; 4] = [FP12::new(), FP12::new(), FP12::new(), FP12::new()];
+        let f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
+        let q = BIG::new_ints(&rom::CURVE_ORDER);
+        let mut t = BIG::new();
+        let mut u = gs(e);
+        let mut w = FP12::new();
+
+        g[0].copy(&d);
+        for i in 1..4 {
+            w.copy(&g[i - 1]);
+            g[i].copy(&w);
+            g[i].frob(&f);
+        }
+        for i in 0..4 {
+            let np = u[i].nbits();
+            t.copy(&BIG::modneg(&mut u[i], &q));
+            let nn = t.nbits();
+            if nn < np {
+                u[i].copy(&t);
+                g[i].conj();
+            }
+            u[i].norm();
+        }
+        r.copy(&FP12::pow4(&mut g, &u));
+    } else {
+        r.copy(&d.pow(e));
+    }
+    return r;
+}
diff --git a/src/pair192.rs b/src/pair192.rs
new file mode 100644
index 0000000..4310b6b
--- /dev/null
+++ b/src/pair192.rs
@@ -0,0 +1,606 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+
+use super::fp::FP;
+use super::ecp::ECP;
+use super::fp2::FP2;
+use super::ecp4::ECP4;
+use super::fp4::FP4;
+use super::fp8::FP8;
+use super::fp24;
+use super::fp24::FP24;
+use super::big::BIG;
+use super::ecp;
+use super::rom;
+use types::{SexticTwist, SignOfX};
+
+#[allow(non_snake_case)]
+fn linedbl(A: &mut ECP4, qx: &FP, qy: &FP) -> FP24 {
+    let mut a = FP8::new();
+    let mut b = FP8::new();
+    let mut c = FP8::new();
+
+    let mut xx = FP4::new_copy(&A.getpx()); //X
+    let mut yy = FP4::new_copy(&A.getpy()); //Y
+    let mut zz = FP4::new_copy(&A.getpz()); //Z
+    let mut yz = FP4::new_copy(&yy); //Y
+    yz.mul(&zz); //YZ
+    xx.sqr(); //X^2
+    yy.sqr(); //Y^2
+    zz.sqr(); //Z^2
+
+    yz.imul(4);
+    yz.neg();
+    yz.norm(); //-2YZ
+    yz.qmul(qy); //-2YZ.Ys
+
+    xx.imul(6); //3X^2
+    xx.qmul(qx); //3X^2.Xs
+
+    let sb = 3 * rom::CURVE_B_I;
+    zz.imul(sb);
+    if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+        zz.div_2i();
+    }
+    if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+        zz.times_i();
+        zz.dbl();
+        yz.times_i();
+    }
+
+    zz.norm(); // 3b.Z^2
+
+    yy.dbl();
+    zz.sub(&yy);
+    zz.norm(); // 3b.Z^2-Y^2
+
+    a.copy(&FP8::new_fp4s(&yz, &zz)); // -2YZ.Ys | 3b.Z^2-Y^2 | 3X^2.Xs
+    if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+        b.copy(&FP8::new_fp4(&xx)); // L(0,1) | L(0,0) | L(1,0)
+    }
+    if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+        c.copy(&FP8::new_fp4(&xx));
+        c.times_i();
+    }
+    A.dbl();
+    let mut res= FP24::new_fp8s(&a, &b, &c);
+    res.settype(fp24::SPARSER);
+    return res;
+}
+
+#[allow(non_snake_case)]
+fn lineadd(A: &mut ECP4, B: &ECP4, qx: &FP, qy: &FP) -> FP24 {
+    let mut a = FP8::new();
+    let mut b = FP8::new();
+    let mut c = FP8::new();
+
+    let mut x1 = FP4::new_copy(&A.getpx()); // X1
+    let mut y1 = FP4::new_copy(&A.getpy()); // Y1
+    let mut t1 = FP4::new_copy(&A.getpz()); // Z1
+    let mut t2 = FP4::new_copy(&A.getpz()); // Z1
+
+    t1.mul(&B.getpy()); // T1=Z1.Y2
+    t2.mul(&B.getpx()); // T2=Z1.X2
+
+    x1.sub(&t2);
+    x1.norm(); // X1=X1-Z1.X2
+    y1.sub(&t1);
+    y1.norm(); // Y1=Y1-Z1.Y2
+
+    t1.copy(&x1); // T1=X1-Z1.X2
+    x1.qmul(qy); // X1=(X1-Z1.X2).Ys
+    if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+        x1.times_i();
+    }
+
+    t1.mul(&B.getpy()); // T1=(X1-Z1.X2).Y2
+
+    t2.copy(&y1); // T2=Y1-Z1.Y2
+    t2.mul(&B.getpx()); // T2=(Y1-Z1.Y2).X2
+    t2.sub(&t1);
+    t2.norm(); // T2=(Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2
+    y1.qmul(qx);
+    y1.neg();
+    y1.norm(); // Y1=-(Y1-Z1.Y2).Xs
+
+    a.copy(&FP8::new_fp4s(&x1, &t2)); // (X1-Z1.X2).Ys  |  (Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2  | - (Y1-Z1.Y2).Xs
+    if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+        b.copy(&FP8::new_fp4(&y1));
+    }
+    if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+        c.copy(&FP8::new_fp4(&y1));
+        c.times_i();
+    }
+
+    A.add(B);
+    let mut res= FP24::new_fp8s(&a, &b, &c);
+    res.settype(fp24::SPARSER);
+    return res;
+}
+
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+#[allow(non_snake_case)]
+fn lbits(n3: &mut BIG,n: &mut BIG) -> usize {
+    n.copy(&BIG::new_ints(&rom::CURVE_BNX));
+    n3.copy(&n);
+    n3.pmul(3);
+    n3.norm();
+    return n3.nbits();
+}
+
+/* prepare for multi-pairing */
+pub fn initmp() -> [FP24; rom::ATE_BITS] {
+    let r: [FP24; rom::ATE_BITS] = [FP24::new_int(1); rom::ATE_BITS];
+    return r
+}
+
+/* basic Miller loop */
+pub fn miller(r:&[FP24]) -> FP24 {
+    let mut res=FP24::new_int(1);
+    for i in (1..rom::ATE_BITS).rev() {
+        res.sqr();
+        res.ssmul(&r[i]);
+    }
+
+    if ecp::SIGN_OF_X==SignOfX::NEGATIVEX {
+        res.conj();
+    }
+    res.ssmul(&r[0]);
+    return res;
+}
+
+/* Accumulate another set of line functions for n-pairing */
+#[allow(non_snake_case)]
+pub fn another(r:&mut [FP24],P1: &ECP4,Q1: &ECP) {
+    let mut n = BIG::new();
+    let mut n3 = BIG::new();
+    
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+    let mut P = ECP4::new();
+    P.copy(P1);
+    P.affine();
+    let mut Q = ECP::new();
+    Q.copy(Q1);
+    Q.affine();
+
+    let qx = FP::new_copy(&Q.getpx());
+    let qy = FP::new_copy(&Q.getpy());
+    let mut A = ECP4::new();
+
+    A.copy(&P);
+    let mut NP = ECP4::new();
+    NP.copy(&P);
+    NP.neg();
+
+    let nb=lbits(&mut n3,&mut n);
+
+    for i in (1..nb-1).rev() {
+        let mut lv=linedbl(&mut A,&qx,&qy);
+
+	let bt=n3.bit(i)-n.bit(i);
+        if bt==1 {
+            let lv2=lineadd(&mut A,&P,&qx,&qy);
+            lv.smul(&lv2);
+        }
+        if bt==-1 {
+            let lv2=lineadd(&mut A,&NP,&qx,&qy);
+            lv.smul(&lv2);
+        }
+        r[i].ssmul(&lv);
+    }
+}
+
+#[allow(non_snake_case)]
+/* Optimal R-ate pairing */
+pub fn ate(P1: &ECP4, Q1: &ECP) -> FP24 {
+    let mut n = BIG::new();
+    let mut n3 = BIG::new();
+
+    let mut P = ECP4::new();
+    P.copy(P1);
+    P.affine();
+    let mut Q = ECP::new();
+    Q.copy(Q1);
+    Q.affine();
+
+    let qx = FP::new_copy(&Q.getpx());
+    let qy = FP::new_copy(&Q.getpy());
+
+    let mut A = ECP4::new();
+    let mut r = FP24::new_int(1);
+
+    A.copy(&P);
+    let mut NP = ECP4::new();
+    NP.copy(&P);
+    NP.neg();
+
+    let nb=lbits(&mut n3,&mut n);
+
+    for i in (1..nb - 1).rev() {
+        r.sqr();
+
+        let mut lv = linedbl(&mut A, &qx, &qy);
+
+        let bt = n3.bit(i) - n.bit(i);
+        if bt == 1 {
+            let lv2 = lineadd(&mut A, &P, &qx, &qy);
+            lv.smul(&lv2);
+        }
+        if bt == -1 {
+            let lv2 = lineadd(&mut A, &NP, &qx, &qy);
+            lv.smul(&lv2);
+        }
+        r.ssmul(&lv);
+    }
+
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        r.conj();
+    }
+
+    return r;
+}
+
+#[allow(non_snake_case)]
+/* Optimal R-ate double pairing e(P,Q).e(R,S) */
+pub fn ate2(P1: &ECP4, Q1: &ECP, R1: &ECP4, S1: &ECP) -> FP24 {
+    let mut n = BIG::new();
+    let mut n3 = BIG::new();
+
+    let mut P = ECP4::new();
+    P.copy(P1);
+    P.affine();
+    let mut Q = ECP::new();
+    Q.copy(Q1);
+    Q.affine();
+    let mut R = ECP4::new();
+    R.copy(R1);
+    R.affine();
+    let mut S = ECP::new();
+    S.copy(S1);
+    S.affine();
+
+    let qx = FP::new_copy(&Q.getpx());
+    let qy = FP::new_copy(&Q.getpy());
+
+    let sx = FP::new_copy(&S.getpx());
+    let sy = FP::new_copy(&S.getpy());
+
+    let mut A = ECP4::new();
+    let mut B = ECP4::new();
+    let mut r = FP24::new_int(1);
+
+    A.copy(&P);
+    B.copy(&R);
+
+    let mut NP = ECP4::new();
+    NP.copy(&P);
+    NP.neg();
+    let mut NR = ECP4::new();
+    NR.copy(&R);
+    NR.neg();
+
+    let nb=lbits(&mut n3,&mut n);
+
+    for i in (1..nb - 1).rev() {
+        r.sqr();
+        let mut lv = linedbl(&mut A, &qx, &qy);
+        let lv2 = linedbl(&mut B, &sx, &sy);
+	lv.smul(&lv2);
+        r.ssmul(&lv);
+        let bt = n3.bit(i) - n.bit(i);
+        if bt == 1 {
+            lv = lineadd(&mut A, &P, &qx, &qy);
+            let lv2 = lineadd(&mut B, &R, &sx, &sy);
+	    lv.smul(&lv2);
+            r.ssmul(&lv);
+        }
+        if bt == -1 {
+            lv = lineadd(&mut A, &NP, &qx, &qy);
+            let lv2 = lineadd(&mut B, &NR, &sx, &sy);
+	    lv.smul(&lv2);
+            r.ssmul(&lv);
+        }
+    }
+
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        r.conj();
+    }
+
+    return r;
+}
+
+/* final exponentiation - keep separate for multi-pairings and to avoid thrashing stack */
+pub fn fexp(m: &FP24) -> FP24 {
+    let f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
+    let mut x = BIG::new_ints(&rom::CURVE_BNX);
+    let mut r = FP24::new_copy(m);
+
+    /* Easy part of final exp */
+    let mut lv = FP24::new_copy(&r);
+    lv.inverse();
+    r.conj();
+
+    r.mul(&lv);
+    lv.copy(&r);
+    r.frob(&f, 4);
+    r.mul(&lv);
+//    if r.isunity() {
+//	r.zero();
+//	return r;
+//    }
+    /* Hard part of final exp */
+    // Ghamman & Fouotsa Method
+
+    let mut t7 = FP24::new_copy(&r);
+    t7.usqr();
+    let mut t1 = t7.pow(&mut x);
+
+    x.fshr(1);
+    let mut t2 = t1.pow(&mut x);
+    x.fshl(1);
+
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t1.conj();
+    }
+    let mut t3 = FP24::new_copy(&t1);
+    t3.conj();
+    t2.mul(&t3);
+    t2.mul(&r);
+
+    t3.copy(&t2.pow(&mut x));
+    let mut t4 = t3.pow(&mut x);
+    let mut t5 = t4.pow(&mut x);
+
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t3.conj();
+        t5.conj();
+    }
+
+    t3.frob(&f, 6);
+    t4.frob(&f, 5);
+    t3.mul(&t4);
+
+    let mut t6 = t5.pow(&mut x);
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t6.conj();
+    }
+
+    t5.frob(&f, 4);
+    t3.mul(&t5);
+
+    let mut t0 = FP24::new_copy(&t2);
+    t0.conj();
+    t6.mul(&t0);
+
+    t5.copy(&t6);
+    t5.frob(&f, 3);
+
+    t3.mul(&t5);
+    t5.copy(&t6.pow(&mut x));
+    t6.copy(&t5.pow(&mut x));
+
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t5.conj();
+    }
+
+    t0.copy(&t5);
+    t0.frob(&f, 2);
+    t3.mul(&t0);
+    t0.copy(&t6);
+    t0.frob(&f, 1);
+
+    t3.mul(&t0);
+    t5.copy(&t6.pow(&mut x));
+
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t5.conj();
+    }
+    t2.frob(&f, 7);
+
+    t5.mul(&t7);
+    t3.mul(&t2);
+    t3.mul(&t5);
+
+    r.mul(&t3);
+
+    r.reduce();
+    return r;
+}
+
+#[allow(non_snake_case)]
+/* GLV method */
+fn glv(e: &BIG) -> [BIG; 2] {
+    let mut u: [BIG; 2] = [BIG::new(), BIG::new()];
+    let q = BIG::new_ints(&rom::CURVE_ORDER);
+    let mut x = BIG::new_ints(&rom::CURVE_BNX);
+    let x2 = BIG::smul(&x, &x);
+    x.copy(&BIG::smul(&x2, &x2));
+    u[0].copy(&e);
+    u[0].rmod(&x);
+    u[1].copy(&e);
+    u[1].div(&x);
+    u[1].rsub(&q);
+
+    return u;
+}
+
+#[allow(non_snake_case)]
+/* Galbraith & Scott Method */
+pub fn gs(e: &BIG) -> [BIG; 8] {
+    let mut u: [BIG; 8] = [
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+    ];
+    let q = BIG::new_ints(&rom::CURVE_ORDER);
+    let x = BIG::new_ints(&rom::CURVE_BNX);
+    let mut w = BIG::new_copy(&e);
+    for i in 0..7 {
+        u[i].copy(&w);
+        u[i].rmod(&x);
+        w.div(&x);
+    }
+    u[7].copy(&w);
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        let mut t = BIG::new();
+        t.copy(&BIG::modneg(&mut u[1], &q));
+        u[1].copy(&t);
+        t.copy(&BIG::modneg(&mut u[3], &q));
+        u[3].copy(&t);
+        t.copy(&BIG::modneg(&mut u[5], &q));
+        u[5].copy(&t);
+        t.copy(&BIG::modneg(&mut u[7], &q));
+        u[7].copy(&t);
+    }
+    return u;
+}
+
+#[allow(non_snake_case)]
+/* Multiply P by e in group G1 */
+pub fn g1mul(P: &ECP, e: &mut BIG) -> ECP {
+    let mut R = ECP::new();
+    if rom::USE_GLV {
+        R.copy(P);
+        let mut Q = ECP::new();
+        Q.copy(P);
+        Q.affine();
+        let q = BIG::new_ints(&rom::CURVE_ORDER);
+        let mut cru = FP::new_big(&BIG::new_ints(&rom::CURVE_CRU));
+        let mut u = glv(e);
+        Q.mulx(&mut cru);
+
+        let mut np = u[0].nbits();
+        let mut t: BIG = BIG::modneg(&mut u[0], &q);
+        let mut nn = t.nbits();
+        if nn < np {
+            u[0].copy(&t);
+            R.neg();
+        }
+
+        np = u[1].nbits();
+        t = BIG::modneg(&mut u[1], &q);
+        nn = t.nbits();
+        if nn < np {
+            u[1].copy(&t);
+            Q.neg();
+        }
+        u[0].norm();
+        u[1].norm();
+        R = R.mul2(&u[0], &mut Q, &u[1]);
+    } else {
+        R = P.mul(e);
+    }
+    return R;
+}
+
+#[allow(non_snake_case)]
+/* Multiply P by e in group G2 */
+pub fn g2mul(P: &ECP4, e: &BIG) -> ECP4 {
+    let mut R = ECP4::new();
+    if rom::USE_GS_G2 {
+        let mut Q: [ECP4; 8] = [
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+            ECP4::new(),
+        ];
+        let q = BIG::new_ints(&rom::CURVE_ORDER);
+        let mut u = gs(e);
+        let mut T = ECP4::new();
+
+        let f = ECP4::frob_constants();
+
+        let mut t = BIG::new();
+        Q[0].copy(&P);
+        for i in 1..8 {
+            T.copy(&Q[i - 1]);
+            Q[i].copy(&T);
+            Q[i].frob(&f, 1);
+        }
+        for i in 0..8 {
+            let np = u[i].nbits();
+            t.copy(&BIG::modneg(&mut u[i], &q));
+            let nn = t.nbits();
+            if nn < np {
+                u[i].copy(&t);
+                Q[i].neg();
+            }
+            u[i].norm();
+        }
+
+        R.copy(&ECP4::mul8(&mut Q, &u));
+    } else {
+        R.copy(&P.mul(e));
+    }
+    return R;
+}
+
+/* f=f^e */
+/* Note that this method requires a lot of RAM! Better to use compressed XTR method, see FP4.java */
+pub fn gtpow(d: &FP24, e: &BIG) -> FP24 {
+    let mut r = FP24::new();
+    if rom::USE_GS_GT {
+        let mut g: [FP24; 8] = [
+            FP24::new(),
+            FP24::new(),
+            FP24::new(),
+            FP24::new(),
+            FP24::new(),
+            FP24::new(),
+            FP24::new(),
+            FP24::new(),
+        ];
+        let f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
+        let q = BIG::new_ints(&rom::CURVE_ORDER);
+        let mut t = BIG::new();
+        let mut u = gs(e);
+        let mut w = FP24::new();
+
+        g[0].copy(&d);
+        for i in 1..8 {
+            w.copy(&g[i - 1]);
+            g[i].copy(&w);
+            g[i].frob(&f, 1);
+        }
+        for i in 0..8 {
+            let np = u[i].nbits();
+            t.copy(&BIG::modneg(&mut u[i], &q));
+            let nn = t.nbits();
+            if nn < np {
+                u[i].copy(&t);
+                g[i].conj();
+            }
+            u[i].norm();
+        }
+        r.copy(&FP24::pow8(&mut g, &u));
+    } else {
+        r.copy(&d.pow(e));
+    }
+    return r;
+}
diff --git a/src/pair256.rs b/src/pair256.rs
new file mode 100644
index 0000000..7cdca72
--- /dev/null
+++ b/src/pair256.rs
@@ -0,0 +1,722 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+
+use super::fp::FP;
+use super::ecp::ECP;
+use super::fp2::FP2;
+use super::ecp8::ECP8;
+use super::fp8::FP8;
+use super::fp16::FP16;
+use super::fp48;
+use super::fp48::FP48;
+use super::big::BIG;
+use super::ecp;
+use super::rom;
+use types::{SignOfX, SexticTwist};
+
+#[allow(non_snake_case)]
+fn linedbl(A: &mut ECP8, qx: &FP, qy: &FP) -> FP48 {
+    let mut a = FP16::new();
+    let mut b = FP16::new();
+    let mut c = FP16::new();
+
+    let mut xx = FP8::new_copy(&A.getpx()); //X
+    let mut yy = FP8::new_copy(&A.getpy()); //Y
+    let mut zz = FP8::new_copy(&A.getpz()); //Z
+    let mut yz = FP8::new_copy(&yy); //Y
+    yz.mul(&zz); //YZ
+    xx.sqr(); //X^2
+    yy.sqr(); //Y^2
+    zz.sqr(); //Z^2
+
+    yz.imul(4);
+    yz.neg();
+    yz.norm(); //-2YZ
+    yz.tmul(qy); //-2YZ.Ys
+
+    xx.imul(6); //3X^2
+    xx.tmul(qx); //3X^2.Xs
+
+    let sb = 3 * rom::CURVE_B_I;
+    zz.imul(sb);
+    if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+        zz.div_2i();
+    }
+    if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+        zz.times_i();
+        zz.dbl();
+        yz.times_i();
+    }
+
+    zz.norm(); // 3b.Z^2
+
+    yy.dbl();
+    zz.sub(&yy);
+    zz.norm(); // 3b.Z^2-Y^2
+
+    a.copy(&FP16::new_fp8s(&yz, &zz)); // -2YZ.Ys | 3b.Z^2-Y^2 | 3X^2.Xs
+    if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+        b.copy(&FP16::new_fp8(&xx)); // L(0,1) | L(0,0) | L(1,0)
+    }
+    if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+        c.copy(&FP16::new_fp8(&xx));
+        c.times_i();
+    }
+    A.dbl();
+    let mut res= FP48::new_fp16s(&a, &b, &c);
+    res.settype(fp48::SPARSER);
+    return res;
+}
+
+#[allow(non_snake_case)]
+fn lineadd(A: &mut ECP8, B: &ECP8, qx: &FP, qy: &FP) -> FP48 {
+    let mut a = FP16::new();
+    let mut b = FP16::new();
+    let mut c = FP16::new();
+
+    let mut x1 = FP8::new_copy(&A.getpx()); // X1
+    let mut y1 = FP8::new_copy(&A.getpy()); // Y1
+    let mut t1 = FP8::new_copy(&A.getpz()); // Z1
+    let mut t2 = FP8::new_copy(&A.getpz()); // Z1
+
+    t1.mul(&B.getpy()); // T1=Z1.Y2
+    t2.mul(&B.getpx()); // T2=Z1.X2
+
+    x1.sub(&t2);
+    x1.norm(); // X1=X1-Z1.X2
+    y1.sub(&t1);
+    y1.norm(); // Y1=Y1-Z1.Y2
+
+    t1.copy(&x1); // T1=X1-Z1.X2
+    x1.tmul(qy); // X1=(X1-Z1.X2).Ys
+    if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+        x1.times_i();
+    }
+
+    t1.mul(&B.getpy()); // T1=(X1-Z1.X2).Y2
+
+    t2.copy(&y1); // T2=Y1-Z1.Y2
+    t2.mul(&B.getpx()); // T2=(Y1-Z1.Y2).X2
+    t2.sub(&t1);
+    t2.norm(); // T2=(Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2
+    y1.tmul(qx);
+    y1.neg();
+    y1.norm(); // Y1=-(Y1-Z1.Y2).Xs
+
+    a.copy(&FP16::new_fp8s(&x1, &t2)); // (X1-Z1.X2).Ys  |  (Y1-Z1.Y2).X2 - (X1-Z1.X2).Y2  | - (Y1-Z1.Y2).Xs
+    if ecp::SEXTIC_TWIST == SexticTwist::D_TYPE {
+        b.copy(&FP16::new_fp8(&y1));
+    }
+    if ecp::SEXTIC_TWIST == SexticTwist::M_TYPE {
+        c.copy(&FP16::new_fp8(&y1));
+        c.times_i();
+    }
+
+    A.add(B);
+    let mut res= FP48::new_fp16s(&a, &b, &c);
+    res.settype(fp48::SPARSER);
+    return res;
+}
+
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+#[allow(non_snake_case)]
+fn lbits(n3: &mut BIG,n: &mut BIG) -> usize {
+    n.copy(&BIG::new_ints(&rom::CURVE_BNX));
+    n3.copy(&n);
+    n3.pmul(3);
+    n3.norm();
+    return n3.nbits();
+}
+
+/* prepare for multi-pairing */
+pub fn initmp() -> [FP48; rom::ATE_BITS] {
+    let r: [FP48; rom::ATE_BITS] = [FP48::new_int(1); rom::ATE_BITS];
+    return r
+}
+
+/* basic Miller loop */
+pub fn miller(r:&[FP48]) -> FP48 {
+    let mut res=FP48::new_int(1);
+    for i in (1..rom::ATE_BITS).rev() {
+        res.sqr();
+        res.ssmul(&r[i]);
+    }
+
+    if ecp::SIGN_OF_X==SignOfX::NEGATIVEX {
+        res.conj();
+    }
+    res.ssmul(&r[0]);
+    return res;
+}
+
+/* Accumulate another set of line functions for n-pairing */
+#[allow(non_snake_case)]
+pub fn another(r:&mut [FP48],P1: &ECP8,Q1: &ECP) {
+    let mut n = BIG::new();
+    let mut n3 = BIG::new();
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+    let mut P = ECP8::new();
+    P.copy(P1);
+    P.affine();
+    let mut Q = ECP::new();
+    Q.copy(Q1);
+    Q.affine();
+
+    let qx = FP::new_copy(&Q.getpx());
+    let qy = FP::new_copy(&Q.getpy());
+    let mut A = ECP8::new();
+
+    A.copy(&P);
+    let mut NP = ECP8::new();
+    NP.copy(&P);
+    NP.neg();
+
+    let nb=lbits(&mut n3,&mut n);
+
+    for i in (1..nb-1).rev() {
+        let mut lv=linedbl(&mut A,&qx,&qy);
+
+	let bt=n3.bit(i)-n.bit(i);
+        if bt==1 {
+            let lv2=lineadd(&mut A,&P,&qx,&qy);
+            lv.smul(&lv2);
+        }
+        if bt==-1 {
+            let lv2=lineadd(&mut A,&NP,&qx,&qy);
+            lv.smul(&lv2);
+        }
+        r[i].ssmul(&lv);
+    }
+}
+
+#[allow(non_snake_case)]
+/* Optimal R-ate pairing */
+pub fn ate(P1: &ECP8, Q1: &ECP) -> FP48 {
+    let mut n = BIG::new();
+    let mut n3 = BIG::new();
+
+    let mut P = ECP8::new();
+    P.copy(P1);
+    P.affine();
+    let mut Q = ECP::new();
+    Q.copy(Q1);
+    Q.affine();
+
+    let qx = FP::new_copy(&Q.getpx());
+    let qy = FP::new_copy(&Q.getpy());
+
+    let mut A = ECP8::new();
+    let mut r = FP48::new_int(1);
+
+    A.copy(&P);
+    let mut NP = ECP8::new();
+    NP.copy(&P);
+    NP.neg();
+
+    let nb=lbits(&mut n3,&mut n);
+
+    for i in (1..nb - 1).rev() {
+        r.sqr();
+        let mut lv = linedbl(&mut A, &qx, &qy);
+        let bt = n3.bit(i) - n.bit(i);
+        if bt == 1 {
+            let lv2 = lineadd(&mut A, &P, &qx, &qy);
+            lv.smul(&lv2);
+        }
+        if bt == -1 {
+            let lv2 = lineadd(&mut A, &NP, &qx, &qy);
+            lv.smul(&lv2);
+        }
+        r.ssmul(&lv);
+    }
+
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        r.conj();
+    }
+
+    return r;
+}
+
+#[allow(non_snake_case)]
+/* Optimal R-ate double pairing e(P,Q).e(R,S) */
+pub fn ate2(P1: &ECP8, Q1: &ECP, R1: &ECP8, S1: &ECP) -> FP48 {
+    let mut n = BIG::new();
+    let mut n3 = BIG::new();
+
+    let mut P = ECP8::new();
+    P.copy(P1);
+    P.affine();
+    let mut Q = ECP::new();
+    Q.copy(Q1);
+    Q.affine();
+    let mut R = ECP8::new();
+    R.copy(R1);
+    R.affine();
+    let mut S = ECP::new();
+    S.copy(S1);
+    S.affine();
+
+    let qx = FP::new_copy(&Q.getpx());
+    let qy = FP::new_copy(&Q.getpy());
+
+    let sx = FP::new_copy(&S.getpx());
+    let sy = FP::new_copy(&S.getpy());
+
+    let mut A = ECP8::new();
+    let mut B = ECP8::new();
+    let mut r = FP48::new_int(1);
+
+    A.copy(&P);
+    B.copy(&R);
+
+    let mut NP = ECP8::new();
+    NP.copy(&P);
+    NP.neg();
+    let mut NR = ECP8::new();
+    NR.copy(&R);
+    NR.neg();
+
+     let nb=lbits(&mut n3,&mut n);
+
+    for i in (1..nb - 1).rev() {
+        r.sqr();
+        let mut lv = linedbl(&mut A, &qx, &qy);
+        let lv2 = linedbl(&mut B, &sx, &sy);
+	lv.smul(&lv2);
+        r.ssmul(&lv);
+        let bt = n3.bit(i) - n.bit(i);
+        if bt == 1 {
+            lv = lineadd(&mut A, &P, &qx, &qy);
+            let lv2 = lineadd(&mut B, &R, &sx, &sy);
+	    lv.smul(&lv2);
+            r.ssmul(&lv);
+        }
+        if bt == -1 {
+            lv = lineadd(&mut A, &NP, &qx, &qy);
+            let lv2 = lineadd(&mut B, &NR, &sx, &sy);
+	    lv.smul(&lv2);
+            r.ssmul(&lv);
+        }
+    }
+
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        r.conj();
+    }
+
+    return r;
+}
+
+/* final exponentiation - keep separate for multi-pairings and to avoid thrashing stack */
+pub fn fexp(m: &FP48) -> FP48 {
+    let f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
+    let mut x = BIG::new_ints(&rom::CURVE_BNX);
+    let mut r = FP48::new_copy(m);
+
+    /* Easy part of final exp */
+    let mut lv = FP48::new_copy(&r);
+    lv.inverse();
+    r.conj();
+
+    r.mul(&lv);
+    lv.copy(&r);
+    r.frob(&f, 8);
+    r.mul(&lv);
+//    if r.isunity() {
+//	r.zero();
+//	return r;
+//    }
+    /* Hard part of final exp */
+    // Ghamman & Fouotsa Method
+
+    let mut t7 = FP48::new_copy(&r);
+    t7.usqr();
+    let mut t1 = t7.pow(&mut x);
+
+    x.fshr(1);
+    let mut t2 = t1.pow(&mut x);
+    x.fshl(1);
+
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t1.conj();
+    }
+
+    let mut t3 = FP48::new_copy(&t1);
+    t3.conj();
+    t2.mul(&t3);
+    t2.mul(&r);
+
+    r.mul(&t7);
+
+    t1.copy(&t2.pow(&mut x));
+
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t1.conj();
+    }
+    t3.copy(&t1);
+    t3.frob(&f, 14);
+    r.mul(&t3);
+    lv.copy(&t1.pow(&mut x));
+    t1.copy(&lv);
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t1.conj();
+    }
+
+    t3.copy(&t1);
+    t3.frob(&f, 13);
+    r.mul(&t3);
+    lv.copy(&t1.pow(&mut x));
+    t1.copy(&lv);
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t1.conj();
+    }
+
+    t3.copy(&t1);
+    t3.frob(&f, 12);
+    r.mul(&t3);
+    lv.copy(&t1.pow(&mut x));
+    t1.copy(&lv);
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t1.conj();
+    }
+
+    t3.copy(&t1);
+    t3.frob(&f, 11);
+    r.mul(&t3);
+    lv.copy(&t1.pow(&mut x));
+    t1.copy(&lv);
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t1.conj();
+    }
+
+    t3.copy(&t1);
+    t3.frob(&f, 10);
+    r.mul(&t3);
+    lv.copy(&t1.pow(&mut x));
+    t1.copy(&lv);
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t1.conj();
+    }
+
+    t3.copy(&t1);
+    t3.frob(&f, 9);
+    r.mul(&t3);
+    lv.copy(&t1.pow(&mut x));
+    t1.copy(&lv);
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t1.conj();
+    }
+
+    t3.copy(&t1);
+    t3.frob(&f, 8);
+    r.mul(&t3);
+    lv.copy(&t1.pow(&mut x));
+    t1.copy(&lv);
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t1.conj();
+    }
+
+    t3.copy(&t2);
+    t3.conj();
+    t1.mul(&t3);
+    t3.copy(&t1);
+    t3.frob(&f, 7);
+    r.mul(&t3);
+    lv.copy(&t1.pow(&mut x));
+    t1.copy(&lv);
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t1.conj();
+    }
+
+    t3.copy(&t1);
+    t3.frob(&f, 6);
+    r.mul(&t3);
+    lv.copy(&t1.pow(&mut x));
+    t1.copy(&lv);
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t1.conj();
+    }
+
+    t3.copy(&t1);
+    t3.frob(&f, 5);
+    r.mul(&t3);
+    lv.copy(&t1.pow(&mut x));
+    t1.copy(&lv);
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t1.conj();
+    }
+
+    t3.copy(&t1);
+    t3.frob(&f, 4);
+    r.mul(&t3);
+    lv.copy(&t1.pow(&mut x));
+    t1.copy(&lv);
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t1.conj();
+    }
+
+    t3.copy(&t1);
+    t3.frob(&f, 3);
+    r.mul(&t3);
+    lv.copy(&t1.pow(&mut x));
+    t1.copy(&lv);
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t1.conj();
+    }
+
+    t3.copy(&t1);
+    t3.frob(&f, 2);
+    r.mul(&t3);
+    lv.copy(&t1.pow(&mut x));
+    t1.copy(&lv);
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t1.conj();
+    }
+
+    t3.copy(&t1);
+    t3.frob(&f, 1);
+    r.mul(&t3);
+    lv.copy(&t1.pow(&mut x));
+    t1.copy(&lv);
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        t1.conj();
+    }
+
+    r.mul(&t1);
+    t2.frob(&f, 15);
+    r.mul(&t2);
+
+    r.reduce();
+    return r;
+}
+
+#[allow(non_snake_case)]
+/* GLV method */
+fn glv(e: &BIG) -> [BIG; 2] {
+    let mut u: [BIG; 2] = [BIG::new(), BIG::new()];
+    let q = BIG::new_ints(&rom::CURVE_ORDER);
+    let mut x = BIG::new_ints(&rom::CURVE_BNX);
+    let mut x2 = BIG::smul(&x, &x);
+    x.copy(&BIG::smul(&x2, &x2));
+    x2.copy(&BIG::smul(&x, &x));
+    u[0].copy(&e);
+    u[0].rmod(&x2);
+    u[1].copy(&e);
+    u[1].div(&x2);
+    u[1].rsub(&q);
+
+    return u;
+}
+
+#[allow(non_snake_case)]
+/* Galbraith & Scott Method */
+pub fn gs(e: &BIG) -> [BIG; 16] {
+    let mut u: [BIG; 16] = [
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+        BIG::new(),
+    ];
+    let q = BIG::new_ints(&rom::CURVE_ORDER);
+    let x = BIG::new_ints(&rom::CURVE_BNX);
+    let mut w = BIG::new_copy(&e);
+    for i in 0..15 {
+        u[i].copy(&w);
+        u[i].rmod(&x);
+        w.div(&x);
+    }
+    u[15].copy(&w);
+    if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
+        let mut t = BIG::new();
+        t.copy(&BIG::modneg(&mut u[1], &q));
+        u[1].copy(&t);
+        t.copy(&BIG::modneg(&mut u[3], &q));
+        u[3].copy(&t);
+        t.copy(&BIG::modneg(&mut u[5], &q));
+        u[5].copy(&t);
+        t.copy(&BIG::modneg(&mut u[7], &q));
+        u[7].copy(&t);
+        t.copy(&BIG::modneg(&mut u[9], &q));
+        u[9].copy(&t);
+        t.copy(&BIG::modneg(&mut u[11], &q));
+        u[11].copy(&t);
+        t.copy(&BIG::modneg(&mut u[13], &q));
+        u[13].copy(&t);
+        t.copy(&BIG::modneg(&mut u[15], &q));
+        u[15].copy(&t);
+    }
+    return u;
+}
+
+#[allow(non_snake_case)]
+/* Multiply P by e in group G1 */
+pub fn g1mul(P: &ECP, e: &mut BIG) -> ECP {
+    let mut R = ECP::new();
+    if rom::USE_GLV {
+        R.copy(P);
+        let mut Q = ECP::new();
+        Q.copy(P);
+        Q.affine();
+        let q = BIG::new_ints(&rom::CURVE_ORDER);
+        let mut cru = FP::new_big(&BIG::new_ints(&rom::CURVE_CRU));
+        let mut u = glv(e);
+        Q.mulx(&mut cru);
+
+        let mut np = u[0].nbits();
+        let mut t: BIG = BIG::modneg(&mut u[0], &q);
+        let mut nn = t.nbits();
+        if nn < np {
+            u[0].copy(&t);
+            R.neg();
+        }
+
+        np = u[1].nbits();
+        t = BIG::modneg(&mut u[1], &q);
+        nn = t.nbits();
+        if nn < np {
+            u[1].copy(&t);
+            Q.neg();
+        }
+        u[0].norm();
+        u[1].norm();
+        R = R.mul2(&u[0], &mut Q, &u[1]);
+    } else {
+        R = P.mul(e);
+    }
+    return R;
+}
+
+#[allow(non_snake_case)]
+/* Multiply P by e in group G2 */
+pub fn g2mul(P: &ECP8, e: &BIG) -> ECP8 {
+    let mut R = ECP8::new();
+    if rom::USE_GS_G2 {
+        let mut Q: [ECP8; 16] = [
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+            ECP8::new(),
+        ];
+        let q = BIG::new_ints(&rom::CURVE_ORDER);
+        let mut u = gs(e);
+        let mut T = ECP8::new();
+
+        let f = ECP8::frob_constants();
+
+        let mut t = BIG::new();
+        Q[0].copy(&P);
+        for i in 1..16 {
+            T.copy(&Q[i - 1]);
+            Q[i].copy(&T);
+            Q[i].frob(&f, 1);
+        }
+        for i in 0..16 {
+            let np = u[i].nbits();
+            t.copy(&BIG::modneg(&mut u[i], &q));
+            let nn = t.nbits();
+            if nn < np {
+                u[i].copy(&t);
+                Q[i].neg();
+            }
+            u[i].norm();
+        }
+
+        R.copy(&ECP8::mul16(&mut Q, &u));
+    } else {
+        R.copy(&P.mul(e));
+    }
+    return R;
+}
+
+/* f=f^e */
+/* Note that this method requires a lot of RAM! Better to use compressed XTR method, see FP4.java */
+pub fn gtpow(d: &FP48, e: &BIG) -> FP48 {
+    let mut r = FP48::new();
+    if rom::USE_GS_GT {
+        let mut g: [FP48; 16] = [
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+            FP48::new(),
+        ];
+        let f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
+        let q = BIG::new_ints(&rom::CURVE_ORDER);
+        let mut t = BIG::new();
+        let mut u = gs(e);
+        let mut w = FP48::new();
+
+        g[0].copy(&d);
+        for i in 1..16 {
+            w.copy(&g[i - 1]);
+            g[i].copy(&w);
+            g[i].frob(&f, 1);
+        }
+        for i in 0..16 {
+            let np = u[i].nbits();
+            t.copy(&BIG::modneg(&mut u[i], &q));
+            let nn = t.nbits();
+            if nn < np {
+                u[i].copy(&t);
+                g[i].conj();
+            }
+            u[i].norm();
+        }
+        r.copy(&FP48::pow16(&mut g, &u));
+    } else {
+        r.copy(&d.pow(e));
+    }
+    return r;
+}
diff --git a/src/rand.rs b/src/rand.rs
new file mode 100644
index 0000000..34c264d
--- /dev/null
+++ b/src/rand.rs
@@ -0,0 +1,180 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+//mod hash256;
+
+use crate::hash256::HASH256;
+
+const RAND_NK: usize = 21;
+const RAND_NJ: usize = 6;
+const RAND_NV: usize = 8;
+
+pub struct RAND {
+    ira: [u32; RAND_NK], /* random number...   */
+    rndptr: usize,
+    borrow: u32,
+    pool_ptr: usize,
+    pool: [u8; 32],
+}
+
+impl RAND {
+    pub fn new() -> RAND {
+        RAND {
+            ira: [0; RAND_NK],
+            rndptr: 0,
+            borrow: 0,
+            pool_ptr: 0,
+            pool: [0; 32],
+        }
+    }
+
+    pub fn clean(&mut self) {
+        self.pool_ptr = 0;
+        self.rndptr = 0;
+        for i in 0..32 {
+            self.pool[i] = 0
+        }
+        for i in 0..RAND_NK {
+            self.ira[i] = 0
+        }
+        self.borrow = 0;
+    }
+
+    fn sbrand(&mut self) -> u32 {
+        /* Marsaglia & Zaman random number generator */
+        self.rndptr += 1;
+        if self.rndptr < RAND_NK {
+            return self.ira[self.rndptr];
+        }
+        self.rndptr = 0;
+        let mut k = RAND_NK - RAND_NJ;
+        for i in 0..RAND_NK {
+            /* calculate next NK values */
+            if k == RAND_NK {
+                k = 0
+            }
+            let t = self.ira[k];
+            let pdiff = t.wrapping_sub(self.ira[i]).wrapping_sub(self.borrow);
+            if pdiff < t {
+                self.borrow = 0
+            }
+            if pdiff > t {
+                self.borrow = 1
+            }
+            self.ira[i] = pdiff;
+            k += 1;
+        }
+        return self.ira[0];
+    }
+
+    fn sirand(&mut self, seed: u32) {
+        let mut m: u32 = 1;
+        let mut sd = seed;
+        self.borrow = 0;
+        self.rndptr = 0;
+        self.ira[0] ^= sd;
+        for i in 1..RAND_NK {
+            /* fill initialisation vector */
+            let inn = (RAND_NV * i) % RAND_NK;
+            self.ira[inn] ^= m; /* note XOR */
+            let t = m;
+            m = sd.wrapping_sub(m);
+            sd = t;
+        }
+        for _ in 0..10000 {
+            self.sbrand();
+        } /* "warm-up" & stir the generator */
+    }
+
+    fn fill_pool(&mut self) {
+        let mut sh = HASH256::new();
+        for _ in 0..128 {
+            sh.process((self.sbrand() & 0xff) as u8)
+        }
+        let w = sh.hash();
+        for i in 0..32 {
+            self.pool[i] = w[i]
+        }
+        self.pool_ptr = 0;
+    }
+
+    fn pack(b: [u8; 4]) -> u32 {
+        /* pack 4 bytes into a 32-bit Word */
+        return (((b[3] as u32) & 0xff) << 24)
+            | (((b[2] as u32) & 0xff) << 16)
+            | (((b[1] as u32) & 0xff) << 8)
+            | ((b[0] as u32) & 0xff);
+    }
+
+    /* Initialize RNG with some real entropy from some external source */
+    pub fn seed(&mut self, rawlen: usize, raw: &[u8]) {
+        /* initialise from at least 128 byte string of raw random entropy */
+        let mut b: [u8; 4] = [0; 4];
+        let mut sh = HASH256::new();
+        self.pool_ptr = 0;
+
+        for i in 0..RAND_NK {
+            self.ira[i] = 0
+        }
+        if rawlen > 0 {
+            for i in 0..rawlen {
+                sh.process(raw[i]);
+            }
+            let digest = sh.hash();
+
+            /* initialise PRNG from distilled randomness */
+
+            for i in 0..8 {
+                b[0] = digest[4 * i];
+                b[1] = digest[4 * i + 1];
+                b[2] = digest[4 * i + 2];
+                b[3] = digest[4 * i + 3];
+                self.sirand(RAND::pack(b));
+            }
+        }
+        self.fill_pool();
+    }
+
+    /* get random byte */
+    pub fn getbyte(&mut self) -> u8 {
+        let r = self.pool[self.pool_ptr];
+        self.pool_ptr += 1;
+        if self.pool_ptr >= 32 {
+            self.fill_pool()
+        }
+        return (r & 0xff) as u8;
+    }
+}
+
+/* test main program */
+/*
+fn main() {
+    let mut raw : [u8;100]=[0;100];
+    let mut rng=RAND::new();
+
+    rng.clean();
+    for i in 0..100 {raw[i]=i as u8}
+
+    rng.seed(100,&raw);
+
+    for _ in 0..1000 {
+        print!("{:03} ",rng.getbyte());
+    }
+}
+*/
diff --git a/src/roms/rom_anssi_32.rs b/src/roms/rom_anssi_32.rs
new file mode 100644
index 0000000..403fb04
--- /dev/null
+++ b/src/roms/rom_anssi_32.rs
@@ -0,0 +1,73 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use anssi::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 28
+// anssi Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0x86E9C03, 0xFCF353D, 0x8CA6DE8, 0xADBCABC, 0x35B3961, 0xE8CE424, 0xF10126D, 0xB3AD58,
+    0x1FD178C, 0xF,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x288CC9C, 0x18D2374, 0x646BD2B, 0x4929E67, 0xD6F7F2D, 0x220E6C1, 0xABCE02E, 0x751B1FD,
+    0x7401B78, 0xE,
+];
+pub const MCONST: Chunk = 0x64E1155;
+
+// anssi Curve
+
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = -3;
+pub const CURVE_B_I: isize = 0;
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0xB7BB73F, 0x75ED967, 0x1A18030, 0xC9AE4B, 0xFDFEC, 0x754A44C, 0xD4ABA, 0x5428A93, 0xE353FCA,
+    0xE,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x6D655E1, 0xFDD459C, 0x2BF941F, 0x67E140D, 0x35B53DC, 0xE8CE424, 0xF10126D, 0xB3AD58,
+    0x1FD178C, 0xF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x98F5CFF, 0xC97A2DD, 0x8B70164, 0xD2DCAF9, 0x3958C27, 0x4749D42, 0xB31183D, 0x56C139E,
+    0x6B3D4C3, 0xB,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x4062CFB, 0x115A155, 0x4C9E183, 0xC307E8E, 0xF8C2701, 0xF0F3ECE, 0x11F9271, 0xC8B2049,
+    0x142E0F7, 0x6,
+];
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 28;
+
+pub const MODBITS: usize = 256;
+pub const MOD8: usize = 7;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 14;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_anssi_64.rs b/src/roms/rom_anssi_64.rs
new file mode 100644
index 0000000..b0add0f
--- /dev/null
+++ b/src/roms/rom_anssi_64.rs
@@ -0,0 +1,91 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use anssi::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 56
+// anssi Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0xFCF353D86E9C03,
+    0xADBCABC8CA6DE8,
+    0xE8CE42435B3961,
+    0xB3AD58F10126D,
+    0xF1FD178C,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x18D2374288CC9C,
+    0x4929E67646BD2B,
+    0x220E6C1D6F7F2D,
+    0x751B1FDABCE02E,
+    0xE7401B78,
+];
+pub const MCONST: Chunk = 0x97483A164E1155;
+
+// anssi Curve
+
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = -3;
+pub const CURVE_B_I: isize = 0;
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0x75ED967B7BB73F,
+    0xC9AE4B1A18030,
+    0x754A44C00FDFEC,
+    0x5428A9300D4ABA,
+    0xEE353FCA,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0xFDD459C6D655E1,
+    0x67E140D2BF941F,
+    0xE8CE42435B53DC,
+    0xB3AD58F10126D,
+    0xF1FD178C,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0xC97A2DD98F5CFF,
+    0xD2DCAF98B70164,
+    0x4749D423958C27,
+    0x56C139EB31183D,
+    0xB6B3D4C3,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x115A1554062CFB,
+    0xC307E8E4C9E183,
+    0xF0F3ECEF8C2701,
+    0xC8B204911F9271,
+    0x6142E0F7,
+];
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 56;
+
+pub const MODBITS: usize = 256;
+pub const MOD8: usize = 7;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 24;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_bls24_32.rs b/src/roms/rom_bls24_32.rs
new file mode 100644
index 0000000..72afe58
--- /dev/null
+++ b/src/roms/rom_bls24_32.rs
@@ -0,0 +1,240 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use bls24::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 29
+// bls24 Modulus
+
+pub const MODULUS: [Chunk; NLEN] = [
+    0xA06152B, 0x2260B3A, 0xB4C36BE, 0x5FFC5D0, 0xBDB6A64, 0x5B78E2E, 0x1C1A28CA, 0x10E6441B,
+    0x1F244061, 0xB4704F0, 0x141E5CCD, 0x9837504, 0x3F2E77E, 0xD763740, 0x1316EA0E, 0xF0079,
+    0x555C,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x8533EA9, 0x6A02789, 0x183B24DE, 0x1E45ECF8, 0xC8F8F37, 0x10CAD209, 0x4C0C4B8, 0x9B1FABD,
+    0xDEBE4C0, 0xDC353F9, 0x18A18E26, 0x10F489BB, 0x31206A5, 0x19673BBF, 0x6BE69F9, 0xB091169,
+    0x9CD,
+];
+pub const MCONST: Chunk = 0x95FE7D;
+pub const FRA: [Chunk; NLEN] = [
+    0x1BF96F1D, 0xAE53A55, 0x31BFEEB, 0x183FF17A, 0x6237469, 0x12A4F4F1, 0x12101FE3, 0x16E79D94,
+    0xFF59267, 0x5EB4EB4, 0x78CC49F, 0x274BA33, 0x149184F3, 0x16C6DCBA, 0x1C90B694, 0x10F729CE,
+    0x4BBC,
+];
+pub const FRB: [Chunk; NLEN] = [
+    0xE0CA60E, 0x1740D0E4, 0x83037D2, 0xDBFD456, 0x5B7F5FA, 0x1312993D, 0xA0A08E6, 0x19FEA687,
+    0xF2EADF9, 0x55BB63C, 0xC91982E, 0x70EBAD1, 0xF61628B, 0x16AF5A85, 0x16863379, 0xF17D6AA,
+    0x99F,
+];
+
+pub const CURVE_COF_I: isize = 0;
+pub const CURVE_A: isize = 0;
+pub const CURVE_B_I: isize = 19;
+pub const CURVE_B: [Chunk; NLEN] = [
+    0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x10000001, 0xD047FF, 0x1FD54464, 0x1E3CE067, 0xE322DDA, 0x1D356F3F, 0x7433B44, 0x49091F9,
+    0x1729CC2, 0x250286C, 0x16E62ED, 0xB403E1E, 0x1001000, 0x80, 0x0, 0x0, 0x0,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0xBE3CCD4, 0x33B07AF, 0x1B67D159, 0x3DFC5B5, 0xEBA1FCC, 0x1A3C1F84, 0x56BE204, 0xEF8DF1B,
+    0x11AE2D84, 0x5FEE546, 0x161B3BF9, 0x183B20EE, 0x1EA5D99B, 0x14F0C5BF, 0xBE521B7, 0x17C682F9,
+    0x1AB2,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x121E5245, 0x65D2E56, 0x11577DB1, 0x16DACC11, 0x14F39746, 0x459F694, 0x12483FCF, 0xC828B04,
+    0xFD63E5A, 0x7B1D52, 0xAFDE738, 0xF349254, 0x1A4529FF, 0x10E53353, 0xF91DEE1, 0x16E18D8A,
+    0x47FC,
+];
+
+pub const CURVE_BNX: [Chunk; NLEN] = [
+    0x11FF80, 0x80010, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_COF: [Chunk; NLEN] = [
+    0x19F415AB, 0x1E0FFDFF, 0x15AAADFF, 0xAA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0x0,
+];
+pub const CURVE_CRU: [Chunk; NLEN] = [
+    0xDD794A9, 0x1DE138A3, 0x2BCCE90, 0xC746127, 0x15223DDC, 0x1DD8890B, 0xED08DB7, 0xE24B9F,
+    0xE379CE6, 0x37011AC, 0x11BAC820, 0x1EEFAD01, 0x200860F, 0x147218A6, 0xF16A209, 0xF0079,
+    0x555C,
+];
+pub const CURVE_PXAA: [Chunk; NLEN] = [
+    0x14E24678, 0x1F149A9B, 0x9609022, 0x1C186868, 0xCDEFC69, 0x1C87BB2E, 0x14A2235F, 0x7586755,
+    0x5896747, 0x159BFE92, 0x3B5572E, 0x1710A521, 0x71EB14A, 0xC643C33, 0x12581DE5, 0x1BCA747D,
+    0x959,
+];
+pub const CURVE_PXAB: [Chunk; NLEN] = [
+    0x1FB099B8, 0x3FCF5D7, 0x4A91C0E, 0xC6EEB40, 0x11FC2385, 0x11B5AE8D, 0x1A9CC3E7, 0x194FE144,
+    0x185DB2A5, 0x930E1C7, 0x14F85F9A, 0x1F2ED4E, 0x1D1BE5AD, 0xF26169C, 0xCF7F194, 0x1DA1062E,
+    0x3B0D,
+];
+pub const CURVE_PXBA: [Chunk; NLEN] = [
+    0x11AD15D3, 0xD0E6F38, 0x17DB85BB, 0x30A62F1, 0x1EA3E09A, 0x17B25FA1, 0x1B7959AC, 0x1165B19A,
+    0x6C74FDB, 0x18F790E1, 0x12278FDA, 0x1E008F79, 0x103F329, 0x14619FF1, 0x1EBCAA8, 0xFF5A9CA,
+    0x3EC2,
+];
+pub const CURVE_PXBB: [Chunk; NLEN] = [
+    0x1EE0F480, 0x3D5943A, 0xF5B12E3, 0x128AADC8, 0x180E1CB9, 0x1EFD916F, 0x48BC7F, 0x1D5EE1FA,
+    0x5698EF5, 0x11D6AED9, 0x1386BC6E, 0x196E900B, 0x1CE2E465, 0xC2A8ED3, 0x1E67DF99, 0x71B7940,
+    0xA5B,
+];
+pub const CURVE_PYAA: [Chunk; NLEN] = [
+    0x14781AA0, 0xC324C98, 0xEDC2AC, 0x16C13B46, 0x145FC44B, 0x12529530, 0x1310A8C4, 0x1768C5C0,
+    0xE19AE68, 0x56E1C1D, 0x13DAF93F, 0x17E94366, 0xF901AD0, 0x76800CC, 0x10250D8B, 0x1E6BAE6D,
+    0x5057,
+];
+pub const CURVE_PYAB: [Chunk; NLEN] = [
+    0xEAE08FA, 0xDDF62BF, 0xA97E5AB, 0xF0EE97, 0x99A42CA, 0x1C326578, 0xF33DC11, 0x8B913F7,
+    0xFEF8552, 0x19F35B90, 0x58DDBDE, 0xFC32FF2, 0x1587B5DF, 0xB5EB07A, 0x1A258DE0, 0x1692CC3D,
+    0x2CE2,
+];
+pub const CURVE_PYBA: [Chunk; NLEN] = [
+    0x5F0CC41, 0xB9813B5, 0x14C2A87D, 0xFF1264A, 0x19AF8A14, 0x6CE6C3, 0x2A7F8A2, 0x121DCA7D,
+    0x7D37153, 0x19D21078, 0x15466DC7, 0x1362982B, 0x1DD3CB5B, 0x1CFC0D1C, 0x18C69AF8, 0x8CC7DC,
+    0x1807,
+];
+pub const CURVE_PYBB: [Chunk; NLEN] = [
+    0x115C1CAE, 0x78D9732, 0x16C26237, 0x5A81A6A, 0x1C38A777, 0x56121FE, 0x4DAD9D7, 0x1BEBA670,
+    0xA1D72FC, 0xD60B274, 0x19734258, 0x1D621775, 0x4691771, 0x14206B68, 0x17B22DE4, 0x29D5B37,
+    0x499D,
+];
+pub const CURVE_W: [[Chunk; NLEN]; 2] = [
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+];
+pub const CURVE_SB: [[[Chunk; NLEN]; 2]; 2] = [
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+];
+pub const CURVE_WB: [[Chunk; NLEN]; 4] = [
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+];
+pub const CURVE_BB: [[[Chunk; NLEN]; 4]; 4] = [
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+];
+
+pub const USE_GLV: bool = true;
+pub const USE_GS_G2: bool = true;
+pub const USE_GS_GT: bool = true;
+pub const GT_STRONG: bool = true;
+
+pub const MODBYTES: usize = 60;
+pub const BASEBITS: usize = 29;
+
+pub const MODBITS: usize = 479;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 14;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 49;
+pub const SIGN_OF_X: SignOfX = SignOfX::POSITIVEX;
+pub const HASH_TYPE: usize = 48;
+pub const AESKEY: usize = 24;
diff --git a/src/roms/rom_bls24_64.rs b/src/roms/rom_bls24_64.rs
new file mode 100644
index 0000000..0c500da
--- /dev/null
+++ b/src/roms/rom_bls24_64.rs
@@ -0,0 +1,288 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use bls24::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 56
+// bls24 Modulus
+
+pub const MODULUS: [Chunk; NLEN] = [
+    0x44C1674A06152B,
+    0xFFE2E82D30DAF8,
+    0x6F1C5CBDB6A642,
+    0x3220DF068A328B,
+    0xE09E1F24406187,
+    0xBA825079733568,
+    0x6E803F2E77E4C1,
+    0x3CCC5BA839AEC,
+    0x555C0078,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x6A4A1FE013DF5B,
+    0xE8E46D4D1BDE65,
+    0x1F841391F45C67,
+    0x9148A4516FB28,
+    0x4398524EDF4C88,
+    0x41C0E241B6DCE8,
+    0xE42C208C19411,
+    0xA7FE6FD73A7B1C,
+    0xFCCCA76,
+];
+pub const MCONST: Chunk = 0xBD5D7D8095FE7D;
+pub const FRA: [Chunk; NLEN] = [
+    0x5CA74ABBF96F1D,
+    0x1FF8BD0C6FFBAD,
+    0x49E9E26237469C,
+    0x3CECA48407F8E5,
+    0x69D68FF59267B7,
+    0x5D199E33127CBD,
+    0xB97549184F313A,
+    0x4E77242DA52D8D,
+    0x4BBC87B9,
+];
+pub const FRB: [Chunk; NLEN] = [
+    0xE81A1C8E0CA60E,
+    0xDFEA2B20C0DF4A,
+    0x25327A5B7F5FA6,
+    0xF5343A828239A6,
+    0x76C78F2EADF9CF,
+    0x5D68B24660B8AB,
+    0xB50AF61628B387,
+    0xB555A18CDE6D5E,
+    0x99F78BE,
+];
+
+pub const CURVE_COF_I: isize = 0;
+pub const CURVE_A: isize = 0;
+pub const CURVE_B_I: isize = 19;
+pub const CURVE_B: [Chunk; NLEN] = [0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x1A08FFF0000001,
+    0x1E7033FF551190,
+    0x6ADE7EE322DDAF,
+    0x848FC9D0CED13A,
+    0x50D81729CC224,
+    0x1F0F05B98BB44A,
+    0x10010010005A0,
+    0x0,
+    0x0,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x6760F5EBE3CCD4,
+    0xEFE2DAED9F4564,
+    0x783F08EBA1FCC1,
+    0xC6F8D95AF88134,
+    0xDCA8D1AE2D8477,
+    0x9077586CEFE4BF,
+    0x8B7FEA5D99BC1D,
+    0x17CAF9486DE9E1,
+    0x1AB2BE34,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0xCBA5CAD21E5245,
+    0x6D6608C55DF6C4,
+    0xB3ED294F39746B,
+    0x145824920FF3C8,
+    0x63AA4FD63E5A64,
+    0x492A2BF79CE00F,
+    0x66A7A4529FF79A,
+    0x6C53E477B861CA,
+    0x47FCB70C,
+];
+
+pub const CURVE_BNX: [Chunk; NLEN] = [0x100020011FF80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_COF: [Chunk; NLEN] = [
+    0xC1FFBFF9F415AB,
+    0x5556AAB7FF,
+    0x0,
+    0x0,
+    0x0,
+    0x0,
+    0x0,
+    0x0,
+    0x0,
+];
+pub const CURVE_CRU: [Chunk; NLEN] = [
+    0xBC27146DD794A9,
+    0x3A30938AF33A43,
+    0xB112175223DDC6,
+    0x125CFBB4236DFB,
+    0x2358E379CE607,
+    0xD680C6EB20806E,
+    0x314C200860FF77,
+    0x3CBC5A88268E4,
+    0x555C0078,
+];
+pub const CURVE_PXAA: [Chunk; NLEN] = [
+    0xE2935374E24678,
+    0xC34342582408B,
+    0xF765CCDEFC69E,
+    0xC33AAD2888D7F9,
+    0x7FD2458967473A,
+    0x52908ED55CBAB3,
+    0x786671EB14AB88,
+    0xA3EC96077958C8,
+    0x959DE53,
+];
+pub const CURVE_PXAB: [Chunk; NLEN] = [
+    0x7F9EBAFFB099B8,
+    0x3775A012A47038,
+    0x6B5D1B1FC23856,
+    0x7F0A26A730F9E3,
+    0x1C38F85DB2A5CA,
+    0x76A753E17E6926,
+    0x2D39D1BE5AD0F9,
+    0x31733DFC651E4C,
+    0x3B0DED08,
+];
+pub const CURVE_PXBA: [Chunk; NLEN] = [
+    0xA1CDE711AD15D3,
+    0x853178DF6E16ED,
+    0x64BF43EA3E09A1,
+    0x2D8CD6DE566B2F,
+    0xF21C26C74FDB8B,
+    0x47BCC89E3F6B1E,
+    0x3FE2103F329F00,
+    0x4E507AF2AA28C3,
+    0x3EC27FAD,
+];
+pub const CURVE_PXBB: [Chunk; NLEN] = [
+    0x7AB2875EE0F480,
+    0x4556E43D6C4B8C,
+    0xFB22DF80E1CB99,
+    0xF70FD0122F1FFD,
+    0xD5DB25698EF5EA,
+    0x4805CE1AF1BA3A,
+    0x1DA7CE2E465CB7,
+    0xCA0799F7E65855,
+    0xA5B38DB,
+];
+pub const CURVE_PYAA: [Chunk; NLEN] = [
+    0x86499314781AA0,
+    0x609DA303B70AB1,
+    0xA52A6145FC44BB,
+    0x462E04C42A3124,
+    0xC383AE19AE68BB,
+    0xA1B34F6BE4FCAD,
+    0x198F901AD0BF4,
+    0x736C094362CED0,
+    0x5057F35D,
+];
+pub const CURVE_PYAB: [Chunk; NLEN] = [
+    0xBBEC57EEAE08FA,
+    0x78774BAA5F96AD,
+    0x64CAF099A42CA0,
+    0xC89FBBCCF70478,
+    0x6B720FEF855245,
+    0x97F916376F7B3E,
+    0x60F5587B5DF7E1,
+    0x61EE89637816BD,
+    0x2CE2B496,
+];
+pub const CURVE_PYBA: [Chunk; NLEN] = [
+    0x730276A5F0CC41,
+    0xF89325530AA1F5,
+    0xD9CD879AF8A147,
+    0xEE53E8A9FE2880,
+    0x420F07D3715390,
+    0x4C15D519B71F3A,
+    0x1A39DD3CB5B9B1,
+    0x3EE631A6BE39F8,
+    0x18070466,
+];
+pub const CURVE_PYBB: [Chunk; NLEN] = [
+    0xF1B2E6515C1CAE,
+    0xD40D355B0988DC,
+    0xC243FDC38A7772,
+    0x5D338136B675CA,
+    0x164E8A1D72FCDF,
+    0xBBAE5CD0961AC,
+    0xD6D04691771EB1,
+    0xD9BDEC8B792840,
+    0x499D14EA,
+];
+pub const CURVE_W: [[Chunk; NLEN]; 2] = [
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+];
+pub const CURVE_SB: [[[Chunk; NLEN]; 2]; 2] = [
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+];
+pub const CURVE_WB: [[Chunk; NLEN]; 4] = [
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+];
+pub const CURVE_BB: [[[Chunk; NLEN]; 4]; 4] = [
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+];
+
+pub const USE_GLV: bool = true;
+pub const USE_GS_G2: bool = true;
+pub const USE_GS_GT: bool = true;
+pub const GT_STRONG: bool = true;
+
+pub const MODBYTES: usize = 60;
+pub const BASEBITS: usize = 56;
+
+pub const MODBITS: usize = 479;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 25;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 49;
+pub const SIGN_OF_X: SignOfX = SignOfX::POSITIVEX;
+pub const HASH_TYPE: usize = 48;
+pub const AESKEY: usize = 24;
diff --git a/src/roms/rom_bls381_32.rs b/src/roms/rom_bls381_32.rs
new file mode 100644
index 0000000..1ba0fe5
--- /dev/null
+++ b/src/roms/rom_bls381_32.rs
@@ -0,0 +1,209 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use bls381::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 29
+// bls381 Modulus
+
+pub const MODULUS: [Chunk; NLEN] = [
+    0x1FFFAAAB, 0xFF7FFFF, 0x14FFFFEE, 0x17FFFD62, 0xF6241EA, 0x9507B58, 0xAFD9CC3, 0x109E70A2,
+    0x1764774B, 0x121A5D66, 0x12C6E9ED, 0x12FFCD34, 0x111EA3, 0xD,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x15BEF7AE, 0x1031CD0E, 0x2DD93E8, 0x9226323, 0xE6E2CD2, 0x11684DAA, 0x1170E5DB, 0x88E25B1,
+    0x1B366399, 0x1C536F47, 0xD1F9CBC, 0x278B67F, 0x1EA66A2B, 0xC,
+];
+pub const MCONST: Chunk = 0x1FFCFFFD;
+pub const FRA: [Chunk; NLEN] = [
+    0x12235FB8, 0x83BAF6C, 0x19E04F63, 0x1D4A7AC7, 0xB9C4F67, 0x1EBC25D, 0x1D3DEC91, 0x1FA797AB,
+    0x1F0FD603, 0x1016068, 0x108C6FAD, 0x5760CCF, 0x104D3BF0, 0xC,
+];
+pub const FRB: [Chunk; NLEN] = [
+    0xDDC4AF3, 0x7BC5093, 0x1B1FB08B, 0x1AB5829A, 0x3C5F282, 0x764B8FB, 0xDBFB032, 0x10F6D8F6,
+    0x1854A147, 0x1118FCFD, 0x23A7A40, 0xD89C065, 0xFC3E2B3, 0x0,
+];
+
+pub const CURVE_COF_I: isize = 0;
+pub const CURVE_A: isize = 0;
+pub const CURVE_B_I: isize = 4;
+pub const CURVE_B: [Chunk; NLEN] = [
+    0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x1, 0x1FFFFFF8, 0x1F96FFBF, 0x1B4805FF, 0x1D80553B, 0xC0404D0, 0x1520CCE7, 0xA6533AF,
+    0x73EDA7, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x1B22C6BB, 0x19D78056, 0x1E86BBFE, 0xBD07FF2, 0x1AC586C5, 0x1D1F8B8D, 0x4168538, 0x9F2EE97,
+    0xFC3688C, 0x27D4D60, 0x9A558E3, 0x32FAF28, 0x1F1D3A73, 0xB,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x6C5E7E1, 0x551194A, 0x222B903, 0x198E8945, 0xB3EDD03, 0xC659602, 0xBD8036C, 0x12BABA01,
+    0x4FCF5E0, 0xBA0EC57, 0x8278C3B, 0x75541E3, 0xB3F481E, 0x4,
+];
+
+pub const CURVE_BNX: [Chunk; NLEN] = [
+    0x10000, 0x10080000, 0x34, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_COF: [Chunk; NLEN] = [
+    0xAAAB, 0x55558, 0x157855A3, 0x191800AA, 0x396, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_CRU: [Chunk; NLEN] = [
+    0x1FFEFFFE, 0x100FFFFF, 0x280008B, 0xFB026C4, 0x9688DE1, 0x149DF37C, 0x1FAB76CE, 0xED41EE,
+    0x11BA69C6, 0x1EFBB672, 0x17C659CB, 0x0, 0x0, 0x0,
+];
+
+pub const CURVE_PXA: [Chunk; NLEN] = [
+    0x121BDB8, 0x402B646, 0x16EFBF5, 0x18064D50, 0x1D1770BA, 0x5B23D71, 0xC0AD144, 0x1A9F4807,
+    0x11C6E47A, 0x196E2882, 0x9820149, 0x11E1522, 0x4AA2B2F, 0x1,
+];
+pub const CURVE_PXB: [Chunk; NLEN] = [
+    0x1D042B7E, 0xD63E82A, 0x51755F9, 0x19E22427, 0x15049334, 0x10DDEE3F, 0x186AD769, 0x1A132416,
+    0x5596BD0, 0x4413A7B, 0x1F6B34E8, 0x4E33EC0, 0x1E02B605, 0x9,
+];
+pub const CURVE_PYA: [Chunk; NLEN] = [
+    0x8B82801, 0xC9AA430, 0xB28A278, 0x15939877, 0xD12C923, 0xD34A8B0, 0xE9DB50A, 0x155197BA,
+    0x1AADFD9B, 0x16D171A8, 0x3327371, 0x4FADC23, 0xE5D5277, 0x6,
+];
+pub const CURVE_PYB: [Chunk; NLEN] = [
+    0x105F79BE, 0x15483AFF, 0x1B07686A, 0xE1A4EB9, 0x99AB3F3, 0x955AB97, 0xEBC99D2, 0xFD0B4EC,
+    0x19CB3E28, 0x15E145C, 0xCAB34AC, 0x1D4E6998, 0x6C4A02, 0x3,
+];
+pub const CURVE_W: [[Chunk; NLEN]; 2] = [
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+];
+pub const CURVE_SB: [[[Chunk; NLEN]; 2]; 2] = [
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+];
+pub const CURVE_WB: [[Chunk; NLEN]; 4] = [
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+];
+pub const CURVE_BB: [[[Chunk; NLEN]; 4]; 4] = [
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+];
+
+pub const USE_GLV: bool = true;
+pub const USE_GS_G2: bool = true;
+pub const USE_GS_GT: bool = true;
+pub const GT_STRONG: bool = false;
+
+pub const MODBYTES: usize = 48;
+pub const BASEBITS: usize = 29;
+
+pub const MODBITS: usize = 381;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 14;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 65;
+pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_bls381_64.rs b/src/roms/rom_bls381_64.rs
new file mode 100644
index 0000000..08df12c
--- /dev/null
+++ b/src/roms/rom_bls381_64.rs
@@ -0,0 +1,211 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use bls381::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 58
+// bls381 Modulus
+
+pub const MODULUS: [Chunk; NLEN] = [
+    0x1FEFFFFFFFFAAAB,
+    0x2FFFFAC54FFFFEE,
+    0x12A0F6B0F6241EA,
+    0x213CE144AFD9CC3,
+    0x2434BACD764774B,
+    0x25FF9A692C6E9ED,
+    0x1A0111EA3,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x20639A1D5BEF7AE,
+    0x1244C6462DD93E8,
+    0x22D09B54E6E2CD2,
+    0x111C4B63170E5DB,
+    0x38A6DE8FB366399,
+    0x4F16CFED1F9CBC,
+    0x19EA66A2B,
+];
+pub const MCONST: Chunk = 0x1F3FFFCFFFCFFFD;
+pub const FRA: [Chunk; NLEN] = [
+    0x10775ED92235FB8,
+    0x3A94F58F9E04F63,
+    0x3D784BAB9C4F67,
+    0x3F4F2F57D3DEC91,
+    0x202C0D1F0FD603,
+    0xAEC199F08C6FAD,
+    0x1904D3BF0,
+];
+pub const FRB: [Chunk; NLEN] = [
+    0xF78A126DDC4AF3,
+    0x356B0535B1FB08B,
+    0xEC971F63C5F282,
+    0x21EDB1ECDBFB032,
+    0x2231F9FB854A147,
+    0x1B1380CA23A7A40,
+    0xFC3E2B3,
+];
+
+pub const CURVE_COF_I: isize = 0;
+pub const CURVE_A: isize = 0;
+pub const CURVE_B_I: isize = 4;
+pub const CURVE_B: [Chunk; NLEN] = [0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x3FFFFFF00000001,
+    0x36900BFFF96FFBF,
+    0x180809A1D80553B,
+    0x14CA675F520CCE7,
+    0x73EDA7,
+    0x0,
+    0x0,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x33AF00ADB22C6BB,
+    0x17A0FFE5E86BBFE,
+    0x3A3F171BAC586C5,
+    0x13E5DD2E4168538,
+    0x4FA9AC0FC3688C,
+    0x65F5E509A558E3,
+    0x17F1D3A73,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0xAA232946C5E7E1,
+    0x331D128A222B903,
+    0x18CB2C04B3EDD03,
+    0x25757402BD8036C,
+    0x1741D8AE4FCF5E0,
+    0xEAA83C68278C3B,
+    0x8B3F481E,
+];
+
+pub const CURVE_BNX: [Chunk; NLEN] = [0x201000000010000, 0x34, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_COF: [Chunk; NLEN] = [0xAAAB0000AAAB, 0x3230015557855A3, 0x396, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_CRU: [Chunk; NLEN] = [
+    0x201FFFFFFFEFFFE,
+    0x1F604D88280008B,
+    0x293BE6F89688DE1,
+    0x1DA83DDFAB76CE,
+    0x3DF76CE51BA69C6,
+    0x17C659CB,
+    0x0,
+];
+
+pub const CURVE_PXA: [Chunk; NLEN] = [
+    0x8056C8C121BDB8,
+    0x300C9AA016EFBF5,
+    0xB647AE3D1770BA,
+    0x353E900EC0AD144,
+    0x32DC51051C6E47A,
+    0x23C2A449820149,
+    0x24AA2B2F,
+];
+pub const CURVE_PXB: [Chunk; NLEN] = [
+    0x1AC7D055D042B7E,
+    0x33C4484E51755F9,
+    0x21BBDC7F5049334,
+    0x3426482D86AD769,
+    0x88274F65596BD0,
+    0x9C67D81F6B34E8,
+    0x13E02B605,
+];
+pub const CURVE_PYA: [Chunk; NLEN] = [
+    0x193548608B82801,
+    0x2B2730EEB28A278,
+    0x1A695160D12C923,
+    0x2AA32F74E9DB50A,
+    0x2DA2E351AADFD9B,
+    0x9F5B8463327371,
+    0xCE5D5277,
+];
+pub const CURVE_PYB: [Chunk; NLEN] = [
+    0x2A9075FF05F79BE,
+    0x1C349D73B07686A,
+    0x12AB572E99AB3F3,
+    0x1FA169D8EBC99D2,
+    0x2BC28B99CB3E28,
+    0x3A9CD330CAB34AC,
+    0x606C4A02,
+];
+pub const CURVE_W: [[Chunk; NLEN]; 2] = [
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+];
+pub const CURVE_SB: [[[Chunk; NLEN]; 2]; 2] = [
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+];
+pub const CURVE_WB: [[Chunk; NLEN]; 4] = [
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+];
+pub const CURVE_BB: [[[Chunk; NLEN]; 4]; 4] = [
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+];
+
+pub const USE_GLV: bool = true;
+pub const USE_GS_G2: bool = true;
+pub const USE_GS_GT: bool = true;
+pub const GT_STRONG: bool = false;
+
+pub const MODBYTES: usize = 48;
+pub const BASEBITS: usize = 58;
+
+pub const MODBITS: usize = 381;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 25;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 65;
+pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_bls383_32.rs b/src/roms/rom_bls383_32.rs
new file mode 100644
index 0000000..0ccb42d
--- /dev/null
+++ b/src/roms/rom_bls383_32.rs
@@ -0,0 +1,207 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use bls383::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 29
+pub const MODULUS: [Chunk; NLEN] = [
+    0x5AAB0AB, 0x11B8EB24, 0x19214AF6, 0x187E5314, 0x124F47A8, 0x1C00B4B0, 0x1446B0C6, 0x59E6CB4,
+    0x4A0AD46, 0xFF5494, 0x81B6B71, 0x956DD6B, 0x16556956, 0x2A,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x116907F4, 0x405B700, 0x1752AC11, 0x67A9E7C, 0x1941C581, 0x1AEA38C4, 0xB1E4D22, 0xCE841AE,
+    0xA0FC49B, 0xB4B1F48, 0x13852312, 0x1B3FDCED, 0x1FECE397, 0x26,
+];
+pub const MCONST: Chunk = 0x73435FD;
+pub const FRA: [Chunk; NLEN] = [
+    0x1311DAC1, 0x296B969, 0x19DCF806, 0x126901FC, 0xD8C8A36, 0x1A2572A8, 0xA1A0959, 0x1A47F743,
+    0x110E4C6C, 0x1608DA97, 0xCE2E7F0, 0x4FED178, 0xACD5BF0, 0x11,
+];
+pub const FRB: [Chunk; NLEN] = [
+    0x1298D5EA, 0xF2231BA, 0x1F4452F0, 0x6155117, 0x4C2BD72, 0x1DB4208, 0xA2CA76D, 0xB567571,
+    0x139260D9, 0xAF679FC, 0x1B388380, 0x4580BF2, 0xB880D66, 0x19,
+];
+
+pub const CURVE_A: isize = 0;
+pub const CURVE_COF_I: isize = 0;
+pub const CURVE_COF: [Chunk; NLEN] = [
+    0x15169EAB, 0xA82AB0A, 0xAAEFFED, 0x15558001, 0x555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x0,
+];
+pub const CURVE_B_I: isize = 15;
+pub const CURVE_B: [Chunk; NLEN] = [
+    0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x1EBC0001, 0x1904CF5F, 0x834E5CE, 0xBE12B42, 0xB381DE0, 0xE40B4C, 0x270110, 0x10018017,
+    0x1002001, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x8734573, 0x623B9C8, 0x1D1DC11E, 0xBB7E107, 0x1E3445C5, 0x1D6C2578, 0x10B0BE1E, 0xED6103E,
+    0x10F31D9F, 0x296ED82, 0x18E0D7D0, 0x12F3D9C9, 0x1FCBA55B, 0x20,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x3F224, 0x968B2F4, 0x1FE63F48, 0xFA93D90, 0x14D2DDE5, 0x54A56F5, 0x12441D4C, 0x18CD76C8,
+    0x199D0DAD, 0xE18E236, 0x92BA73, 0x99F6600, 0x8F16727, 0x3,
+];
+
+pub const CURVE_BNX: [Chunk; NLEN] = [
+    0x1001200, 0x400000, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_CRU: [Chunk; NLEN] = [
+    0xEAAC2A9, 0x61B3A81, 0x17D974B7, 0xBED0345, 0xA341BC2, 0x17A51A6F, 0x5738948, 0x69B7BAE,
+    0x14605445, 0x374A43, 0x8116AD1, 0x956DD69, 0x16556956, 0x2A,
+];
+pub const CURVE_PXA: [Chunk; NLEN] = [
+    0xD7F2D86, 0x1E59DB1, 0x17474F85, 0x1FB56CF2, 0x572EE81, 0xE487AB1, 0x96F51FC, 0x190A5AAE,
+    0x6432501, 0x13E58F3A, 0x101E6425, 0xFD807D1, 0x34D2240, 0x3,
+];
+pub const CURVE_PXB: [Chunk; NLEN] = [
+    0x452DE15, 0x1ECF20F6, 0x1FF9837B, 0x95651AA, 0xD5D75B5, 0x5D44749, 0x12277F66, 0x1DB3A0B9,
+    0x1D24F498, 0x19441B0E, 0x1CDE9DC5, 0x2C975, 0xD78006, 0x18,
+];
+pub const CURVE_PYA: [Chunk; NLEN] = [
+    0x1408CB41, 0x34785DC, 0x3586597, 0x13DBC9E4, 0x1A2E75B4, 0x1D65489, 0xCF9A25E, 0x1ACE7933,
+    0x1B6E990E, 0x19FF31A3, 0x12527615, 0x1A44A68F, 0x1792CF93, 0x19,
+];
+pub const CURVE_PYB: [Chunk; NLEN] = [
+    0x1F479093, 0x16C2321B, 0x1889218E, 0x87961BC, 0x1BC98B01, 0x197A24FB, 0xA3DEBC2, 0x88D67DF,
+    0x1CE0D, 0x1E8AD3D7, 0x93B9EE9, 0x59B18D6, 0xE5247DD, 0x10,
+];
+pub const CURVE_W: [[Chunk; NLEN]; 2] = [
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+];
+pub const CURVE_SB: [[[Chunk; NLEN]; 2]; 2] = [
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+];
+pub const CURVE_WB: [[Chunk; NLEN]; 4] = [
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+];
+pub const CURVE_BB: [[[Chunk; NLEN]; 4]; 4] = [
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+];
+
+pub const USE_GLV: bool = true;
+pub const USE_GS_G2: bool = true;
+pub const USE_GS_GT: bool = true;
+pub const GT_STRONG: bool = true;
+
+pub const MODBYTES: usize = 48;
+pub const BASEBITS: usize = 29;
+
+pub const MODBITS: usize = 383;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 14;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 65;
+pub const SIGN_OF_X: SignOfX = SignOfX::POSITIVEX;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_bls383_64.rs b/src/roms/rom_bls383_64.rs
new file mode 100644
index 0000000..71bef66
--- /dev/null
+++ b/src/roms/rom_bls383_64.rs
@@ -0,0 +1,218 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use bls383::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 58
+pub const MODULUS: [Chunk; NLEN] = [
+    0x2371D6485AAB0AB,
+    0x30FCA6299214AF6,
+    0x3801696124F47A8,
+    0xB3CD969446B0C6,
+    0x1FEA9284A0AD46,
+    0x12ADBAD681B6B71,
+    0x556556956,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x80B6E0116907F4,
+    0xCF53CF9752AC11,
+    0x35D47189941C581,
+    0x19D0835CB1E4D22,
+    0x16963E90A0FC49B,
+    0x367FB9DB3852312,
+    0x4DFECE397,
+];
+pub const MCONST: Chunk = 0x1BC0571073435FD;
+pub const FRA: [Chunk; NLEN] = [
+    0x52D72D3311DAC1,
+    0x24D203F99DCF806,
+    0x344AE550D8C8A36,
+    0x348FEE86A1A0959,
+    0x2C11B52F10E4C6C,
+    0x9FDA2F0CE2E7F0,
+    0x22ACD5BF0,
+];
+pub const FRB: [Chunk; NLEN] = [
+    0x1E446375298D5EA,
+    0xC2AA22FF4452F0,
+    0x3B684104C2BD72,
+    0x16ACEAE2A2CA76D,
+    0x15ECF3F939260D9,
+    0x8B017E5B388380,
+    0x32B880D66,
+];
+
+// Base Bits= 58
+
+pub const CURVE_A: isize = 0;
+pub const CURVE_COF_I: isize = 0;
+pub const CURVE_COF: [Chunk; NLEN] = [
+    0x150556155169EAB,
+    0x2AAB0002AAEFFED,
+    0x555,
+    0x0,
+    0x0,
+    0x0,
+    0x0,
+];
+pub const CURVE_B_I: isize = 15;
+pub const CURVE_B: [Chunk; NLEN] = [0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x32099EBFEBC0001,
+    0x17C25684834E5CE,
+    0x1C81698B381DE0,
+    0x2003002E0270110,
+    0x1002001,
+    0x0,
+    0x0,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0xC4773908734573,
+    0x176FC20FD1DC11E,
+    0x3AD84AF1E3445C5,
+    0x1DAC207D0B0BE1E,
+    0x52DDB050F31D9F,
+    0x25E7B3938E0D7D0,
+    0x41FCBA55B,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x12D165E8003F224,
+    0x1F527B21FE63F48,
+    0xA94ADEB4D2DDE5,
+    0x319AED912441D4C,
+    0x1C31C46D99D0DAD,
+    0x133ECC00092BA73,
+    0x68F16727,
+];
+
+pub const CURVE_BNX: [Chunk; NLEN] = [0x8000001001200, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_CRU: [Chunk; NLEN] = [
+    0xC367502EAAC2A9,
+    0x17DA068B7D974B7,
+    0x2F4A34DEA341BC2,
+    0xD36F75C5738948,
+    0x6E94874605445,
+    0x12ADBAD28116AD1,
+    0x556556956,
+];
+pub const CURVE_PXA: [Chunk; NLEN] = [
+    0x3CB3B62D7F2D86,
+    0x3F6AD9E57474F85,
+    0x1C90F562572EE81,
+    0x3214B55C96F51FC,
+    0x27CB1E746432501,
+    0x1FB00FA301E6425,
+    0x634D2240,
+];
+pub const CURVE_PXB: [Chunk; NLEN] = [
+    0x3D9E41EC452DE15,
+    0x12ACA355FF9837B,
+    0xBA88E92D5D75B5,
+    0x3B6741732277F66,
+    0x3288361DD24F498,
+    0x592EBCDE9DC5,
+    0x300D78006,
+];
+pub const CURVE_PYA: [Chunk; NLEN] = [
+    0x68F0BB9408CB41,
+    0x27B793C83586597,
+    0x3ACA913A2E75B4,
+    0x359CF266CF9A25E,
+    0x33FE6347B6E990E,
+    0x34894D1F2527615,
+    0x33792CF93,
+];
+pub const CURVE_PYB: [Chunk; NLEN] = [
+    0x2D846437F479093,
+    0x10F2C379889218E,
+    0x32F449F7BC98B01,
+    0x111ACFBEA3DEBC2,
+    0x3D15A7AE001CE0D,
+    0xB3631AC93B9EE9,
+    0x20E5247DD,
+];
+pub const CURVE_W: [[Chunk; NLEN]; 2] = [
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+];
+pub const CURVE_SB: [[[Chunk; NLEN]; 2]; 2] = [
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+];
+pub const CURVE_WB: [[Chunk; NLEN]; 4] = [
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+];
+pub const CURVE_BB: [[[Chunk; NLEN]; 4]; 4] = [
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+];
+
+pub const USE_GLV: bool = true;
+pub const USE_GS_G2: bool = true;
+pub const USE_GS_GT: bool = true;
+pub const GT_STRONG: bool = true;
+
+pub const MODBYTES: usize = 48;
+pub const BASEBITS: usize = 58;
+
+pub const MODBITS: usize = 383;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 23;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 65;
+pub const SIGN_OF_X: SignOfX = SignOfX::POSITIVEX;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_bls461_32.rs b/src/roms/rom_bls461_32.rs
new file mode 100644
index 0000000..c452a9f
--- /dev/null
+++ b/src/roms/rom_bls461_32.rs
@@ -0,0 +1,209 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use bls461::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 28
+// bls461 Modulus
+
+pub const MODULUS: [Chunk; NLEN] = [
+    0xAAAAAAB, 0xAC0000A, 0x54AAAAA, 0x5555, 0x400020, 0x91557F0, 0xF26AA, 0xFA5C1CC, 0xB42A8DF,
+    0x7B14848, 0x8BACCA4, 0x6F1E32D, 0x4935FBD, 0x55D6941, 0xD5A555A, 0x5545554, 0x1555,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0xC9B6A33, 0x2ECD087, 0x3CCB2B1, 0xCD461FE, 0x8CB5AB2, 0xC5B9635, 0x5312E92, 0xB659F64,
+    0x3B596FA, 0x8679006, 0xA92E2B3, 0x3CE05E3, 0x363550F, 0x7C07A8E, 0x382C083, 0x6347FEA, 0xBD,
+];
+pub const MCONST: Chunk = 0xFFFFFFD;
+pub const FRA: [Chunk; NLEN] = [
+    0xB812A3A, 0x7117BF9, 0x99C400F, 0xC6308A5, 0x5BF8A1, 0x510E075, 0x45FA5A6, 0xCE4858D,
+    0x770B31A, 0xBC2CB04, 0xE2FC61E, 0xD073588, 0x4366190, 0x4DFEFA8, 0x69E55E2, 0x504B7F, 0x12E4,
+];
+pub const FRB: [Chunk; NLEN] = [
+    0xF298071, 0x3AE8410, 0xBAE6A9B, 0x39D4CAF, 0xFE4077E, 0x404777A, 0xBAF8104, 0x2C13C3E,
+    0x3D1F5C5, 0xBEE7D44, 0xA8B0685, 0x9EAADA4, 0x5CFE2C, 0x7D7999, 0x6BBFF78, 0x50409D5, 0x271,
+];
+
+// bls461 Curve
+pub const CURVE_COF_I: isize = 0;
+pub const CURVE_A: isize = 0;
+pub const CURVE_B_I: isize = 9;
+pub const CURVE_B: [Chunk; NLEN] = [
+    0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x1, 0x0, 0xFFFFC00, 0x7FEFFFE, 0x110000, 0x7FFC800, 0x801FC01, 0x5FD000E, 0x17FE0, 0xFFFC018,
+    0xFFFFFF7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0xADEE93D, 0x4D026A8, 0x74B7411, 0xD9C00EE, 0x31AC7F2, 0xC3981B5, 0x9218229, 0xD3564DC,
+    0xA096650, 0x6F7C292, 0x9743616, 0xBE922B1, 0x12CF668, 0xC81327, 0x463B73A, 0xE74E99B, 0xAD0,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0xAD1D465, 0xF763157, 0xC4FF470, 0x17884C8, 0xB8D215D, 0xA819E66, 0xF4959D0, 0xE5C3245,
+    0xB84910A, 0xB8BFA40, 0xBE96EEC, 0x8BF9F8C, 0xF277ACC, 0x5F1C3F2, 0x5F68C9, 0xCDB14B3, 0x77B,
+];
+pub const CURVE_BNX: [Chunk; NLEN] = [
+    0x0, 0xFBFFFE0, 0x1FFFFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_COF: [Chunk; NLEN] = [
+    0xAAAAAAB, 0xA7FFFEA, 0x1556AA, 0xD55AAAB, 0x554FFFF, 0x1555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_CRU: [Chunk; NLEN] = [
+    0xFFFFFFE, 0x40001F, 0xFE00000, 0xFFE7FFF, 0xF0FFF6F, 0x7200C47, 0x7BCC604, 0x15796DB,
+    0xCF47771, 0x9875433, 0x613F0E8, 0x5000502, 0xEBFFF60, 0x1FFFFF, 0x0, 0x0, 0x0,
+];
+pub const CURVE_PXA: [Chunk; NLEN] = [
+    0x6D0A37C, 0x5B50318, 0x75DCC46, 0xC2E492E, 0xD6878A9, 0xE01F919, 0xF92F564, 0x86DB74F,
+    0x66803F0, 0x46D581A, 0x7ED78D, 0x2F97C29, 0xC270C89, 0xF679453, 0x6A50A9A, 0x54138A0, 0x10CC,
+];
+pub const CURVE_PXB: [Chunk; NLEN] = [
+    0x2C1C0AD, 0xF85CA8C, 0x25CADE9, 0x6CD66C4, 0xA289609, 0xC612951, 0xEE2401A, 0x529ABEB,
+    0xF65B17D, 0xBA09D33, 0xD4C5AF5, 0x4D4371E, 0x46A672E, 0xA279D22, 0xACEA37C, 0x1FB4FE5, 0x95C,
+];
+pub const CURVE_PYA: [Chunk; NLEN] = [
+    0x2FB006, 0xCCD0C1B, 0xA12A337, 0x3D194A4, 0xC92C895, 0x4960CFC, 0x39FC68B, 0x3A9B00F,
+    0xED1BA0F, 0xA7DBBC5, 0xA9CDFD8, 0x27CC2F7, 0x4E73ED2, 0x6070F4F, 0xEBA7E67, 0xAC848E7, 0x226,
+];
+pub const CURVE_PYB: [Chunk; NLEN] = [
+    0xDF1457C, 0xA506ADF, 0x4C20A8, 0xD6A31DC, 0x36E3FB4, 0xEA9A8F1, 0x92F5668, 0x3C3BE44,
+    0x67A1297, 0x74BEABA, 0x56A20BE, 0x4C42E38, 0x45157F0, 0x2AB1D00, 0xBB402EA, 0x101B4FA, 0xE38,
+];
+pub const CURVE_W: [[Chunk; NLEN]; 2] = [
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+];
+pub const CURVE_SB: [[[Chunk; NLEN]; 2]; 2] = [
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+];
+pub const CURVE_WB: [[Chunk; NLEN]; 4] = [
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+];
+pub const CURVE_BB: [[[Chunk; NLEN]; 4]; 4] = [
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+];
+
+pub const USE_GLV: bool = true;
+pub const USE_GS_G2: bool = true;
+pub const USE_GS_GT: bool = true;
+pub const GT_STRONG: bool = false;
+
+pub const MODBYTES: usize = 58;
+pub const BASEBITS: usize = 28;
+
+pub const MODBITS: usize = 461;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 14;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 78;
+pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_bls461_64.rs b/src/roms/rom_bls461_64.rs
new file mode 100644
index 0000000..500ef04
--- /dev/null
+++ b/src/roms/rom_bls461_64.rs
@@ -0,0 +1,232 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use bls461::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 60
+// bls461 Modulus
+
+pub const MODULUS: [Chunk; NLEN] = [
+    0xAAC0000AAAAAAAB,
+    0x20000555554AAAA,
+    0x6AA91557F004000,
+    0xA8DFFA5C1CC00F2,
+    0xACCA47B14848B42,
+    0x935FBD6F1E32D8B,
+    0xD5A555A55D69414,
+    0x15555545554,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x96D08774614DDA8,
+    0xCD45F539225D5BD,
+    0xD712EB760C95AB1,
+    0xB3B687155F30B55,
+    0xC4E62A05C3F5B81,
+    0xBA1151676CA3CD0,
+    0x7EDD8A958F442BE,
+    0x12B89DD3F91,
+];
+pub const MCONST: Chunk = 0xC0005FFFFFFFD;
+pub const FRA: [Chunk; NLEN] = [
+    0xF7117BF9B812A3A,
+    0xA1C6308A599C400,
+    0x5A6510E07505BF8,
+    0xB31ACE4858D45FA,
+    0xFC61EBC2CB04770,
+    0x366190D073588E2,
+    0x69E55E24DFEFA84,
+    0x12E40504B7F,
+];
+pub const FRB: [Chunk; NLEN] = [
+    0xB3AE8410F298071,
+    0x7E39D4CAFBAE6A9,
+    0x104404777AFE407,
+    0xF5C52C13C3EBAF8,
+    0xB0685BEE7D443D1,
+    0x5CFE2C9EAADA4A8,
+    0x6BBFF7807D79990,
+    0x27150409D5,
+];
+
+// bls461 Curve
+pub const CURVE_COF_I: isize = 0;
+pub const CURVE_A: isize = 0;
+pub const CURVE_B_I: isize = 9;
+pub const CURVE_B: [Chunk; NLEN] = [0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x1,
+    0x7FEFFFEFFFFC0,
+    0xC017FFC80001100,
+    0x7FE05FD000E801F,
+    0xFFFF7FFFC018001,
+    0xFF,
+    0x0,
+    0x0,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x14D026A8ADEE93D,
+    0xF2D9C00EE74B741,
+    0x229C3981B531AC7,
+    0x6650D3564DC9218,
+    0x436166F7C292A09,
+    0x2CF668BE922B197,
+    0x463B73A0C813271,
+    0xAD0E74E99B,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0xF763157AD1D465,
+    0x5D17884C8C4FF47,
+    0x9D0A819E66B8D21,
+    0x910AE5C3245F495,
+    0x96EECB8BFA40B84,
+    0x277ACC8BF9F8CBE,
+    0x5F68C95F1C3F2F,
+    0x77BCDB14B3,
+];
+
+pub const CURVE_BNX: [Chunk; NLEN] = [0xFFBFFFE00000000, 0x1FFFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_COF: [Chunk; NLEN] = [
+    0xAA7FFFEAAAAAAAB,
+    0xFFD55AAAB01556A,
+    0x1555554FF,
+    0x0,
+    0x0,
+    0x0,
+    0x0,
+    0x0,
+];
+pub const CURVE_CRU: [Chunk; NLEN] = [
+    0x40001FFFFFFFE,
+    0x6FFFE7FFFFE0000,
+    0x6047200C47F0FFF,
+    0x777115796DB7BCC,
+    0x3F0E89875433CF4,
+    0xBFFF60500050261,
+    0x1FFFFFE,
+    0x0,
+];
+pub const CURVE_PXA: [Chunk; NLEN] = [
+    0x65B503186D0A37C,
+    0xA9C2E492E75DCC4,
+    0x564E01F919D6878,
+    0x3F086DB74FF92F,
+    0xED78D46D581A668,
+    0x270C892F97C2907,
+    0x6A50A9AF679453C,
+    0x10CC54138A0,
+];
+pub const CURVE_PXB: [Chunk; NLEN] = [
+    0x9F85CA8C2C1C0AD,
+    0x96CD66C425CADE,
+    0x1AC612951A2896,
+    0xB17D529ABEBEE24,
+    0xC5AF5BA09D33F65,
+    0x6A672E4D4371ED4,
+    0xACEA37CA279D224,
+    0x95C1FB4FE5,
+];
+pub const CURVE_PYA: [Chunk; NLEN] = [
+    0x7CCD0C1B02FB006,
+    0x953D194A4A12A33,
+    0x68B4960CFCC92C8,
+    0xBA0F3A9B00F39FC,
+    0xCDFD8A7DBBC5ED1,
+    0xE73ED227CC2F7A9,
+    0xEBA7E676070F4F4,
+    0x226AC848E7,
+];
+pub const CURVE_PYB: [Chunk; NLEN] = [
+    0x8A506ADFDF1457C,
+    0xB4D6A31DC04C20A,
+    0x668EA9A8F136E3F,
+    0x12973C3BE4492F5,
+    0xA20BE74BEABA67A,
+    0x5157F04C42E3856,
+    0xBB402EA2AB1D004,
+    0xE38101B4FA,
+];
+pub const CURVE_W: [[Chunk; NLEN]; 2] = [
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+];
+pub const CURVE_SB: [[[Chunk; NLEN]; 2]; 2] = [
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+];
+pub const CURVE_WB: [[Chunk; NLEN]; 4] = [
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+];
+pub const CURVE_BB: [[[Chunk; NLEN]; 4]; 4] = [
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+];
+
+pub const USE_GLV: bool = true;
+pub const USE_GS_G2: bool = true;
+pub const USE_GS_GT: bool = true;
+pub const GT_STRONG: bool = false;
+
+pub const MODBYTES: usize = 58;
+pub const BASEBITS: usize = 60;
+
+pub const MODBITS: usize = 461;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 19;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 78;
+pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_bls48_32.rs b/src/roms/rom_bls48_32.rs
new file mode 100644
index 0000000..83517a8
--- /dev/null
+++ b/src/roms/rom_bls48_32.rs
@@ -0,0 +1,309 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use bls48::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 29
+// bls48 Modulus
+
+pub const MODULUS: [Chunk; NLEN] = [
+    0x1CF6AC0B, 0x17B7307F, 0x19877E7B, 0x12CE0134, 0x14228402, 0x1BD4C386, 0x1DACBB04, 0x40410D0,
+    0x25A415, 0x980B53E, 0xDE6E250, 0x15D9AAD6, 0x5DA950, 0x1029B7A, 0x54AB351, 0x14AD90CE,
+    0x3729047, 0x1FE7E2D9, 0x145F610B, 0x1F,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0xD59D0FA, 0x12F01FD0, 0xDE8FD41, 0x35AAEE1, 0xB937F48, 0x50700E8, 0x1F50EFCE, 0x1019B13C,
+    0x3470A2F, 0x11094115, 0xF9FB72D, 0x6AD10E2, 0x1CFD9F8, 0x44F4785, 0x2B48793, 0x1148ED3,
+    0xF609E61, 0x1EE34BC7, 0x1735D29E, 0x0,
+];
+pub const MCONST: Chunk = 0x9DA805D;
+pub const FRA: [Chunk; NLEN] = [
+    0x1325BF89, 0x1311E7EC, 0xCD0A56F, 0x1A0FD46E, 0xE83BCCA, 0xCA97DD0, 0x18D1D297, 0x5F1E137,
+    0x7AB9F2C, 0x13FC255F, 0x1C9DECEB, 0x9DEF4A2, 0x3C0F60B, 0x1D9909E4, 0x1FF27FF7, 0x1DBF8208,
+    0x89BB36C, 0x40044E0, 0x62E01EE, 0x5,
+];
+pub const FRB: [Chunk; NLEN] = [
+    0x1325BF89, 0x1311E7EC, 0xCD0A56F, 0x1A0FD46E, 0xE83BCCA, 0xCA97DD0, 0x18D1D297, 0x5F1E137,
+    0x7AB9F2C, 0x13FC255F, 0x1C9DECEB, 0x9DEF4A2, 0x3C0F60B, 0x1D9909E4, 0x1FF27FF7, 0x1DBF8208,
+    0x89BB36C, 0x40044E0, 0x62E01EE, 0x5,
+];
+
+pub const CURVE_COF_I: isize = 0;
+pub const CURVE_A: isize = 0;
+pub const CURVE_B_I: isize = 17;
+pub const CURVE_B: [Chunk; NLEN] = [
+    0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x0,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x1, 0x17FFF800, 0xA769C21, 0x8AA813C, 0x2029C21, 0xA68F58B, 0xB6307F4, 0x1184DA51, 0x6DFED78,
+    0x1A3C85E9, 0x571037B, 0x1637F1F9, 0x1C465FB0, 0x98354B9, 0x118DF17A, 0x1422355D, 0x43BF73E,
+    0x6, 0x0, 0x0,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x5D71D33, 0x1943697B, 0x18CB783F, 0x1B00AA9F, 0x1711EE0B, 0x7F80B23, 0x129FD8CC, 0x1345E03F,
+    0x9A80F66, 0x7038173, 0xC056511, 0x142801F5, 0x42B2C3A, 0x1AF09869, 0x7924166, 0x8381264,
+    0x957EDD7, 0xBACAEDC, 0xA27A4A1, 0x13,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0xA6ED83A, 0x14D2D9FF, 0xA29C33D, 0x1B8972A9, 0x6958677, 0x19C8F547, 0x1DED7E3E, 0x14F9E3DC,
+    0x18FB7229, 0x27171C0, 0x1551E32D, 0xE6184CC, 0x6260E3C, 0x733D204, 0x579C437, 0x1534665C,
+    0x2B3349D, 0x3162FD7, 0xB634253, 0x1,
+];
+
+pub const CURVE_BNX: [Chunk; NLEN] = [
+    0x1DE40020, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0x0,
+];
+pub const CURVE_COF: [Chunk; NLEN] = [
+    0x1F12ABEB, 0x516887B, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_CRU: [Chunk; NLEN] = [
+    0xCBBA429, 0x1B273F3, 0xD3DD160, 0x19C61452, 0x308093A, 0x146E1E34, 0xAE0E768, 0x1185948,
+    0x1B73BC2D, 0x93D855C, 0x1B1A639C, 0x118C919B, 0xFF04AE3, 0xF1CCD77, 0x91318E5, 0x10644780,
+    0x3A79F7, 0x1BE77919, 0x145F60F3, 0x1F,
+];
+pub const CURVE_PXAAA: [Chunk; NLEN] = [
+    0x923CE4A, 0x14697474, 0xAE04F4A, 0x17AE205A, 0x1313A20C, 0x10B2EC50, 0x18DF074F, 0x15FE3FE8,
+    0x7C90B98, 0x959BF85, 0xE57BD37, 0x14376C96, 0xBF57375, 0xE20B625, 0x12EE2172, 0x1CBBCE85,
+    0x1A5D9487, 0xD0E024B, 0x195E3602, 0x1C,
+];
+pub const CURVE_PXAAB: [Chunk; NLEN] = [
+    0xC0A1BE1, 0x138E6E2D, 0x1DF5FDC, 0x151FC760, 0x33972C5, 0x56AA3C2, 0x2491D8C, 0x115B9FD7,
+    0x140A11FA, 0x1873AE35, 0x1F259C26, 0x74B0647, 0x12D18B04, 0x4672431, 0x1C27F419, 0x1CAA4D35,
+    0x18DB48B6, 0x13A54BDA, 0x5080497, 0x5,
+];
+pub const CURVE_PXABA: [Chunk; NLEN] = [
+    0x170C5DC4, 0x11D39263, 0x16B3BCB6, 0x152C95BB, 0x19BEC736, 0x8849A12, 0x49AB2A8, 0xC7162D3,
+    0xC58CD55, 0x15C2659, 0x11EE8B90, 0xB40CAFC, 0xE233167, 0x7BEC8BE, 0x129335BD, 0x151C7DBB,
+    0x78B689B, 0x1B6B8EED, 0x14BFBE3D, 0x16,
+];
+pub const CURVE_PXABB: [Chunk; NLEN] = [
+    0x1A64B740, 0x6B14B34, 0x12481578, 0x23FA931, 0x323ADD1, 0x206B82A, 0xD789E1B, 0x1FCFA666,
+    0x1F4EEA7, 0xF1E39E2, 0x1968610, 0xAF3EBD3, 0x590D3B, 0xDA0C35A, 0x17306AAF, 0xCF9DD2B,
+    0x3F63B1A, 0x96FF2F9, 0xE102A76, 0x12,
+];
+pub const CURVE_PXBAA: [Chunk; NLEN] = [
+    0x12F1E01F, 0xDD8630B, 0x12C29802, 0x186239A6, 0x19218788, 0x4C87D1, 0x16AE2501, 0x775C076,
+    0x870C80B, 0x1A394429, 0x1637D478, 0x4A420E8, 0x1C3AD4D4, 0x10E5E713, 0x111E6AD5, 0x514FCF0,
+    0x7CC49D3, 0xC678A2, 0x1787BDFD, 0x1B,
+];
+pub const CURVE_PXBAB: [Chunk; NLEN] = [
+    0x637383D, 0x1851C11C, 0x661F866, 0x14404A7F, 0x15D3D212, 0x9AE28F6, 0x8051F25, 0x1E1CE2BF,
+    0x137D882F, 0xB231CEB, 0xA8DB8FC, 0x18957645, 0x5E54DA8, 0x1FF41C44, 0x1A297414, 0x17E1CBC5,
+    0x1014F91F, 0x4282AB7, 0xB6CE9E3, 0x10,
+];
+pub const CURVE_PXBBA: [Chunk; NLEN] = [
+    0x1711939C, 0xB41ED9E, 0x69066BA, 0x137CA3AD, 0xCF2F6C0, 0x5E6DAB9, 0x2CE1323, 0x946E448,
+    0xF353D1C, 0x14D9919F, 0x46B7046, 0x1A12015, 0x3D6070, 0x18C3E8D2, 0x1F23BA45, 0x1F1A337C,
+    0x435A9CC, 0x6CA1DF1, 0x8A9CE1, 0x15,
+];
+pub const CURVE_PXBBB: [Chunk; NLEN] = [
+    0x56F4899, 0x196A0854, 0xA959750, 0x38A3D72, 0x190BC9BC, 0x145752BC, 0x1E9E26DA, 0x1403F88,
+    0x71895E3, 0x14162F5D, 0x19FEC5FF, 0x14190B16, 0x7597C, 0x19A3CF18, 0x26A4B00, 0x113D1BB6,
+    0x7857A32, 0xE0B78AB, 0x1DD51E0F, 0x1B,
+];
+pub const CURVE_PYAAA: [Chunk; NLEN] = [
+    0x14137844, 0x1704BE7D, 0x1FD3CCDD, 0x189D8C93, 0x1C768851, 0xF5C37D5, 0xE29C659, 0x20AB1C1,
+    0xF8896E0, 0x1E08663E, 0x1D1D539C, 0x117E1C47, 0x156CDD39, 0x161F1017, 0x143E8C72, 0x174B22FD,
+    0x18706190, 0x49AA47E, 0x19BB42E1, 0xE,
+];
+pub const CURVE_PYAAB: [Chunk; NLEN] = [
+    0xDC83190, 0x12F19247, 0x1AA26424, 0x15D55E88, 0xC418D32, 0xB0E91DD, 0x47CBFF7, 0x2D992C1,
+    0xDE03C1F, 0x7694AE5, 0x5C741A2, 0x1D423AC6, 0x5E02B9E, 0x1E903F10, 0x4EA6513, 0x433A1F1,
+    0x8EFA1C4, 0xED54713, 0x1E72CE4F, 0x4,
+];
+pub const CURVE_PYABA: [Chunk; NLEN] = [
+    0x1985C0D, 0xEE2FE82, 0x64770FA, 0x11A809B4, 0x1483ACE9, 0x18BCD2FA, 0x171F32C, 0x1612D58D,
+    0x1E658341, 0x1CBE2201, 0x186E971, 0x73F0E1, 0xB0A5F40, 0xAC90FB0, 0x1635E008, 0x237498B,
+    0x1F3140D6, 0xBF789A9, 0x1166F259, 0x1A,
+];
+pub const CURVE_PYABB: [Chunk; NLEN] = [
+    0x159D42F8, 0x1B7F0540, 0x45895D7, 0x14875FA2, 0x1E9E7F2B, 0x10139D87, 0x10F3FD7D, 0x11D3717F,
+    0x69E5006, 0xF9BB3C4, 0x13C9ED8D, 0x16516DA, 0x102F51DE, 0x2725FEC, 0x1F125B66, 0xFFC324,
+    0x1ED80731, 0x1C16C4D, 0x383AAA8, 0x14,
+];
+pub const CURVE_PYBAA: [Chunk; NLEN] = [
+    0x1F38039F, 0x6A8959C, 0x13C68984, 0x11DD12AF, 0x58093CF, 0x1C8550A0, 0xFFA1622, 0xFF85979,
+    0x1F2ABB75, 0x18862E62, 0x1EB6A2C9, 0x1EC80B64, 0x8EC2F18, 0xE7BF713, 0xC36B65A, 0x19C5DD89,
+    0x18A1D1AB, 0xF772C8D, 0xC11927C, 0x5,
+];
+pub const CURVE_PYBAB: [Chunk; NLEN] = [
+    0x95F7865, 0x134F0379, 0x1CE9A0E, 0x17E0EADD, 0x1DACADD7, 0x1B18F9F8, 0x181D3943, 0x186679A,
+    0x2505BB0, 0x1FDF1DC8, 0x11B36A49, 0x11E254E9, 0xA438576, 0x102B09AE, 0x139984F4, 0x15BC0233,
+    0x1B6F180E, 0x960562B, 0x48CA65B, 0x6,
+];
+pub const CURVE_PYBBA: [Chunk; NLEN] = [
+    0x7CC1979, 0xEC1D4FB, 0x1D89E6F0, 0x955F38E, 0x1635FDA9, 0x123D8E10, 0x10076209, 0x494404A,
+    0xD733D7, 0x17678BCF, 0x153841F9, 0x10696FFD, 0x5BC9FE8, 0x1A20D8B2, 0xE22EC9D, 0x18449116,
+    0x108C86C5, 0x1B4CD720, 0x34967, 0x19,
+];
+pub const CURVE_PYBBB: [Chunk; NLEN] = [
+    0xFC9F25B, 0x7E44AB1, 0xE9AB5D3, 0x589F00D, 0x1C9D264F, 0xC7478B4, 0x16B24A13, 0x1D2C146B,
+    0xEF84D9A, 0xF47ECDE, 0x1BFEE16A, 0x1B69071E, 0x11AB4C1C, 0xBE9D9EF, 0x390F005, 0x78C8288,
+    0x1B9BF549, 0x9320730, 0x3D84D97, 0x14,
+];
+pub const CURVE_W: [[Chunk; NLEN]; 2] = [
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        0x0, 0x0,
+    ],
+];
+pub const CURVE_SB: [[[Chunk; NLEN]; 2]; 2] = [
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+    ],
+];
+pub const CURVE_WB: [[Chunk; NLEN]; 4] = [
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        0x0, 0x0,
+    ],
+    [
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        0x0, 0x0,
+    ],
+];
+pub const CURVE_BB: [[[Chunk; NLEN]; 4]; 4] = [
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+        [
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0,
+        ],
+    ],
+];
+
+pub const USE_GLV: bool = true;
+pub const USE_GS_G2: bool = true;
+pub const USE_GS_GT: bool = true;
+pub const GT_STRONG: bool = true;
+
+pub const MODBYTES: usize = 70;
+pub const BASEBITS: usize = 29;
+
+pub const MODBITS: usize = 556;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 14;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 32;
+pub const SIGN_OF_X: SignOfX = SignOfX::POSITIVEX;
+pub const HASH_TYPE: usize = 64;
+pub const AESKEY: usize = 32;
diff --git a/src/roms/rom_bls48_64.rs b/src/roms/rom_bls48_64.rs
new file mode 100644
index 0000000..129c776
--- /dev/null
+++ b/src/roms/rom_bls48_64.rs
@@ -0,0 +1,401 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use bls48::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 58
+// bls48 Modulus
+
+pub const MODULUS: [Chunk; NLEN] = [
+    0x2F6E60FFCF6AC0B,
+    0x259C02699877E7B,
+    0x37A9870D4228402,
+    0x80821A1DACBB04,
+    0x13016A7C025A415,
+    0x2BB355ACDE6E250,
+    0x20536F405DA950,
+    0x295B219C54AB351,
+    0x3FCFC5B23729047,
+    0x3F45F610B,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x25E03FA0D59D0FA,
+    0x6B55DC2DE8FD41,
+    0xA0E01D0B937F48,
+    0x20336279F50EFCE,
+    0x2212822A3470A2F,
+    0xD5A21C4F9FB72D,
+    0x89E8F0A1CFD9F8,
+    0x2291DA62B48793,
+    0x3DC6978EF609E61,
+    0x1735D29E,
+];
+pub const MCONST: Chunk = 0x21BFCBCA9DA805D;
+pub const FRA: [Chunk; NLEN] = [
+    0x2623CFD9325BF89,
+    0x341FA8DCCD0A56F,
+    0x1952FBA0E83BCCA,
+    0xBE3C26F8D1D297,
+    0x27F84ABE7AB9F2C,
+    0x13BDE945C9DECEB,
+    0x3B3213C83C0F60B,
+    0x3B7F0411FF27FF7,
+    0x80089C089BB36C,
+    0xA62E01EE,
+];
+pub const FRB: [Chunk; NLEN] = [
+    0x2623CFD9325BF89,
+    0x341FA8DCCD0A56F,
+    0x1952FBA0E83BCCA,
+    0xBE3C26F8D1D297,
+    0x27F84ABE7AB9F2C,
+    0x13BDE945C9DECEB,
+    0x3B3213C83C0F60B,
+    0x3B7F0411FF27FF7,
+    0x80089C089BB36C,
+    0xA62E01EE,
+];
+
+pub const CURVE_COF_I: isize = 0;
+pub const CURVE_A: isize = 0;
+pub const CURVE_B_I: isize = 17;
+pub const CURVE_B: [Chunk; NLEN] = [0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x2FFFF0000000001,
+    0x11550278A769C21,
+    0x14D1EB162029C21,
+    0x2309B4A2B6307F4,
+    0x34790BD26DFED78,
+    0x2C6FE3F2571037B,
+    0x1306A973C465FB0,
+    0x28446ABB18DF17A,
+    0xC43BF73E,
+    0x0,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x3286D2F65D71D33,
+    0x3601553F8CB783F,
+    0xFF01647711EE0B,
+    0x268BC07F29FD8CC,
+    0xE0702E69A80F66,
+    0x285003EAC056511,
+    0x35E130D242B2C3A,
+    0x107024C87924166,
+    0x17595DB8957EDD7,
+    0x26A27A4A1,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x29A5B3FEA6ED83A,
+    0x3712E552A29C33D,
+    0x3391EA8E6958677,
+    0x29F3C7B9DED7E3E,
+    0x4E2E3818FB7229,
+    0x1CC30999551E32D,
+    0xE67A4086260E3C,
+    0x2A68CCB8579C437,
+    0x62C5FAE2B3349D,
+    0x2B634253,
+];
+
+pub const CURVE_BNX: [Chunk; NLEN] = [0x7DE40020, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_COF: [Chunk; NLEN] = [
+    0xA2D10F7F12ABEB,
+    0x5,
+    0x0,
+    0x0,
+    0x0,
+    0x0,
+    0x0,
+    0x0,
+    0x0,
+    0x0,
+];
+pub const CURVE_CRU: [Chunk; NLEN] = [
+    0x364E7E6CBBA429,
+    0x338C28A4D3DD160,
+    0x28DC3C68308093A,
+    0x230B290AE0E768,
+    0x127B0AB9B73BC2D,
+    0x23192337B1A639C,
+    0x1E399AEEFF04AE3,
+    0x20C88F0091318E5,
+    0x37CEF23203A79F7,
+    0x3F45F60F3,
+];
+pub const CURVE_PXAAA: [Chunk; NLEN] = [
+    0x28D2E8E8923CE4A,
+    0x2F5C40B4AE04F4A,
+    0x2165D8A1313A20C,
+    0x2BFC7FD18DF074F,
+    0x12B37F0A7C90B98,
+    0x286ED92CE57BD37,
+    0x1C416C4ABF57375,
+    0x39779D0B2EE2172,
+    0x1A1C0497A5D9487,
+    0x3995E3602,
+];
+pub const CURVE_PXAAB: [Chunk; NLEN] = [
+    0x271CDC5AC0A1BE1,
+    0x2A3F8EC01DF5FDC,
+    0xAD5478433972C5,
+    0x22B73FAE2491D8C,
+    0x30E75C6B40A11FA,
+    0xE960C8FF259C26,
+    0x8CE48632D18B04,
+    0x39549A6BC27F419,
+    0x274A97B58DB48B6,
+    0xA5080497,
+];
+pub const CURVE_PXABA: [Chunk; NLEN] = [
+    0x23A724C770C5DC4,
+    0x2A592B776B3BCB6,
+    0x110934259BEC736,
+    0x18E2C5A649AB2A8,
+    0x2B84CB2C58CD55,
+    0x168195F91EE8B90,
+    0xF7D917CE233167,
+    0x2A38FB7729335BD,
+    0x36D71DDA78B689B,
+    0x2D4BFBE3D,
+];
+pub const CURVE_PXABB: [Chunk; NLEN] = [
+    0xD629669A64B740,
+    0x47F52632481578,
+    0x40D7054323ADD1,
+    0x3F9F4CCCD789E1B,
+    0x1E3C73C41F4EEA7,
+    0x15E7D7A61968610,
+    0x1B4186B40590D3B,
+    0x19F3BA577306AAF,
+    0x12DFE5F23F63B1A,
+    0x24E102A76,
+];
+pub const CURVE_PXBAA: [Chunk; NLEN] = [
+    0x1BB0C6172F1E01F,
+    0x30C4734D2C29802,
+    0x990FA39218788,
+    0xEEB80ED6AE2501,
+    0x34728852870C80B,
+    0x94841D1637D478,
+    0x21CBCE27C3AD4D4,
+    0xA29F9E111E6AD5,
+    0x18CF1447CC49D3,
+    0x37787BDFD,
+];
+pub const CURVE_PXBAB: [Chunk; NLEN] = [
+    0x30A38238637383D,
+    0x288094FE661F866,
+    0x135C51ED5D3D212,
+    0x3C39C57E8051F25,
+    0x164639D737D882F,
+    0x312AEC8AA8DB8FC,
+    0x3FE838885E54DA8,
+    0x2FC3978BA297414,
+    0x850556F014F91F,
+    0x20B6CE9E3,
+];
+pub const CURVE_PXBBA: [Chunk; NLEN] = [
+    0x1683DB3D711939C,
+    0x26F9475A69066BA,
+    0xBCDB572CF2F6C0,
+    0x128DC8902CE1323,
+    0x29B3233EF353D1C,
+    0x342402A46B7046,
+    0x3187D1A403D6070,
+    0x3E3466F9F23BA45,
+    0xD943BE2435A9CC,
+    0x2A08A9CE1,
+];
+pub const CURVE_PXBBB: [Chunk; NLEN] = [
+    0x32D410A856F4899,
+    0x7147AE4A959750,
+    0x28AEA57990BC9BC,
+    0x2807F11E9E26DA,
+    0x282C5EBA71895E3,
+    0x2832162D9FEC5FF,
+    0x33479E30007597C,
+    0x227A376C26A4B00,
+    0x1C16F1567857A32,
+    0x37DD51E0F,
+];
+pub const CURVE_PYAAA: [Chunk; NLEN] = [
+    0x2E097CFB4137844,
+    0x313B1927FD3CCDD,
+    0x1EB86FABC768851,
+    0x4156382E29C659,
+    0x3C10CC7CF8896E0,
+    0x22FC388FD1D539C,
+    0x2C3E202F56CDD39,
+    0x2E9645FB43E8C72,
+    0x93548FD8706190,
+    0x1D9BB42E1,
+];
+pub const CURVE_PYAAB: [Chunk; NLEN] = [
+    0x25E3248EDC83190,
+    0x2BAABD11AA26424,
+    0x161D23BAC418D32,
+    0x5B3258247CBFF7,
+    0xED295CADE03C1F,
+    0x3A84758C5C741A2,
+    0x3D207E205E02B9E,
+    0x86743E24EA6513,
+    0x1DAA8E268EFA1C4,
+    0x9E72CE4F,
+];
+pub const CURVE_PYABA: [Chunk; NLEN] = [
+    0x1DC5FD041985C0D,
+    0x2350136864770FA,
+    0x3179A5F5483ACE9,
+    0x2C25AB1A171F32C,
+    0x397C4403E658341,
+    0xE7E1C2186E971,
+    0x15921F60B0A5F40,
+    0x46E9317635E008,
+    0x17EF1353F3140D6,
+    0x35166F259,
+];
+pub const CURVE_PYABB: [Chunk; NLEN] = [
+    0x36FE0A8159D42F8,
+    0x290EBF4445895D7,
+    0x20273B0FE9E7F2B,
+    0x23A6E2FF0F3FD7D,
+    0x1F37678869E5006,
+    0x2CA2DB53C9ED8D,
+    0x4E4BFD902F51DE,
+    0x1FF8649F125B66,
+    0x382D89BED80731,
+    0x28383AAA8,
+];
+pub const CURVE_PYBAA: [Chunk; NLEN] = [
+    0xD512B39F38039F,
+    0x23BA255F3C68984,
+    0x390AA14058093CF,
+    0x1FF0B2F2FFA1622,
+    0x310C5CC5F2ABB75,
+    0x3D9016C9EB6A2C9,
+    0x1CF7EE268EC2F18,
+    0x338BBB12C36B65A,
+    0x1EEE591B8A1D1AB,
+    0xAC11927C,
+];
+pub const CURVE_PYBAB: [Chunk; NLEN] = [
+    0x269E06F295F7865,
+    0x2FC1D5BA1CE9A0E,
+    0x3631F3F1DACADD7,
+    0x30CCF3581D3943,
+    0x3FBE3B902505BB0,
+    0x23C4A9D31B36A49,
+    0x2056135CA438576,
+    0x2B78046739984F4,
+    0x12C0AC57B6F180E,
+    0xC48CA65B,
+];
+pub const CURVE_PYBBA: [Chunk; NLEN] = [
+    0x1D83A9F67CC1979,
+    0x12ABE71DD89E6F0,
+    0x247B1C21635FDA9,
+    0x92880950076209,
+    0x2ECF179E0D733D7,
+    0x20D2DFFB53841F9,
+    0x3441B1645BC9FE8,
+    0x3089222CE22EC9D,
+    0x3699AE4108C86C5,
+    0x320034967,
+];
+pub const CURVE_PYBBB: [Chunk; NLEN] = [
+    0xFC89562FC9F25B,
+    0xB13E01AE9AB5D3,
+    0x18E8F169C9D264F,
+    0x3A5828D76B24A13,
+    0x1E8FD9BCEF84D9A,
+    0x36D20E3DBFEE16A,
+    0x17D3B3DF1AB4C1C,
+    0xF190510390F005,
+    0x12640E61B9BF549,
+    0x283D84D97,
+];
+pub const CURVE_W: [[Chunk; NLEN]; 2] = [
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+];
+pub const CURVE_SB: [[[Chunk; NLEN]; 2]; 2] = [
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+];
+pub const CURVE_WB: [[Chunk; NLEN]; 4] = [
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+];
+pub const CURVE_BB: [[[Chunk; NLEN]; 4]; 4] = [
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+];
+
+pub const USE_GLV: bool = true;
+pub const USE_GS_G2: bool = true;
+pub const USE_GS_GT: bool = true;
+pub const GT_STRONG: bool = true;
+
+pub const MODBYTES: usize = 70;
+pub const BASEBITS: usize = 58;
+
+pub const MODBITS: usize = 556;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 24;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 32;
+pub const SIGN_OF_X: SignOfX = SignOfX::POSITIVEX;
+pub const HASH_TYPE: usize = 64;
+pub const AESKEY: usize = 32;
diff --git a/src/roms/rom_bn254CX_32.rs b/src/roms/rom_bn254CX_32.rs
new file mode 100644
index 0000000..e1cff0d
--- /dev/null
+++ b/src/roms/rom_bn254CX_32.rs
@@ -0,0 +1,183 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use bn254CX::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 28
+// bn254CX Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0xC1B55B3, 0x6623EF5, 0x93EE1BE, 0xD6EE180, 0x6D3243F, 0x647A636, 0xDB0BDDF, 0x8702A0,
+    0x4000000, 0x2,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x8A0800A, 0x466A061, 0x43056A3, 0x2B3A225, 0x9C6600, 0x148515B, 0x6BDF50, 0xEC9EA56,
+    0xC992E66, 0x1,
+];
+pub const MCONST: Chunk = 0x9789E85;
+pub const FRA: [Chunk; NLEN] = [
+    0x5C80EA3, 0xD908335, 0x3F8215B, 0x7326F17, 0x8986867, 0x8AACA71, 0x4AFE18B, 0xA63A016,
+    0x359082F, 0x1,
+];
+pub const FRB: [Chunk; NLEN] = [
+    0x6534710, 0x8D1BBC0, 0x546C062, 0x63C7269, 0xE3ABBD8, 0xD9CDBC4, 0x900DC53, 0x623628A,
+    0xA6F7D0, 0x1,
+];
+
+// bn254CX Curve
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = 0;
+pub const CURVE_B_I: isize = 2;
+pub const CURVE_B: [Chunk; NLEN] = [0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x6EB1F6D, 0x11C0A63, 0x906CEBE, 0xD6EE0CC, 0x6D2C43F, 0x647A636, 0xDB0BDDF, 0x8702A0,
+    0x4000000, 0x2,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0xC1B55B2, 0x6623EF5, 0x93EE1BE, 0xD6EE180, 0x6D3243F, 0x647A636, 0xDB0BDDF, 0x8702A0,
+    0x4000000, 0x2,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_BNX: [Chunk; NLEN] = [0x3C012B1, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_CRU: [Chunk; NLEN] = [
+    0x4235C97, 0xE093179, 0xF875631, 0xDF6471E, 0xF1440BD, 0xCA83, 0x480000, 0x0, 0x0, 0x0,
+];
+
+pub const CURVE_PXA: [Chunk; NLEN] = [
+    0x4D2EC74, 0x851CEEE, 0xE2726C0, 0x85BFA03, 0xBBB907C, 0xF5C34, 0x6358B25, 0x7053B25,
+    0x9682D2C, 0x1,
+];
+pub const CURVE_PXB: [Chunk; NLEN] = [
+    0xE29CFE1, 0xA58E8B2, 0x9C30F47, 0x97B0C20, 0x743F81B, 0x37A8E99, 0xAA011C9, 0x3E19F64,
+    0x466B9EC, 0x1,
+];
+pub const CURVE_PYA: [Chunk; NLEN] = [
+    0xF0BE09F, 0xFBFCEBC, 0xEC1B30C, 0xB33D847, 0x2096361, 0x157DAEE, 0xDD81E22, 0x72332B8,
+    0xA79EDD9, 0x0,
+];
+pub const CURVE_PYB: [Chunk; NLEN] = [
+    0x898EE9D, 0x904B228, 0x2EDEBED, 0x4EA569D, 0x461C286, 0x512D8D3, 0x35C6E4, 0xECC4C09,
+    0x6160C39, 0x0,
+];
+
+pub const CURVE_W: [[Chunk; NLEN]; 2] = [
+    [
+        0x62FEB83, 0x5463491, 0x381200, 0xB4, 0x6000, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [0x7802561, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+];
+pub const CURVE_SB: [[[Chunk; NLEN]; 2]; 2] = [
+    [
+        [
+            0xDB010E4, 0x5463491, 0x381280, 0xB4, 0x6000, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [0x7802561, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x7802561, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [
+            0xBB33EA, 0xBD5D5D2, 0x8CEBCBD, 0xD6EE018, 0x6D2643F, 0x647A636, 0xDB0BDDF, 0x8702A0,
+            0x4000000, 0x2,
+        ],
+    ],
+];
+pub const CURVE_WB: [[Chunk; NLEN]; 4] = [
+    [
+        0x67A84B0, 0x1C21185, 0x12B040, 0x3C, 0x2000, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0xE220475, 0xCDF995B, 0xA7F9A36, 0x94EDA8C, 0xA0DC07E, 0x8702, 0x300000, 0x0, 0x0, 0x0,
+    ],
+    [
+        0xF10B93, 0x66FCCAE, 0x53FCD3B, 0x4A76D46, 0x506E03F, 0x4381, 0x180000, 0x0, 0x0, 0x0,
+    ],
+    [
+        0xDFAAA11, 0x1C21185, 0x12B0C0, 0x3C, 0x2000, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+];
+pub const CURVE_BB: [[[Chunk; NLEN]; 4]; 4] = [
+    [
+        [
+            0x32B0CBD, 0x11C0A63, 0x906CE7E, 0xD6EE0CC, 0x6D2C43F, 0x647A636, 0xDB0BDDF, 0x8702A0,
+            0x4000000, 0x2,
+        ],
+        [
+            0x32B0CBC, 0x11C0A63, 0x906CE7E, 0xD6EE0CC, 0x6D2C43F, 0x647A636, 0xDB0BDDF, 0x8702A0,
+            0x4000000, 0x2,
+        ],
+        [
+            0x32B0CBC, 0x11C0A63, 0x906CE7E, 0xD6EE0CC, 0x6D2C43F, 0x647A636, 0xDB0BDDF, 0x8702A0,
+            0x4000000, 0x2,
+        ],
+        [0x7802562, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x7802561, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [
+            0x32B0CBC, 0x11C0A63, 0x906CE7E, 0xD6EE0CC, 0x6D2C43F, 0x647A636, 0xDB0BDDF, 0x8702A0,
+            0x4000000, 0x2,
+        ],
+        [
+            0x32B0CBD, 0x11C0A63, 0x906CE7E, 0xD6EE0CC, 0x6D2C43F, 0x647A636, 0xDB0BDDF, 0x8702A0,
+            0x4000000, 0x2,
+        ],
+        [
+            0x32B0CBC, 0x11C0A63, 0x906CE7E, 0xD6EE0CC, 0x6D2C43F, 0x647A636, 0xDB0BDDF, 0x8702A0,
+            0x4000000, 0x2,
+        ],
+    ],
+    [
+        [0x7802562, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x7802561, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x7802561, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x7802561, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x3C012B2, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0xF004AC2, 0x0, 0x100, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [
+            0xF6AFA0A, 0x11C0A62, 0x906CE3E, 0xD6EE0CC, 0x6D2C43F, 0x647A636, 0xDB0BDDF, 0x8702A0,
+            0x4000000, 0x2,
+        ],
+        [0x3C012B2, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+];
+
+pub const USE_GLV: bool = true;
+pub const USE_GS_G2: bool = true;
+pub const USE_GS_GT: bool = true;
+pub const GT_STRONG: bool = true;
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 28;
+
+pub const MODBITS: usize = 254;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 14;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BN;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::D_TYPE;
+pub const ATE_BITS: usize = 66;
+pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_bn254CX_64.rs b/src/roms/rom_bn254CX_64.rs
new file mode 100644
index 0000000..7f6d274
--- /dev/null
+++ b/src/roms/rom_bn254CX_64.rs
@@ -0,0 +1,242 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use bn254CX::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+
+// Base Bits= 56
+// bn254CX Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0x6623EF5C1B55B3,
+    0xD6EE18093EE1BE,
+    0x647A6366D3243F,
+    0x8702A0DB0BDDF,
+    0x24000000,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x466A0618A0800A,
+    0x2B3A22543056A3,
+    0x148515B09C6600,
+    0xEC9EA5606BDF50,
+    0x1C992E66,
+];
+pub const MCONST: Chunk = 0x4E205BF9789E85;
+pub const FRA: [Chunk; NLEN] = [
+    0xD9083355C80EA3,
+    0x7326F173F8215B,
+    0x8AACA718986867,
+    0xA63A0164AFE18B,
+    0x1359082F,
+];
+pub const FRB: [Chunk; NLEN] = [
+    0x8D1BBC06534710,
+    0x63C7269546C062,
+    0xD9CDBC4E3ABBD8,
+    0x623628A900DC53,
+    0x10A6F7D0,
+];
+
+// bn254CX Curve
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = 0;
+pub const CURVE_B_I: isize = 2;
+pub const CURVE_B: [Chunk; NLEN] = [0x2, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x11C0A636EB1F6D,
+    0xD6EE0CC906CEBE,
+    0x647A6366D2C43F,
+    0x8702A0DB0BDDF,
+    0x24000000,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x6623EF5C1B55B2,
+    0xD6EE18093EE1BE,
+    0x647A6366D3243F,
+    0x8702A0DB0BDDF,
+    0x24000000,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_BNX: [Chunk; NLEN] = [0x3C012B1, 0x40, 0x0, 0x0, 0x0];
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_CRU: [Chunk; NLEN] = [
+    0xE0931794235C97,
+    0xDF6471EF875631,
+    0xCA83F1440BD,
+    0x480000,
+    0x0,
+];
+
+pub const CURVE_PXA: [Chunk; NLEN] = [
+    0x851CEEE4D2EC74,
+    0x85BFA03E2726C0,
+    0xF5C34BBB907C,
+    0x7053B256358B25,
+    0x19682D2C,
+];
+pub const CURVE_PXB: [Chunk; NLEN] = [
+    0xA58E8B2E29CFE1,
+    0x97B0C209C30F47,
+    0x37A8E99743F81B,
+    0x3E19F64AA011C9,
+    0x1466B9EC,
+];
+pub const CURVE_PYA: [Chunk; NLEN] = [
+    0xFBFCEBCF0BE09F,
+    0xB33D847EC1B30C,
+    0x157DAEE2096361,
+    0x72332B8DD81E22,
+    0xA79EDD9,
+];
+pub const CURVE_PYB: [Chunk; NLEN] = [
+    0x904B228898EE9D,
+    0x4EA569D2EDEBED,
+    0x512D8D3461C286,
+    0xECC4C09035C6E4,
+    0x6160C39,
+];
+
+pub const CURVE_W: [[Chunk; NLEN]; 2] = [
+    [0x546349162FEB83, 0xB40381200, 0x6000, 0x0, 0x0],
+    [0x7802561, 0x80, 0x0, 0x0, 0x0],
+];
+pub const CURVE_SB: [[[Chunk; NLEN]; 2]; 2] = [
+    [
+        [0x5463491DB010E4, 0xB40381280, 0x6000, 0x0, 0x0],
+        [0x7802561, 0x80, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x7802561, 0x80, 0x0, 0x0, 0x0],
+        [
+            0xBD5D5D20BB33EA,
+            0xD6EE0188CEBCBD,
+            0x647A6366D2643F,
+            0x8702A0DB0BDDF,
+            0x24000000,
+        ],
+    ],
+];
+pub const CURVE_WB: [[Chunk; NLEN]; 4] = [
+    [0x1C2118567A84B0, 0x3C012B040, 0x2000, 0x0, 0x0],
+    [
+        0xCDF995BE220475,
+        0x94EDA8CA7F9A36,
+        0x8702A0DC07E,
+        0x300000,
+        0x0,
+    ],
+    [
+        0x66FCCAE0F10B93,
+        0x4A76D4653FCD3B,
+        0x4381506E03F,
+        0x180000,
+        0x0,
+    ],
+    [0x1C21185DFAAA11, 0x3C012B0C0, 0x2000, 0x0, 0x0],
+];
+pub const CURVE_BB: [[[Chunk; NLEN]; 4]; 4] = [
+    [
+        [
+            0x11C0A6332B0CBD,
+            0xD6EE0CC906CE7E,
+            0x647A6366D2C43F,
+            0x8702A0DB0BDDF,
+            0x24000000,
+        ],
+        [
+            0x11C0A6332B0CBC,
+            0xD6EE0CC906CE7E,
+            0x647A6366D2C43F,
+            0x8702A0DB0BDDF,
+            0x24000000,
+        ],
+        [
+            0x11C0A6332B0CBC,
+            0xD6EE0CC906CE7E,
+            0x647A6366D2C43F,
+            0x8702A0DB0BDDF,
+            0x24000000,
+        ],
+        [0x7802562, 0x80, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x7802561, 0x80, 0x0, 0x0, 0x0],
+        [
+            0x11C0A6332B0CBC,
+            0xD6EE0CC906CE7E,
+            0x647A6366D2C43F,
+            0x8702A0DB0BDDF,
+            0x24000000,
+        ],
+        [
+            0x11C0A6332B0CBD,
+            0xD6EE0CC906CE7E,
+            0x647A6366D2C43F,
+            0x8702A0DB0BDDF,
+            0x24000000,
+        ],
+        [
+            0x11C0A6332B0CBC,
+            0xD6EE0CC906CE7E,
+            0x647A6366D2C43F,
+            0x8702A0DB0BDDF,
+            0x24000000,
+        ],
+    ],
+    [
+        [0x7802562, 0x80, 0x0, 0x0, 0x0],
+        [0x7802561, 0x80, 0x0, 0x0, 0x0],
+        [0x7802561, 0x80, 0x0, 0x0, 0x0],
+        [0x7802561, 0x80, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x3C012B2, 0x40, 0x0, 0x0, 0x0],
+        [0xF004AC2, 0x100, 0x0, 0x0, 0x0],
+        [
+            0x11C0A62F6AFA0A,
+            0xD6EE0CC906CE3E,
+            0x647A6366D2C43F,
+            0x8702A0DB0BDDF,
+            0x24000000,
+        ],
+        [0x3C012B2, 0x40, 0x0, 0x0, 0x0],
+    ],
+];
+
+pub const USE_GLV: bool = true;
+pub const USE_GS_G2: bool = true;
+pub const USE_GS_GT: bool = true;
+pub const GT_STRONG: bool = true;
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 56;
+
+pub const MODBITS: usize = 254;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 64;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BN;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::D_TYPE;
+pub const ATE_BITS: usize = 66;
+pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_bn254_32.rs b/src/roms/rom_bn254_32.rs
new file mode 100644
index 0000000..c9ae5ec
--- /dev/null
+++ b/src/roms/rom_bn254_32.rs
@@ -0,0 +1,171 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use bn254::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 28
+pub const MODULUS: [Chunk; NLEN] = [
+    0x13, 0x0, 0x13A7, 0x0, 0x86121, 0x8000000, 0x1BA344D, 0x4000000, 0x5236482, 0x2,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0xF5E7E39, 0x2F2A96F, 0xB96F13C, 0x64E8642, 0xC7146, 0x9926F7B, 0x4DACD24, 0x8321E7B,
+    0xD127A2E, 0x1,
+];
+pub const MCONST: Chunk = 0x79435E5;
+pub const FRA: [Chunk; NLEN] = [
+    0xF2A6DE9, 0x7DE6C06, 0xF77C2E1, 0x74924D3, 0x53F8509, 0x50A8469, 0xCB6499B, 0x212E7C8,
+    0xB377619, 0x1,
+];
+pub const FRB: [Chunk; NLEN] = [
+    0xD5922A, 0x82193F9, 0x8850C5, 0x8B6DB2C, 0xAC8DC17, 0x2F57B96, 0x503EAB2, 0x1ED1837,
+    0x9EBEE69, 0x0,
+];
+
+// BN254 Curve
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = 0;
+pub const CURVE_B_I: isize = 2;
+pub const CURVE_B: [Chunk; NLEN] = [0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0xD, 0x0, 0x10A1, 0x8000000, 0x7FF9F, 0x8000000, 0x1BA344D, 0x4000000, 0x5236482, 0x2,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x12, 0x0, 0x13A7, 0x0, 0x86121, 0x8000000, 0x1BA344D, 0x4000000, 0x5236482, 0x2,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_BNX: [Chunk; NLEN] = [0x1, 0x8000000, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_CRU: [Chunk; NLEN] = [
+    0x7, 0x8000000, 0x6CD, 0x0, 0x24909, 0x4000000, 0x49B362, 0x0, 0x0, 0x0,
+];
+pub const CURVE_PXA: [Chunk; NLEN] = [
+    0x803FB2B, 0xEE4224C, 0x8BF0D91, 0x8BBB489, 0xDB6A464, 0x7E8C61E, 0xFEB8D8C, 0x519EB62,
+    0x61A10BB, 0x0,
+];
+pub const CURVE_PXB: [Chunk; NLEN] = [
+    0x7D54CF3, 0x8C34C1E, 0x784B70D, 0x746BAE3, 0xA5B1F4D, 0x8C5982A, 0x3310AA7, 0xBA73783,
+    0x516AAF9, 0x0,
+];
+pub const CURVE_PYA: [Chunk; NLEN] = [
+    0x1CD2B9A, 0xF0E0789, 0xE09BD19, 0xAE6BDB, 0x22329BD, 0x96698C8, 0x39A90E0, 0x6BAF934,
+    0x21897A0, 0x0,
+];
+pub const CURVE_PYB: [Chunk; NLEN] = [
+    0xB3ACE9B, 0x2D1AEC6, 0x9C9578A, 0x6FFD73, 0xD37B090, 0x56F5F38, 0x68F6D44, 0x7C8B152,
+    0xEBB2B0E, 0x0,
+];
+pub const CURVE_W: [[Chunk; NLEN]; 2] = [
+    [0x3, 0x0, 0x204, 0x8000000, 0x6181, 0x0, 0x0, 0x0, 0x0, 0x0],
+    [0x1, 0x0, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+];
+pub const CURVE_SB: [[[Chunk; NLEN]; 2]; 2] = [
+    [
+        [0x4, 0x0, 0x285, 0x8000000, 0x6181, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x1, 0x0, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x1, 0x0, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [
+            0xA, 0x0, 0xE9D, 0x0, 0x79E1E, 0x8000000, 0x1BA344D, 0x4000000, 0x5236482, 0x2,
+        ],
+    ],
+];
+pub const CURVE_WB: [[Chunk; NLEN]; 4] = [
+    [
+        0x0, 0x8000000, 0x40, 0x8000000, 0x2080, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x5, 0x8000000, 0x54A, 0x0, 0x1C707, 0x8000000, 0x312241, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x3, 0x8000000, 0x2C5, 0x8000000, 0xE383, 0xC000000, 0x189120, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x1, 0x8000000, 0xC1, 0x8000000, 0x2080, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+];
+pub const CURVE_BB: [[[Chunk; NLEN]; 4]; 4] = [
+    [
+        [
+            0xD, 0x8000000, 0x1060, 0x8000000, 0x7FF9F, 0x8000000, 0x1BA344D, 0x4000000, 0x5236482,
+            0x2,
+        ],
+        [
+            0xC, 0x8000000, 0x1060, 0x8000000, 0x7FF9F, 0x8000000, 0x1BA344D, 0x4000000, 0x5236482,
+            0x2,
+        ],
+        [
+            0xC, 0x8000000, 0x1060, 0x8000000, 0x7FF9F, 0x8000000, 0x1BA344D, 0x4000000, 0x5236482,
+            0x2,
+        ],
+        [0x2, 0x0, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x1, 0x0, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [
+            0xC, 0x8000000, 0x1060, 0x8000000, 0x7FF9F, 0x8000000, 0x1BA344D, 0x4000000, 0x5236482,
+            0x2,
+        ],
+        [
+            0xD, 0x8000000, 0x1060, 0x8000000, 0x7FF9F, 0x8000000, 0x1BA344D, 0x4000000, 0x5236482,
+            0x2,
+        ],
+        [
+            0xC, 0x8000000, 0x1060, 0x8000000, 0x7FF9F, 0x8000000, 0x1BA344D, 0x4000000, 0x5236482,
+            0x2,
+        ],
+    ],
+    [
+        [0x2, 0x0, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x1, 0x0, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x1, 0x0, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x1, 0x0, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x2, 0x8000000, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x2, 0x0, 0x102, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [
+            0xA, 0x0, 0x1020, 0x8000000, 0x7FF9F, 0x8000000, 0x1BA344D, 0x4000000, 0x5236482, 0x2,
+        ],
+        [0x2, 0x8000000, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+];
+
+pub const USE_GLV: bool = true;
+pub const USE_GS_G2: bool = true;
+pub const USE_GS_GT: bool = true;
+pub const GT_STRONG: bool = false;
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 28;
+
+pub const MODBITS: usize = 254;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 14;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BN;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::D_TYPE;
+pub const ATE_BITS: usize = 66;
+pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_bn254_64.rs b/src/roms/rom_bn254_64.rs
new file mode 100644
index 0000000..f61b542
--- /dev/null
+++ b/src/roms/rom_bn254_64.rs
@@ -0,0 +1,208 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use bn254::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// BN254 Modulus
+// Base Bits= 56
+pub const MODULUS: [Chunk; NLEN] = [0x13, 0x13A7, 0x80000000086121, 0x40000001BA344D, 0x25236482];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x2F2A96FF5E7E39,
+    0x64E8642B96F13C,
+    0x9926F7B00C7146,
+    0x8321E7B4DACD24,
+    0x1D127A2E,
+];
+pub const MCONST: Chunk = 0x435E50D79435E5;
+pub const FRA: [Chunk; NLEN] = [
+    0x7DE6C06F2A6DE9,
+    0x74924D3F77C2E1,
+    0x50A846953F8509,
+    0x212E7C8CB6499B,
+    0x1B377619,
+];
+pub const FRB: [Chunk; NLEN] = [
+    0x82193F90D5922A,
+    0x8B6DB2C08850C5,
+    0x2F57B96AC8DC17,
+    0x1ED1837503EAB2,
+    0x9EBEE69,
+];
+
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = 0;
+pub const CURVE_B_I: isize = 2;
+pub const CURVE_B: [Chunk; NLEN] = [0x2, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0xD,
+    0x800000000010A1,
+    0x8000000007FF9F,
+    0x40000001BA344D,
+    0x25236482,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [0x12, 0x13A7, 0x80000000086121, 0x40000001BA344D, 0x25236482];
+pub const CURVE_GY: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_BNX: [Chunk; NLEN] = [0x80000000000001, 0x40, 0x0, 0x0, 0x0];
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_CRU: [Chunk; NLEN] = [0x80000000000007, 0x6CD, 0x40000000024909, 0x49B362, 0x0];
+pub const CURVE_PXA: [Chunk; NLEN] = [
+    0xEE4224C803FB2B,
+    0x8BBB4898BF0D91,
+    0x7E8C61EDB6A464,
+    0x519EB62FEB8D8C,
+    0x61A10BB,
+];
+pub const CURVE_PXB: [Chunk; NLEN] = [
+    0x8C34C1E7D54CF3,
+    0x746BAE3784B70D,
+    0x8C5982AA5B1F4D,
+    0xBA737833310AA7,
+    0x516AAF9,
+];
+pub const CURVE_PYA: [Chunk; NLEN] = [
+    0xF0E07891CD2B9A,
+    0xAE6BDBE09BD19,
+    0x96698C822329BD,
+    0x6BAF93439A90E0,
+    0x21897A0,
+];
+pub const CURVE_PYB: [Chunk; NLEN] = [
+    0x2D1AEC6B3ACE9B,
+    0x6FFD739C9578A,
+    0x56F5F38D37B090,
+    0x7C8B15268F6D44,
+    0xEBB2B0E,
+];
+pub const CURVE_W: [[Chunk; NLEN]; 2] = [
+    [0x3, 0x80000000000204, 0x6181, 0x0, 0x0],
+    [0x1, 0x81, 0x0, 0x0, 0x0],
+];
+pub const CURVE_SB: [[[Chunk; NLEN]; 2]; 2] = [
+    [
+        [0x4, 0x80000000000285, 0x6181, 0x0, 0x0],
+        [0x1, 0x81, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x1, 0x81, 0x0, 0x0, 0x0],
+        [0xA, 0xE9D, 0x80000000079E1E, 0x40000001BA344D, 0x25236482],
+    ],
+];
+pub const CURVE_WB: [[Chunk; NLEN]; 4] = [
+    [0x80000000000000, 0x80000000000040, 0x2080, 0x0, 0x0],
+    [0x80000000000005, 0x54A, 0x8000000001C707, 0x312241, 0x0],
+    [
+        0x80000000000003,
+        0x800000000002C5,
+        0xC000000000E383,
+        0x189120,
+        0x0,
+    ],
+    [0x80000000000001, 0x800000000000C1, 0x2080, 0x0, 0x0],
+];
+pub const CURVE_BB: [[[Chunk; NLEN]; 4]; 4] = [
+    [
+        [
+            0x8000000000000D,
+            0x80000000001060,
+            0x8000000007FF9F,
+            0x40000001BA344D,
+            0x25236482,
+        ],
+        [
+            0x8000000000000C,
+            0x80000000001060,
+            0x8000000007FF9F,
+            0x40000001BA344D,
+            0x25236482,
+        ],
+        [
+            0x8000000000000C,
+            0x80000000001060,
+            0x8000000007FF9F,
+            0x40000001BA344D,
+            0x25236482,
+        ],
+        [0x2, 0x81, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x1, 0x81, 0x0, 0x0, 0x0],
+        [
+            0x8000000000000C,
+            0x80000000001060,
+            0x8000000007FF9F,
+            0x40000001BA344D,
+            0x25236482,
+        ],
+        [
+            0x8000000000000D,
+            0x80000000001060,
+            0x8000000007FF9F,
+            0x40000001BA344D,
+            0x25236482,
+        ],
+        [
+            0x8000000000000C,
+            0x80000000001060,
+            0x8000000007FF9F,
+            0x40000001BA344D,
+            0x25236482,
+        ],
+    ],
+    [
+        [0x2, 0x81, 0x0, 0x0, 0x0],
+        [0x1, 0x81, 0x0, 0x0, 0x0],
+        [0x1, 0x81, 0x0, 0x0, 0x0],
+        [0x1, 0x81, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x80000000000002, 0x40, 0x0, 0x0, 0x0],
+        [0x2, 0x102, 0x0, 0x0, 0x0],
+        [
+            0xA,
+            0x80000000001020,
+            0x8000000007FF9F,
+            0x40000001BA344D,
+            0x25236482,
+        ],
+        [0x80000000000002, 0x40, 0x0, 0x0, 0x0],
+    ],
+];
+
+pub const USE_GLV: bool = true;
+pub const USE_GS_G2: bool = true;
+pub const USE_GS_GT: bool = true;
+pub const GT_STRONG: bool = false;
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 56;
+
+pub const MODBITS: usize = 254;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 26;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BN;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::D_TYPE;
+pub const ATE_BITS: usize = 66;
+pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
\ No newline at end of file
diff --git a/src/roms/rom_brainpool_32.rs b/src/roms/rom_brainpool_32.rs
new file mode 100644
index 0000000..b788632
--- /dev/null
+++ b/src/roms/rom_brainpool_32.rs
@@ -0,0 +1,74 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Note that the original curve has been transformed to an isomorphic curve with A=-3 */
+
+use brainpool::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 28
+// brainpool Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0xF6E5377, 0x13481D1, 0x6202820, 0xF623D52, 0xD726E3B, 0x909D838, 0xC3E660A, 0xA1EEA9B,
+    0x9FB57DB, 0xA,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0xB9A3787, 0x9E04F49, 0x8F3CF49, 0x2931721, 0xF1DBC89, 0x54E8C3C, 0xF7559CA, 0xBB411A3,
+    0x773E15F, 0x9,
+];
+pub const MCONST: Chunk = 0xEFD89B9;
+
+// brainpool Curve
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = -3;
+pub const CURVE_B_I: isize = 0;
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0xEE92B04, 0xE58101F, 0xF49256A, 0xEBC4AF2, 0x6B7BF93, 0x733D0B7, 0x4FE66A7, 0x30D84EA,
+    0x62C61C4, 0x6,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x74856A7, 0x1E0E829, 0x1A6F790, 0x7AA3B56, 0xD718C39, 0x909D838, 0xC3E660A, 0xA1EEA9B,
+    0x9FB57DB, 0xA,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0xE1305F4, 0xA191562, 0xFBC2B79, 0x42C47AA, 0x149AFA1, 0xB23A656, 0x7732213, 0xC1CFE7B,
+    0x3E8EB3C, 0xA,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0xB25C9BE, 0xABE8F35, 0x27001D, 0xB6DE39D, 0x17E69BC, 0xE146444, 0xD7F7B22, 0x3439C56,
+    0xD996C82, 0x2,
+];
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 28;
+
+pub const MODBITS: usize = 256;
+pub const MOD8: usize = 7;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 14;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_brainpool_64.rs b/src/roms/rom_brainpool_64.rs
new file mode 100644
index 0000000..8191561
--- /dev/null
+++ b/src/roms/rom_brainpool_64.rs
@@ -0,0 +1,92 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Note that the original curve has been transformed to an isomorphic curve with A=-3 */
+
+use brainpool::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 56
+// brainpool Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0x13481D1F6E5377,
+    0xF623D526202820,
+    0x909D838D726E3B,
+    0xA1EEA9BC3E660A,
+    0xA9FB57DB,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x9E04F49B9A3787,
+    0x29317218F3CF49,
+    0x54E8C3CF1DBC89,
+    0xBB411A3F7559CA,
+    0x9773E15F,
+];
+pub const MCONST: Chunk = 0xA75590CEFD89B9;
+
+// brainpool Curve
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = -3;
+pub const CURVE_B_I: isize = 0;
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0xE58101FEE92B04,
+    0xEBC4AF2F49256A,
+    0x733D0B76B7BF93,
+    0x30D84EA4FE66A7,
+    0x662C61C4,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x1E0E82974856A7,
+    0x7AA3B561A6F790,
+    0x909D838D718C39,
+    0xA1EEA9BC3E660A,
+    0xA9FB57DB,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0xA191562E1305F4,
+    0x42C47AAFBC2B79,
+    0xB23A656149AFA1,
+    0xC1CFE7B7732213,
+    0xA3E8EB3C,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0xABE8F35B25C9BE,
+    0xB6DE39D027001D,
+    0xE14644417E69BC,
+    0x3439C56D7F7B22,
+    0x2D996C82,
+];
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 56;
+
+pub const MODBITS: usize = 256;
+pub const MOD8: usize = 7;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 24;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const ATE_BITS: usize = 0;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_c25519_32.rs b/src/roms/rom_c25519_32.rs
new file mode 100644
index 0000000..6da6a05
--- /dev/null
+++ b/src/roms/rom_c25519_32.rs
@@ -0,0 +1,59 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use c25519::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 29
+// Curve25519 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0x1FFFFFED, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x7FFFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [0x169000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const MCONST: Chunk = 0x13;
+
+// c25519 Curve
+pub const CURVE_COF_I: isize = 8;
+pub const CURVE_A: isize = 486662;
+pub const CURVE_B_I: isize = 0;
+pub const CURVE_COF: [Chunk; NLEN] = [0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x1CF5D3ED, 0x9318D2, 0x1DE73596, 0x1DF3BD45, 0x14D, 0x0, 0x0, 0x0, 0x100000,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_GY: [Chunk; NLEN] = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 29;
+
+pub const MODBITS: usize = 255;
+pub const MOD8: usize = 5;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 6;
+
+pub const CURVETYPE: CurveType = CurveType::MONTGOMERY;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_c25519_64.rs b/src/roms/rom_c25519_64.rs
new file mode 100644
index 0000000..9a8c59f
--- /dev/null
+++ b/src/roms/rom_c25519_64.rs
@@ -0,0 +1,61 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use c25519::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 56
+// Curve25519 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0xFFFFFFFFFFFFED,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0x7FFFFFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [0xA4000000000000, 0x5, 0x0, 0x0, 0x0];
+pub const MCONST: Chunk = 0x13;
+
+// c25519 Curve
+pub const CURVE_COF_I: isize = 8;
+pub const CURVE_A: isize = 486662;
+pub const CURVE_B_I: isize = 0;
+pub const CURVE_COF: [Chunk; NLEN] = [0x8, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] =
+    [0x12631A5CF5D3ED, 0xF9DEA2F79CD658, 0x14DE, 0x0, 0x10000000];
+pub const CURVE_GX: [Chunk; NLEN] = [0x9, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_GY: [Chunk; NLEN] = [0x0, 0x0, 0x0, 0x0, 0x0];
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 56;
+
+pub const MODBITS: usize = 255;
+pub const MOD8: usize = 5;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 25;
+
+pub const CURVETYPE: CurveType = CurveType::MONTGOMERY;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_c41417_32.rs b/src/roms/rom_c41417_32.rs
new file mode 100644
index 0000000..a8330cb
--- /dev/null
+++ b/src/roms/rom_c41417_32.rs
@@ -0,0 +1,71 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use c41417::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 29
+// c41417 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0x1FFFFFEF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0xFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x0, 0x242000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const MCONST: Chunk = 0x11;
+
+// c41417 Curve
+pub const CURVE_COF_I: isize = 8;
+pub const CURVE_A: isize = 1;
+pub const CURVE_B_I: isize = 3617;
+pub const CURVE_COF: [Chunk; NLEN] = [
+    0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0xE21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x106AF79, 0x18738D2F, 0x18F3C606, 0x1806715A, 0x22B36F1, 0xA67B830, 0xCF32490, 0x1FFFFFFD,
+    0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1F,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x13CBC595, 0x7E9C097, 0x14DF1931, 0x14E7F550, 0x1A111301, 0x15A6B6B5, 0xD526292, 0x18FEAFFE,
+    0x1F44C03E, 0x1E6A31B4, 0x70C9B97, 0x43180C6, 0x1443300, 0x19A4828A, 0x68,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+
+pub const MODBYTES: usize = 52;
+pub const BASEBITS: usize = 29;
+
+pub const MODBITS: usize = 414;
+pub const MOD8: usize = 7;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 14;
+
+pub const CURVETYPE: CurveType = CurveType::EDWARDS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 64;
+pub const AESKEY: usize = 32;
diff --git a/src/roms/rom_c41417_64.rs b/src/roms/rom_c41417_64.rs
new file mode 100644
index 0000000..52b51b1
--- /dev/null
+++ b/src/roms/rom_c41417_64.rs
@@ -0,0 +1,78 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use c41417::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 60
+// c41417 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0xFFFFFFFFFFFFFEF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [0x121000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const MCONST: Chunk = 0x11;
+
+// c41417 Curve
+pub const CURVE_COF_I: isize = 8;
+pub const CURVE_A: isize = 1;
+pub const CURVE_B_I: isize = 3617;
+pub const CURVE_COF: [Chunk; NLEN] = [0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [0xE21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0xB0E71A5E106AF79,
+    0x1C0338AD63CF181,
+    0x414CF706022B36F,
+    0xFFFFFFFFEB3CC92,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0x7FFFFFFFFFFFF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x4FD3812F3CBC595,
+    0x1A73FAA8537C64C,
+    0x4AB4D6D6BA11130,
+    0x3EC7F57FF35498A,
+    0xE5FCD46369F44C0,
+    0x300218C0631C326,
+    0x1A334905141443,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+
+pub const MODBYTES: usize = 52;
+pub const BASEBITS: usize = 60;
+
+pub const MODBITS: usize = 414;
+pub const MOD8: usize = 7;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 6;
+
+pub const CURVETYPE: CurveType = CurveType::EDWARDS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 64;
+pub const AESKEY: usize = 32;
diff --git a/src/roms/rom_ed25519_32.rs b/src/roms/rom_ed25519_32.rs
new file mode 100644
index 0000000..be1d156
--- /dev/null
+++ b/src/roms/rom_ed25519_32.rs
@@ -0,0 +1,68 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use ed25519::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 29
+// Curve25519 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0x1FFFFFED, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x7FFFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [0x169000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const MCONST: Chunk = 0x13;
+
+// Ed25519 Curve
+pub const CURVE_COF_I: isize = 8;
+pub const CURVE_A: isize = -1;
+pub const CURVE_B_I: isize = 0;
+pub const CURVE_COF: [Chunk; NLEN] = [0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0x135978A3, 0xF5A6E50, 0x10762ADD, 0x149A82, 0x1E898007, 0x3CBBBC, 0x19CE331D, 0x1DC56DFF,
+    0x52036C,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x1CF5D3ED, 0x9318D2, 0x1DE73596, 0x1DF3BD45, 0x14D, 0x0, 0x0, 0x0, 0x100000,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0xF25D51A, 0xAB16B04, 0x969ECB2, 0x198EC12A, 0xDC5C692, 0x1118FEEB, 0xFFB0293, 0x1A79ADCA,
+    0x216936,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x6666658, 0x13333333, 0x19999999, 0xCCCCCCC, 0x6666666, 0x13333333, 0x19999999, 0xCCCCCCC,
+    0x666666,
+];
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 29;
+
+pub const MODBITS: usize = 255;
+pub const MOD8: usize = 5;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 6;
+
+pub const CURVETYPE: CurveType = CurveType::EDWARDS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_ed25519_64.rs b/src/roms/rom_ed25519_64.rs
new file mode 100644
index 0000000..cf23672
--- /dev/null
+++ b/src/roms/rom_ed25519_64.rs
@@ -0,0 +1,79 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use ed25519::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 56
+// Curve25519 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0xFFFFFFFFFFFFED,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0x7FFFFFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [0xA4000000000000, 0x5, 0x0, 0x0, 0x0];
+pub const MCONST: Chunk = 0x13;
+
+// Ed25519 Curve
+pub const CURVE_COF_I: isize = 8;
+pub const CURVE_A: isize = -1;
+pub const CURVE_B_I: isize = 0;
+pub const CURVE_COF: [Chunk; NLEN] = [0x8, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0xEB4DCA135978A3,
+    0xA4D4141D8AB75,
+    0x797779E8980070,
+    0x2B6FFE738CC740,
+    0x52036CEE,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] =
+    [0x12631A5CF5D3ED, 0xF9DEA2F79CD658, 0x14DE, 0x0, 0x10000000];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x562D608F25D51A,
+    0xC7609525A7B2C9,
+    0x31FDD6DC5C692C,
+    0xCD6E53FEC0A4E2,
+    0x216936D3,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x66666666666658,
+    0x66666666666666,
+    0x66666666666666,
+    0x66666666666666,
+    0x66666666,
+];
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 56;
+
+pub const MODBITS: usize = 255;
+pub const MOD8: usize = 5;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 25;
+
+pub const CURVETYPE: CurveType = CurveType::EDWARDS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_fp256bn_32.rs b/src/roms/rom_fp256bn_32.rs
new file mode 100644
index 0000000..e3cabe1
--- /dev/null
+++ b/src/roms/rom_fp256bn_32.rs
@@ -0,0 +1,180 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use fp256bn::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+
+// Base Bits= 28
+pub const MODULUS: [Chunk; NLEN] = [
+    0xED33013, 0x292DDBA, 0x80A82D3, 0x65FB129, 0x49F0CDC, 0x5EEE71A, 0xD46E5F2, 0xFFFCF0C,
+    0xFFFFFFF, 0xF,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x3B9F8B, 0xEDE3363, 0xFEC54E8, 0x92FFEE9, 0x3C55F79, 0x13C1C06, 0xC0123FA, 0xA12F2EA,
+    0xE559B2A, 0x8,
+];
+pub const MCONST: Chunk = 0x537E5E5;
+
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = 0;
+pub const CURVE_B_I: isize = 3;
+pub const CURVE_B: [Chunk; NLEN] = [0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x10B500D, 0x2D536CD, 0x9921AF6, 0x65FB129, 0x49E0CDC, 0x5EEE71A, 0xD46E5F2, 0xFFFCF0C,
+    0xFFFFFFF, 0xF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_GY: [Chunk; NLEN] = [0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+
+pub const FRA: [Chunk; NLEN] = [
+    0xF943106, 0x760328A, 0xAB28F74, 0x71511E3, 0x7CF39A1, 0x8DDB086, 0x52D1A6E, 0xCA786F3,
+    0xD617662, 0x3,
+];
+pub const FRB: [Chunk; NLEN] = [
+    0xF3EFF0D, 0xB32AB2F, 0xD57F35E, 0xF4A9F45, 0xCCFD33A, 0xD113693, 0x819CB83, 0x3584819,
+    0x29E899D, 0xC,
+];
+pub const CURVE_BNX: [Chunk; NLEN] = [0xB0A801, 0x82F5C03, 0x68, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_CRU: [Chunk; NLEN] = [
+    0x3A1B807, 0x1C0A24A, 0x32D1EDB, 0xD79DF19, 0x8659BCD, 0x4092101, 0x13988E1, 0x0, 0x0, 0x0,
+];
+pub const CURVE_PXA: [Chunk; NLEN] = [
+    0x9C09EFB, 0x2616B68, 0xF843CD2, 0x539A12B, 0x13ACE1C, 0x577C289, 0x28560F, 0xB4C96C2,
+    0xE0C3350, 0xF,
+];
+pub const CURVE_PXB: [Chunk; NLEN] = [
+    0x37E6A2B, 0x69ED34A, 0x3589D2, 0x78E287D, 0x3B924DD, 0xC637D81, 0x4DB5AE1, 0x738AC05,
+    0xEA66057, 0x4,
+];
+pub const CURVE_PYA: [Chunk; NLEN] = [
+    0xEDC27FF, 0x9B481B, 0x15848E9, 0x24758D6, 0xE51EFCB, 0x75124E3, 0x376770D, 0xC542A3B,
+    0x2046E7, 0x7,
+];
+pub const CURVE_PYB: [Chunk; NLEN] = [
+    0xAAD049B, 0x1281114, 0xA98B3E0, 0xBE80821, 0x29F8B4C, 0x49297EB, 0x42EEA6, 0xD388C29,
+    0x554E3BC, 0x0,
+];
+pub const CURVE_W: [[Chunk; NLEN]; 2] = [
+    [
+        0xB054003, 0xF0036E1, 0xE78663A, 0xFFFFFFF, 0xFFFF, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [0x1615001, 0x5EB806, 0xD1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+];
+pub const CURVE_SB: [[[Chunk; NLEN]; 2]; 2] = [
+    [
+        [
+            0xC669004, 0xF5EEEE7, 0xE78670B, 0xFFFFFFF, 0xFFFF, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [0x1615001, 0x5EB806, 0xD1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x1615001, 0x5EB806, 0xD1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [
+            0x606100A, 0x3D4FFEB, 0xB19B4BB, 0x65FB129, 0x49D0CDC, 0x5EEE71A, 0xD46E5F2, 0xFFFCF0C,
+            0xFFFFFFF, 0xF,
+        ],
+    ],
+];
+pub const CURVE_WB: [[Chunk; NLEN]; 4] = [
+    [
+        0xD30A800, 0x20678F0, 0x4D2CC10, 0x5555555, 0x5555, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0xD7DC805, 0xD6764C0, 0xBC3AD1A, 0x8FBEA10, 0x4467DE, 0x8061601, 0xD105EB, 0x0, 0x0, 0x0,
+    ],
+    [
+        0xF173803, 0xACB6061, 0x5E1D6C1, 0x47DF508, 0x82233EF, 0xC030B00, 0x6882F5, 0x0, 0x0, 0x0,
+    ],
+    [
+        0xE91F801, 0x26530F6, 0x4D2CCE1, 0x5555555, 0x5555, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+];
+pub const CURVE_BB: [[[Chunk; NLEN]; 4]; 4] = [
+    [
+        [
+            0x5AA80D, 0xAA5DACA, 0x9921A8D, 0x65FB129, 0x49E0CDC, 0x5EEE71A, 0xD46E5F2, 0xFFFCF0C,
+            0xFFFFFFF, 0xF,
+        ],
+        [
+            0x5AA80C, 0xAA5DACA, 0x9921A8D, 0x65FB129, 0x49E0CDC, 0x5EEE71A, 0xD46E5F2, 0xFFFCF0C,
+            0xFFFFFFF, 0xF,
+        ],
+        [
+            0x5AA80C, 0xAA5DACA, 0x9921A8D, 0x65FB129, 0x49E0CDC, 0x5EEE71A, 0xD46E5F2, 0xFFFCF0C,
+            0xFFFFFFF, 0xF,
+        ],
+        [0x1615002, 0x5EB806, 0xD1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x1615001, 0x5EB806, 0xD1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [
+            0x5AA80C, 0xAA5DACA, 0x9921A8D, 0x65FB129, 0x49E0CDC, 0x5EEE71A, 0xD46E5F2, 0xFFFCF0C,
+            0xFFFFFFF, 0xF,
+        ],
+        [
+            0x5AA80D, 0xAA5DACA, 0x9921A8D, 0x65FB129, 0x49E0CDC, 0x5EEE71A, 0xD46E5F2, 0xFFFCF0C,
+            0xFFFFFFF, 0xF,
+        ],
+        [
+            0x5AA80C, 0xAA5DACA, 0x9921A8D, 0x65FB129, 0x49E0CDC, 0x5EEE71A, 0xD46E5F2, 0xFFFCF0C,
+            0xFFFFFFF, 0xF,
+        ],
+    ],
+    [
+        [0x1615002, 0x5EB806, 0xD1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x1615001, 0x5EB806, 0xD1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x1615001, 0x5EB806, 0xD1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [0x1615001, 0x5EB806, 0xD1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0xB0A802, 0x82F5C03, 0x68, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+        [
+            0x2C2A002, 0xBD700C, 0x1A2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0xFAA000A, 0x2767EC6, 0x9921A25, 0x65FB129, 0x49E0CDC, 0x5EEE71A, 0xD46E5F2, 0xFFFCF0C,
+            0xFFFFFFF, 0xF,
+        ],
+        [0xB0A802, 0x82F5C03, 0x68, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
+    ],
+];
+
+pub const USE_GLV: bool = true;
+pub const USE_GS_G2: bool = true;
+pub const USE_GS_GT: bool = true;
+pub const GT_STRONG: bool = false;
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 28;
+
+pub const MODBITS: usize = 256;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 14;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BN;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 66;
+pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_fp256bn_64.rs b/src/roms/rom_fp256bn_64.rs
new file mode 100644
index 0000000..bde7639
--- /dev/null
+++ b/src/roms/rom_fp256bn_64.rs
@@ -0,0 +1,233 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use fp256bn::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// fp256bn Modulus
+// Base Bits= 56
+pub const MODULUS: [Chunk; NLEN] = [
+    0x292DDBAED33013,
+    0x65FB12980A82D3,
+    0x5EEE71A49F0CDC,
+    0xFFFCF0CD46E5F2,
+    0xFFFFFFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0xEDE336303B9F8B,
+    0x92FFEE9FEC54E8,
+    0x13C1C063C55F79,
+    0xA12F2EAC0123FA,
+    0x8E559B2A,
+];
+pub const MCONST: Chunk = 0x6C964E0537E5E5;
+
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = 0;
+pub const CURVE_B_I: isize = 3;
+pub const CURVE_B: [Chunk; NLEN] = [0x3, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x2D536CD10B500D,
+    0x65FB1299921AF6,
+    0x5EEE71A49E0CDC,
+    0xFFFCF0CD46E5F2,
+    0xFFFFFFFF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_GY: [Chunk; NLEN] = [0x2, 0x0, 0x0, 0x0, 0x0];
+
+pub const FRA: [Chunk; NLEN] = [
+    0x760328AF943106,
+    0x71511E3AB28F74,
+    0x8DDB0867CF39A1,
+    0xCA786F352D1A6E,
+    0x3D617662,
+];
+pub const FRB: [Chunk; NLEN] = [
+    0xB32AB2FF3EFF0D,
+    0xF4A9F45D57F35E,
+    0xD113693CCFD33A,
+    0x3584819819CB83,
+    0xC29E899D,
+];
+pub const CURVE_BNX: [Chunk; NLEN] = [0x82F5C030B0A801, 0x68, 0x0, 0x0, 0x0];
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_CRU: [Chunk; NLEN] = [
+    0x1C0A24A3A1B807,
+    0xD79DF1932D1EDB,
+    0x40921018659BCD,
+    0x13988E1,
+    0x0,
+];
+pub const CURVE_PXA: [Chunk; NLEN] = [
+    0x2616B689C09EFB,
+    0x539A12BF843CD2,
+    0x577C28913ACE1C,
+    0xB4C96C2028560F,
+    0xFE0C3350,
+];
+pub const CURVE_PXB: [Chunk; NLEN] = [
+    0x69ED34A37E6A2B,
+    0x78E287D03589D2,
+    0xC637D813B924DD,
+    0x738AC054DB5AE1,
+    0x4EA66057,
+];
+pub const CURVE_PYA: [Chunk; NLEN] = [
+    0x9B481BEDC27FF,
+    0x24758D615848E9,
+    0x75124E3E51EFCB,
+    0xC542A3B376770D,
+    0x702046E7,
+];
+pub const CURVE_PYB: [Chunk; NLEN] = [
+    0x1281114AAD049B,
+    0xBE80821A98B3E0,
+    0x49297EB29F8B4C,
+    0xD388C29042EEA6,
+    0x554E3BC,
+];
+pub const CURVE_W: [[Chunk; NLEN]; 2] = [
+    [0xF0036E1B054003, 0xFFFFFFFE78663A, 0xFFFF, 0x0, 0x0],
+    [0x5EB8061615001, 0xD1, 0x0, 0x0, 0x0],
+];
+pub const CURVE_SB: [[[Chunk; NLEN]; 2]; 2] = [
+    [
+        [0xF5EEEE7C669004, 0xFFFFFFFE78670B, 0xFFFF, 0x0, 0x0],
+        [0x5EB8061615001, 0xD1, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x5EB8061615001, 0xD1, 0x0, 0x0, 0x0],
+        [
+            0x3D4FFEB606100A,
+            0x65FB129B19B4BB,
+            0x5EEE71A49D0CDC,
+            0xFFFCF0CD46E5F2,
+            0xFFFFFFFF,
+        ],
+    ],
+];
+pub const CURVE_WB: [[Chunk; NLEN]; 4] = [
+    [0x20678F0D30A800, 0x55555554D2CC10, 0x5555, 0x0, 0x0],
+    [
+        0xD6764C0D7DC805,
+        0x8FBEA10BC3AD1A,
+        0x806160104467DE,
+        0xD105EB,
+        0x0,
+    ],
+    [
+        0xACB6061F173803,
+        0x47DF5085E1D6C1,
+        0xC030B0082233EF,
+        0x6882F5,
+        0x0,
+    ],
+    [0x26530F6E91F801, 0x55555554D2CCE1, 0x5555, 0x0, 0x0],
+];
+pub const CURVE_BB: [[[Chunk; NLEN]; 4]; 4] = [
+    [
+        [
+            0xAA5DACA05AA80D,
+            0x65FB1299921A8D,
+            0x5EEE71A49E0CDC,
+            0xFFFCF0CD46E5F2,
+            0xFFFFFFFF,
+        ],
+        [
+            0xAA5DACA05AA80C,
+            0x65FB1299921A8D,
+            0x5EEE71A49E0CDC,
+            0xFFFCF0CD46E5F2,
+            0xFFFFFFFF,
+        ],
+        [
+            0xAA5DACA05AA80C,
+            0x65FB1299921A8D,
+            0x5EEE71A49E0CDC,
+            0xFFFCF0CD46E5F2,
+            0xFFFFFFFF,
+        ],
+        [0x5EB8061615002, 0xD1, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x5EB8061615001, 0xD1, 0x0, 0x0, 0x0],
+        [
+            0xAA5DACA05AA80C,
+            0x65FB1299921A8D,
+            0x5EEE71A49E0CDC,
+            0xFFFCF0CD46E5F2,
+            0xFFFFFFFF,
+        ],
+        [
+            0xAA5DACA05AA80D,
+            0x65FB1299921A8D,
+            0x5EEE71A49E0CDC,
+            0xFFFCF0CD46E5F2,
+            0xFFFFFFFF,
+        ],
+        [
+            0xAA5DACA05AA80C,
+            0x65FB1299921A8D,
+            0x5EEE71A49E0CDC,
+            0xFFFCF0CD46E5F2,
+            0xFFFFFFFF,
+        ],
+    ],
+    [
+        [0x5EB8061615002, 0xD1, 0x0, 0x0, 0x0],
+        [0x5EB8061615001, 0xD1, 0x0, 0x0, 0x0],
+        [0x5EB8061615001, 0xD1, 0x0, 0x0, 0x0],
+        [0x5EB8061615001, 0xD1, 0x0, 0x0, 0x0],
+    ],
+    [
+        [0x82F5C030B0A802, 0x68, 0x0, 0x0, 0x0],
+        [0xBD700C2C2A002, 0x1A2, 0x0, 0x0, 0x0],
+        [
+            0x2767EC6FAA000A,
+            0x65FB1299921A25,
+            0x5EEE71A49E0CDC,
+            0xFFFCF0CD46E5F2,
+            0xFFFFFFFF,
+        ],
+        [0x82F5C030B0A802, 0x68, 0x0, 0x0, 0x0],
+    ],
+];
+
+pub const USE_GLV: bool = true;
+pub const USE_GS_G2: bool = true;
+pub const USE_GS_GT: bool = true;
+pub const GT_STRONG: bool = false;
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 56;
+
+pub const MODBITS: usize = 256;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 24;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BN;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 66;
+pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_fp512bn_32.rs b/src/roms/rom_fp512bn_32.rs
new file mode 100644
index 0000000..cdc44af
--- /dev/null
+++ b/src/roms/rom_fp512bn_32.rs
@@ -0,0 +1,249 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use fp512bn::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 29
+pub const MODULUS: [Chunk; NLEN] = [
+    0x2ADEF33, 0x7594049, 0x131919ED, 0x14AB9CBE, 0x16FE1916, 0x12EF5591, 0x2E39231, 0x3D597D3,
+    0x55146CF, 0x88D877A, 0x102EF8F0, 0x1196A60F, 0x1C60BA1D, 0x1CF63F80, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x7FFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0xFD68B47, 0xFCF5D2C, 0x437675A, 0x1BBC3FBF, 0x1411E413, 0x13453559, 0x10B5639, 0x1C34CE79,
+    0x6D476BF, 0xFD05F2B, 0x15D17C28, 0x6C9F76E, 0x1C2375B3, 0x78CCE9B, 0x15F0AB33, 0x1960F32E,
+    0x1A8D44E, 0x57A38,
+];
+pub const MCONST: Chunk = 0x1CCC5C05;
+
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = 0;
+pub const CURVE_B_I: isize = 3;
+pub const CURVE_B: [Chunk; NLEN] = [
+    0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x119A09ED, 0x153252FA, 0x1E68AD01, 0x627C09, 0x79A34A1, 0x12EF5593, 0x2E39231, 0x3D597D3,
+    0x45146CF, 0x88D877A, 0x102EF8F0, 0x1196A60F, 0x1C60BA1D, 0x1CF63F80, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x7FFFF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+
+pub const FRA: [Chunk; NLEN] = [
+    0x14B73AB2, 0x4B0BD8F, 0xABB47D, 0x2A29EC4, 0x18681E17, 0x104069DE, 0x12EED67D, 0x1553D0A5,
+    0x398E9F8, 0x7971034, 0xAC9AF23, 0x52DEF23, 0x14EA18A5, 0x1463E345, 0x6DE465A, 0x17F212B4,
+    0x1AA9CF5B, 0xF7B8,
+];
+pub const FRB: [Chunk; NLEN] = [
+    0xDF6B481, 0x2A882B9, 0x126D6570, 0x1208FDFA, 0x1E95FAFF, 0x2AEEBB2, 0xFF4BBB4, 0xE81C72D,
+    0x1B85CD6, 0xF67746, 0x56549CD, 0xC68B6EC, 0x776A178, 0x8925C3B, 0x1921B9A5, 0x80DED4B,
+    0x55630A4, 0x70847,
+];
+pub const CURVE_BNX: [Chunk; NLEN] = [
+    0x1E1BD80F, 0x59835DA, 0xC3DFC04, 0x5EB8061, 0x688, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_COF: [Chunk; NLEN] = [
+    0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_CRU: [Chunk; NLEN] = [
+    0x1C79298A, 0x1838B104, 0x2C5F052, 0x1DCCF337, 0x6092AEC, 0x4B35F29, 0x1EB361E, 0x11384EA,
+    0x3074B20, 0x17BB08FD, 0x3A8B3E3, 0xD70D66F, 0x3D2A614, 0x1CF63EE4, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x7FFFF,
+];
+pub const CURVE_PXA: [Chunk; NLEN] = [
+    0xDB646B5, 0x183D4B70, 0x1CBFFA3, 0x11F0E632, 0x1C78F221, 0x1F10DE5D, 0x171B715E, 0xF0C6A29,
+    0x10B02453, 0xBE63C66, 0xE6D5F69, 0x166B1E1B, 0x4BBBD29, 0x179E750F, 0x6E9D04, 0xC912B10,
+    0x1339E138, 0x1D8B2,
+];
+pub const CURVE_PXB: [Chunk; NLEN] = [
+    0x1A8AE0E9, 0xDAE5F7E, 0x22446CF, 0x1948239B, 0x15ADCE40, 0xB709C1E, 0x18357943, 0xE50AA4D,
+    0x19781E22, 0x12B35CA6, 0x11DAA2C0, 0x18D8DDE4, 0x5EA656D, 0x15F45A41, 0xD311A02, 0xCFCD913,
+    0x13CBF850, 0x240E0,
+];
+pub const CURVE_PYA: [Chunk; NLEN] = [
+    0xDDE67A1, 0x12401895, 0x17BEE178, 0x142F5AC2, 0xB7BC5CD, 0x92A1404, 0x1A3B748C, 0x17BD82A7,
+    0x14B6CD18, 0xAC34CE, 0x1740FB97, 0x1ECC15F9, 0x17085B1D, 0x1D1BA793, 0x1BD6AC32, 0x18F70525,
+    0xC84C827, 0x3780F,
+];
+pub const CURVE_PYB: [Chunk; NLEN] = [
+    0x84F8E8B, 0xC5B8C36, 0xFDD85A1, 0xB84449, 0x19C08DFF, 0x56BF713, 0x1C5290C4, 0x187C5CA0,
+    0x1DA2897F, 0x24B0CA0, 0x326D8F4, 0x2310CF6, 0x1021438C, 0xFBAEC8F, 0xD9030C5, 0x1CF06358,
+    0x1CEC8B04, 0x28D1D,
+];
+pub const CURVE_W: [[Chunk; NLEN]; 2] = [
+    [
+        0x9834583, 0x887C4BA, 0x5A85CFC, 0xBF7223A, 0xF63FE96, 0x1FFFFFFE, 0x1FFFFFFF, 0x1FFFFFFF,
+        0xFFFFFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x1C37B01F, 0xB306BB5, 0x187BF808, 0xBD700C2, 0xD10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+];
+pub const CURVE_SB: [[[Chunk; NLEN]; 2]; 2] = [
+    [
+        [
+            0xD4B9564, 0x1D575904, 0xD2C64F3, 0x202177, 0xF63F186, 0x1FFFFFFE, 0x1FFFFFFF,
+            0x1FFFFFFF, 0xFFFFFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x156259CE, 0xA01E744, 0x5ECB4F9, 0x148B7B47, 0x79A2790, 0x12EF5593, 0x2E39231,
+            0x3D597D3, 0x45146CF, 0x88D877A, 0x102EF8F0, 0x1196A60F, 0x1C60BA1D, 0x1CF63F80,
+            0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x7FFFF,
+        ],
+    ],
+    [
+        [
+            0x1C37B01F, 0xB306BB5, 0x187BF808, 0xBD700C2, 0xD10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x9834583, 0x887C4BA, 0x5A85CFC, 0xBF7223A, 0xF63FE96, 0x1FFFFFFE, 0x1FFFFFFF,
+            0x1FFFFFFF, 0xFFFFFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+];
+pub const CURVE_WB: [[Chunk; NLEN]; 4] = [
+    [
+        0x155A29F0, 0x16D59B55, 0xF4C305, 0x18858C0B, 0x5215FBF, 0xAAAAAAA, 0x15555555, 0xAAAAAAA,
+        0x555555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x2355D4B, 0x1758095D, 0x1FE13C5F, 0x41F83FA, 0xBB5E5CF, 0x97D4EF1, 0xB503D62, 0x172C0C9B,
+        0x16315274, 0x15E1A9A8, 0x859835D, 0x2C3DFC0, 0x105EB806, 0x68, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x289AAD, 0x1E781F9C, 0x60F9C31, 0x1505822E, 0x15DAF62B, 0x4BEA778, 0x15A81EB1, 0xB96064D,
+        0xB18A93A, 0x1AF0D4D4, 0x42CC1AE, 0x161EFE0, 0x82F5C03, 0x34, 0x0, 0x0, 0x0, 0x0,
+    ],
+    [
+        0x192279D1, 0xBA52F9F, 0x878CAFD, 0xCAE8B48, 0x52152AF, 0xAAAAAAA, 0x15555555, 0xAAAAAAA,
+        0x555555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ],
+];
+pub const CURVE_BB: [[[Chunk; NLEN]; 4]; 4] = [
+    [
+        [
+            0x1E1BD810, 0x59835DA, 0xC3DFC04, 0x5EB8061, 0x688, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x1E1BD80F, 0x59835DA, 0xC3DFC04, 0x5EB8061, 0x688, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x1E1BD80F, 0x59835DA, 0xC3DFC04, 0x5EB8061, 0x688, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x156259CF, 0xA01E744, 0x5ECB4F9, 0x148B7B47, 0x79A2790, 0x12EF5593, 0x2E39231,
+            0x3D597D3, 0x45146CF, 0x88D877A, 0x102EF8F0, 0x1196A60F, 0x1C60BA1D, 0x1CF63F80,
+            0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x7FFFF,
+        ],
+    ],
+    [
+        [
+            0x1C37B01F, 0xB306BB5, 0x187BF808, 0xBD700C2, 0xD10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x137E31DE, 0xF9A1D1F, 0x122AB0FD, 0x1A76FBA8, 0x79A2E18, 0x12EF5593, 0x2E39231,
+            0x3D597D3, 0x45146CF, 0x88D877A, 0x102EF8F0, 0x1196A60F, 0x1C60BA1D, 0x1CF63F80,
+            0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x7FFFF,
+        ],
+        [
+            0x137E31DD, 0xF9A1D1F, 0x122AB0FD, 0x1A76FBA8, 0x79A2E18, 0x12EF5593, 0x2E39231,
+            0x3D597D3, 0x45146CF, 0x88D877A, 0x102EF8F0, 0x1196A60F, 0x1C60BA1D, 0x1CF63F80,
+            0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x7FFFF,
+        ],
+        [
+            0x137E31DE, 0xF9A1D1F, 0x122AB0FD, 0x1A76FBA8, 0x79A2E18, 0x12EF5593, 0x2E39231,
+            0x3D597D3, 0x45146CF, 0x88D877A, 0x102EF8F0, 0x1196A60F, 0x1C60BA1D, 0x1CF63F80,
+            0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x7FFFF,
+        ],
+    ],
+    [
+        [
+            0x1C37B01E, 0xB306BB5, 0x187BF808, 0xBD700C2, 0xD10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x1C37B01F, 0xB306BB5, 0x187BF808, 0xBD700C2, 0xD10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x1C37B01F, 0xB306BB5, 0x187BF808, 0xBD700C2, 0xD10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x1C37B01F, 0xB306BB5, 0x187BF808, 0xBD700C2, 0xD10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+    ],
+    [
+        [
+            0x137E31DF, 0xF9A1D1F, 0x122AB0FD, 0x1A76FBA8, 0x79A2E18, 0x12EF5593, 0x2E39231,
+            0x3D597D3, 0x45146CF, 0x88D877A, 0x102EF8F0, 0x1196A60F, 0x1C60BA1D, 0x1CF63F80,
+            0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x7FFFF,
+        ],
+        [
+            0x192AA9AF, 0x1ED17B8E, 0xD70BCF0, 0x8B47A84, 0x79A1A80, 0x12EF5593, 0x2E39231,
+            0x3D597D3, 0x45146CF, 0x88D877A, 0x102EF8F0, 0x1196A60F, 0x1C60BA1D, 0x1CF63F80,
+            0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x7FFFF,
+        ],
+        [
+            0x1C37B01D, 0xB306BB5, 0x187BF808, 0xBD700C2, 0xD10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ],
+        [
+            0x137E31DF, 0xF9A1D1F, 0x122AB0FD, 0x1A76FBA8, 0x79A2E18, 0x12EF5593, 0x2E39231,
+            0x3D597D3, 0x45146CF, 0x88D877A, 0x102EF8F0, 0x1196A60F, 0x1C60BA1D, 0x1CF63F80,
+            0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x7FFFF,
+        ],
+    ],
+];
+
+pub const USE_GLV: bool = true;
+pub const USE_GS_G2: bool = true;
+pub const USE_GS_GT: bool = true;
+pub const GT_STRONG: bool = false;
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 28;
+
+pub const MODBITS: usize = 512;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 10;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BN;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 130;
+pub const SIGN_OF_X: SignOfX = SignOfX::POSITIVEX;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_fp512bn_64.rs b/src/roms/rom_fp512bn_64.rs
new file mode 100644
index 0000000..54d85c4
--- /dev/null
+++ b/src/roms/rom_fp512bn_64.rs
@@ -0,0 +1,482 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use fp512bn::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 60
+pub const MODULUS: [Chunk; NLEN] = [
+    0x4EB280922ADEF33,
+    0x6A55CE5F4C6467B,
+    0xC65DEAB236FE191,
+    0xCF1EACBE98B8E48,
+    0x3C111B0EF455146,
+    0xA1D8CB5307C0BBE,
+    0xFFFF9EC7F01C60B,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x1FA6DCEF99812E9,
+    0xAB3452895A0B74E,
+    0xC53EA988C079E1E,
+    0x1E90E033BA630B9,
+    0xF1EA41C0714D8B0,
+    0xE72785387509E28,
+    0xD86794F834DAB00,
+    0x9757C2ACCD342A1,
+    0x44ECB079,
+];
+pub const MCONST: Chunk = 0x692A189FCCC5C05;
+
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = 0;
+pub const CURVE_B_I: isize = 3;
+pub const CURVE_B: [Chunk; NLEN] = [0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x6A64A5F519A09ED,
+    0x10313E04F9A2B40,
+    0xC65DEAB2679A34A,
+    0xCF1EACBE98B8E48,
+    0x3C111B0EF445146,
+    0xA1D8CB5307C0BBE,
+    0xFFFF9EC7F01C60B,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_GY: [Chunk; NLEN] = [0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+
+pub const FRA: [Chunk; NLEN] = [
+    0x49617B1F4B73AB2,
+    0x71514F6202AED1F,
+    0xF6080D3BD8681E1,
+    0xF8AA9E852CBBB59,
+    0xC8CF2E2068398E9,
+    0x8A5296F791AB26B,
+    0x196A8C7C68B4EA1,
+    0xCF5BBF9095A1B79,
+    0x1EF71AA9,
+];
+pub const FRB: [Chunk; NLEN] = [
+    0x5510572DF6B481,
+    0xF9047EFD49B595C,
+    0xD055DD765E95FAF,
+    0xD6740E396BFD2EE,
+    0x7341ECEE8C1B85C,
+    0x1786345B7615952,
+    0xE695124B876776A,
+    0x30A4406F6A5E486,
+    0xE108E556,
+];
+pub const CURVE_BNX: [Chunk; NLEN] = [
+    0xB306BB5E1BD80F,
+    0x82F5C030B0F7F01,
+    0x68,
+    0x0,
+    0x0,
+    0x0,
+    0x0,
+    0x0,
+    0x0,
+];
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_CRU: [Chunk; NLEN] = [
+    0xB0716209C79298A,
+    0xCEE6799B8B17C14,
+    0x78966BE526092AE,
+    0x20089C27507ACD8,
+    0xF8EF7611FA3074B,
+    0x6146B86B378EA2C,
+    0xFFFF9EC7DC83D2A,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFF,
+];
+pub const CURVE_PXA: [Chunk; NLEN] = [
+    0xF07A96E0DB646B5,
+    0x18F87319072FFE8,
+    0x7BE21BCBBC78F22,
+    0x537863514DC6DC5,
+    0xDA57CC78CD0B024,
+    0xD29B358F0DB9B57,
+    0x7412F3CEA1E4BBB,
+    0xE138648958801BA,
+    0x3B165339,
+];
+pub const CURVE_PXB: [Chunk; NLEN] = [
+    0xDB5CBEFDA8AE0E9,
+    0xCA411CD88911B3,
+    0xD6E1383D5ADCE4,
+    0x227285526E0D5E5,
+    0xB02566B94D9781E,
+    0x56DC6C6EF2476A8,
+    0x680ABE8B4825EA6,
+    0xF85067E6C89B4C4,
+    0x481C13CB,
+];
+pub const CURVE_PYA: [Chunk; NLEN] = [
+    0x2480312ADDE67A1,
+    0xDA17AD615EFB85E,
+    0x312542808B7BC5C,
+    0x18BDEC153E8EDD2,
+    0xE5C158699D4B6CD,
+    0xB1DF660AFCDD03E,
+    0xB0CBA374F277085,
+    0xC827C7B8292EF5A,
+    0x6F01EC84,
+];
+pub const CURVE_PYB: [Chunk; NLEN] = [
+    0x58B7186C84F8E8B,
+    0xF05C2224BF76168,
+    0x10AD7EE279C08DF,
+    0x7FC3E2E50714A43,
+    0x3D04961941DA289,
+    0x38C118867B0C9B6,
+    0xC315F75D91F0214,
+    0x8B04E7831AC3640,
+    0x51A3BCEC,
+];
+pub const CURVE_W: [[Chunk; NLEN]; 2] = [
+    [
+        0x110F89749834583,
+        0x65FB911D16A173F,
+        0xFFFFFFFFCF63FE9,
+        0xFFFFFFFFFFFFFFF,
+        0xFFFF,
+        0x0,
+        0x0,
+        0x0,
+        0x0,
+    ],
+    [
+        0x1660D76BC37B01F,
+        0x5EB806161EFE02,
+        0xD1,
+        0x0,
+        0x0,
+        0x0,
+        0x0,
+        0x0,
+        0x0,
+    ],
+];
+pub const CURVE_SB: [[[Chunk; NLEN]; 2]; 2] = [
+    [
+        [
+            0xFAAEB208D4B9564,
+            0x601010BBB4B193C,
+            0xFFFFFFFFCF63F18,
+            0xFFFFFFFFFFFFFFF,
+            0xFFFF,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+        ],
+        [
+            0x5403CE8956259CE,
+            0xA45BDA397B2D3E,
+            0xC65DEAB2679A279,
+            0xCF1EACBE98B8E48,
+            0x3C111B0EF445146,
+            0xA1D8CB5307C0BBE,
+            0xFFFF9EC7F01C60B,
+            0xFFFFFFFFFFFFFFF,
+            0xFFFFFFFF,
+        ],
+    ],
+    [
+        [
+            0x1660D76BC37B01F,
+            0x5EB806161EFE02,
+            0xD1,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+        ],
+        [
+            0x110F89749834583,
+            0x65FB911D16A173F,
+            0xFFFFFFFFCF63FE9,
+            0xFFFFFFFFFFFFFFF,
+            0xFFFF,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+        ],
+    ],
+];
+pub const CURVE_WB: [[Chunk; NLEN]; 4] = [
+    [
+        0x6DAB36AB55A29F0,
+        0xFC42C60583D30C1,
+        0x5555555545215FB,
+        0x555555555555555,
+        0x5555,
+        0x0,
+        0x0,
+        0x0,
+        0x0,
+    ],
+    [
+        0xEEB012BA2355D4B,
+        0xF20FC1FD7F84F17,
+        0x892FA9DE2BB5E5C,
+        0x74B96064DAD40F5,
+        0xD76BC3535163152,
+        0x806161EFE021660,
+        0xD105EB,
+        0x0,
+        0x0,
+    ],
+    [
+        0x7CF03F380289AAD,
+        0xBA82C117183E70C,
+        0xC497D4EF15DAF62,
+        0x3A5CB0326D6A07A,
+        0x6BB5E1A9A8B18A9,
+        0xC030B0F7F010B30,
+        0x6882F5,
+        0x0,
+        0x0,
+    ],
+    [
+        0x574A5F3F92279D1,
+        0xF65745A421E32BF,
+        0x55555555452152A,
+        0x555555555555555,
+        0x5555,
+        0x0,
+        0x0,
+        0x0,
+        0x0,
+    ],
+];
+pub const CURVE_BB: [[[Chunk; NLEN]; 4]; 4] = [
+    [
+        [
+            0xB306BB5E1BD810,
+            0x82F5C030B0F7F01,
+            0x68,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+        ],
+        [
+            0xB306BB5E1BD80F,
+            0x82F5C030B0F7F01,
+            0x68,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+        ],
+        [
+            0xB306BB5E1BD80F,
+            0x82F5C030B0F7F01,
+            0x68,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+        ],
+        [
+            0x5403CE8956259CF,
+            0xA45BDA397B2D3E,
+            0xC65DEAB2679A279,
+            0xCF1EACBE98B8E48,
+            0x3C111B0EF445146,
+            0xA1D8CB5307C0BBE,
+            0xFFFF9EC7F01C60B,
+            0xFFFFFFFFFFFFFFF,
+            0xFFFFFFFF,
+        ],
+    ],
+    [
+        [
+            0x1660D76BC37B01F,
+            0x5EB806161EFE02,
+            0xD1,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+        ],
+        [
+            0x5F343A3F37E31DE,
+            0x8D3B7DD448AAC3F,
+            0xC65DEAB2679A2E1,
+            0xCF1EACBE98B8E48,
+            0x3C111B0EF445146,
+            0xA1D8CB5307C0BBE,
+            0xFFFF9EC7F01C60B,
+            0xFFFFFFFFFFFFFFF,
+            0xFFFFFFFF,
+        ],
+        [
+            0x5F343A3F37E31DD,
+            0x8D3B7DD448AAC3F,
+            0xC65DEAB2679A2E1,
+            0xCF1EACBE98B8E48,
+            0x3C111B0EF445146,
+            0xA1D8CB5307C0BBE,
+            0xFFFF9EC7F01C60B,
+            0xFFFFFFFFFFFFFFF,
+            0xFFFFFFFF,
+        ],
+        [
+            0x5F343A3F37E31DE,
+            0x8D3B7DD448AAC3F,
+            0xC65DEAB2679A2E1,
+            0xCF1EACBE98B8E48,
+            0x3C111B0EF445146,
+            0xA1D8CB5307C0BBE,
+            0xFFFF9EC7F01C60B,
+            0xFFFFFFFFFFFFFFF,
+            0xFFFFFFFF,
+        ],
+    ],
+    [
+        [
+            0x1660D76BC37B01E,
+            0x5EB806161EFE02,
+            0xD1,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+        ],
+        [
+            0x1660D76BC37B01F,
+            0x5EB806161EFE02,
+            0xD1,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+        ],
+        [
+            0x1660D76BC37B01F,
+            0x5EB806161EFE02,
+            0xD1,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+        ],
+        [
+            0x1660D76BC37B01F,
+            0x5EB806161EFE02,
+            0xD1,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+        ],
+    ],
+    [
+        [
+            0x5F343A3F37E31DF,
+            0x8D3B7DD448AAC3F,
+            0xC65DEAB2679A2E1,
+            0xCF1EACBE98B8E48,
+            0x3C111B0EF445146,
+            0xA1D8CB5307C0BBE,
+            0xFFFF9EC7F01C60B,
+            0xFFFFFFFFFFFFFFF,
+            0xFFFFFFFF,
+        ],
+        [
+            0x3DA2F71D92AA9AF,
+            0x45A3D4235C2F3C,
+            0xC65DEAB2679A1A8,
+            0xCF1EACBE98B8E48,
+            0x3C111B0EF445146,
+            0xA1D8CB5307C0BBE,
+            0xFFFF9EC7F01C60B,
+            0xFFFFFFFFFFFFFFF,
+            0xFFFFFFFF,
+        ],
+        [
+            0x1660D76BC37B01D,
+            0x5EB806161EFE02,
+            0xD1,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+        ],
+        [
+            0x5F343A3F37E31DF,
+            0x8D3B7DD448AAC3F,
+            0xC65DEAB2679A2E1,
+            0xCF1EACBE98B8E48,
+            0x3C111B0EF445146,
+            0xA1D8CB5307C0BBE,
+            0xFFFF9EC7F01C60B,
+            0xFFFFFFFFFFFFFFF,
+            0xFFFFFFFF,
+        ],
+    ],
+];
+
+pub const USE_GLV: bool = true;
+pub const USE_GS_G2: bool = true;
+pub const USE_GS_GT: bool = true;
+pub const GT_STRONG: bool = false;
+
+pub const MODBYTES: usize = 64;
+pub const BASEBITS: usize = 60;
+
+pub const MODBITS: usize = 512;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 28;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BN;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 130;
+pub const SIGN_OF_X: SignOfX = SignOfX::POSITIVEX;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_goldilocks_32.rs b/src/roms/rom_goldilocks_32.rs
new file mode 100644
index 0000000..5d5f8bb
--- /dev/null
+++ b/src/roms/rom_goldilocks_32.rs
@@ -0,0 +1,73 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use goldilocks::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 29
+// Goldilocks modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FDFFFFF,
+    0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const MCONST: Chunk = 0x1;
+
+// Goldilocks curve
+pub const CURVE_COF_I: isize = 4;
+pub const CURVE_A: isize = 1;
+pub const CURVE_B_I: isize = -39081;
+pub const CURVE_COF: [Chunk; NLEN] = [
+    0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0x1FFF6756, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FDFFFFF,
+    0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFF,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0xB5844F3, 0x1BC61495, 0x1163D548, 0x1984E51B, 0x3690216, 0xDA4D76B, 0xFA7113B, 0x1FEF9944,
+    0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x7FF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x15555555, 0xAAAAAAA, 0x15555555, 0xAAAAAAA, 0x15555555, 0xAAAAAAA, 0x15555555, 0x152AAAAA,
+    0xAAAAAAA, 0x15555555, 0xAAAAAAA, 0x15555555, 0xAAAAAAA, 0x15555555, 0xAAAAAAA, 0x1555,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0xA9386ED, 0x1757DE6F, 0x13681AF6, 0x19657DA3, 0x3098BBB, 0x12C19D15, 0x12E03595, 0xE515B18,
+    0x17B7E36D, 0x1AC426E, 0xDBB5E8, 0x10D8560, 0x159D6205, 0xB8246D9, 0x17A58D2B, 0x15C0,
+];
+
+pub const MODBYTES: usize = 56;
+pub const BASEBITS: usize = 29;
+
+pub const MODBITS: usize = 448;
+pub const MOD8: usize = 7;
+pub const MODTYPE: ModType = ModType::GENERALISED_MERSENNE;
+pub const SH: usize = 14;
+
+pub const CURVETYPE: CurveType = CurveType::EDWARDS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 64;
+pub const AESKEY: usize = 32;
diff --git a/src/roms/rom_goldilocks_64.rs b/src/roms/rom_goldilocks_64.rs
new file mode 100644
index 0000000..d6cadf1
--- /dev/null
+++ b/src/roms/rom_goldilocks_64.rs
@@ -0,0 +1,99 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use goldilocks::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 58
+// Goldilocks modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0x3FBFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [0x200000000, 0x0, 0x0, 0x0, 0x3000000, 0x0, 0x0, 0x0];
+pub const MCONST: Chunk = 0x1;
+
+// Goldilocks curve
+pub const CURVE_COF_I: isize = 4;
+pub const CURVE_A: isize = 1;
+pub const CURVE_B_I: isize = -39081;
+pub const CURVE_COF: [Chunk; NLEN] = [0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0x3FFFFFFFFFF6756,
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0x3FBFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFF,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x378C292AB5844F3,
+    0x3309CA37163D548,
+    0x1B49AED63690216,
+    0x3FDF3288FA7113B,
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0xFFFFFFFFFF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x155555555555555,
+    0x155555555555555,
+    0x155555555555555,
+    0x2A5555555555555,
+    0x2AAAAAAAAAAAAAA,
+    0x2AAAAAAAAAAAAAA,
+    0x2AAAAAAAAAAAAAA,
+    0x2AAAAAAAAAA,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x2EAFBCDEA9386ED,
+    0x32CAFB473681AF6,
+    0x25833A2A3098BBB,
+    0x1CA2B6312E03595,
+    0x35884DD7B7E36D,
+    0x21B0AC00DBB5E8,
+    0x17048DB359D6205,
+    0x2B817A58D2B,
+];
+
+pub const MODBYTES: usize = 56;
+pub const BASEBITS: usize = 58;
+
+pub const MODBITS: usize = 448;
+pub const MOD8: usize = 7;
+pub const MODTYPE: ModType = ModType::GENERALISED_MERSENNE;
+pub const SH: usize = 16;
+
+pub const CURVETYPE: CurveType = CurveType::EDWARDS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 64;
+pub const AESKEY: usize = 32;
diff --git a/src/roms/rom_hifive_32.rs b/src/roms/rom_hifive_32.rs
new file mode 100644
index 0000000..cfa9f59
--- /dev/null
+++ b/src/roms/rom_hifive_32.rs
@@ -0,0 +1,68 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use hifive::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 29
+
+// hifive Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0x1FFFFFFD, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x9000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const MCONST: Chunk = 0x3;
+
+// hifive Curve
+pub const CURVE_COF_I: isize = 8;
+pub const CURVE_A: isize = 1;
+pub const CURVE_B_I: isize = 11111;
+pub const CURVE_COF: [Chunk; NLEN] = [0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0x2B67, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x1E9FA805, 0x197CACB9, 0x1E4EEA9E, 0x17AD70F, 0x1FA9850C, 0x38A0A, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x4000,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x5FE8632, 0x15F63428, 0xD976C4, 0x1AACA194, 0x35B6DB5, 0x8E3F7A, 0x52D1B0E, 0xF0A7A36,
+    0x1C161D00, 0x8170C70, 0x1185AD59, 0x181B,
+];
+
+pub const MODBYTES: usize = 42;
+pub const BASEBITS: usize = 29;
+
+pub const MODBITS: usize = 336;
+pub const MOD8: usize = 5;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 12;
+
+pub const CURVETYPE: CurveType = CurveType::EDWARDS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 48;
+pub const AESKEY: usize = 24;
diff --git a/src/roms/rom_hifive_64.rs b/src/roms/rom_hifive_64.rs
new file mode 100644
index 0000000..b2eebb9
--- /dev/null
+++ b/src/roms/rom_hifive_64.rs
@@ -0,0 +1,77 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use hifive::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 60
+
+// Base Bits= 60
+// hifive Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0xFFFFFFFFFFFFFFD,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [0x9000000000000, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const MCONST: Chunk = 0x3;
+
+// hifive Curve
+pub const CURVE_COF_I: isize = 8;
+pub const CURVE_A: isize = 1;
+pub const CURVE_B_I: isize = 11111;
+pub const CURVE_COF: [Chunk; NLEN] = [0x8, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [0x2B67, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0xB2F95973E9FA805,
+    0xC0BD6B87F93BAA7,
+    0x71415FA9850,
+    0x0,
+    0x0,
+    0x200000000,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [0xC, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x2BEC68505FE8632,
+    0x5D5650CA0365DB1,
+    0x3811C7EF435B6DB,
+    0x7853D1B14B46C,
+    0x56502E18E1C161D,
+    0xC0DC616B,
+];
+
+pub const MODBYTES: usize = 42;
+pub const BASEBITS: usize = 60;
+
+pub const MODBITS: usize = 336;
+pub const MOD8: usize = 5;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 24;
+
+pub const CURVETYPE: CurveType = CurveType::EDWARDS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 48;
+pub const AESKEY: usize = 24;
diff --git a/src/roms/rom_nist256_32.rs b/src/roms/rom_nist256_32.rs
new file mode 100644
index 0000000..6859da5
--- /dev/null
+++ b/src/roms/rom_nist256_32.rs
@@ -0,0 +1,74 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+use nist256::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 28
+
+// Base Bits= 28
+// nist256 modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFF, 0x0, 0x0, 0x1000000, 0x0, 0xFFFFFFF, 0xF,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x50000, 0x300000, 0x0, 0x0, 0xFFFFFFA, 0xFFFFFBF, 0xFFFFEFF, 0xFFFAFFF, 0x2FFFF, 0x0,
+];
+pub const MCONST: Chunk = 0x1;
+
+// nist256 curve
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = -3;
+pub const CURVE_B_I: isize = 0;
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0x7D2604B, 0xCE3C3E2, 0x3B0F63B, 0x6B0CC5, 0x6BC651D, 0x5576988, 0x7B3EBBD, 0xAA3A93E,
+    0xAC635D8, 0x5,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0xC632551, 0xB9CAC2F, 0x79E84F3, 0xFAADA71, 0xFFFBCE6, 0xFFFFFFF, 0xFFFFFF, 0x0, 0xFFFFFFF,
+    0xF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x898C296, 0xA13945D, 0xB33A0F4, 0x7D812DE, 0xF27703, 0xE563A44, 0x7F8BCE6, 0xE12C424,
+    0xB17D1F2, 0x6,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x7BF51F5, 0xB640683, 0x15ECECB, 0x33576B3, 0xE162BCE, 0x4A7C0F9, 0xB8EE7EB, 0xFE1A7F9,
+    0xFE342E2, 0x4,
+];
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 28;
+
+pub const MODBITS: usize = 256;
+pub const MOD8: usize = 7;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 14;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_nist256_64.rs b/src/roms/rom_nist256_64.rs
new file mode 100644
index 0000000..015bbb3
--- /dev/null
+++ b/src/roms/rom_nist256_64.rs
@@ -0,0 +1,86 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+use nist256::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 56
+// nist256 modulus
+pub const MODULUS: [Chunk; NLEN] = [0xFFFFFFFFFFFFFF, 0xFFFFFFFFFF, 0x0, 0x1000000, 0xFFFFFFFF];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x3000000050000,
+    0x0,
+    0xFFFFFBFFFFFFFA,
+    0xFFFAFFFFFFFEFF,
+    0x2FFFF,
+];
+pub const MCONST: Chunk = 0x1;
+
+// nist256 curve
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = -3;
+pub const CURVE_B_I: isize = 0;
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0xCE3C3E27D2604B,
+    0x6B0CC53B0F63B,
+    0x55769886BC651D,
+    0xAA3A93E7B3EBBD,
+    0x5AC635D8,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0xB9CAC2FC632551,
+    0xFAADA7179E84F3,
+    0xFFFFFFFFFFBCE6,
+    0xFFFFFF,
+    0xFFFFFFFF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0xA13945D898C296,
+    0x7D812DEB33A0F4,
+    0xE563A440F27703,
+    0xE12C4247F8BCE6,
+    0x6B17D1F2,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0xB6406837BF51F5,
+    0x33576B315ECECB,
+    0x4A7C0F9E162BCE,
+    0xFE1A7F9B8EE7EB,
+    0x4FE342E2,
+];
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 56;
+
+pub const MODBITS: usize = 256;
+pub const MOD8: usize = 7;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 24;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_nist384_32.rs b/src/roms/rom_nist384_32.rs
new file mode 100644
index 0000000..48d7826
--- /dev/null
+++ b/src/roms/rom_nist384_32.rs
@@ -0,0 +1,76 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use nist384::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 29
+
+// Base Bits= 29
+// nist384 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0x1FFFFFFF, 0x7, 0x0, 0x1FFFFE00, 0x1FFFEFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x7F,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x0, 0x8000, 0x1FF80000, 0x1FFFFF, 0x2000000, 0x0, 0x0, 0x1FFFFFFC, 0xF, 0x100, 0x400, 0x0,
+    0x0, 0x0,
+];
+pub const MCONST: Chunk = 0x1;
+
+// nist384 Curve
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = -3;
+pub const CURVE_B_I: isize = 0;
+pub const CURVE_COF: [Chunk; NLEN] = [
+    0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0x13EC2AEF, 0x142E476E, 0xBB4674A, 0xC731B14, 0x1875AC65, 0x447A809, 0x4480C50, 0xDDFD028,
+    0x19181D9C, 0x1F1FC168, 0x623815A, 0x47DCFC9, 0x1312FA7E, 0x59,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0xCC52973, 0x760CB56, 0xC29DEBB, 0x141B6491, 0x12DDF581, 0x6C0FA1B, 0x1FFF1D8D, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x7F,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x12760AB7, 0x12A2F1C3, 0x154A5B0E, 0x5E4BB7E, 0x2A38550, 0xF0412A, 0xE6167DD, 0xC5174F3,
+    0x146E1D3B, 0x1799056B, 0x3AC71C7, 0x1D160A6F, 0x87CA22B, 0x55,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x10EA0E5F, 0x1218EBE4, 0x1FA0675E, 0x1639C3A, 0xB8C00A6, 0x1889DAF8, 0x11F3A768, 0x17A51342,
+    0x9F8F41D, 0x1C9496E1, 0x1767A62F, 0xC4C58DE, 0x17DE4A9, 0x1B,
+];
+
+pub const MODBYTES: usize = 48;
+pub const BASEBITS: usize = 29;
+
+pub const MODBITS: usize = 384;
+pub const MOD8: usize = 7;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 14;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 48;
+pub const AESKEY: usize = 24;
diff --git a/src/roms/rom_nist384_64.rs b/src/roms/rom_nist384_64.rs
new file mode 100644
index 0000000..f0f5c69
--- /dev/null
+++ b/src/roms/rom_nist384_64.rs
@@ -0,0 +1,104 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use nist384::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 56
+
+// nist384 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0xFFFFFFFF,
+    0xFFFF0000000000,
+    0xFFFFFFFFFEFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0xFE000000010000,
+    0xFFFFFF,
+    0x2,
+    0xFFFFFFFE00,
+    0x1000000020000,
+    0x0,
+    0x0,
+];
+pub const MCONST: Chunk = 0x100000001;
+
+
+// nist384 Curve
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = -3;
+pub const CURVE_B_I: isize = 0;
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0x85C8EDD3EC2AEF,
+    0x398D8A2ED19D2A,
+    0x8F5013875AC656,
+    0xFE814112031408,
+    0xF82D19181D9C6E,
+    0xE7E4988E056BE3,
+    0xB3312FA7E23E,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0xEC196ACCC52973,
+    0xDB248B0A77AEC,
+    0x81F4372DDF581A,
+    0xFFFFFFFFC7634D,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x545E3872760AB7,
+    0xF25DBF55296C3A,
+    0xE082542A385502,
+    0x8BA79B9859F741,
+    0x20AD746E1D3B62,
+    0x5378EB1C71EF3,
+    0xAA87CA22BE8B,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x431D7C90EA0E5F,
+    0xB1CE1D7E819D7A,
+    0x13B5F0B8C00A60,
+    0x289A147CE9DA31,
+    0x92DC29F8F41DBD,
+    0x2C6F5D9E98BF92,
+    0x3617DE4A9626,
+];
+
+pub const MODBYTES: usize = 48;
+pub const BASEBITS: usize = 56;
+
+pub const MODBITS: usize = 384;
+pub const MOD8: usize = 7;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 8;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 48;
+pub const AESKEY: usize = 24;
diff --git a/src/roms/rom_nist521_32.rs b/src/roms/rom_nist521_32.rs
new file mode 100644
index 0000000..55cbf30
--- /dev/null
+++ b/src/roms/rom_nist521_32.rs
@@ -0,0 +1,79 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use nist521::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 28
+// nist521 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF,
+    0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF,
+    0xFFFFFFF, 0xFFFFFFF, 0x1FFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x400000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x0,
+];
+pub const MCONST: Chunk = 0x1;
+
+// nist521 Curve
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = -3;
+pub const CURVE_B_I: isize = 0;
+pub const CURVE_COF: [Chunk; NLEN] = [
+    0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0xB503F00, 0x451FD46, 0xC34F1EF, 0xDF883D2, 0xF073573, 0xBD3BB1B, 0xB1652C0, 0xEC7E937,
+    0x6193951, 0xF109E15, 0x489918E, 0x15F3B8B, 0x25B99B3, 0xEEA2DA7, 0xB68540, 0x929A21A,
+    0xE1C9A1F, 0x3EB9618, 0x5195,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x1386409, 0x6FB71E9, 0xC47AEBB, 0xC9B8899, 0x5D03BB5, 0x48F709A, 0xB7FCC01, 0xBF2F966,
+    0x1868783, 0xFFFFFA5, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF,
+    0xFFFFFFF, 0xFFFFFFF, 0x1FFFF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x2E5BD66, 0x7E7E31C, 0xA429BF9, 0xB3C1856, 0x8DE3348, 0x27A2FFA, 0x8FE1DC1, 0xEFE7592,
+    0x14B5E77, 0x4D3DBAA, 0x8AF606B, 0xB521F82, 0x139053F, 0x429C648, 0x62395B4, 0x9E3ECB6,
+    0x404E9CD, 0x8E06B70, 0xC685,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0xFD16650, 0xBE94769, 0x2C24088, 0x7086A27, 0x761353C, 0x13FAD0, 0xC550B9, 0x5EF4264,
+    0x7EE7299, 0x3E662C9, 0xFBD1727, 0x446817A, 0x449579B, 0xD998F54, 0x42C7D1B, 0x5C8A5FB,
+    0xA3BC004, 0x296A789, 0x11839,
+];
+
+pub const MODBYTES: usize = 66;
+pub const BASEBITS: usize = 28;
+
+pub const MODBITS: usize = 521;
+pub const MOD8: usize = 7;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 11;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 64;
+pub const AESKEY: usize = 32;
diff --git a/src/roms/rom_nist521_64.rs b/src/roms/rom_nist521_64.rs
new file mode 100644
index 0000000..bf241b2
--- /dev/null
+++ b/src/roms/rom_nist521_64.rs
@@ -0,0 +1,104 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use nist521::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 60
+// nist521 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0x1FFFFFFFFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [0x4000000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const MCONST: Chunk = 0x1;
+
+// nist521 Curve
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = -3;
+pub const CURVE_B_I: isize = 0;
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0xF451FD46B503F00,
+    0x73DF883D2C34F1E,
+    0x2C0BD3BB1BF0735,
+    0x3951EC7E937B165,
+    0x9918EF109E15619,
+    0x5B99B315F3B8B48,
+    0xB68540EEA2DA72,
+    0x8E1C9A1F929A21A,
+    0x51953EB961,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0xB6FB71E91386409,
+    0xB5C9B8899C47AEB,
+    0xC0148F709A5D03B,
+    0x8783BF2F966B7FC,
+    0xFFFFFFFFFFA5186,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0x1FFFFFFFFFF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x97E7E31C2E5BD66,
+    0x48B3C1856A429BF,
+    0xDC127A2FFA8DE33,
+    0x5E77EFE75928FE1,
+    0xF606B4D3DBAA14B,
+    0x39053FB521F828A,
+    0x62395B4429C6481,
+    0x404E9CD9E3ECB6,
+    0xC6858E06B7,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x8BE94769FD16650,
+    0x3C7086A272C2408,
+    0xB9013FAD076135,
+    0x72995EF42640C55,
+    0xD17273E662C97EE,
+    0x49579B446817AFB,
+    0x42C7D1BD998F544,
+    0x9A3BC0045C8A5FB,
+    0x11839296A78,
+];
+
+pub const MODBYTES: usize = 66;
+pub const BASEBITS: usize = 60;
+
+pub const MODBITS: usize = 521;
+pub const MOD8: usize = 7;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 19;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 64;
+pub const AESKEY: usize = 32;
diff --git a/src/roms/rom_nums256e_32.rs b/src/roms/rom_nums256e_32.rs
new file mode 100644
index 0000000..45506bd
--- /dev/null
+++ b/src/roms/rom_nums256e_32.rs
@@ -0,0 +1,71 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use nums256e::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 29
+
+// Base Bits= 29
+// nums256 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0x1FFFFF43, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF,
+    0xFFFFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [0x22E2400, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const MCONST: Chunk = 0xBD;
+
+// nums256e Curve
+pub const CURVE_COF_I: isize = 4;
+pub const CURVE_A: isize = 1;
+pub const CURVE_B_I: isize = -15342;
+pub const CURVE_COF: [Chunk; NLEN] = [0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0x1FFFC355, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF,
+    0xFFFFFF,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0xEDD4AF5, 0x123D8C87, 0x1650E6C6, 0xAB54A5E, 0x419, 0x0, 0x0, 0x0, 0x400000,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0xEED13DA, 0x6F60481, 0x20D61A8, 0x13141DC6, 0x9BD60C3, 0x1EAFB490, 0xDF73478, 0x1F6D5D44,
+    0x8A7514,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x198A89E6, 0x1D30B73B, 0x15BB4CB, 0x1EC3B021, 0x18010715, 0x12ECD325, 0x171F3A59, 0x13FB3B24,
+    0x44D53E,
+];
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 29;
+
+pub const MODBITS: usize = 256;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 5;
+
+pub const CURVETYPE: CurveType = CurveType::EDWARDS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
+
diff --git a/src/roms/rom_nums256e_64.rs b/src/roms/rom_nums256e_64.rs
new file mode 100644
index 0000000..4382924
--- /dev/null
+++ b/src/roms/rom_nums256e_64.rs
@@ -0,0 +1,80 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use nums256e::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 56
+// nums256 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0xFFFFFFFFFFFF43,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [0x89000000000000, 0x8B, 0x0, 0x0, 0x0];
+pub const MCONST: Chunk = 0xBD;
+
+// nums256e Curve
+pub const CURVE_COF_I: isize = 4;
+pub const CURVE_A: isize = 1;
+pub const CURVE_B_I: isize = -15342;
+pub const CURVE_COF: [Chunk; NLEN] = [0x4, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0xFFFFFFFFFFC355,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFF,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] =
+    [0x47B190EEDD4AF5, 0x5AA52F59439B1A, 0x4195, 0x0, 0x40000000];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0xDEC0902EED13DA,
+    0x8A0EE3083586A0,
+    0x5F69209BD60C39,
+    0x6AEA237DCD1E3D,
+    0x8A7514FB,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0xA616E7798A89E6,
+    0x61D810856ED32F,
+    0xD9A64B8010715F,
+    0xD9D925C7CE9665,
+    0x44D53E9F,
+];
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 56;
+
+pub const MODBITS: usize = 256;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 24;
+
+pub const CURVETYPE: CurveType = CurveType::EDWARDS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
+
diff --git a/src/roms/rom_nums256w_32.rs b/src/roms/rom_nums256w_32.rs
new file mode 100644
index 0000000..b768380
--- /dev/null
+++ b/src/roms/rom_nums256w_32.rs
@@ -0,0 +1,66 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use nums256w::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 28
+// nums256 modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0xFFFFF43, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF,
+    0xFFFFFFF, 0xF,
+];
+pub const R2MODP: [Chunk; NLEN] = [0x0, 0x8900000, 0x8B, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const MCONST: Chunk = 0xBD;
+
+// nums256w curve
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = -3;
+pub const CURVE_B_I: isize = 152961;
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [0x25581, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x751A825, 0xAB20294, 0x65C6020, 0x8275EA2, 0xFFFE43C, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF,
+    0xFFFFFFF, 0xF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x21AACB1, 0x52EE1EB, 0x4C73ABC, 0x9B0903D, 0xB098357, 0xA04F42C, 0x1297A95, 0x5AAADB6,
+    0xC9ED6B6, 0xB,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x184DE9F, 0xB5B9CB2, 0x10FBB80, 0xC3D1153, 0x35C955, 0xF77E04E, 0x673448B, 0x3399B6A,
+    0x8FC0F1, 0xD,
+];
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 28;
+
+pub const MODBITS: usize = 256;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 14;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_nums256w_64.rs b/src/roms/rom_nums256w_64.rs
new file mode 100644
index 0000000..5b5d491
--- /dev/null
+++ b/src/roms/rom_nums256w_64.rs
@@ -0,0 +1,78 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use nums256w::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 56
+// nums256 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0xFFFFFFFFFFFF43,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [0x89000000000000, 0x8B, 0x0, 0x0, 0x0];
+pub const MCONST: Chunk = 0xBD;
+
+// nums256w Curve
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = -3;
+pub const CURVE_B_I: isize = 152961;
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [0x25581, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0xAB20294751A825,
+    0x8275EA265C6020,
+    0xFFFFFFFFFFE43C,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x52EE1EB21AACB1,
+    0x9B0903D4C73ABC,
+    0xA04F42CB098357,
+    0x5AAADB61297A95,
+    0xBC9ED6B6,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0xB5B9CB2184DE9F,
+    0xC3D115310FBB80,
+    0xF77E04E035C955,
+    0x3399B6A673448B,
+    0xD08FC0F1,
+];
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 56;
+
+pub const MODBITS: usize = 256;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 24;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_nums384e_32.rs b/src/roms/rom_nums384e_32.rs
new file mode 100644
index 0000000..eaad1c8
--- /dev/null
+++ b/src/roms/rom_nums384e_32.rs
@@ -0,0 +1,74 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use nums384e::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 29
+// nums384 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0x1FFFFEC3, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x7F,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x0, 0x4448000, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const MCONST: Chunk = 0x13D;
+
+// nums384e Curve
+pub const CURVE_COF_I: isize = 4;
+pub const CURVE_A: isize = 1;
+pub const CURVE_B_I: isize = -11556;
+pub const CURVE_COF: [Chunk; NLEN] = [
+    0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0x1FFFD19F, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x7F,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x6A3897D, 0x5CEE627, 0xD721E48, 0x8AAB556, 0x1E1CF61E, 0xD0E5A35, 0x1FFF891C, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1F,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0xC206BDE, 0x6AA0723, 0x116504D4, 0x52562CA, 0x163406FF, 0x1FD47998, 0x10015D8F, 0x8DCB7C9,
+    0x15B30BF4, 0x14D72AED, 0x102DA884, 0xB524CD9, 0x1B111FB4, 0x30,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x10729392, 0xC681F0F, 0x1B123727, 0x561F28D, 0x1964B007, 0xC7BFB22, 0x1D5A0C3E, 0xE9E284B,
+    0x1716AD82, 0x11D886E, 0x1CE2C69, 0x134DDD61, 0x983E67B, 0x41,
+];
+
+pub const MODBYTES: usize = 48;
+pub const BASEBITS: usize = 29;
+
+pub const MODBITS: usize = 384;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 14;
+
+pub const CURVETYPE: CurveType = CurveType::EDWARDS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 48;
+pub const AESKEY: usize = 24;
+
diff --git a/src/roms/rom_nums384e_64.rs b/src/roms/rom_nums384e_64.rs
new file mode 100644
index 0000000..08d83c2
--- /dev/null
+++ b/src/roms/rom_nums384e_64.rs
@@ -0,0 +1,95 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use nums384e::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 56
+// nums384 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0xFFFFFFFFFFFEC3,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [0x188890000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const MCONST: Chunk = 0x13D;
+
+// nums384e Curve
+pub const CURVE_COF_I: isize = 4;
+pub const CURVE_A: isize = 1;
+pub const CURVE_B_I: isize = -11556;
+pub const CURVE_COF: [Chunk; NLEN] = [0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0xFFFFFFFFFFD19F,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFF,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0xB9DCC4E6A3897D,
+    0x555AAB35C87920,
+    0x1CB46BE1CF61E4,
+    0xFFFFFFFFE2471A,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0xD540E46C206BDE,
+    0x92B16545941350,
+    0xA8F33163406FF2,
+    0xE5BE4C005763FF,
+    0xE55DB5B30BF446,
+    0x266CC0B6A2129A,
+    0x61B111FB45A9,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x8D03E1F0729392,
+    0xB0F946EC48DC9D,
+    0xF7F645964B0072,
+    0xF1425F56830F98,
+    0xB10DD716AD8274,
+    0xEEB08738B1A423,
+    0x82983E67B9A6,
+];
+
+pub const MODBYTES: usize = 48;
+pub const BASEBITS: usize = 56;
+
+pub const MODBITS: usize = 384;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 8;
+
+pub const CURVETYPE: CurveType = CurveType::EDWARDS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 48;
+pub const AESKEY: usize = 24;
+
diff --git a/src/roms/rom_nums384w_32.rs b/src/roms/rom_nums384w_32.rs
new file mode 100644
index 0000000..92181a6
--- /dev/null
+++ b/src/roms/rom_nums384w_32.rs
@@ -0,0 +1,74 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use nums384w::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 29
+// nums384 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0x1FFFFEC3, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x7F,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x0, 0x4448000, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const MCONST: Chunk = 0x13D;
+
+// nums384w Curve
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = -3;
+pub const CURVE_B_I: isize = -34568;
+pub const CURVE_COF: [Chunk; NLEN] = [
+    0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0x1FFF77BB, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x7F,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x1B0E61B9, 0x26C0FB3, 0xDF89E98, 0x153A7A98, 0x16881BED, 0x178F75AE, 0x1FFF587A, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x7F,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x98152A, 0x1CE5D021, 0x18711EFA, 0x1DDA201E, 0xC742522, 0x148D9536, 0x7D3CEF4, 0x19BF703F,
+    0x60225C1, 0x12082F8D, 0x12203288, 0x2DE3038, 0x17956F0B, 0x3A,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x6180716, 0x3A5C763, 0x1D2B4997, 0xD69B77F, 0x837EBCD, 0x1BE890D, 0xE72E482, 0xEFF0FEE,
+    0x1EB00469, 0x2C267B, 0x15F8CF4C, 0x3371C71, 0xDEE368E, 0x56,
+];
+
+pub const MODBYTES: usize = 48;
+pub const BASEBITS: usize = 29;
+
+pub const MODBITS: usize = 384;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 14;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 48;
+pub const AESKEY: usize = 24;
+
diff --git a/src/roms/rom_nums384w_64.rs b/src/roms/rom_nums384w_64.rs
new file mode 100644
index 0000000..3f51d94
--- /dev/null
+++ b/src/roms/rom_nums384w_64.rs
@@ -0,0 +1,94 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use nums384w::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 58
+// nums384 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0x3FFFFFFFFFFFEC3,
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0xFFFFFFFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [0x88900000000000, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const MCONST: Chunk = 0x13D;
+
+// nums384w Curve
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = -3;
+pub const CURVE_B_I: isize = -34568;
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0x3FFFFFFFFFF77BB,
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0xFFFFFFFFF,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x4D81F67B0E61B9,
+    0x2A74F530DF89E98,
+    0x2F1EEB5D6881BED,
+    0x3FFFFFFFFFF587A,
+    0x3FFFFFFFFFFFFFF,
+    0x3FFFFFFFFFFFFFF,
+    0xFFFFFFFFF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x39CBA042098152A,
+    0x3BB4403D8711EFA,
+    0x291B2A6CC742522,
+    0x337EE07E7D3CEF4,
+    0x24105F1A60225C1,
+    0x5BC60712203288,
+    0x757956F0B,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x74B8EC66180716,
+    0x1AD36EFFD2B4997,
+    0x37D121A837EBCD,
+    0x1DFE1FDCE72E482,
+    0x584CF7EB00469,
+    0x66E38E35F8CF4C,
+    0xACDEE368E,
+];
+pub const MODBYTES: usize = 48;
+pub const BASEBITS: usize = 58;
+
+pub const MODBITS: usize = 384;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 22;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 48;
+pub const AESKEY: usize = 24;
+
diff --git a/src/roms/rom_nums512e_32.rs b/src/roms/rom_nums512e_32.rs
new file mode 100644
index 0000000..8d53f9f
--- /dev/null
+++ b/src/roms/rom_nums512e_32.rs
@@ -0,0 +1,79 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use nums512e::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 29
+// nums512 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0x1FFFFDC7, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x7FFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0xB100000, 0x278, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x0,
+];
+pub const MCONST: Chunk = 0x239;
+
+// nums512e Curve
+pub const CURVE_COF_I: isize = 4;
+pub const CURVE_A: isize = 1;
+pub const CURVE_B_I: isize = -78296;
+pub const CURVE_COF: [Chunk; NLEN] = [
+    0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0x1FFECBEF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x7FFFF,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x1BEED46D, 0x1A3467A8, 0x1BFB3FD9, 0xC0AF0DB, 0x86F52A4, 0xC64B85B, 0x6EA78FF, 0xDA5F9F2,
+    0x1FB4F063, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x1FFFF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x19EC57FE, 0xDCD594C, 0x113C0571, 0xA4A84F9, 0x104AD0FE, 0x4C92B44, 0xC3DE2F7, 0x9DDC8CE,
+    0x74621C1, 0x1139DC0A, 0x9E85FAF, 0x1B894704, 0x1D1E79F4, 0x9E29997, 0x32DE223, 0x16D38F43,
+    0x116D128D, 0x6FC71,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x1E2F5E1, 0x136EF606, 0x1C7407CC, 0xDA71537, 0xC1FD026, 0x3431576, 0x15898068, 0x1E5D32C6,
+    0x120CA53, 0xC84F41A, 0xA4ADAE5, 0x104B3A45, 0x76F726D, 0x1512B772, 0x3D5DEA0, 0x194E3316,
+    0x1FF39D49, 0x3684D,
+];
+
+pub const MODBYTES: usize = 64;
+pub const BASEBITS: usize = 29;
+
+pub const MODBITS: usize = 512;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 10;
+
+pub const CURVETYPE: CurveType = CurveType::EDWARDS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 64;
+pub const AESKEY: usize = 32;
diff --git a/src/roms/rom_nums512e_64.rs b/src/roms/rom_nums512e_64.rs
new file mode 100644
index 0000000..298f3cc
--- /dev/null
+++ b/src/roms/rom_nums512e_64.rs
@@ -0,0 +1,104 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use nums512e::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 60
+// nums512 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0xFFFFFFFFFFFFDC7,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [0x100000000000000, 0x4F0B, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const MCONST: Chunk = 0x239;
+
+// nums512e Curve
+pub const CURVE_COF_I: isize = 4;
+pub const CURVE_A: isize = 1;
+pub const CURVE_B_I: isize = -78296;
+pub const CURVE_COF: [Chunk; NLEN] = [0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0xFFFFFFFFFFECBEF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFF,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x7468CF51BEED46D,
+    0x4605786DEFECFF6,
+    0xFD8C970B686F52A,
+    0x636D2FCF91BA9E3,
+    0xFFFFFFFFFFFB4F0,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0x3FFFFFFF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x5B9AB2999EC57FE,
+    0xE525427CC4F015C,
+    0xDC992568904AD0F,
+    0xC14EEE46730F78B,
+    0xEBE273B81474621,
+    0x9F4DC4A38227A17,
+    0x888D3C5332FD1E7,
+    0x128DB69C7A18CB7,
+    0xDF8E316D,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x26DDEC0C1E2F5E1,
+    0x66D38A9BF1D01F3,
+    0xA06862AECC1FD02,
+    0x53F2E9963562601,
+    0xB95909E834120CA,
+    0x26D8259D22A92B6,
+    0x7A82A256EE476F7,
+    0x9D49CA7198B0F57,
+    0x6D09BFF3,
+];
+
+pub const MODBYTES: usize = 64;
+pub const BASEBITS: usize = 60;
+
+pub const MODBITS: usize = 512;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 28;
+
+pub const CURVETYPE: CurveType = CurveType::EDWARDS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 64;
+pub const AESKEY: usize = 32;
diff --git a/src/roms/rom_nums512w_32.rs b/src/roms/rom_nums512w_32.rs
new file mode 100644
index 0000000..785070c
--- /dev/null
+++ b/src/roms/rom_nums512w_32.rs
@@ -0,0 +1,77 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use nums512w::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 29
+// nums512 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0x1FFFFDC7, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x7FFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0xB100000, 0x278, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    0x0,
+];
+pub const MCONST: Chunk = 0x239;
+
+// nums512w Curve
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = -3;
+pub const CURVE_B_I: isize = 121243;
+pub const CURVE_COF: [Chunk; NLEN] = [
+    0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_B: [Chunk; NLEN] = [
+    0x1D99B, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x433555D, 0x10A9F9C8, 0x1F3490F3, 0xD166CC0, 0xBDC63B5, 0xC76CBE8, 0xC6D3F09, 0x1F729CF0,
+    0x1F5B3CA4, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF, 0x1FFFFFFF,
+    0x1FFFFFFF, 0x7FFFF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0xCABAE57, 0x4143CAC, 0x1BD778B7, 0x1AC026FA, 0x15831D5, 0x14312AB, 0x167A4DE5, 0xA20ED66,
+    0x195021A1, 0x129836CF, 0x1141B830, 0xA03ED0A, 0xCAD83BB, 0x1E9DA94C, 0xDC00A80, 0x1527B45,
+    0x1447141D, 0x1D601,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x183527A6, 0x1D043B01, 0x1F43FA48, 0x16B83C99, 0x5602CF2, 0x1420592D, 0x17A70486, 0x1B5161DD,
+    0x14A28415, 0x3DE8A78, 0x3D2C983, 0x17797719, 0x197DBDEA, 0x15D88025, 0x1BBB718F, 0xAD679C1,
+    0x14CA29AD, 0x4A1D2,
+];
+
+pub const MODBYTES: usize = 64;
+pub const BASEBITS: usize = 29;
+
+pub const MODBITS: usize = 512;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 10;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 64;
+pub const AESKEY: usize = 32;
diff --git a/src/roms/rom_nums512w_64.rs b/src/roms/rom_nums512w_64.rs
new file mode 100644
index 0000000..6868c87
--- /dev/null
+++ b/src/roms/rom_nums512w_64.rs
@@ -0,0 +1,94 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use nums512w::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 60
+// nums512 Modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0xFFFFFFFFFFFFDC7,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [0x100000000000000, 0x4F0B, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const MCONST: Chunk = 0x239;
+
+// nums512w Curve
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_A: isize = -3;
+pub const CURVE_B_I: isize = 121243;
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B: [Chunk; NLEN] = [0x1D99B, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0xE153F390433555D,
+    0x568B36607CD243C,
+    0x258ED97D0BDC63B,
+    0xA4FB94E7831B4FC,
+    0xFFFFFFFFFFF5B3C,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFFF,
+    0xFFFFFFFF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0xC8287958CABAE57,
+    0x5D60137D6F5DE2D,
+    0x94286255615831D,
+    0xA151076B359E937,
+    0xC25306D9F95021,
+    0x3BB501F6854506E,
+    0x2A03D3B5298CAD8,
+    0x141D0A93DA2B700,
+    0x3AC03447,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x3A08760383527A6,
+    0x2B5C1E4CFD0FE92,
+    0x1A840B25A5602CF,
+    0x15DA8B0EEDE9C12,
+    0x60C7BD14F14A284,
+    0xDEABBCBB8C8F4B2,
+    0xC63EBB1004B97DB,
+    0x29AD56B3CE0EEED,
+    0x943A54CA,
+];
+
+pub const MODBYTES: usize = 64;
+pub const BASEBITS: usize = 60;
+
+pub const MODBITS: usize = 512;
+pub const MOD8: usize = 3;
+pub const MODTYPE: ModType = ModType::PSEUDO_MERSENNE;
+pub const SH: usize = 28;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 64;
+pub const AESKEY: usize = 32;
diff --git a/src/roms/rom_rsa2048_32.rs b/src/roms/rom_rsa2048_32.rs
new file mode 100644
index 0000000..6dd06a6
--- /dev/null
+++ b/src/roms/rom_rsa2048_32.rs
@@ -0,0 +1,3 @@
+pub const MODBYTES: usize = 128;
+pub const BASEBITS: usize = 28;
+pub const FFLEN: usize = 2;
\ No newline at end of file
diff --git a/src/roms/rom_rsa2048_64.rs b/src/roms/rom_rsa2048_64.rs
new file mode 100644
index 0000000..be3ba0d
--- /dev/null
+++ b/src/roms/rom_rsa2048_64.rs
@@ -0,0 +1,3 @@
+pub const MODBYTES: usize = 128;
+pub const BASEBITS: usize = 58;
+pub const FFLEN: usize = 2;
\ No newline at end of file
diff --git a/src/roms/rom_rsa3072_32.rs b/src/roms/rom_rsa3072_32.rs
new file mode 100644
index 0000000..d5622e2
--- /dev/null
+++ b/src/roms/rom_rsa3072_32.rs
@@ -0,0 +1,3 @@
+pub const MODBYTES: usize = 48;
+pub const BASEBITS: usize = 28;
+pub const FFLEN: usize = 8;
\ No newline at end of file
diff --git a/src/roms/rom_rsa3072_64.rs b/src/roms/rom_rsa3072_64.rs
new file mode 100644
index 0000000..4d1ed2b
--- /dev/null
+++ b/src/roms/rom_rsa3072_64.rs
@@ -0,0 +1,3 @@
+pub const MODBYTES: usize = 48;
+pub const BASEBITS: usize = 58;
+pub const FFLEN: usize = 8;
\ No newline at end of file
diff --git a/src/roms/rom_rsa4096_32.rs b/src/roms/rom_rsa4096_32.rs
new file mode 100644
index 0000000..223cb7d
--- /dev/null
+++ b/src/roms/rom_rsa4096_32.rs
@@ -0,0 +1,3 @@
+pub const MODBYTES: usize = 64;
+pub const BASEBITS: usize = 29;
+pub const FFLEN: usize = 8;
\ No newline at end of file
diff --git a/src/roms/rom_rsa4096_64.rs b/src/roms/rom_rsa4096_64.rs
new file mode 100644
index 0000000..77d3eb6
--- /dev/null
+++ b/src/roms/rom_rsa4096_64.rs
@@ -0,0 +1,3 @@
+pub const MODBYTES: usize = 64;
+pub const BASEBITS: usize = 60;
+pub const FFLEN: usize = 8;
\ No newline at end of file
diff --git a/src/roms/rom_secp256k1_32.rs b/src/roms/rom_secp256k1_32.rs
new file mode 100644
index 0000000..94bbbaa
--- /dev/null
+++ b/src/roms/rom_secp256k1_32.rs
@@ -0,0 +1,70 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+use secp256k1::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 28
+// secp256k1 modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0xFFFFC2F, 0xFFFFFEF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF,
+    0xFFFFFFF, 0xF,
+];
+pub const R2MODP: [Chunk; NLEN] = [
+    0x0, 0xA100000, 0x2000E90, 0x7A, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
+];
+pub const MCONST: Chunk = 0x2253531;
+
+// secp256k1 curve
+pub const CURVE_A: isize = 0;
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B_I: isize = 7;
+pub const CURVE_B: [Chunk; NLEN] = [0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0x364141, 0xD25E8CD, 0x8A03BBF, 0xDCE6AF4, 0xFFEBAAE, 0xFFFFFFF, 0xFFFFFFF, 0xFFFFFFF,
+    0xFFFFFFF, 0xF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0x6F81798, 0xF2815B1, 0xE28D959, 0xFCDB2DC, 0xB07029B, 0x95CE870, 0xC55A062, 0xF9DCBBA,
+    0x9BE667E, 0x7,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0xB10D4B8, 0x47D08FF, 0x554199C, 0xB448A68, 0x8A8FD17, 0xFC0E110, 0x55DA4FB, 0x26A3C46,
+    0x83ADA77, 0x4,
+];
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 28;
+
+pub const MODBITS: usize = 256;
+pub const MOD8: usize = 7;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 14;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/roms/rom_secp256k1_64.rs b/src/roms/rom_secp256k1_64.rs
new file mode 100644
index 0000000..b22d875
--- /dev/null
+++ b/src/roms/rom_secp256k1_64.rs
@@ -0,0 +1,81 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/* Fixed Data in ROM - Field and Curve parameters */
+
+use secp256k1::big::NLEN;
+use super::super::arch::Chunk;
+use types::{ModType, CurveType, CurvePairingType, SexticTwist, SignOfX};
+
+// Base Bits= 56
+
+// secp256k1 modulus
+pub const MODULUS: [Chunk; NLEN] = [
+    0xFFFFFEFFFFFC2F,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFF,
+];
+pub const R2MODP: [Chunk; NLEN] = [0xA1000000000000, 0x7A2000E90, 0x1, 0x0, 0x0];
+pub const MCONST: Chunk = 0x38091DD2253531;
+
+// secp256k1 curve
+pub const CURVE_A: isize = 0;
+pub const CURVE_COF_I: isize = 1;
+pub const CURVE_COF: [Chunk; NLEN] = [0x1, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_B_I: isize = 7;
+pub const CURVE_B: [Chunk; NLEN] = [0x7, 0x0, 0x0, 0x0, 0x0];
+pub const CURVE_ORDER: [Chunk; NLEN] = [
+    0xD25E8CD0364141,
+    0xDCE6AF48A03BBF,
+    0xFFFFFFFFFEBAAE,
+    0xFFFFFFFFFFFFFF,
+    0xFFFFFFFF,
+];
+pub const CURVE_GX: [Chunk; NLEN] = [
+    0xF2815B16F81798,
+    0xFCDB2DCE28D959,
+    0x95CE870B07029B,
+    0xF9DCBBAC55A062,
+    0x79BE667E,
+];
+pub const CURVE_GY: [Chunk; NLEN] = [
+    0x47D08FFB10D4B8,
+    0xB448A68554199C,
+    0xFC0E1108A8FD17,
+    0x26A3C4655DA4FB,
+    0x483ADA77,
+];
+
+pub const MODBYTES: usize = 32;
+pub const BASEBITS: usize = 56;
+
+pub const MODBITS: usize = 256;
+pub const MOD8: usize = 7;
+pub const MODTYPE: ModType = ModType::NOT_SPECIAL;
+pub const SH: usize = 24;
+
+pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
+pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
+pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
+pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const HASH_TYPE: usize = 32;
+pub const AESKEY: usize = 16;
diff --git a/src/rsa.rs b/src/rsa.rs
new file mode 100644
index 0000000..1a59f7a
--- /dev/null
+++ b/src/rsa.rs
@@ -0,0 +1,469 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+use super::big;
+use super::ff;
+use super::ff::FF;
+
+use crate::hash256::HASH256;
+use crate::hash384::HASH384;
+use crate::hash512::HASH512;
+use crate::rand::RAND;
+
+pub const RFS: usize = (big::MODBYTES as usize) * ff::FFLEN;
+pub const SHA256: usize = 32;
+pub const SHA384: usize = 48;
+pub const SHA512: usize = 64;
+
+pub const HASH_TYPE: usize = SHA256;
+
+pub struct RsaPrivateKey {
+    p: FF,
+    q: FF,
+    dp: FF,
+    dq: FF,
+    c: FF,
+}
+
+pub struct RsaPublicKey {
+    e: isize,
+    n: FF,
+}
+
+pub fn new_private_key(n: usize) -> RsaPrivateKey {
+    RsaPrivateKey {
+        p: FF::new_int(n),
+        q: FF::new_int(n),
+        dp: FF::new_int(n),
+        dq: FF::new_int(n),
+        c: FF::new_int(n),
+    }
+}
+
+pub fn new_public_key(m: usize) -> RsaPublicKey {
+    RsaPublicKey {
+        e: 0,
+        n: FF::new_int(m),
+    }
+}
+
+fn hashit(sha: usize, a: Option<&[u8]>, n: isize, w: &mut [u8]) {
+    if sha == SHA256 {
+        let mut h = HASH256::new();
+        if let Some(x) = a {
+            h.process_array(x);
+        }
+        if n >= 0 {
+            h.process_num(n as i32)
+        }
+        let hs = h.hash();
+        for i in 0..sha {
+            w[i] = hs[i]
+        }
+    }
+    if sha == SHA384 {
+        let mut h = HASH384::new();
+        if let Some(x) = a {
+            h.process_array(x);
+        }
+        if n >= 0 {
+            h.process_num(n as i32)
+        }
+        let hs = h.hash();
+        for i in 0..sha {
+            w[i] = hs[i]
+        }
+    }
+    if sha == SHA512 {
+        let mut h = HASH512::new();
+        if let Some(x) = a {
+            h.process_array(x);
+        }
+        if n >= 0 {
+            h.process_num(n as i32)
+        }
+        let hs = h.hash();
+        for i in 0..sha {
+            w[i] = hs[i]
+        }
+    }
+}
+
+pub fn key_pair(rng: &mut RAND, e: isize, prv: &mut RsaPrivateKey, pbc: &mut RsaPublicKey) {
+    /* IEEE1363 A16.11/A16.12 more or less */
+    let n = pbc.n.getlen() / 2;
+    let mut t = FF::new_int(n);
+    let mut p1 = FF::new_int(n);
+    let mut q1 = FF::new_int(n);
+
+    loop {
+        prv.p.random(rng);
+        while prv.p.lastbits(2) != 3 {
+            prv.p.inc(1)
+        }
+        while !FF::prime(&prv.p, rng) {
+            prv.p.inc(4);
+        }
+
+        p1.copy(&prv.p);
+        p1.dec(1);
+
+        if p1.cfactor(e) {
+            continue;
+        }
+        break;
+    }
+
+    loop {
+        prv.q.random(rng);
+        while prv.q.lastbits(2) != 3 {
+            prv.q.inc(1)
+        }
+        while !FF::prime(&prv.q, rng) {
+            prv.q.inc(4);
+        }
+
+        q1.copy(&prv.q);
+        q1.dec(1);
+
+        if q1.cfactor(e) {
+            continue;
+        }
+
+        break;
+    }
+
+    pbc.n = FF::mul(&prv.p, &prv.q);
+    pbc.e = e;
+
+    t.copy(&p1);
+    t.shr();
+    prv.dp.set(e);
+    prv.dp.invmodp(&t);
+    if prv.dp.parity() == 0 {
+        prv.dp.add(&t)
+    }
+    prv.dp.norm();
+
+    t.copy(&q1);
+    t.shr();
+    prv.dq.set(e);
+    prv.dq.invmodp(&t);
+    if prv.dq.parity() == 0 {
+        prv.dq.add(&t)
+    }
+    prv.dq.norm();
+
+    prv.c.copy(&prv.p);
+    prv.c.invmodp(&prv.q);
+}
+
+/* Mask Generation Function */
+
+pub fn mgf1(sha: usize, z: &[u8], olen: usize, k: &mut [u8]) {
+    let hlen = sha;
+
+    let mut j = 0;
+    for i in 0..k.len() {
+        k[i] = 0
+    }
+
+    let mut cthreshold = olen / hlen;
+    if olen % hlen != 0 {
+        cthreshold += 1
+    }
+    for counter in 0..cthreshold {
+        let mut b: [u8; 64] = [0; 64];
+        hashit(sha, Some(z), counter as isize, &mut b);
+
+        if j + hlen > olen {
+            for i in 0..(olen % hlen) {
+                k[j] = b[i];
+                j += 1
+            }
+        } else {
+            for i in 0..hlen {
+                k[j] = b[i];
+                j += 1
+            }
+        }
+    }
+}
+
+/* SHAXXX identifier strings */
+const SHA256ID: [u8; 19] = [
+    0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
+    0x00, 0x04, 0x20,
+];
+const SHA384ID: [u8; 19] = [
+    0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
+    0x00, 0x04, 0x30,
+];
+const SHA512ID: [u8; 19] = [
+    0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
+    0x00, 0x04, 0x40,
+];
+
+pub fn pkcs15(sha: usize, m: &[u8], w: &mut [u8]) -> bool {
+    let olen = ff::FF_BITS / 8;
+    let hlen = sha;
+    let idlen = 19;
+    let mut b: [u8; 64] = [0; 64]; /* Not good */
+
+    if olen < idlen + hlen + 10 {
+        return false;
+    }
+    hashit(sha, Some(m), -1, &mut b);
+
+    for i in 0..w.len() {
+        w[i] = 0
+    }
+    let mut i = 0;
+    w[i] = 0;
+    i += 1;
+    w[i] = 1;
+    i += 1;
+    for _ in 0..olen - idlen - hlen - 3 {
+        w[i] = 0xff;
+        i += 1
+    }
+    w[i] = 0;
+    i += 1;
+
+    if hlen == SHA256 {
+        for j in 0..idlen {
+            w[i] = SHA256ID[j];
+            i += 1
+        }
+    }
+    if hlen == SHA384 {
+        for j in 0..idlen {
+            w[i] = SHA384ID[j];
+            i += 1
+        }
+    }
+    if hlen == SHA512 {
+        for j in 0..idlen {
+            w[i] = SHA512ID[j];
+            i += 1
+        }
+    }
+    for j in 0..hlen {
+        w[i] = b[j];
+        i += 1
+    }
+
+    return true;
+}
+
+/* OAEP Message Encoding for Encryption */
+pub fn oaep_encode(sha: usize, m: &[u8], rng: &mut RAND, p: Option<&[u8]>, f: &mut [u8]) -> bool {
+    let olen = RFS - 1;
+    let mlen = m.len();
+
+    let hlen = sha;
+
+    let mut seed: [u8; 64] = [0; 64];
+
+    let seedlen = hlen;
+    if mlen > olen - hlen - seedlen - 1 {
+        return false;
+    }
+
+    let mut dbmask: [u8; RFS] = [0; RFS];
+
+    hashit(sha, p, -1, f);
+    let slen = olen - mlen - hlen - seedlen - 1;
+
+    for i in 0..slen {
+        f[hlen + i] = 0
+    }
+    f[hlen + slen] = 1;
+    for i in 0..mlen {
+        f[hlen + slen + 1 + i] = m[i]
+    }
+
+    for i in 0..seedlen {
+        seed[i] = rng.getbyte()
+    }
+
+    mgf1(sha, &seed, olen - seedlen, &mut dbmask);
+
+    for i in 0..olen - seedlen {
+        dbmask[i] ^= f[i]
+    }
+
+    mgf1(sha, &dbmask[0..olen - seedlen], seedlen, f);
+
+    for i in 0..seedlen {
+        f[i] ^= seed[i]
+    }
+
+    for i in 0..olen - seedlen {
+        f[i + seedlen] = dbmask[i]
+    }
+
+    /* pad to length RFS */
+    let d = 1;
+    for i in (d..RFS).rev() {
+        f[i] = f[i - d];
+    }
+    for i in (0..d).rev() {
+        f[i] = 0;
+    }
+    return true;
+}
+
+/* OAEP Message Decoding for Decryption */
+pub fn oaep_decode(sha: usize, p: Option<&[u8]>, f: &mut [u8]) -> usize {
+    let olen = RFS - 1;
+
+    let hlen = sha;
+    let mut seed: [u8; 64] = [0; 64];
+    let seedlen = hlen;
+    let mut chash: [u8; 64] = [0; 64];
+
+    if olen < seedlen + hlen + 1 {
+        return 0;
+    }
+    let mut dbmask: [u8; RFS] = [0; RFS];
+    //for i in 0..olen-seedlen {dbmask[i]=0}
+
+    if f.len() < RFS {
+        let d = RFS - f.len();
+        for i in (d..RFS).rev() {
+            f[i] = f[i - d];
+        }
+        for i in (0..d).rev() {
+            f[i] = 0;
+        }
+    }
+
+    hashit(sha, p, -1, &mut chash);
+
+    let x = f[0];
+
+    for i in seedlen..olen {
+        dbmask[i - seedlen] = f[i + 1];
+    }
+
+    mgf1(sha, &dbmask[0..olen - seedlen], seedlen, &mut seed);
+    for i in 0..seedlen {
+        seed[i] ^= f[i + 1]
+    }
+    mgf1(sha, &seed, olen - seedlen, f);
+    for i in 0..olen - seedlen {
+        dbmask[i] ^= f[i]
+    }
+
+    let mut comp = true;
+    for i in 0..hlen {
+        if chash[i] != dbmask[i] {
+            comp = false
+        }
+    }
+
+    for i in 0..olen - seedlen - hlen {
+        dbmask[i] = dbmask[i + hlen]
+    }
+
+    for i in 0..hlen {
+        seed[i] = 0;
+        chash[i] = 0
+    }
+
+    let mut k = 0;
+    loop {
+        if k >= olen - seedlen - hlen {
+            return 0;
+        }
+        if dbmask[k] != 0 {
+            break;
+        }
+        k += 1;
+    }
+
+    let t = dbmask[k];
+    if !comp || x != 0 || t != 0x01 {
+        for i in 0..olen - seedlen {
+            dbmask[i] = 0
+        }
+        return 0;
+    }
+
+    for i in 0..olen - seedlen - hlen - k - 1 {
+        f[i] = dbmask[i + k + 1];
+    }
+
+    for i in 0..olen - seedlen {
+        dbmask[i] = 0
+    }
+
+    return olen - seedlen - hlen - k - 1;
+}
+
+/* destroy the Private Key structure */
+pub fn private_key_kill(prv: &mut RsaPrivateKey) {
+    prv.p.zero();
+    prv.q.zero();
+    prv.dp.zero();
+    prv.dq.zero();
+    prv.c.zero();
+}
+
+/* RSA encryption with the public key */
+pub fn encrypt(pbc: &RsaPublicKey, f: &[u8], g: &mut [u8]) {
+    let m = pbc.n.getlen();
+    let mut r = FF::new_int(m);
+
+    FF::frombytes(&mut r, f);
+    r.power(pbc.e, &pbc.n);
+    r.tobytes(g);
+}
+
+/* RSA decryption with the private key */
+pub fn decrypt(prv: &RsaPrivateKey, g: &[u8], f: &mut [u8]) {
+    let n = prv.p.getlen();
+    let mut r = FF::new_int(2 * n);
+
+    FF::frombytes(&mut r, g);
+    let mut jp = r.dmod(&prv.p);
+    let mut jq = r.dmod(&prv.q);
+
+    jp.skpow(&prv.dp, &prv.p);
+    jq.skpow(&prv.dq, &prv.q);
+
+    r.zero();
+    r.dscopy(&jp);
+    jp.rmod(&prv.q);
+    if FF::comp(&jp, &jq) > 0 {
+        jq.add(&prv.q)
+    }
+    jq.sub(&jp);
+    jq.norm();
+
+    let mut t = FF::mul(&prv.c, &jq);
+    jq = t.dmod(&prv.q);
+
+    t = FF::mul(&jq, &prv.p);
+    r.add(&t);
+    r.norm();
+
+    r.tobytes(f);
+}
diff --git a/src/sha3.rs b/src/sha3.rs
new file mode 100644
index 0000000..eeaf953
--- /dev/null
+++ b/src/sha3.rs
@@ -0,0 +1,270 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+pub const HASH224: usize = 28;
+pub const HASH256: usize = 32;
+pub const HASH384: usize = 48;
+pub const HASH512: usize = 64;
+pub const SHAKE128: usize = 16;
+pub const SHAKE256: usize = 32;
+
+const ROUNDS: usize = 24;
+
+const RC: [u64; 24] = [
+    0x0000000000000001,
+    0x0000000000008082,
+    0x800000000000808A,
+    0x8000000080008000,
+    0x000000000000808B,
+    0x0000000080000001,
+    0x8000000080008081,
+    0x8000000000008009,
+    0x000000000000008A,
+    0x0000000000000088,
+    0x0000000080008009,
+    0x000000008000000A,
+    0x000000008000808B,
+    0x800000000000008B,
+    0x8000000000008089,
+    0x8000000000008003,
+    0x8000000000008002,
+    0x8000000000000080,
+    0x000000000000800A,
+    0x800000008000000A,
+    0x8000000080008081,
+    0x8000000000008080,
+    0x0000000080000001,
+    0x8000000080008008,
+];
+
+pub struct SHA3 {
+    length: u64,
+    rate: usize,
+    len: usize,
+    s: [[u64; 5]; 5],
+}
+
+impl SHA3 {
+    fn rotl(x: u64, n: u64) -> u64 {
+        return ((x) << n) | ((x) >> (64 - n));
+    }
+
+    fn transform(&mut self) {
+        /* basic transformation step */
+        let mut c: [u64; 5] = [0; 5];
+        let mut d: [u64; 5] = [0; 5];
+        let mut b: [[u64; 5]; 5] = [[0; 5]; 5];
+
+        for k in 0..ROUNDS {
+            c[0] = self.s[0][0] ^ self.s[0][1] ^ self.s[0][2] ^ self.s[0][3] ^ self.s[0][4];
+            c[1] = self.s[1][0] ^ self.s[1][1] ^ self.s[1][2] ^ self.s[1][3] ^ self.s[1][4];
+            c[2] = self.s[2][0] ^ self.s[2][1] ^ self.s[2][2] ^ self.s[2][3] ^ self.s[2][4];
+            c[3] = self.s[3][0] ^ self.s[3][1] ^ self.s[3][2] ^ self.s[3][3] ^ self.s[3][4];
+            c[4] = self.s[4][0] ^ self.s[4][1] ^ self.s[4][2] ^ self.s[4][3] ^ self.s[4][4];
+
+            d[0] = c[4] ^ SHA3::rotl(c[1], 1);
+            d[1] = c[0] ^ SHA3::rotl(c[2], 1);
+            d[2] = c[1] ^ SHA3::rotl(c[3], 1);
+            d[3] = c[2] ^ SHA3::rotl(c[4], 1);
+            d[4] = c[3] ^ SHA3::rotl(c[0], 1);
+
+            for i in 0..5 {
+                for j in 0..5 {
+                    self.s[i][j] ^= d[i];
+                }
+            }
+
+            b[0][0] = self.s[0][0];
+            b[1][3] = SHA3::rotl(self.s[0][1], 36);
+            b[2][1] = SHA3::rotl(self.s[0][2], 3);
+            b[3][4] = SHA3::rotl(self.s[0][3], 41);
+            b[4][2] = SHA3::rotl(self.s[0][4], 18);
+
+            b[0][2] = SHA3::rotl(self.s[1][0], 1);
+            b[1][0] = SHA3::rotl(self.s[1][1], 44);
+            b[2][3] = SHA3::rotl(self.s[1][2], 10);
+            b[3][1] = SHA3::rotl(self.s[1][3], 45);
+            b[4][4] = SHA3::rotl(self.s[1][4], 2);
+
+            b[0][4] = SHA3::rotl(self.s[2][0], 62);
+            b[1][2] = SHA3::rotl(self.s[2][1], 6);
+            b[2][0] = SHA3::rotl(self.s[2][2], 43);
+            b[3][3] = SHA3::rotl(self.s[2][3], 15);
+            b[4][1] = SHA3::rotl(self.s[2][4], 61);
+
+            b[0][1] = SHA3::rotl(self.s[3][0], 28);
+            b[1][4] = SHA3::rotl(self.s[3][1], 55);
+            b[2][2] = SHA3::rotl(self.s[3][2], 25);
+            b[3][0] = SHA3::rotl(self.s[3][3], 21);
+            b[4][3] = SHA3::rotl(self.s[3][4], 56);
+
+            b[0][3] = SHA3::rotl(self.s[4][0], 27);
+            b[1][1] = SHA3::rotl(self.s[4][1], 20);
+            b[2][4] = SHA3::rotl(self.s[4][2], 39);
+            b[3][2] = SHA3::rotl(self.s[4][3], 8);
+            b[4][0] = SHA3::rotl(self.s[4][4], 14);
+
+            for i in 0..5 {
+                for j in 0..5 {
+                    self.s[i][j] = b[i][j] ^ (!b[(i + 1) % 5][j] & b[(i + 2) % 5][j]);
+                }
+            }
+
+            self.s[0][0] ^= RC[k];
+        }
+    }
+
+    /* Initialise Hash function */
+    pub fn init(&mut self, olen: usize) {
+        /* initialise */
+        for i in 0..5 {
+            for j in 0..5 {
+                self.s[i][j] = 0;
+            }
+        }
+        self.length = 0;
+        self.len = olen;
+        self.rate = 200 - 2 * olen;
+    }
+
+    pub fn new(olen: usize) -> SHA3 {
+        let mut nh = SHA3 {
+            length: 0,
+            rate: 0,
+            len: 0,
+            s: [[0; 5]; 5],
+        };
+        nh.init(olen);
+        return nh;
+    }
+
+    /* process a single byte */
+    pub fn process(&mut self, byt: u8) {
+        /* process the next message byte */
+        let cnt = (self.length % (self.rate as u64)) as usize;
+        let b = cnt % 8;
+        let ind = cnt / 8;
+        let i = ind % 5;
+        let j = ind / 5;
+        self.s[i][j] ^= ((byt & 0xff) as u64) << (8 * b);
+        self.length += 1;
+        if cnt + 1 == self.rate {
+            self.transform();
+        }
+    }
+
+    pub fn squeeze(&mut self, buff: &mut [u8], olen: usize) {
+        //let olen=buff.len();
+        let mut done = false;
+        let mut m = 0;
+        loop {
+            for j in 0..5 {
+                for i in 0..5 {
+                    let mut el = self.s[i][j];
+                    for _ in 0..8 {
+                        buff[m] = (el & 0xff) as u8;
+                        m += 1;
+                        if m >= olen || (m % self.rate) == 0 {
+                            done = true;
+                            break;
+                        }
+                        el >>= 8;
+                    }
+                    if done {
+                        break;
+                    }
+                }
+                if done {
+                    break;
+                }
+            }
+            if m >= olen {
+                break;
+            }
+            done = false;
+            self.transform();
+        }
+    }
+
+    /* Generate 32-byte Hash */
+    pub fn hash(&mut self, digest: &mut [u8]) {
+        /* pad message and finish - supply digest */
+        let q = self.rate - (self.length % (self.rate as u64)) as usize;
+        if q == 1 {
+            self.process(0x86);
+        } else {
+            self.process(0x06);
+            while (self.length % (self.rate as u64)) as usize != self.rate - 1 {
+                self.process(0x00)
+            }
+            self.process(0x80);
+        }
+        let hlen = self.len as usize;
+        self.squeeze(digest, hlen);
+    }
+
+    pub fn shake(&mut self, digest: &mut [u8], olen: usize) {
+        let q = self.rate - (self.length % (self.rate as u64)) as usize;
+        if q == 1 {
+            self.process(0x9f);
+        } else {
+            self.process(0x1f);
+            while (self.length % (self.rate as u64)) as usize != self.rate - 1 {
+                self.process(0x00)
+            }
+            self.process(0x80);
+        }
+        self.squeeze(digest, olen);
+    }
+}
+
+//916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18
+//afebb2ef542e6579c50cad06d2e578f9f8dd6881d7dc824d26360feebf18a4fa73e3261122948efcfd492e74e82e2189ed0fb440d187f382270cb455f21dd185
+//98be04516c04cc73593fef3ed0352ea9f6443942d6950e29a372a681c3deaf4535423709b02843948684e029010badcc0acd8303fc85fdad3eabf4f78cae165635f57afd28810fc2
+/*
+fn main() {
+    let s = String::from("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
+    let mut digest: [u8;100]=[0;100];
+    let test = s.into_bytes();
+
+    let mut sh=SHA3::new(HASH256);
+    for i in 0..test.len(){
+        sh.process(test[i]);
+    }
+    sh.hash(&mut digest);
+    for i in 0..32 {print!("{:02x}",digest[i])}
+    println!("");
+
+    sh=SHA3::new(HASH512);
+    for i in 0..test.len(){
+        sh.process(test[i]);
+    }
+    sh.hash(&mut digest);
+    for i in 0..64 {print!("{:02x}",digest[i])}
+    println!("");
+
+    sh=SHA3::new(SHAKE256);
+    for i in 0..test.len(){
+        sh.process(test[i]);
+    }
+    sh.shake(&mut digest,72);
+    for i in 0..72 {print!("{:02x}",digest[i])}
+    println!("");
+
+} */
diff --git a/src/types.rs b/src/types.rs
new file mode 100644
index 0000000..ea310d7
--- /dev/null
+++ b/src/types.rs
@@ -0,0 +1,45 @@
+#[derive(PartialEq)]
+pub enum ModType {
+    NOT_SPECIAL,
+    PSEUDO_MERSENNE,
+    MONTGOMERY_FRIENDLY,
+    GENERALISED_MERSENNE,
+}
+
+#[derive(PartialEq)]
+pub enum CurveType {
+    EDWARDS,
+    WEIERSTRASS,
+    MONTGOMERY,
+}
+
+#[derive(PartialEq)]
+pub enum CurvePairingType {
+    NOT,
+    BN,
+    BLS,
+}
+
+#[derive(PartialEq)]
+pub enum SexticTwist {
+    NOT,
+    D_TYPE,
+    M_TYPE,
+}
+impl Into<usize> for SexticTwist {
+    fn into(self) -> usize {
+        match self {
+            SexticTwist::NOT => 0,
+            SexticTwist::D_TYPE => 0,
+            SexticTwist::M_TYPE => 1,
+        }
+    }
+}
+
+#[derive(PartialEq)]
+pub enum SignOfX {
+    NOT,
+    POSITIVEX,
+    NEGATIVEX,
+}
+