You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@milagro.apache.org by km...@apache.org on 2019/06/26 09:01:42 UTC

[incubator-milagro-crypto-rust] 01/01: initial commit

This is an automated email from the ASF dual-hosted git repository.

kmccusker pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-milagro-crypto-rust.git

commit 3948f75f9adb2ff91578af3171b5909ae99a0e85
Author: Kealan McCusker <ke...@gmail.com>
AuthorDate: Wed Jun 26 10:01:25 2019 +0100

    initial commit
---
 BenchtestALL.rs               |  957 ++++++++++++++++++++++++
 Cargo.lock                    |    6 +
 Cargo.toml                    |   46 ++
 LICENSE                       |  202 ++++++
 TestALL.rs                    | 1459 +++++++++++++++++++++++++++++++++++++
 TestBLS.rs                    |  190 +++++
 TestNHS.rs                    |   77 ++
 deploy.token                  |    1 +
 readme.md                     |   83 +++
 src/aes.rs                    |  772 ++++++++++++++++++++
 src/arch/arch32.rs            |   22 +
 src/arch/arch64.rs            |   22 +
 src/big.rs                    | 1070 +++++++++++++++++++++++++++
 src/bls.rs                    |   96 +++
 src/bls192.rs                 |   96 +++
 src/bls256.rs                 |   96 +++
 src/dbig.rs                   |  301 ++++++++
 src/ecdh.rs                   |  744 +++++++++++++++++++
 src/ecp.rs                    | 1261 ++++++++++++++++++++++++++++++++
 src/ecp2.rs                   |  784 ++++++++++++++++++++
 src/ecp4.rs                   |  866 ++++++++++++++++++++++
 src/ecp8.rs                   | 1155 +++++++++++++++++++++++++++++
 src/ff.rs                     | 1058 +++++++++++++++++++++++++++
 src/fp.rs                     |  717 ++++++++++++++++++
 src/fp12.rs                   | 1109 ++++++++++++++++++++++++++++
 src/fp16.rs                   |  602 +++++++++++++++
 src/fp2.rs                    |  407 +++++++++++
 src/fp24.rs                   | 1268 ++++++++++++++++++++++++++++++++
 src/fp4.rs                    |  697 ++++++++++++++++++
 src/fp48.rs                   | 1610 +++++++++++++++++++++++++++++++++++++++++
 src/fp8.rs                    |  701 ++++++++++++++++++
 src/gcm.rs                    |  481 ++++++++++++
 src/hash256.rs                |  216 ++++++
 src/hash384.rs                |  288 ++++++++
 src/hash512.rs                |  288 ++++++++
 src/lib.rs                    |  561 ++++++++++++++
 src/mpin.rs                   |  945 ++++++++++++++++++++++++
 src/mpin192.rs                |  960 ++++++++++++++++++++++++
 src/mpin256.rs                |  993 +++++++++++++++++++++++++
 src/nhs.rs                    |  705 ++++++++++++++++++
 src/pair.rs                   |  755 +++++++++++++++++++
 src/pair192.rs                |  606 ++++++++++++++++
 src/pair256.rs                |  722 ++++++++++++++++++
 src/rand.rs                   |  180 +++++
 src/roms/rom_anssi_32.rs      |   73 ++
 src/roms/rom_anssi_64.rs      |   91 +++
 src/roms/rom_bls24_32.rs      |  240 ++++++
 src/roms/rom_bls24_64.rs      |  288 ++++++++
 src/roms/rom_bls381_32.rs     |  209 ++++++
 src/roms/rom_bls381_64.rs     |  211 ++++++
 src/roms/rom_bls383_32.rs     |  207 ++++++
 src/roms/rom_bls383_64.rs     |  218 ++++++
 src/roms/rom_bls461_32.rs     |  209 ++++++
 src/roms/rom_bls461_64.rs     |  232 ++++++
 src/roms/rom_bls48_32.rs      |  309 ++++++++
 src/roms/rom_bls48_64.rs      |  401 ++++++++++
 src/roms/rom_bn254CX_32.rs    |  183 +++++
 src/roms/rom_bn254CX_64.rs    |  242 +++++++
 src/roms/rom_bn254_32.rs      |  171 +++++
 src/roms/rom_bn254_64.rs      |  208 ++++++
 src/roms/rom_brainpool_32.rs  |   74 ++
 src/roms/rom_brainpool_64.rs  |   92 +++
 src/roms/rom_c25519_32.rs     |   59 ++
 src/roms/rom_c25519_64.rs     |   61 ++
 src/roms/rom_c41417_32.rs     |   71 ++
 src/roms/rom_c41417_64.rs     |   78 ++
 src/roms/rom_ed25519_32.rs    |   68 ++
 src/roms/rom_ed25519_64.rs    |   79 ++
 src/roms/rom_fp256bn_32.rs    |  180 +++++
 src/roms/rom_fp256bn_64.rs    |  233 ++++++
 src/roms/rom_fp512bn_32.rs    |  249 +++++++
 src/roms/rom_fp512bn_64.rs    |  482 ++++++++++++
 src/roms/rom_goldilocks_32.rs |   73 ++
 src/roms/rom_goldilocks_64.rs |   99 +++
 src/roms/rom_hifive_32.rs     |   68 ++
 src/roms/rom_hifive_64.rs     |   77 ++
 src/roms/rom_nist256_32.rs    |   74 ++
 src/roms/rom_nist256_64.rs    |   86 +++
 src/roms/rom_nist384_32.rs    |   76 ++
 src/roms/rom_nist384_64.rs    |  104 +++
 src/roms/rom_nist521_32.rs    |   79 ++
 src/roms/rom_nist521_64.rs    |  104 +++
 src/roms/rom_nums256e_32.rs   |   71 ++
 src/roms/rom_nums256e_64.rs   |   80 ++
 src/roms/rom_nums256w_32.rs   |   66 ++
 src/roms/rom_nums256w_64.rs   |   78 ++
 src/roms/rom_nums384e_32.rs   |   74 ++
 src/roms/rom_nums384e_64.rs   |   95 +++
 src/roms/rom_nums384w_32.rs   |   74 ++
 src/roms/rom_nums384w_64.rs   |   94 +++
 src/roms/rom_nums512e_32.rs   |   79 ++
 src/roms/rom_nums512e_64.rs   |  104 +++
 src/roms/rom_nums512w_32.rs   |   77 ++
 src/roms/rom_nums512w_64.rs   |   94 +++
 src/roms/rom_rsa2048_32.rs    |    3 +
 src/roms/rom_rsa2048_64.rs    |    3 +
 src/roms/rom_rsa3072_32.rs    |    3 +
 src/roms/rom_rsa3072_64.rs    |    3 +
 src/roms/rom_rsa4096_32.rs    |    3 +
 src/roms/rom_rsa4096_64.rs    |    3 +
 src/roms/rom_secp256k1_32.rs  |   70 ++
 src/roms/rom_secp256k1_64.rs  |   81 +++
 src/rsa.rs                    |  469 ++++++++++++
 src/sha3.rs                   |  270 +++++++
 src/types.rs                  |   45 ++
 105 files changed, 34172 insertions(+)

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 <ni...@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/4Q4jUoy8CtrZu6BCxAK7muLLWaI91gWpducaJKZ6dVHZ5tBz7XHG7NFpqRssCUfIMhg [...]
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();
... 24926 lines suppressed ...