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:41 UTC

[incubator-milagro-crypto-rust] branch master created (now 3948f75)

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

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


      at 3948f75  initial commit

This branch includes the following new commits:

     new 3948f75  initial commit

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



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

Posted by km...@apache.org.
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 ...