Merge pull request #5 from apache/update-code

update code
diff --git a/LICENSE-2.0.TXT b/LICENSE-2.0.TXT
index 9b5e401..7a4a3ea 100644
--- a/LICENSE-2.0.TXT
+++ b/LICENSE-2.0.TXT
@@ -1,202 +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

+
+                                 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/NOTICE.txt b/NOTICE.txt
index 2e6c028..434d2bb 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -1,5 +1,5 @@
-Apache Milagro Crypto Libraries

-Copyright 2016 The Apache Software Foundation

-

-This product includes software developed at

-The Apache Software Foundation (http://www.apache.org/).

+Apache Milagro Crypto Libraries
+Copyright 2016 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
diff --git a/version3/amcl3.pdf b/version3/amcl3.pdf
index 3fb26e1..0908bcd 100644
--- a/version3/amcl3.pdf
+++ b/version3/amcl3.pdf
Binary files differ
diff --git a/version3/c/amcl.h b/version3/c/amcl.h
index 2558911..f72557d 100644
--- a/version3/c/amcl.h
+++ b/version3/c/amcl.h
@@ -57,6 +57,13 @@
 #define D_TYPE 0
 #define M_TYPE 1
 
+#define FP_ZERO 0
+#define FP_UNITY 1
+#define FP_SPARSER 2
+#define FP_SPARSE 3
+#define FP_DENSE 4
+
+
 /**
  * @brief SHA256 hash function instance */
 typedef struct
diff --git a/version3/c/bls.c b/version3/c/bls.c
index 6706316..388bf2f 100644
--- a/version3/c/bls.c
+++ b/version3/c/bls.c
@@ -81,8 +81,20 @@
 	ECP2_ZZZ_generator(&G);
 	ECP2_ZZZ_fromOctet(&PK,W);
 	ECP_ZZZ_neg(&D);
-    PAIR_ZZZ_double_ate(&v,&G,&D,&PK,&HM);
-    PAIR_ZZZ_fexp(&v);
+
+
+// Use new multi-pairing mechanism 
+
+	FP12_YYY r[ATE_BITS_ZZZ];
+	PAIR_ZZZ_initmp(r);
+	PAIR_ZZZ_another(r,&G,&D);
+	PAIR_ZZZ_another(r,&PK,&HM);
+	PAIR_ZZZ_miller(&v,r);
+
+//.. or alternatively
+//    PAIR_ZZZ_double_ate(&v,&G,&D,&PK,&HM);
+    
+	PAIR_ZZZ_fexp(&v);
     if (FP12_YYY_isunity(&v)) return BLS_OK;
 	return BLS_FAIL;
 }
diff --git a/version3/c/bls192.c b/version3/c/bls192.c
index 5eb8852..55d5373 100644
--- a/version3/c/bls192.c
+++ b/version3/c/bls192.c
@@ -81,7 +81,18 @@
 	ECP4_ZZZ_generator(&G);
 	ECP4_ZZZ_fromOctet(&PK,W);
 	ECP_ZZZ_neg(&D);
-    PAIR_ZZZ_double_ate(&v,&G,&D,&PK,&HM);
+
+// Use new multi-pairing mechanism 
+
+	FP24_YYY r[ATE_BITS_ZZZ];
+	PAIR_ZZZ_initmp(r);
+	PAIR_ZZZ_another(r,&G,&D);
+	PAIR_ZZZ_another(r,&PK,&HM);
+	PAIR_ZZZ_miller(&v,r);
+
+//.. or alternatively
+//    PAIR_ZZZ_double_ate(&v,&G,&D,&PK,&HM);
+
     PAIR_ZZZ_fexp(&v);
     if (FP24_YYY_isunity(&v)) return BLS_OK;
 	return BLS_FAIL;
diff --git a/version3/c/bls256.c b/version3/c/bls256.c
index 8f228b2..09db152 100644
--- a/version3/c/bls256.c
+++ b/version3/c/bls256.c
@@ -81,7 +81,18 @@
 	ECP8_ZZZ_generator(&G);
 	ECP8_ZZZ_fromOctet(&PK,W);
 	ECP_ZZZ_neg(&D);
-    PAIR_ZZZ_double_ate(&v,&G,&D,&PK,&HM);
+
+// Use new multi-pairing mechanism 
+
+	FP48_YYY r[ATE_BITS_ZZZ];
+	PAIR_ZZZ_initmp(r);
+	PAIR_ZZZ_another(r,&G,&D);
+	PAIR_ZZZ_another(r,&PK,&HM);
+	PAIR_ZZZ_miller(&v,r);
+
+//.. or alternatively
+//    PAIR_ZZZ_double_ate(&v,&G,&D,&PK,&HM);
+
     PAIR_ZZZ_fexp(&v);
     if (FP48_YYY_isunity(&v)) return BLS_OK;
 	return BLS_FAIL;
diff --git a/version3/c/config16.py b/version3/c/config16.py
index ba77919..3854184 100644
--- a/version3/c/config16.py
+++ b/version3/c/config16.py
@@ -72,7 +72,7 @@
 	replace(fnameh,"XXX",bd)
 	os.system("gcc -O3 -std=c99 -c "+fnamec)
 
-def curveset(tb,tf,tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,cs) :
+def curveset(tb,tf,tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,ab,cs) :
 	bd=tb+"_"+base
 	fnameh="config_big_"+bd+".h"
 	os.system(copytext+" config_big.h "+fnameh)
@@ -107,6 +107,8 @@
 	replace(fnameh,"@ST@",stw)
 	replace(fnameh,"@SX@",sx)
 	replace(fnameh,"@CS@",cs)
+	replace(fnameh,"@AB@",ab)
+
 
 	fnamec="big_"+bd+".c"
 	fnameh="big_"+bd+".h"
@@ -194,8 +196,10 @@
 		os.system(copytext+" fp12.h "+fnameh)
 		replace(fnamec,"YYY",tf)
 		replace(fnamec,"XXX",bd)
+		replace(fnamec,"ZZZ",tc)
 		replace(fnameh,"YYY",tf)
 		replace(fnameh,"XXX",bd)
+		replace(fnameh,"ZZZ",tc)
 		os.system("gcc -O3 -std=c99 -c "+fnamec)
 
 		fnamec="ecp2_"+tc+".c"
@@ -287,7 +291,7 @@
 	selection.append(x)
 	ptr=ptr+1
 
-# curveset(big,field,curve,big_length_bytes,bits_in_base,modulus_bits,modulus_mod_8,modulus_type,curve_type,pairing_friendly,sextic twist,sign of x)
+# curveset(big,field,curve,big_length_bytes,bits_in_base,modulus_bits,modulus_mod_8,modulus_type,curve_type,pairing_friendly,sextic twist,sign of x,ate bits,curve security)
 # for each curve give names for big, field and curve. In many cases the latter two will be the same. 
 # Typically "big" is the size in bits, always a multiple of 8, "field" describes the modulus, and "curve" is the common name for the elliptic curve   
 # big_length_bytes is "big" divided by 8
@@ -297,21 +301,22 @@
 # modulus_type is NOT_SPECIAL, or PSEUDO_MERSENNE, or MONTGOMERY_Friendly, or GENERALISED_MERSENNE (supported for GOLDILOCKS only)
 # curve_type is WEIERSTRASS, EDWARDS or MONTGOMERY
 # pairing_friendly is BN, BLS or NOT (if not pairing friendly)
+# ate bits is number of bits in Ate parameter (from romgen program)
 # if pairing friendly. M or D type twist, and sign of the family parameter x
 
 	if x==1:
-		curveset("256","25519","ED25519","32","13","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","128")
+		curveset("256","25519","ED25519","32","13","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","","128")
 		curve_selected=True
 	if x==2:
-		curveset("256","256PME","NUMS256E","32","13","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","128")
+		curveset("256","256PME","NUMS256E","32","13","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","","128")
 		curve_selected=True
 
 
 	if x==3:
-		curveset("256","BN254","BN254","32","13","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("256","BN254","BN254","32","13","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==4:
-		curveset("256","BN254CX","BN254CX","32","13","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("256","BN254CX","BN254CX","32","13","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 
 # rsaset(big,ring,big_length_bytes,bits_in_base,multiplier)
diff --git a/version3/c/config32.py b/version3/c/config32.py
index e848122..130f608 100644
--- a/version3/c/config32.py
+++ b/version3/c/config32.py
@@ -72,7 +72,7 @@
 	replace(fnameh,"XXX",bd)
 	os.system("gcc -O3 -std=c99 -c "+fnamec)
 
-def curveset(tb,tf,tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,cs) :
+def curveset(tb,tf,tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,ab,cs) :
 	bd=tb+"_"+base
 
 	fnameh="config_big_"+bd+".h"
@@ -112,6 +112,7 @@
 	replace(fnameh,"@ST@",stw)
 	replace(fnameh,"@SX@",sx)
 	replace(fnameh,"@CS@",cs)
+	replace(fnameh,"@AB@",ab)
 
 	fnamec="big_"+bd+".c"
 	fnameh="big_"+bd+".h"
@@ -201,8 +202,10 @@
 			os.system(copytext+" fp12.h "+fnameh)
 			replace(fnamec,"YYY",tf)
 			replace(fnamec,"XXX",bd)
+			replace(fnamec,"ZZZ",tc)
 			replace(fnameh,"YYY",tf)
 			replace(fnameh,"XXX",bd)
+			replace(fnameh,"ZZZ",tc)
 			os.system("gcc -O3 -std=c99 -c "+fnamec)
 
 			fnamec="ecp2_"+tc+".c"
@@ -279,8 +282,10 @@
 			os.system(copytext+" fp24.h "+fnameh)
 			replace(fnamec,"YYY",tf)
 			replace(fnamec,"XXX",bd)
+			replace(fnamec,"ZZZ",tc)
 			replace(fnameh,"YYY",tf)
 			replace(fnameh,"XXX",bd)
+			replace(fnameh,"ZZZ",tc)
 			os.system("gcc -O3 -std=c99 -c "+fnamec)
 
 			fnamec="ecp4_"+tc+".c"
@@ -386,8 +391,10 @@
 			os.system(copytext+" fp48.h "+fnameh)
 			replace(fnamec,"YYY",tf)
 			replace(fnamec,"XXX",bd)
+			replace(fnamec,"ZZZ",tc)
 			replace(fnameh,"YYY",tf)
 			replace(fnameh,"XXX",bd)
+			replace(fnameh,"ZZZ",tc)
 			os.system("gcc -O3 -std=c99 -c "+fnamec)
 
 
@@ -490,7 +497,7 @@
 	selection.append(x)
 	ptr=ptr+1
 
-# curveset(big,field,curve,big_length_bytes,bits_in_base,modulus_bits,modulus_mod_8,modulus_type,curve_type,pairing_friendly,sextic twist,sign of x,curve security)
+# curveset(big,field,curve,big_length_bytes,bits_in_base,modulus_bits,modulus_mod_8,modulus_type,curve_type,pairing_friendly,sextic twist,sign of x,ate bits,curve security)
 # for each curve give names for big, field and curve. In many cases the latter two will be the same. 
 # Typically "big" is the size in bits, always a multiple of 8, "field" describes the modulus, and "curve" is the common name for the elliptic curve   
 # big_length_bytes is "big" divided by 8
@@ -501,96 +508,97 @@
 # curve_type is WEIERSTRASS, EDWARDS or MONTGOMERY
 # pairing_friendly is BN, BLS or NOT (if not pairing friendly)
 # if pairing friendly. M or D type twist, and sign of the family parameter x
+# ate bits is number of bits in Ate parameter (from romgen program)
 # curve security is AES equiavlent, rounded up.
 
 	if x==1:
-		curveset("256","25519","ED25519","32","29","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","128")
+		curveset("256","25519","ED25519","32","29","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","","128")
 		curve_selected=True
 	if x==2:
-		curveset("256","25519","C25519","32","29","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","","","128")
+		curveset("256","25519","C25519","32","29","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","","","","128")
 		curve_selected=True
 	if x==3:
-		curveset("256","NIST256","NIST256","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","128")
+		curveset("256","NIST256","NIST256","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 	if x==4:
-		curveset("256","BRAINPOOL","BRAINPOOL","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","128")
+		curveset("256","BRAINPOOL","BRAINPOOL","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 	if x==5:
-		curveset("256","ANSSI","ANSSI","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","128")
+		curveset("256","ANSSI","ANSSI","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 
 	if x==6:
-		curveset("336","HIFIVE","HIFIVE","42","29","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","192")
+		curveset("336","HIFIVE","HIFIVE","42","29","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","","192")
 		curve_selected=True
 	if x==7:
-		curveset("448","GOLDILOCKS","GOLDILOCKS","56","29","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","","","256")
+		curveset("448","GOLDILOCKS","GOLDILOCKS","56","29","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","","","","256")
 		curve_selected=True
 	if x==8:
-		curveset("384","NIST384","NIST384","48","29","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","192")
+		curveset("384","NIST384","NIST384","48","29","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","192")
 		curve_selected=True
 	if x==9:
-		curveset("416","C41417","C41417","52","29","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","","","256")
+		curveset("416","C41417","C41417","52","29","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","","","","256")
 		curve_selected=True
 	if x==10:
-		curveset("528","NIST521","NIST521","66","28","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","256")
+		curveset("528","NIST521","NIST521","66","28","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","","256")
 		curve_selected=True
 
 	if x==11:
-		curveset("256","256PMW","NUMS256W","32","28","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","128")
+		curveset("256","256PMW","NUMS256W","32","28","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 	if x==12:
-		curveset("256","256PME","NUMS256E","32","29","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","128")
+		curveset("256","256PME","NUMS256E","32","29","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","","128")
 		curve_selected=True
 	if x==13:
-		curveset("384","384PM","NUMS384W","48","29","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","192")
+		curveset("384","384PM","NUMS384W","48","29","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","","192")
 		curve_selected=True
 	if x==14:
-		curveset("384","384PM","NUMS384E","48","29","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","192")
+		curveset("384","384PM","NUMS384E","48","29","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","","192")
 		curve_selected=True
 	if x==15:
-		curveset("512","512PM","NUMS512W","64","29","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","256")
+		curveset("512","512PM","NUMS512W","64","29","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","","256")
 		curve_selected=True
 	if x==16:
-		curveset("512","512PM","NUMS512E","64","29","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","","","256")
+		curveset("512","512PM","NUMS512E","64","29","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","","","","256")
 		curve_selected=True
 
 	if x==17:
-		curveset("256","SECP256K1","SECP256K1","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","128")
+		curveset("256","SECP256K1","SECP256K1","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 
 
 	if x==18:
-		curveset("256","BN254","BN254","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("256","BN254","BN254","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==19:
-		curveset("256","BN254CX","BN254CX","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("256","BN254CX","BN254CX","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==20:
-		curveset("384","BLS383","BLS383","48","29","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","128")
+		curveset("384","BLS383","BLS383","48","29","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","65","128")
 		pfcurve_selected=True
 
 	if x==21:
-		curveset("384","BLS381","BLS381","48","29","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128")
+		curveset("384","BLS381","BLS381","48","29","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","65","128")
 		pfcurve_selected=True
 
 	if x==22:
-		curveset("256","FP256BN","FP256BN","32","28","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","128")
+		curveset("256","FP256BN","FP256BN","32","28","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==23:
-		curveset("512","FP512BN","FP512BN","64","29","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","128")
+		curveset("512","FP512BN","FP512BN","64","29","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","130","128")
 		pfcurve_selected=True
 # https://eprint.iacr.org/2017/334.pdf
 	if x==24:
-		curveset("464","BLS461","BLS461","58","28","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128")
+		curveset("464","BLS461","BLS461","58","28","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","78","128")
 		pfcurve_selected=True
 
 	if x==25:
-		curveset("480","BLS24","BLS24","60","29","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","192")
+		curveset("480","BLS24","BLS24","60","29","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","49","192")
 		pfcurve_selected=True
 
 
 	if x==26:
-		curveset("560","BLS48","BLS48","70","29","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","256")
+		curveset("560","BLS48","BLS48","70","29","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","32","256")
 		pfcurve_selected=True
 
 
diff --git a/version3/c/config64.py b/version3/c/config64.py
index 22fc9b8..9f2ba6b 100644
--- a/version3/c/config64.py
+++ b/version3/c/config64.py
@@ -72,7 +72,7 @@
 	replace(fnameh,"XXX",bd)
 	os.system("gcc -O3 -std=c99 -c "+fnamec)
 
-def curveset(tb,tf,tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,cs) :
+def curveset(tb,tf,tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,ab,cs) :
 	bd=tb+"_"+base
 	fnameh="config_big_"+bd+".h"
 	os.system(copytext+" config_big.h "+fnameh)
@@ -107,6 +107,8 @@
 	replace(fnameh,"@ST@",stw)
 	replace(fnameh,"@SX@",sx)
 	replace(fnameh,"@CS@",cs)
+	replace(fnameh,"@AB@",ab)
+
 
 	fnamec="big_"+bd+".c"
 	fnameh="big_"+bd+".h"
@@ -195,8 +197,10 @@
 			os.system(copytext+" fp12.h "+fnameh)
 			replace(fnamec,"YYY",tf)
 			replace(fnamec,"XXX",bd)
+			replace(fnamec,"ZZZ",tc)
 			replace(fnameh,"YYY",tf)
 			replace(fnameh,"XXX",bd)
+			replace(fnameh,"ZZZ",tc)	
 			os.system("gcc -O3 -std=c99 -c "+fnamec)
 
 			fnamec="ecp2_"+tc+".c"
@@ -273,8 +277,10 @@
 			os.system(copytext+" fp24.h "+fnameh)
 			replace(fnamec,"YYY",tf)
 			replace(fnamec,"XXX",bd)
+			replace(fnamec,"ZZZ",tc)
 			replace(fnameh,"YYY",tf)
 			replace(fnameh,"XXX",bd)
+			replace(fnameh,"ZZZ",tc)
 			os.system("gcc -O3 -std=c99 -c "+fnamec)
 
 			fnamec="ecp4_"+tc+".c"
@@ -380,8 +386,10 @@
 			os.system(copytext+" fp48.h "+fnameh)
 			replace(fnamec,"YYY",tf)
 			replace(fnamec,"XXX",bd)
+			replace(fnamec,"ZZZ",tc)
 			replace(fnameh,"YYY",tf)
 			replace(fnameh,"XXX",bd)
+			replace(fnameh,"ZZZ",tc)
 			os.system("gcc -O3 -std=c99 -c "+fnamec)
 
 
@@ -484,7 +492,7 @@
 	selection.append(x)
 	ptr=ptr+1
 
-# curveset(big,field,curve,big_length_bytes,bits_in_base,modulus_bits,modulus_mod_8,modulus_type,curve_type,pairing_friendly,sextic twist,sign of x,curve security)
+# curveset(big,field,curve,big_length_bytes,bits_in_base,modulus_bits,modulus_mod_8,modulus_type,curve_type,pairing_friendly,sextic twist,sign of x,ate bits,curve security)
 # for each curve give names for big, field and curve. In many cases the latter two will be the same. 
 # Typically "big" is the size in bits, always a multiple of 8, "field" describes the modulus, and "curve" is the common name for the elliptic curve   
 # big_length_bytes is "big" divided by 8
@@ -495,96 +503,97 @@
 # curve_type is WEIERSTRASS, EDWARDS or MONTGOMERY
 # pairing_friendly is BN, BLS or NOT (if not pairing friendly)
 # if pairing friendly. M or D type twist, and sign of the family parameter x
+# ate bits is number of bits in Ate parameter (from romgen program)
 # curve security is AES equiavlent, rounded up.
 
 	if x==1:
-		curveset("256","25519","ED25519","32","56","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","128")
+		curveset("256","25519","ED25519","32","56","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","","128")
 		curve_selected=True
 	if x==2:
-		curveset("256","25519","C25519","32","56","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","","","128")
+		curveset("256","25519","C25519","32","56","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","","","","128")
 		curve_selected=True
 	if x==3:
-		curveset("256","NIST256","NIST256","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","128")
+		curveset("256","NIST256","NIST256","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 	if x==4:
-		curveset("256","BRAINPOOL","BRAINPOOL","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","128")
+		curveset("256","BRAINPOOL","BRAINPOOL","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 	if x==5:
-		curveset("256","ANSSI","ANSSI","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","128")
+		curveset("256","ANSSI","ANSSI","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 
 	if x==6:
-		curveset("336","HIFIVE","HIFIVE","42","60","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","128")
+		curveset("336","HIFIVE","HIFIVE","42","60","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","","128")
 		curve_selected=True
 	if x==7:
-		curveset("448","GOLDILOCKS","GOLDILOCKS","56","58","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","","","128")
+		curveset("448","GOLDILOCKS","GOLDILOCKS","56","58","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","","","","128")
 		curve_selected=True
 	if x==8:
-		curveset("384","NIST384","NIST384","48","56","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","128")
+		curveset("384","NIST384","NIST384","48","56","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 	if x==9:
-		curveset("416","C41417","C41417","52","60","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","","","128")
+		curveset("416","C41417","C41417","52","60","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","","","","128")
 		curve_selected=True
 	if x==10:
-		curveset("528","NIST521","NIST521","66","60","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","128")
+		curveset("528","NIST521","NIST521","66","60","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 
 	if x==11:
-		curveset("256","256PMW","NUMS256W","32","56","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","128")
+		curveset("256","256PMW","NUMS256W","32","56","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 	if x==12:
-		curveset("256","256PME","NUMS256E","32","56","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","128")
+		curveset("256","256PME","NUMS256E","32","56","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","","128")
 		curve_selected=True
 	if x==13:
-		curveset("384","384PM","NUMS384W","48","56","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","128")
+		curveset("384","384PM","NUMS384W","48","56","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 	if x==14:
-		curveset("384","384PM","NUMS384E","48","56","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","128")
+		curveset("384","384PM","NUMS384E","48","56","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","","128")
 		curve_selected=True
 	if x==15:
-		curveset("512","512PM","NUMS512W","64","56","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","128")
+		curveset("512","512PM","NUMS512W","64","56","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 	if x==16:
-		curveset("512","512PM","NUMS512E","64","56","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","","","128")
+		curveset("512","512PM","NUMS512E","64","56","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","","","","128")
 		curve_selected=True
 
 	if x==17:
-		curveset("256","SECP256K1","SECP256K1","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","128")
+		curveset("256","SECP256K1","SECP256K1","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 
 
 	if x==18:
-		curveset("256","BN254","BN254","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("256","BN254","BN254","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==19:
-		curveset("256","BN254CX","BN254CX","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("256","BN254CX","BN254CX","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==20:
-		curveset("384","BLS383","BLS383","48","58","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","128")
+		curveset("384","BLS383","BLS383","48","58","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","65","128")
 		pfcurve_selected=True
 
 	if x==21:
-		curveset("384","BLS381","BLS381","48","58","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128")
+		curveset("384","BLS381","BLS381","48","58","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","65","128")
 		pfcurve_selected=True
 
 
 	if x==22:
-		curveset("256","FP256BN","FP256BN","32","56","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","128")
+		curveset("256","FP256BN","FP256BN","32","56","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==23:
-		curveset("512","FP512BN","FP512BN","64","60","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","128")
+		curveset("512","FP512BN","FP512BN","64","60","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","130","128")
 		pfcurve_selected=True
 # https://eprint.iacr.org/2017/334.pdf
 	if x==24:
-		curveset("464","BLS461","BLS461","58","60","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128")
+		curveset("464","BLS461","BLS461","58","60","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","78","128")
 		pfcurve_selected=True
 
 	if x==25:
-		curveset("480","BLS24","BLS24","60","56","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","192")
+		curveset("480","BLS24","BLS24","60","56","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","49","192")
 		pfcurve_selected=True
 
 	if x==26:
-		curveset("560","BLS48","BLS48","70","58","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","256")
+		curveset("560","BLS48","BLS48","70","58","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","32","256")
 		pfcurve_selected=True
 
 
diff --git a/version3/c/config_curve.h b/version3/c/config_curve.h
index 58208df..458585b 100644
--- a/version3/c/config_curve.h
+++ b/version3/c/config_curve.h
@@ -22,6 +22,8 @@
 #define SEXTIC_TWIST_ZZZ @ST@
 #define SIGN_OF_X_ZZZ @SX@
 
+#define ATE_BITS_ZZZ @AB@
+
 #endif
 
 #if CURVE_SECURITY_ZZZ == 128
diff --git a/version3/c/fp12.c b/version3/c/fp12.c
index c103fb3..a6a7830 100644
--- a/version3/c/fp12.c
+++ b/version3/c/fp12.c
@@ -22,6 +22,7 @@
 /* FP12 elements are of the form a+i.b+i^2.c */
 
 #include "fp12_YYY.h"
+#include "config_curve_ZZZ.h"
 
 /* return 1 if b==c, no branching */
 static int teq(sign32 b,sign32 c)
@@ -81,6 +82,7 @@
     FP4_YYY_copy(&(w->a),&(x->a));
     FP4_YYY_copy(&(w->b),&(x->b));
     FP4_YYY_copy(&(w->c),&(x->c));
+	w->type=x->type;
 }
 
 /* FP12 w=1 */
@@ -90,6 +92,7 @@
     FP4_YYY_one(&(w->a));
     FP4_YYY_zero(&(w->b));
     FP4_YYY_zero(&(w->c));
+	w->type=FP_UNITY;
 }
 
 /* return 1 if x==y, else 0 */
@@ -118,6 +121,7 @@
     FP4_YYY_copy(&(w->a),a);
     FP4_YYY_zero(&(w->b));
     FP4_YYY_zero(&(w->c));
+	w->type=FP_SPARSER;
 }
 
 /* Create FP12 from 3 FP4's */
@@ -127,6 +131,7 @@
     FP4_YYY_copy(&(w->a),a);
     FP4_YYY_copy(&(w->b),b);
     FP4_YYY_copy(&(w->c),c);
+	w->type=FP_DENSE;
 }
 
 /* Granger-Scott Unitary Squaring. This does not benefit from lazy reduction */
@@ -167,6 +172,7 @@
     FP4_YYY_add(&(w->b),&B,&(w->b));
     FP4_YYY_add(&(w->c),&C,&(w->c));
 
+	w->type=FP_DENSE;
     FP12_YYY_reduce(w);	    /* reduce here as in pow function repeated squarings would trigger multiple reductions */
 }
 
@@ -178,6 +184,12 @@
 
     FP4_YYY A,B,C,D;
 
+	if (x->type<=FP_UNITY)
+	{
+		FP12_YYY_copy(w,x);
+		return;
+	}
+
     FP4_YYY_sqr(&A,&(x->a));
     FP4_YYY_mul(&B,&(x->b),&(x->c));
     FP4_YYY_add(&B,&B,&B);
@@ -209,6 +221,11 @@
     FP4_YYY_add(&(w->b),&C,&D);
     FP4_YYY_add(&(w->c),&(w->c),&A);
 
+	if (x->type==FP_SPARSER)
+		w->type=FP_SPARSE;
+	else
+		w->type=FP_DENSE;
+
     FP12_YYY_norm(w);
 }
 
@@ -267,100 +284,287 @@
     FP4_YYY_times_i(&z3);
     FP4_YYY_add(&(w->a),&z0,&z3);
 
+	w->type=FP_DENSE;
+    FP12_YYY_norm(w);
+}
+
+/* FP12 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+void FP12_YYY_ssmul(FP12_YYY *w,FP12_YYY *y)
+{
+	FP4_YYY z0,z1,z2,z3,t0,t1;
+	if (w->type==FP_UNITY)
+	{
+		FP12_YYY_copy(w,y);
+		return;
+	}
+	if (y->type==FP_UNITY)
+		return;
+
+	if (y->type >= FP_SPARSE)
+	{
+		FP4_YYY_mul(&z0,&(w->a),&(y->a));  // xa.ya   always 11x11
+
+#if SEXTIC_TWIST_ZZZ == M_TYPE
+		if (y->type==FP_SPARSE || w->type==FP_SPARSE)
+		{
+			FP2_YYY_mul(&z2.b,&(w->b).b,&(y->b).b);
+			FP2_YYY_zero(&z2.a);
+			if (y->type!=FP_SPARSE)
+				FP2_YYY_mul(&z2.a,&(w->b).b,&(y->b).a);
+			if (w->type!=FP_SPARSE)
+				FP2_YYY_mul(&z2.a,&(w->b).a,&(y->b).b);
+			FP4_YYY_times_i(&z2);
+		}
+		else
+#endif
+			FP4_YYY_mul(&z2,&(w->b),&(y->b));  // xb.yb  could be 00x00 or 01x01 or or 10x10 or 11x00 or 11x10 or 11x01 or 11x11 
+
+		FP4_YYY_add(&t0,&(w->a),&(w->b));  // (xa+xb)
+		FP4_YYY_add(&t1,&(y->a),&(y->b));  // (ya+yb)
+
+		FP4_YYY_norm(&t0);
+		FP4_YYY_norm(&t1);
+
+		FP4_YYY_mul(&z1,&t0,&t1); // (xa+xb)(ya+yb)  always 11x11
+		FP4_YYY_add(&t0,&(w->b),&(w->c));  // (xb+xc)
+		FP4_YYY_add(&t1,&(y->b),&(y->c));  // (yb+yc)
+
+		FP4_YYY_norm(&t0);
+		FP4_YYY_norm(&t1);
+
+		FP4_YYY_mul(&z3,&t0,&t1);	// (xb+xc)(yb+yc)   could be anything...
+		FP4_YYY_neg(&t0,&z0);		// -(xa.ya)
+		FP4_YYY_neg(&t1,&z2);		// -(xb.yb)
+
+		FP4_YYY_add(&z1,&z1,&t0);  
+		FP4_YYY_add(&(w->b),&z1,&t1); // /wb = (xa+xb)(ya+yb) -(xa.ya) -(xb.yb)						= xa.yb + xb.ya
+
+		FP4_YYY_add(&z3,&z3,&t1);        // (xb+xc)(yb+yc) -(xb.yb)
+		FP4_YYY_add(&z2,&z2,&t0);        // (xb.yb) - (xa.ya)
+
+		FP4_YYY_add(&t0,&(w->a),&(w->c));  // (xa+xc)
+		FP4_YYY_add(&t1,&(y->a),&(y->c));  // (ya+yc)
+
+		FP4_YYY_norm(&t0);
+		FP4_YYY_norm(&t1);
+
+		FP4_YYY_mul(&t0,&t1,&t0);	// (xa+xc)(ya+yc)    always 11x11
+		FP4_YYY_add(&z2,&z2,&t0);	// (xb.yb) - (xa.ya) + (xa+xc)(ya+yc)
+
+#if SEXTIC_TWIST_ZZZ == D_TYPE
+		if (y->type==FP_SPARSE || w->type==FP_SPARSE)
+		{
+			FP2_YYY_mul(&t0.a,&(w->c).a,&(y->c).a);
+			FP2_YYY_zero(&t0.b);
+			if (y->type!=FP_SPARSE)
+				FP2_YYY_mul(&t0.b,&(w->c).a,&(y->c).b);
+			if (w->type!=FP_SPARSE)
+				FP2_YYY_mul(&t0.b,&(w->c).b,&(y->c).a);
+		}
+		else
+#endif
+			FP4_YYY_mul(&t0,&(w->c),&(y->c)); // (xc.yc)  could be anything
+			
+		FP4_YYY_neg(&t1,&t0);			  // -(xc.yc) 
+
+		FP4_YYY_add(&(w->c),&z2,&t1);		// wc = (xb.yb) - (xa.ya) + (xa+xc)(ya+yc) - (xc.yc)	=  xb.yb + xc.ya + xa.yc
+		FP4_YYY_add(&z3,&z3,&t1);			// (xb+xc)(yb+yc) -(xb.yb) - (xc.yc)					=  xb.yc + xc.yb
+		FP4_YYY_times_i(&t0);				// i.(xc.yc)
+		FP4_YYY_add(&(w->b),&(w->b),&t0);   // wb = (xa+xb)(ya+yb) -(xa.ya) -(xb.yb) +i(xc.yc)
+		FP4_YYY_norm(&z3);
+		FP4_YYY_times_i(&z3);				// i[(xb+xc)(yb+yc) -(xb.yb) - (xc.yc)]					= i(xb.yc + xc.yb)
+		FP4_YYY_add(&(w->a),&z0,&z3);		// wa = xa.ya + i(xb.yc + xc.yb)
+	} else {
+		if (w->type==FP_SPARSER)
+		{
+			FP12_YYY_smul(w,y);
+			return;
+		}
+ // dense by sparser - 13m 
+#if SEXTIC_TWIST_ZZZ == D_TYPE
+		FP4_YYY_copy(&z3,&(w->b));
+		FP4_YYY_mul(&z0,&(w->a),&(y->a));
+
+		FP4_YYY_pmul(&z2,&(w->b),&(y->b).a);
+		FP4_YYY_add(&(w->b),&(w->a),&(w->b));
+		FP4_YYY_copy(&t1,&(y->a));
+		FP2_YYY_add(&t1.a,&t1.a,&(y->b).a);
+
+		FP4_YYY_norm(&t1);
+		FP4_YYY_norm(&(w->b));
+
+		FP4_YYY_mul(&(w->b),&(w->b),&t1);
+		FP4_YYY_add(&z3,&z3,&(w->c));
+		FP4_YYY_norm(&z3);
+		FP4_YYY_pmul(&z3,&z3,&(y->b).a);
+		FP4_YYY_neg(&t0,&z0);
+		FP4_YYY_neg(&t1,&z2);
+
+		FP4_YYY_add(&(w->b),&(w->b),&t0);   // z1=z1-z0
+		FP4_YYY_add(&(w->b),&(w->b),&t1);   // z1=z1-z2
+
+		FP4_YYY_add(&z3,&z3,&t1);        // z3=z3-z2
+		FP4_YYY_add(&z2,&z2,&t0);        // z2=z2-z0
+
+		FP4_YYY_add(&t0,&(w->a),&(w->c));
+		FP4_YYY_norm(&t0);
+		FP4_YYY_norm(&z3);
+
+		FP4_YYY_mul(&t0,&(y->a),&t0);
+		FP4_YYY_add(&(w->c),&z2,&t0);
+
+		FP4_YYY_times_i(&z3);
+		FP4_YYY_add(&(w->a),&z0,&z3);
+#endif
+#if SEXTIC_TWIST_ZZZ == M_TYPE
+		FP4_YYY_mul(&z0,&(w->a),&(y->a));
+		FP4_YYY_add(&t0,&(w->a),&(w->b));
+		FP4_YYY_norm(&t0);
+
+		FP4_YYY_mul(&z1,&t0,&(y->a));
+		FP4_YYY_add(&t0,&(w->b),&(w->c));
+		FP4_YYY_norm(&t0);
+
+		FP4_YYY_pmul(&z3,&t0,&(y->c).b);
+		FP4_YYY_times_i(&z3);
+
+		FP4_YYY_neg(&t0,&z0);
+		FP4_YYY_add(&z1,&z1,&t0);   // z1=z1-z0
+
+		FP4_YYY_copy(&(w->b),&z1);
+		FP4_YYY_copy(&z2,&t0);
+
+		FP4_YYY_add(&t0,&(w->a),&(w->c));
+		FP4_YYY_add(&t1,&(y->a),&(y->c));
+
+		FP4_YYY_norm(&t0);
+		FP4_YYY_norm(&t1);
+
+		FP4_YYY_mul(&t0,&t1,&t0);
+		FP4_YYY_add(&z2,&z2,&t0);
+
+		FP4_YYY_pmul(&t0,&(w->c),&(y->c).b);
+		FP4_YYY_times_i(&t0);
+		FP4_YYY_neg(&t1,&t0);
+		FP4_YYY_times_i(&t0);
+
+		FP4_YYY_add(&(w->c),&z2,&t1);
+		FP4_YYY_add(&z3,&z3,&t1);
+
+		FP4_YYY_add(&(w->b),&(w->b),&t0);
+		FP4_YYY_norm(&z3);
+		FP4_YYY_times_i(&z3);
+		FP4_YYY_add(&(w->a),&z0,&z3);
+
+#endif
+	}
+	w->type=FP_DENSE;
     FP12_YYY_norm(w);
 }
 
 /* FP12 multiplication w=w*y */
-/* SU= 744 */
 /* catering for special case that arises from special form of ATE pairing line function */
-void FP12_YYY_smul(FP12_YYY *w,FP12_YYY *y,int type)
+/* w and y are both sparser line functions - cost = 6m */ 
+void FP12_YYY_smul(FP12_YYY *w,FP12_YYY *y)
 {
-    FP4_YYY z0,z1,z2,z3,t0,t1;
+	FP2_YYY w1,w2,w3,ta,tb,tc,td,te,t;
 
-    if (type==D_TYPE)
-    {
-        // y->c is 0
+//	if (type==D_TYPE)
+//	{ 
+#if SEXTIC_TWIST_ZZZ == D_TYPE
+	FP2_YYY_mul(&w1,&(w->a).a,&(y->a).a); // A1.A2
+	FP2_YYY_mul(&w2,&(w->a).b,&(y->a).b); // B1.B2
+	FP2_YYY_mul(&w3,&(w->b).a,&(y->b).a); // C1.C2
 
-        FP4_YYY_copy(&z3,&(w->b));
-        FP4_YYY_mul(&z0,&(w->a),&(y->a));
+	FP2_YYY_add(&ta,&(w->a).a,&(w->a).b); // A1+B1
+	FP2_YYY_add(&tb,&(y->a).a,&(y->a).b); // A2+B2
+	FP2_YYY_norm(&ta);
+	FP2_YYY_norm(&tb);
+	FP2_YYY_mul(&tc,&ta,&tb);			// (A1+B1)(A2+B2)
+	FP2_YYY_add(&t,&w1,&w2);
+	FP2_YYY_neg(&t,&t);
+	FP2_YYY_add(&tc,&tc,&t);			// (A1+B1)(A2+B2)-A1.A2-B1*B2 =  (A1.B2+A2.B1)		
+				
+	FP2_YYY_add(&ta,&(w->a).a,&(w->b).a); // A1+C1
+	FP2_YYY_add(&tb,&(y->a).a,&(y->b).a); // A2+C2
+	FP2_YYY_norm(&ta);
+	FP2_YYY_norm(&tb);
+	FP2_YYY_mul(&td,&ta,&tb);			// (A1+C1)(A2+C2)
+	FP2_YYY_add(&t,&w1,&w3);
+	FP2_YYY_neg(&t,&t);
+	FP2_YYY_add(&td,&td,&t);			// (A1+C1)(A2+C2)-A1.A2-C1*C2 =  (A1.C2+A2.C1)		
 
-        FP4_YYY_pmul(&z2,&(w->b),&(y->b).a);
-        FP4_YYY_add(&(w->b),&(w->a),&(w->b));
-        FP4_YYY_copy(&t1,&(y->a));
-        FP2_YYY_add(&t1.a,&t1.a,&(y->b).a);
+	FP2_YYY_add(&ta,&(w->a).b,&(w->b).a); // B1+C1
+	FP2_YYY_add(&tb,&(y->a).b,&(y->b).a); // B2+C2
+	FP2_YYY_norm(&ta);
+	FP2_YYY_norm(&tb);
+	FP2_YYY_mul(&te,&ta,&tb);			// (B1+C1)(B2+C2)
+	FP2_YYY_add(&t,&w2,&w3);
+	FP2_YYY_neg(&t,&t);
+	FP2_YYY_add(&te,&te,&t);			// (B1+C1)(B2+C2)-B1.B2-C1*C2 =  (B1.C2+B2.C1)		
 
-        FP4_YYY_norm(&t1);
-        FP4_YYY_norm(&(w->b));
+	FP2_YYY_mul_ip(&w2);
+	FP2_YYY_add(&w1,&w1,&w2);
+	FP4_YYY_from_FP2s(&(w->a),&w1,&tc);
+	FP4_YYY_from_FP2s(&(w->b),&td,&te); // only norm these 2
+	FP4_YYY_from_FP2(&(w->c),&w3);
 
-        FP4_YYY_mul(&(w->b),&(w->b),&t1);
-        FP4_YYY_add(&z3,&z3,&(w->c));
-        FP4_YYY_norm(&z3);
-        FP4_YYY_pmul(&z3,&z3,&(y->b).a);
-        FP4_YYY_neg(&t0,&z0);
-        FP4_YYY_neg(&t1,&z2);
+	FP4_YYY_norm(&(w->a));
+	FP4_YYY_norm(&(w->b));
+#endif
+//	} else { 
+#if SEXTIC_TWIST_ZZZ == M_TYPE
+	FP2_YYY_mul(&w1,&(w->a).a,&(y->a).a); // A1.A2
+	FP2_YYY_mul(&w2,&(w->a).b,&(y->a).b); // B1.B2
+	FP2_YYY_mul(&w3,&(w->c).b,&(y->c).b); // F1.F2
 
-        FP4_YYY_add(&(w->b),&(w->b),&t0);   // z1=z1-z0
-        FP4_YYY_add(&(w->b),&(w->b),&t1);   // z1=z1-z2
+	FP2_YYY_add(&ta,&(w->a).a,&(w->a).b); // A1+B1
+	FP2_YYY_add(&tb,&(y->a).a,&(y->a).b); // A2+B2
+	FP2_YYY_norm(&ta);
+	FP2_YYY_norm(&tb);
+	FP2_YYY_mul(&tc,&ta,&tb);			// (A1+B1)(A2+B2)
+	FP2_YYY_add(&t,&w1,&w2);
+	FP2_YYY_neg(&t,&t);
+	FP2_YYY_add(&tc,&tc,&t);			// (A1+B1)(A2+B2)-A1.A2-B1*B2 =  (A1.B2+A2.B1)		
+				
+	FP2_YYY_add(&ta,&(w->a).a,&(w->c).b); // A1+F1
+	FP2_YYY_add(&tb,&(y->a).a,&(y->c).b); // A2+F2
+	FP2_YYY_norm(&ta);
+	FP2_YYY_norm(&tb);
+	FP2_YYY_mul(&td,&ta,&tb);			// (A1+F1)(A2+F2)
+	FP2_YYY_add(&t,&w1,&w3);
+	FP2_YYY_neg(&t,&t);
+	FP2_YYY_add(&td,&td,&t);			// (A1+F1)(A2+F2)-A1.A2-F1*F2 =  (A1.F2+A2.F1)		
 
-        FP4_YYY_add(&z3,&z3,&t1);        // z3=z3-z2
-        FP4_YYY_add(&z2,&z2,&t0);        // z2=z2-z0
+	FP2_YYY_add(&ta,&(w->a).b,&(w->c).b); // B1+F1
+	FP2_YYY_add(&tb,&(y->a).b,&(y->c).b); // B2+F2
+	FP2_YYY_norm(&ta);
+	FP2_YYY_norm(&tb);
+	FP2_YYY_mul(&te,&ta,&tb);			// (B1+F1)(B2+F2)
+	FP2_YYY_add(&t,&w2,&w3);
+	FP2_YYY_neg(&t,&t);
+	FP2_YYY_add(&te,&te,&t);			// (B1+F1)(B2+F2)-B1.B2-F1*F2 =  (B1.F2+B2.F1)	
 
-        FP4_YYY_add(&t0,&(w->a),&(w->c));
+	FP2_YYY_mul_ip(&w2);
+	FP2_YYY_add(&w1,&w1,&w2);
+	FP4_YYY_from_FP2s(&(w->a),&w1,&tc);
 
-        FP4_YYY_norm(&t0);
-        FP4_YYY_norm(&z3);
+	FP2_YYY_mul_ip(&w3);
+	FP2_YYY_norm(&w3);
+	FP4_YYY_from_FP2H(&(w->b),&w3);
 
-        FP4_YYY_mul(&t0,&(y->a),&t0);
-        FP4_YYY_add(&(w->c),&z2,&t0);
+	FP2_YYY_norm(&te);
+	FP2_YYY_mul_ip(&te);
+	FP4_YYY_from_FP2s(&(w->c),&te,&td);
 
-        FP4_YYY_times_i(&z3);
-        FP4_YYY_add(&(w->a),&z0,&z3);
-    }
+	FP4_YYY_norm(&(w->a));
+	FP4_YYY_norm(&(w->c));
+#endif
 
-    if (type==M_TYPE)
-    {
-        // y->b is zero
-        FP4_YYY_mul(&z0,&(w->a),&(y->a));
-        FP4_YYY_add(&t0,&(w->a),&(w->b));
-        FP4_YYY_norm(&t0);
-
-        FP4_YYY_mul(&z1,&t0,&(y->a));
-        FP4_YYY_add(&t0,&(w->b),&(w->c));
-        FP4_YYY_norm(&t0);
-
-        FP4_YYY_pmul(&z3,&t0,&(y->c).b);
-        FP4_YYY_times_i(&z3);
-
-        FP4_YYY_neg(&t0,&z0);
-        FP4_YYY_add(&z1,&z1,&t0);   // z1=z1-z0
-
-        FP4_YYY_copy(&(w->b),&z1);
-
-        FP4_YYY_copy(&z2,&t0);
-
-        FP4_YYY_add(&t0,&(w->a),&(w->c));
-        FP4_YYY_add(&t1,&(y->a),&(y->c));
-
-        FP4_YYY_norm(&t0);
-        FP4_YYY_norm(&t1);
-
-        FP4_YYY_mul(&t0,&t1,&t0);
-        FP4_YYY_add(&z2,&z2,&t0);
-
-        FP4_YYY_pmul(&t0,&(w->c),&(y->c).b);
-        FP4_YYY_times_i(&t0);
-        FP4_YYY_neg(&t1,&t0);
-        FP4_YYY_times_i(&t0);
-
-        FP4_YYY_add(&(w->c),&z2,&t1);
-        FP4_YYY_add(&z3,&z3,&t1);
-
-        FP4_YYY_add(&(w->b),&(w->b),&t0);
-        FP4_YYY_norm(&z3);
-        FP4_YYY_times_i(&z3);
-        FP4_YYY_add(&(w->a),&z0,&z3);
-    }
-    FP12_YYY_norm(w);
+//	}
+	w->type=FP_SPARSE;
 }
 
 /* Set w=1/x */
@@ -401,7 +605,7 @@
     FP4_YYY_mul(&(w->a),&f0,&f3);
     FP4_YYY_mul(&(w->b),&f1,&f3);
     FP4_YYY_mul(&(w->c),&f2,&f3);
-
+	w->type=FP_DENSE;
 }
 
 /* constant time powering by small integer of max length bts */
@@ -611,6 +815,7 @@
 
     FP4_YYY_pmul(&(w->b),&(w->b),f);
     FP4_YYY_pmul(&(w->c),&(w->c),&f2);
+	w->type=FP_DENSE;
 }
 
 /* SU= 8 */
@@ -722,5 +927,7 @@
     FP4_YYY_cmove(&(f->a),&(g->a),d);
     FP4_YYY_cmove(&(f->b),&(g->b),d);
     FP4_YYY_cmove(&(f->c),&(g->c),d);
+	d=~(d-1);
+	f->type^=(f->type^g->type)&d;
 }
 
diff --git a/version3/c/fp12.h b/version3/c/fp12.h
index 99fed17..bf83a57 100644
--- a/version3/c/fp12.h
+++ b/version3/c/fp12.h
@@ -38,6 +38,7 @@
     FP4_YYY a; /**< first part of FP12 */
     FP4_YYY b; /**< second part of FP12 */
     FP4_YYY c; /**< third part of FP12 */
+	int type;
 } FP12_YYY;
 
 extern const BIG_XXX Fra_YYY; /**< real part of BN curve Frobenius Constant */
@@ -108,15 +109,23 @@
 	@param y FP12 instance
  */
 extern void FP12_YYY_sqr(FP12_YYY *x,FP12_YYY *y);
-/**	@brief Fast multiplication of an FP12 by an FP12 that arises from an ATE pairing line function
+
+/**	@brief Fast multiplication of two sparse FP12s that arises from ATE pairing line functions
  *
-	Here the multiplier has a special form that can be exploited
 	@param x FP12 instance, on exit = x*y
 	@param y FP12 instance, of special form
-	@param t D_TYPE or M_TYPE twist
  */
-extern void FP12_YYY_smul(FP12_YYY *x,FP12_YYY *y,int t);
-/**	@brief Multiplication of two FP12s
+extern void FP12_YYY_smul(FP12_YYY *x,FP12_YYY *y);
+
+/**	@brief Fast multiplication of what may be sparse multiplicands
+ *
+	@param x FP12 instance, on exit = x*y
+	@param y FP12 instance, of special form
+ */
+extern void FP12_YYY_ssmul(FP12_YYY *x,FP12_YYY *y);
+
+
+/**	@brief Full unconditional Multiplication of two FP12s
  *
 	@param x FP12 instance, on exit = x*y
 	@param y FP12 instance, the multiplier
diff --git a/version3/c/fp24.c b/version3/c/fp24.c
index 901d7a9..8951b22 100644
--- a/version3/c/fp24.c
+++ b/version3/c/fp24.c
@@ -79,6 +79,7 @@
     FP8_YYY_copy(&(w->a),&(x->a));
     FP8_YYY_copy(&(w->b),&(x->b));
     FP8_YYY_copy(&(w->c),&(x->c));
+	w->type=x->type;
 }
 
 /* FP24 w=1 */
@@ -88,6 +89,7 @@
     FP8_YYY_one(&(w->a));
     FP8_YYY_zero(&(w->b));
     FP8_YYY_zero(&(w->c));
+	w->type=FP_UNITY;
 }
 
 /* return 1 if x==y, else 0 */
@@ -116,6 +118,7 @@
     FP8_YYY_copy(&(w->a),a);
     FP8_YYY_zero(&(w->b));
     FP8_YYY_zero(&(w->c));
+	w->type=FP_SPARSER;
 }
 
 /* Create FP24 from 3 FP8's */
@@ -125,6 +128,7 @@
     FP8_YYY_copy(&(w->a),a);
     FP8_YYY_copy(&(w->b),b);
     FP8_YYY_copy(&(w->c),c);
+	w->type=FP_DENSE;
 }
 
 /* Granger-Scott Unitary Squaring. This does not benefit from lazy reduction */
@@ -164,7 +168,7 @@
     FP8_YYY_add(&(w->c),&(w->c),&(w->c));
     FP8_YYY_add(&(w->b),&B,&(w->b));
     FP8_YYY_add(&(w->c),&C,&(w->c));
-
+	w->type=FP_DENSE;
     FP24_YYY_reduce(w);	    /* reduce here as in pow function repeated squarings would trigger multiple reductions */
 }
 
@@ -176,6 +180,12 @@
 
     FP8_YYY A,B,C,D;
 
+	if (x->type<=FP_UNITY)
+	{
+		FP24_YYY_copy(w,x);
+		return;
+	}
+
     FP8_YYY_sqr(&A,&(x->a));
     FP8_YYY_mul(&B,&(x->b),&(x->c));
     FP8_YYY_add(&B,&B,&B);
@@ -209,6 +219,11 @@
     FP8_YYY_add(&(w->b),&C,&D);
     FP8_YYY_add(&(w->c),&(w->c),&A);
 
+	if (x->type==FP_SPARSER)
+		w->type=FP_SPARSE;
+	else
+		w->type=FP_DENSE;
+
     FP24_YYY_norm(w);
 }
 
@@ -266,20 +281,106 @@
 	FP8_YYY_norm(&z3);
     FP8_YYY_times_i(&z3);
     FP8_YYY_add(&(w->a),&z0,&z3);
-
+	w->type=FP_DENSE;
     FP24_YYY_norm(w);
 }
 
-/* FP24 multiplication w=w*y */
-/* SU= 744 */
-/* catering for special case that arises from special form of ATE pairing line function */
-void FP24_YYY_smul(FP24_YYY *w,FP24_YYY *y,int type)
+/* FP24 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+void FP24_YYY_ssmul(FP24_YYY *w,FP24_YYY *y)
 {
-    FP8_YYY z0,z1,z2,z3,t0,t1;
+	FP8_YYY z0,z1,z2,z3,t0,t1;
+	if (w->type==FP_UNITY)
+	{
+		FP24_YYY_copy(w,y);
+		return;
+	}
+	if (y->type==FP_UNITY)
+		return;
 
-	if (type==D_TYPE)
-	{ // y->c is 0
+	if (y->type >= FP_SPARSE)
+	{
+		FP8_YYY_mul(&z0,&(w->a),&(y->a));  // xa.ya   always 11x11
 
+#if SEXTIC_TWIST_ZZZ == M_TYPE
+		if (y->type==FP_SPARSE || w->type==FP_SPARSE)
+		{
+			FP4_YYY_mul(&z2.b,&(w->b).b,&(y->b).b);
+			FP4_YYY_zero(&z2.a);
+			if (y->type!=FP_SPARSE)
+				FP4_YYY_mul(&z2.a,&(w->b).b,&(y->b).a);
+			if (w->type!=FP_SPARSE)
+				FP4_YYY_mul(&z2.a,&(w->b).a,&(y->b).b);
+			FP8_YYY_times_i(&z2);
+		}
+		else
+#endif 
+			FP8_YYY_mul(&z2,&(w->b),&(y->b));  // xb.yb  could be 00x00 or 01x01 or or 10x10 or 11x00 or 11x10 or 11x01 or 11x11 
+
+		FP8_YYY_add(&t0,&(w->a),&(w->b));  // (xa+xb)
+		FP8_YYY_add(&t1,&(y->a),&(y->b));  // (ya+yb)
+
+		FP8_YYY_norm(&t0);
+		FP8_YYY_norm(&t1);
+
+		FP8_YYY_mul(&z1,&t0,&t1); // (xa+xb)(ya+yb)  always 11x11
+		FP8_YYY_add(&t0,&(w->b),&(w->c));  // (xb+xc)
+		FP8_YYY_add(&t1,&(y->b),&(y->c));  // (yb+yc)
+
+		FP8_YYY_norm(&t0);
+		FP8_YYY_norm(&t1);
+
+		FP8_YYY_mul(&z3,&t0,&t1);	// (xb+xc)(yb+yc)   could be anything...
+		FP8_YYY_neg(&t0,&z0);		// -(xa.ya)
+		FP8_YYY_neg(&t1,&z2);		// -(xb.yb)
+
+		FP8_YYY_add(&z1,&z1,&t0);  
+		FP8_YYY_add(&(w->b),&z1,&t1); // /wb = (xa+xb)(ya+yb) -(xa.ya) -(xb.yb)						= xa.yb + xb.ya
+
+		FP8_YYY_add(&z3,&z3,&t1);        // (xb+xc)(yb+yc) -(xb.yb)
+		FP8_YYY_add(&z2,&z2,&t0);        // (xb.yb) - (xa.ya)
+
+		FP8_YYY_add(&t0,&(w->a),&(w->c));  // (xa+xc)
+		FP8_YYY_add(&t1,&(y->a),&(y->c));  // (ya+yc)
+
+		FP8_YYY_norm(&t0);
+		FP8_YYY_norm(&t1);
+
+		FP8_YYY_mul(&t0,&t1,&t0);	// (xa+xc)(ya+yc)    always 11x11
+		FP8_YYY_add(&z2,&z2,&t0);	// (xb.yb) - (xa.ya) + (xa+xc)(ya+yc)
+
+#if SEXTIC_TWIST_ZZZ == D_TYPE
+		if (y->type==FP_SPARSE || w->type==FP_SPARSE)
+		{
+			FP4_YYY_mul(&t0.a,&(w->c).a,&(y->c).a);
+			FP4_YYY_zero(&t0.b);
+			if (y->type!=FP_SPARSE)
+				FP4_YYY_mul(&t0.b,&(w->c).a,&(y->c).b);
+			if (w->type!=FP_SPARSE)
+				FP4_YYY_mul(&t0.b,&(w->c).b,&(y->c).a);
+		}
+		else
+#endif
+			FP8_YYY_mul(&t0,&(w->c),&(y->c)); // (xc.yc)  could be anything
+			
+		FP8_YYY_neg(&t1,&t0);			  // -(xc.yc) 
+
+		FP8_YYY_add(&(w->c),&z2,&t1);		// wc = (xb.yb) - (xa.ya) + (xa+xc)(ya+yc) - (xc.yc)	=  xb.yb + xc.ya + xa.yc
+		FP8_YYY_add(&z3,&z3,&t1);			// (xb+xc)(yb+yc) -(xb.yb) - (xc.yc)					=  xb.yc + xc.yb
+		FP8_YYY_times_i(&t0);				// i.(xc.yc)
+		FP8_YYY_add(&(w->b),&(w->b),&t0);   // wb = (xa+xb)(ya+yb) -(xa.ya) -(xb.yb) +i(xc.yc)
+		FP8_YYY_norm(&z3);
+		FP8_YYY_times_i(&z3);				// i[(xb+xc)(yb+yc) -(xb.yb) - (xc.yc)]					= i(xb.yc + xc.yb)
+		FP8_YYY_add(&(w->a),&z0,&z3);		// wa = xa.ya + i(xb.yc + xc.yb)
+	} else {
+		if (w->type==FP_SPARSER)
+		{
+			FP24_YYY_smul(w,y);
+			return;
+		}
+// dense by sparser - 13m 
+#if SEXTIC_TWIST_ZZZ == D_TYPE
 		FP8_YYY_copy(&z3,&(w->b));
 		FP8_YYY_mul(&z0,&(w->a),&(y->a));
 
@@ -305,7 +406,6 @@
 		FP8_YYY_add(&z2,&z2,&t0);        // z2=z2-z0
 
 		FP8_YYY_add(&t0,&(w->a),&(w->c));
-
 		FP8_YYY_norm(&t0);
 		FP8_YYY_norm(&z3);
 
@@ -314,10 +414,8 @@
 
 		FP8_YYY_times_i(&z3);
 		FP8_YYY_add(&(w->a),&z0,&z3);
-	}
-
-	if (type==M_TYPE)
-	{ // y->b is zero
+#endif
+#if SEXTIC_TWIST_ZZZ == M_TYPE
 		FP8_YYY_mul(&z0,&(w->a),&(y->a));
 		FP8_YYY_add(&t0,&(w->a),&(w->b));
 		FP8_YYY_norm(&t0);
@@ -333,7 +431,6 @@
 		FP8_YYY_add(&z1,&z1,&t0);   // z1=z1-z0
 
 		FP8_YYY_copy(&(w->b),&z1);
-
 		FP8_YYY_copy(&z2,&t0);
 
 		FP8_YYY_add(&t0,&(w->a),&(w->c));
@@ -357,10 +454,112 @@
 		FP8_YYY_norm(&z3);
 		FP8_YYY_times_i(&z3);
 		FP8_YYY_add(&(w->a),&z0,&z3);
+#endif
 	}
+	w->type=FP_DENSE;
     FP24_YYY_norm(w);
 }
 
+/* FP24 multiplication w=w*y */
+/* catering for special case that arises from special form of ATE pairing line function */
+/* w and y are both sparser line functions - cost = 6m */ 
+void FP24_YYY_smul(FP24_YYY *w,FP24_YYY *y)
+{
+	FP4_YYY w1,w2,w3,ta,tb,tc,td,te,t;
+
+
+#if SEXTIC_TWIST_ZZZ == D_TYPE
+	FP4_YYY_mul(&w1,&(w->a).a,&(y->a).a); // A1.A2
+	FP4_YYY_mul(&w2,&(w->a).b,&(y->a).b); // B1.B2
+	FP4_YYY_mul(&w3,&(w->b).a,&(y->b).a); // C1.C2
+
+	FP4_YYY_add(&ta,&(w->a).a,&(w->a).b); // A1+B1
+	FP4_YYY_add(&tb,&(y->a).a,&(y->a).b); // A2+B2
+	FP4_YYY_norm(&ta);
+	FP4_YYY_norm(&tb);
+	FP4_YYY_mul(&tc,&ta,&tb);			// (A1+B1)(A2+B2)
+	FP4_YYY_add(&t,&w1,&w2);
+	FP4_YYY_neg(&t,&t);
+	FP4_YYY_add(&tc,&tc,&t);			// (A1+B1)(A2+B2)-A1.A2-B1*B2 =  (A1.B2+A2.B1)		
+				
+	FP4_YYY_add(&ta,&(w->a).a,&(w->b).a); // A1+C1
+	FP4_YYY_add(&tb,&(y->a).a,&(y->b).a); // A2+C2
+	FP4_YYY_norm(&ta);
+	FP4_YYY_norm(&tb);
+	FP4_YYY_mul(&td,&ta,&tb);			// (A1+C1)(A2+C2)
+	FP4_YYY_add(&t,&w1,&w3);
+	FP4_YYY_neg(&t,&t);
+	FP4_YYY_add(&td,&td,&t);			// (A1+C1)(A2+C2)-A1.A2-C1*C2 =  (A1.C2+A2.C1)		
+
+	FP4_YYY_add(&ta,&(w->a).b,&(w->b).a); // B1+C1
+	FP4_YYY_add(&tb,&(y->a).b,&(y->b).a); // B2+C2
+	FP4_YYY_norm(&ta);
+	FP4_YYY_norm(&tb);
+	FP4_YYY_mul(&te,&ta,&tb);			// (B1+C1)(B2+C2)
+	FP4_YYY_add(&t,&w2,&w3);
+	FP4_YYY_neg(&t,&t);
+	FP4_YYY_add(&te,&te,&t);			// (B1+C1)(B2+C2)-B1.B2-C1*C2 =  (B1.C2+B2.C1)		
+
+	FP4_YYY_times_i(&w2);
+	FP4_YYY_add(&w1,&w1,&w2);
+	FP8_YYY_from_FP4s(&(w->a),&w1,&tc);
+	FP8_YYY_from_FP4s(&(w->b),&td,&te); // only norm these 2
+	FP8_YYY_from_FP4(&(w->c),&w3);
+
+	FP8_YYY_norm(&(w->a));
+	FP8_YYY_norm(&(w->b));
+#endif
+#if SEXTIC_TWIST_ZZZ == M_TYPE
+	FP4_YYY_mul(&w1,&(w->a).a,&(y->a).a); // A1.A2
+	FP4_YYY_mul(&w2,&(w->a).b,&(y->a).b); // B1.B2
+	FP4_YYY_mul(&w3,&(w->c).b,&(y->c).b); // F1.F2
+
+	FP4_YYY_add(&ta,&(w->a).a,&(w->a).b); // A1+B1
+	FP4_YYY_add(&tb,&(y->a).a,&(y->a).b); // A2+B2
+	FP4_YYY_norm(&ta);
+	FP4_YYY_norm(&tb);
+	FP4_YYY_mul(&tc,&ta,&tb);			// (A1+B1)(A2+B2)
+	FP4_YYY_add(&t,&w1,&w2);
+	FP4_YYY_neg(&t,&t);
+	FP4_YYY_add(&tc,&tc,&t);			// (A1+B1)(A2+B2)-A1.A2-B1*B2 =  (A1.B2+A2.B1)		
+				
+	FP4_YYY_add(&ta,&(w->a).a,&(w->c).b); // A1+F1
+	FP4_YYY_add(&tb,&(y->a).a,&(y->c).b); // A2+F2
+	FP4_YYY_norm(&ta);
+	FP4_YYY_norm(&tb);
+	FP4_YYY_mul(&td,&ta,&tb);			// (A1+F1)(A2+F2)
+	FP4_YYY_add(&t,&w1,&w3);
+	FP4_YYY_neg(&t,&t);
+	FP4_YYY_add(&td,&td,&t);			// (A1+F1)(A2+F2)-A1.A2-F1*F2 =  (A1.F2+A2.F1)		
+
+	FP4_YYY_add(&ta,&(w->a).b,&(w->c).b); // B1+F1
+	FP4_YYY_add(&tb,&(y->a).b,&(y->c).b); // B2+F2
+	FP4_YYY_norm(&ta);
+	FP4_YYY_norm(&tb);
+	FP4_YYY_mul(&te,&ta,&tb);			// (B1+F1)(B2+F2)
+	FP4_YYY_add(&t,&w2,&w3);
+	FP4_YYY_neg(&t,&t);
+	FP4_YYY_add(&te,&te,&t);			// (B1+F1)(B2+F2)-B1.B2-F1*F2 =  (B1.F2+B2.F1)	
+
+	FP4_YYY_times_i(&w2);
+	FP4_YYY_add(&w1,&w1,&w2);
+	FP8_YYY_from_FP4s(&(w->a),&w1,&tc);
+
+	FP4_YYY_times_i(&w3);
+	FP4_YYY_norm(&w3);
+	FP8_YYY_from_FP4H(&(w->b),&w3);
+
+	FP4_YYY_norm(&te);
+	FP4_YYY_times_i(&te);
+	FP8_YYY_from_FP4s(&(w->c),&te,&td);
+
+	FP8_YYY_norm(&(w->a));
+	FP8_YYY_norm(&(w->c));
+#endif
+
+	w->type=FP_SPARSE;
+}
+
 /* Set w=1/x */
 /* SU= 600 */
 void FP24_YYY_inv(FP24_YYY *w,FP24_YYY *x)
@@ -398,7 +597,7 @@
     FP8_YYY_mul(&(w->a),&f0,&f3);
     FP8_YYY_mul(&(w->b),&f1,&f3);
     FP8_YYY_mul(&(w->c),&f2,&f3);
-
+	w->type=FP_DENSE;
 }
 
 /* constant time powering by small integer of max length bts */
@@ -657,6 +856,7 @@
 		FP8_YYY_qmul(&(w->b),&(w->b),f); FP8_YYY_times_i2(&(w->b));
 		FP8_YYY_qmul(&(w->c),&(w->c),&f2); FP8_YYY_times_i2(&(w->c)); FP8_YYY_times_i2(&(w->c));
 	}
+	w->type=FP_DENSE;
 }
 
 /* SU= 8 */
@@ -821,5 +1021,7 @@
     FP8_YYY_cmove(&(f->a),&(g->a),d);
     FP8_YYY_cmove(&(f->b),&(g->b),d);
     FP8_YYY_cmove(&(f->c),&(g->c),d);
+	d=~(d-1);
+	f->type^=(f->type^g->type)&d;
 }
 
diff --git a/version3/c/fp24.h b/version3/c/fp24.h
index 13ff25c..02e09fa 100644
--- a/version3/c/fp24.h
+++ b/version3/c/fp24.h
@@ -12,6 +12,7 @@
     FP8_YYY a; /**< first part of FP12 */
     FP8_YYY b; /**< second part of FP12 */
     FP8_YYY c; /**< third part of FP12 */
+	int type;
 } FP24_YYY;
 
 extern const BIG_XXX Fra_YYY; /**< real part of BN curve Frobenius Constant */
@@ -82,20 +83,26 @@
 	@param y FP24 instance
  */
 extern void FP24_YYY_sqr(FP24_YYY *x,FP24_YYY *y);
-/**	@brief Fast multiplication of an FP24 by an FP24 that arises from an ATE pairing line function
+/**	@brief Fast multiplication of two sparse FP24s that arises from ATE pairing line functions
  *
-	Here the multiplier has a special form that can be exploited
 	@param x FP24 instance, on exit = x*y
 	@param y FP24 instance, of special form
-	@param t D_TYPE or M_TYPE twist
  */
-extern void FP24_YYY_smul(FP24_YYY *x,FP24_YYY *y,int t);
-/**	@brief Multiplication of two FP24s
+extern void FP24_YYY_smul(FP24_YYY *x,FP24_YYY *y);
+
+/**	@brief Fast multiplication of what may be sparse multiplicands
+ *
+	@param x FP24 instance, on exit = x*y
+	@param y FP24 instance, of special form
+ */
+extern void FP24_YYY_ssmul(FP24_YYY *x,FP24_YYY *y);
+/**	@brief Full unconditional Multiplication of two FP24s
  *
 	@param x FP24 instance, on exit = x*y
 	@param y FP24 instance, the multiplier
  */
 extern void FP24_YYY_mul(FP24_YYY *x,FP24_YYY *y);
+
 /**	@brief Inverting an FP24
  *
 	@param x FP24 instance, on exit = 1/y
diff --git a/version3/c/fp48.c b/version3/c/fp48.c
index 68f4ddd..88bf25b 100644
--- a/version3/c/fp48.c
+++ b/version3/c/fp48.c
@@ -80,6 +80,7 @@
     FP16_YYY_copy(&(w->a),&(x->a));
     FP16_YYY_copy(&(w->b),&(x->b));
     FP16_YYY_copy(&(w->c),&(x->c));
+	w->type=x->type;
 }
 
 /* FP48 w=1 */
@@ -89,6 +90,7 @@
     FP16_YYY_one(&(w->a));
     FP16_YYY_zero(&(w->b));
     FP16_YYY_zero(&(w->c));
+	w->type=FP_UNITY;
 }
 
 /* return 1 if x==y, else 0 */
@@ -117,6 +119,7 @@
     FP16_YYY_copy(&(w->a),a);
     FP16_YYY_zero(&(w->b));
     FP16_YYY_zero(&(w->c));
+	w->type=FP_SPARSER;
 }
 
 /* Create FP48 from 3 FP16's */
@@ -126,6 +129,7 @@
     FP16_YYY_copy(&(w->a),a);
     FP16_YYY_copy(&(w->b),b);
     FP16_YYY_copy(&(w->c),c);
+	w->type=FP_DENSE;
 }
 
 /* Granger-Scott Unitary Squaring. This does not benefit from lazy reduction */
@@ -165,7 +169,7 @@
     FP16_YYY_add(&(w->c),&(w->c),&(w->c));
     FP16_YYY_add(&(w->b),&B,&(w->b));
     FP16_YYY_add(&(w->c),&C,&(w->c));
-
+	w->type=FP_DENSE;
     FP48_YYY_reduce(w);	    /* reduce here as in pow function repeated squarings would trigger multiple reductions */
 }
 
@@ -177,6 +181,12 @@
 
     FP16_YYY A,B,C,D;
 
+	if (x->type<=FP_UNITY)
+	{
+		FP48_YYY_copy(w,x);
+		return;
+	}
+
     FP16_YYY_sqr(&A,&(x->a));
     FP16_YYY_mul(&B,&(x->b),&(x->c));
     FP16_YYY_add(&B,&B,&B);
@@ -210,6 +220,11 @@
     FP16_YYY_add(&(w->b),&C,&D);
     FP16_YYY_add(&(w->c),&(w->c),&A);
 
+	if (x->type==FP_SPARSER)
+		w->type=FP_SPARSE;
+	else
+		w->type=FP_DENSE;
+
     FP48_YYY_norm(w);
 }
 
@@ -267,20 +282,106 @@
 	FP16_YYY_norm(&z3);
     FP16_YYY_times_i(&z3);
     FP16_YYY_add(&(w->a),&z0,&z3);
-
+	w->type=FP_DENSE;
     FP48_YYY_norm(w);
 }
 
-/* FP48 multiplication w=w*y */
-/* SU= 744 */
-/* catering for special case that arises from special form of ATE pairing line function */
-void FP48_YYY_smul(FP48_YYY *w,FP48_YYY *y,int type)
+/* FP48 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+void FP48_YYY_ssmul(FP48_YYY *w,FP48_YYY *y)
 {
-    FP16_YYY z0,z1,z2,z3,t0,t1;
+	FP16_YYY z0,z1,z2,z3,t0,t1;
+	if (w->type==FP_UNITY)
+	{
+		FP48_YYY_copy(w,y);
+		return;
+	}
+	if (y->type==FP_UNITY)
+		return;
 
-	if (type==D_TYPE)
-	{ // y->c is 0
+	if (y->type >= FP_SPARSE)
+	{
+		FP16_YYY_mul(&z0,&(w->a),&(y->a));  // xa.ya   always 11x11
 
+#if SEXTIC_TWIST_ZZZ == M_TYPE
+		if (y->type==FP_SPARSE || w->type==FP_SPARSE)
+		{
+			FP8_YYY_mul(&z2.b,&(w->b).b,&(y->b).b);
+			FP8_YYY_zero(&z2.a);
+			if (y->type!=FP_SPARSE)
+				FP8_YYY_mul(&z2.a,&(w->b).b,&(y->b).a);
+			if (w->type!=FP_SPARSE)
+				FP8_YYY_mul(&z2.a,&(w->b).a,&(y->b).b);
+			FP16_YYY_times_i(&z2);
+		}
+		else
+#endif 
+			FP16_YYY_mul(&z2,&(w->b),&(y->b));  // xb.yb  could be 00x00 or 01x01 or or 10x10 or 11x00 or 11x10 or 11x01 or 11x11 
+
+		FP16_YYY_add(&t0,&(w->a),&(w->b));  // (xa+xb)
+		FP16_YYY_add(&t1,&(y->a),&(y->b));  // (ya+yb)
+
+		FP16_YYY_norm(&t0);
+		FP16_YYY_norm(&t1);
+
+		FP16_YYY_mul(&z1,&t0,&t1); // (xa+xb)(ya+yb)  always 11x11
+		FP16_YYY_add(&t0,&(w->b),&(w->c));  // (xb+xc)
+		FP16_YYY_add(&t1,&(y->b),&(y->c));  // (yb+yc)
+
+		FP16_YYY_norm(&t0);
+		FP16_YYY_norm(&t1);
+
+		FP16_YYY_mul(&z3,&t0,&t1);	// (xb+xc)(yb+yc)   could be anything...
+		FP16_YYY_neg(&t0,&z0);		// -(xa.ya)
+		FP16_YYY_neg(&t1,&z2);		// -(xb.yb)
+
+		FP16_YYY_add(&z1,&z1,&t0);  
+		FP16_YYY_add(&(w->b),&z1,&t1); // /wb = (xa+xb)(ya+yb) -(xa.ya) -(xb.yb)						= xa.yb + xb.ya
+
+		FP16_YYY_add(&z3,&z3,&t1);        // (xb+xc)(yb+yc) -(xb.yb)
+		FP16_YYY_add(&z2,&z2,&t0);        // (xb.yb) - (xa.ya)
+
+		FP16_YYY_add(&t0,&(w->a),&(w->c));  // (xa+xc)
+		FP16_YYY_add(&t1,&(y->a),&(y->c));  // (ya+yc)
+
+		FP16_YYY_norm(&t0);
+		FP16_YYY_norm(&t1);
+
+		FP16_YYY_mul(&t0,&t1,&t0);	// (xa+xc)(ya+yc)    always 11x11
+		FP16_YYY_add(&z2,&z2,&t0);	// (xb.yb) - (xa.ya) + (xa+xc)(ya+yc)
+
+#if SEXTIC_TWIST_ZZZ == D_TYPE
+		if (y->type==FP_SPARSE || w->type==FP_SPARSE)
+		{
+			FP8_YYY_mul(&t0.a,&(w->c).a,&(y->c).a);
+			FP8_YYY_zero(&t0.b);
+			if (y->type!=FP_SPARSE)
+				FP8_YYY_mul(&t0.b,&(w->c).a,&(y->c).b);
+			if (w->type!=FP_SPARSE)
+				FP8_YYY_mul(&t0.b,&(w->c).b,&(y->c).a);
+		}
+		else
+#endif
+			FP16_YYY_mul(&t0,&(w->c),&(y->c)); // (xc.yc)  could be anything
+			
+		FP16_YYY_neg(&t1,&t0);			  // -(xc.yc) 
+
+		FP16_YYY_add(&(w->c),&z2,&t1);		// wc = (xb.yb) - (xa.ya) + (xa+xc)(ya+yc) - (xc.yc)	=  xb.yb + xc.ya + xa.yc
+		FP16_YYY_add(&z3,&z3,&t1);			// (xb+xc)(yb+yc) -(xb.yb) - (xc.yc)					=  xb.yc + xc.yb
+		FP16_YYY_times_i(&t0);				// i.(xc.yc)
+		FP16_YYY_add(&(w->b),&(w->b),&t0);   // wb = (xa+xb)(ya+yb) -(xa.ya) -(xb.yb) +i(xc.yc)
+		FP16_YYY_norm(&z3);
+		FP16_YYY_times_i(&z3);				// i[(xb+xc)(yb+yc) -(xb.yb) - (xc.yc)]					= i(xb.yc + xc.yb)
+		FP16_YYY_add(&(w->a),&z0,&z3);		// wa = xa.ya + i(xb.yc + xc.yb)
+	} else {
+		if (w->type==FP_SPARSER)
+		{
+			FP48_YYY_smul(w,y);
+			return;
+		}
+// dense by sparser - 13m 
+#if SEXTIC_TWIST_ZZZ == D_TYPE
 		FP16_YYY_copy(&z3,&(w->b));
 		FP16_YYY_mul(&z0,&(w->a),&(y->a));
 
@@ -306,7 +407,6 @@
 		FP16_YYY_add(&z2,&z2,&t0);        // z2=z2-z0
 
 		FP16_YYY_add(&t0,&(w->a),&(w->c));
-
 		FP16_YYY_norm(&t0);
 		FP16_YYY_norm(&z3);
 
@@ -315,10 +415,8 @@
 
 		FP16_YYY_times_i(&z3);
 		FP16_YYY_add(&(w->a),&z0,&z3);
-	}
-
-	if (type==M_TYPE)
-	{ // y->b is zero
+#endif
+#if SEXTIC_TWIST_ZZZ == M_TYPE
 		FP16_YYY_mul(&z0,&(w->a),&(y->a));
 		FP16_YYY_add(&t0,&(w->a),&(w->b));
 		FP16_YYY_norm(&t0);
@@ -334,7 +432,6 @@
 		FP16_YYY_add(&z1,&z1,&t0);   // z1=z1-z0
 
 		FP16_YYY_copy(&(w->b),&z1);
-
 		FP16_YYY_copy(&z2,&t0);
 
 		FP16_YYY_add(&t0,&(w->a),&(w->c));
@@ -358,10 +455,113 @@
 		FP16_YYY_norm(&z3);
 		FP16_YYY_times_i(&z3);
 		FP16_YYY_add(&(w->a),&z0,&z3);
+#endif
 	}
+	w->type=FP_DENSE;
     FP48_YYY_norm(w);
 }
 
+/* FP48 multiplication w=w*y */
+/* catering for special case that arises from special form of ATE pairing line function */
+/* w and y are both sparser line functions - cost = 6m */ 
+void FP48_YYY_smul(FP48_YYY *w,FP48_YYY *y)
+{
+	FP8_YYY w1,w2,w3,ta,tb,tc,td,te,t;
+
+
+#if SEXTIC_TWIST_ZZZ == D_TYPE
+	FP8_YYY_mul(&w1,&(w->a).a,&(y->a).a); // A1.A2
+	FP8_YYY_mul(&w2,&(w->a).b,&(y->a).b); // B1.B2
+	FP8_YYY_mul(&w3,&(w->b).a,&(y->b).a); // C1.C2
+
+	FP8_YYY_add(&ta,&(w->a).a,&(w->a).b); // A1+B1
+	FP8_YYY_add(&tb,&(y->a).a,&(y->a).b); // A2+B2
+	FP8_YYY_norm(&ta);
+	FP8_YYY_norm(&tb);
+	FP8_YYY_mul(&tc,&ta,&tb);			// (A1+B1)(A2+B2)
+	FP8_YYY_add(&t,&w1,&w2);
+	FP8_YYY_neg(&t,&t);
+	FP8_YYY_add(&tc,&tc,&t);			// (A1+B1)(A2+B2)-A1.A2-B1*B2 =  (A1.B2+A2.B1)		
+				
+	FP8_YYY_add(&ta,&(w->a).a,&(w->b).a); // A1+C1
+	FP8_YYY_add(&tb,&(y->a).a,&(y->b).a); // A2+C2
+	FP8_YYY_norm(&ta);
+	FP8_YYY_norm(&tb);
+	FP8_YYY_mul(&td,&ta,&tb);			// (A1+C1)(A2+C2)
+	FP8_YYY_add(&t,&w1,&w3);
+	FP8_YYY_neg(&t,&t);
+	FP8_YYY_add(&td,&td,&t);			// (A1+C1)(A2+C2)-A1.A2-C1*C2 =  (A1.C2+A2.C1)		
+
+	FP8_YYY_add(&ta,&(w->a).b,&(w->b).a); // B1+C1
+	FP8_YYY_add(&tb,&(y->a).b,&(y->b).a); // B2+C2
+	FP8_YYY_norm(&ta);
+	FP8_YYY_norm(&tb);
+	FP8_YYY_mul(&te,&ta,&tb);			// (B1+C1)(B2+C2)
+	FP8_YYY_add(&t,&w2,&w3);
+	FP8_YYY_neg(&t,&t);
+	FP8_YYY_add(&te,&te,&t);			// (B1+C1)(B2+C2)-B1.B2-C1*C2 =  (B1.C2+B2.C1)		
+
+	FP8_YYY_times_i(&w2);
+	FP8_YYY_add(&w1,&w1,&w2);
+	FP16_YYY_from_FP8s(&(w->a),&w1,&tc);
+	FP16_YYY_from_FP8s(&(w->b),&td,&te); // only norm these 2
+	FP16_YYY_from_FP8(&(w->c),&w3);
+
+	FP16_YYY_norm(&(w->a));
+	FP16_YYY_norm(&(w->b));
+#endif
+#if SEXTIC_TWIST_ZZZ == M_TYPE
+	FP8_YYY_mul(&w1,&(w->a).a,&(y->a).a); // A1.A2
+	FP8_YYY_mul(&w2,&(w->a).b,&(y->a).b); // B1.B2
+	FP8_YYY_mul(&w3,&(w->c).b,&(y->c).b); // F1.F2
+
+	FP8_YYY_add(&ta,&(w->a).a,&(w->a).b); // A1+B1
+	FP8_YYY_add(&tb,&(y->a).a,&(y->a).b); // A2+B2
+	FP8_YYY_norm(&ta);
+	FP8_YYY_norm(&tb);
+	FP8_YYY_mul(&tc,&ta,&tb);			// (A1+B1)(A2+B2)
+	FP8_YYY_add(&t,&w1,&w2);
+	FP8_YYY_neg(&t,&t);
+	FP8_YYY_add(&tc,&tc,&t);			// (A1+B1)(A2+B2)-A1.A2-B1*B2 =  (A1.B2+A2.B1)		
+				
+	FP8_YYY_add(&ta,&(w->a).a,&(w->c).b); // A1+F1
+	FP8_YYY_add(&tb,&(y->a).a,&(y->c).b); // A2+F2
+	FP8_YYY_norm(&ta);
+	FP8_YYY_norm(&tb);
+	FP8_YYY_mul(&td,&ta,&tb);			// (A1+F1)(A2+F2)
+	FP8_YYY_add(&t,&w1,&w3);
+	FP8_YYY_neg(&t,&t);
+	FP8_YYY_add(&td,&td,&t);			// (A1+F1)(A2+F2)-A1.A2-F1*F2 =  (A1.F2+A2.F1)		
+
+	FP8_YYY_add(&ta,&(w->a).b,&(w->c).b); // B1+F1
+	FP8_YYY_add(&tb,&(y->a).b,&(y->c).b); // B2+F2
+	FP8_YYY_norm(&ta);
+	FP8_YYY_norm(&tb);
+	FP8_YYY_mul(&te,&ta,&tb);			// (B1+F1)(B2+F2)
+	FP8_YYY_add(&t,&w2,&w3);
+	FP8_YYY_neg(&t,&t);
+	FP8_YYY_add(&te,&te,&t);			// (B1+F1)(B2+F2)-B1.B2-F1*F2 =  (B1.F2+B2.F1)	
+
+	FP8_YYY_times_i(&w2);
+	FP8_YYY_add(&w1,&w1,&w2);
+	FP16_YYY_from_FP8s(&(w->a),&w1,&tc);
+
+	FP8_YYY_times_i(&w3);
+	FP8_YYY_norm(&w3);
+	FP16_YYY_from_FP8H(&(w->b),&w3);
+
+	FP8_YYY_norm(&te);
+	FP8_YYY_times_i(&te);
+	FP16_YYY_from_FP8s(&(w->c),&te,&td);
+
+	FP16_YYY_norm(&(w->a));
+	FP16_YYY_norm(&(w->c));
+#endif
+
+	w->type=FP_SPARSE;
+}
+
+
 /* Set w=1/x */
 /* SU= 600 */
 void FP48_YYY_inv(FP48_YYY *w,FP48_YYY *x)
@@ -399,7 +599,7 @@
     FP16_YYY_mul(&(w->a),&f0,&f3);
     FP16_YYY_mul(&(w->b),&f1,&f3);
     FP16_YYY_mul(&(w->c),&f2,&f3);
-
+	w->type=FP_DENSE;
 }
 
 /* constant time powering by small integer of max length bts */
@@ -724,8 +924,8 @@
   
 		FP16_YYY_qmul(&(w->b),&(w->b),f); FP16_YYY_times_i4(&(w->b)); FP16_YYY_times_i2(&(w->b)); 
 		FP16_YYY_qmul(&(w->c),&(w->c),&f2); FP16_YYY_times_i4(&(w->c)); FP16_YYY_times_i4(&(w->c)); FP16_YYY_times_i4(&(w->c)); 
-
 	}
+	w->type=FP_DENSE;
 }
 
 /* SU= 8 */
@@ -1028,4 +1228,6 @@
     FP16_YYY_cmove(&(f->a),&(g->a),d);
     FP16_YYY_cmove(&(f->b),&(g->b),d);
     FP16_YYY_cmove(&(f->c),&(g->c),d);
+	d=~(d-1);
+	f->type^=(f->type^g->type)&d;
 }
diff --git a/version3/c/fp48.h b/version3/c/fp48.h
index 75065b5..e0a1f80 100644
--- a/version3/c/fp48.h
+++ b/version3/c/fp48.h
@@ -12,6 +12,7 @@
     FP16_YYY a; /**< first part of FP12 */
     FP16_YYY b; /**< second part of FP12 */
     FP16_YYY c; /**< third part of FP12 */
+	int type;
 } FP48_YYY;
 
 extern const BIG_XXX Fra_YYY; /**< real part of BN curve Frobenius Constant */
@@ -82,15 +83,20 @@
 	@param y FP48 instance
  */
 extern void FP48_YYY_sqr(FP48_YYY *x,FP48_YYY *y);
-/**	@brief Fast multiplication of an FP48 by an FP48 that arises from an ATE pairing line function
+/**	@brief Fast multiplication of two sparse FP24s that arises from ATE pairing line functions
  *
-	Here the multiplier has a special form that can be exploited
 	@param x FP48 instance, on exit = x*y
 	@param y FP48 instance, of special form
-	@param t D_TYPE or M_TYPE twist
  */
-extern void FP48_YYY_smul(FP48_YYY *x,FP48_YYY *y,int t);
-/**	@brief Multiplication of two FP48s
+extern void FP48_YYY_smul(FP48_YYY *x,FP48_YYY *y);
+
+/**	@brief Fast multiplication of what may be sparse multiplicands
+ *
+	@param x FP48 instance, on exit = x*y
+	@param y FP48 instance, of special form
+ */
+extern void FP48_YYY_ssmul(FP48_YYY *x,FP48_YYY *y);
+/**	@brief Full unconditional Multiplication of two FP24s
  *
 	@param x FP48 instance, on exit = x*y
 	@param y FP48 instance, the multiplier
diff --git a/version3/c/pair.c b/version3/c/pair.c
index 57cad56..f88a3d5 100644
--- a/version3/c/pair.c
+++ b/version3/c/pair.c
@@ -131,6 +131,140 @@
     }
 
     FP12_YYY_from_FP4s(v,&a,&b,&c);
+	v->type=FP_SPARSER;
+}
+
+
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+int PAIR_ZZZ_nbits(BIG_XXX n3,BIG_XXX n)
+{
+	BIG_XXX x;
+    BIG_XXX_rcopy(x,CURVE_Bnx_ZZZ);
+
+#if PAIRING_FRIENDLY_ZZZ==BN
+    BIG_XXX_pmul(n,x,6);
+#if SIGN_OF_X_ZZZ==POSITIVEX
+	BIG_XXX_inc(n,2);
+#else
+    BIG_XXX_dec(n,2);
+#endif
+
+#else
+    BIG_XXX_copy(n,x);
+#endif
+
+    BIG_XXX_norm(n);
+	BIG_XXX_pmul(n3,n,3);
+	BIG_XXX_norm(n3);
+
+    return BIG_XXX_nbits(n3);
+}
+
+/*
+	For multi-pairing, product of n pairings
+	1. Declare FP12 array of length number of bits in Ate parameter
+	2. Initialise this array by calling PAIR_initmp()
+	3. Accumulate each pairing by calling PAIR_another() n times
+	4. Call PAIR_miller()
+	5. Call final exponentiation PAIR_fexp()
+*/
+
+/* prepare for multi-pairing */
+void PAIR_ZZZ_initmp(FP12_YYY r[])
+{
+	int i;
+	for (i=ATE_BITS_ZZZ-1; i>=0; i--)
+		FP12_YYY_one(&r[i]);
+	return;
+}
+
+/* basic Miller loop */
+void PAIR_ZZZ_miller(FP12_YYY *res,FP12_YYY r[])
+{
+	int i;
+    FP12_YYY_one(res);
+	for (i=ATE_BITS_ZZZ-1; i>=1; i--)
+	{
+		FP12_YYY_sqr(res,res);
+		FP12_YYY_ssmul(res,&r[i]);
+	}
+
+#if SIGN_OF_X_ZZZ==NEGATIVEX
+    FP12_YYY_conj(res,res);
+#endif
+	FP12_YYY_ssmul(res,&r[0]);
+	return;
+}
+
+/* Accumulate another set of line functions for n-pairing */
+void PAIR_ZZZ_another(FP12_YYY r[],ECP2_ZZZ* PV,ECP_ZZZ* QV)
+{
+    int i,j,nb,bt;
+	BIG_XXX x,n,n3;
+    FP12_YYY lv,lv2;
+    ECP2_ZZZ A,NP,P;
+	ECP_ZZZ Q;
+	FP_YYY Qx,Qy;
+#if PAIRING_FRIENDLY_ZZZ==BN
+	ECP2_ZZZ K;
+    FP2_YYY X;
+    FP_YYY_rcopy(&Qx,Fra_YYY);
+    FP_YYY_rcopy(&Qy,Frb_YYY);
+    FP2_YYY_from_FPs(&X,&Qx,&Qy);
+#if SEXTIC_TWIST_ZZZ==M_TYPE
+	FP2_YYY_inv(&X,&X);
+	FP2_YYY_norm(&X);
+#endif
+#endif
+
+	nb=PAIR_ZZZ_nbits(n3,n);
+
+	ECP2_ZZZ_copy(&P,PV);
+	ECP_ZZZ_copy(&Q,QV);
+
+	ECP2_ZZZ_affine(&P);
+	ECP_ZZZ_affine(&Q);
+
+	FP_YYY_copy(&Qx,&(Q.x));
+	FP_YYY_copy(&Qy,&(Q.y));
+
+	ECP2_ZZZ_copy(&A,&P);
+	ECP2_ZZZ_copy(&NP,&P); ECP2_ZZZ_neg(&NP);
+
+	for (i=nb-2; i>=1; i--)
+	{
+		PAIR_ZZZ_line(&lv,&A,&A,&Qx,&Qy);
+
+		bt=BIG_XXX_bit(n3,i)-BIG_XXX_bit(n,i); // bt=BIG_bit(n,i);
+		if (bt==1)
+		{
+			PAIR_ZZZ_line(&lv2,&A,&P,&Qx,&Qy);
+			FP12_YYY_smul(&lv,&lv2);
+		}
+		if (bt==-1)
+		{
+			PAIR_ZZZ_line(&lv2,&A,&NP,&Qx,&Qy);
+			FP12_YYY_smul(&lv,&lv2);
+		}
+		FP12_YYY_ssmul(&r[i],&lv);
+	}
+
+#if PAIRING_FRIENDLY_ZZZ==BN
+
+#if SIGN_OF_X_ZZZ==NEGATIVEX
+	ECP2_ZZZ_neg(&A);
+#endif
+
+	ECP2_ZZZ_copy(&K,&P);
+	ECP2_ZZZ_frob(&K,&X);
+	PAIR_ZZZ_line(&lv,&A,&K,&Qx,&Qy);
+	ECP2_ZZZ_frob(&K,&X);
+	ECP2_ZZZ_neg(&K);
+	PAIR_ZZZ_line(&lv2,&A,&K,&Qx,&Qy);
+	FP12_YYY_smul(&lv,&lv2);
+	FP12_YYY_ssmul(&r[0],&lv);
+
+#endif
 }
 
 /* Optimal R-ate pairing r=e(P,Q) */
@@ -142,7 +276,7 @@
     int i,nb,bt;
     ECP2_ZZZ A,NP,P;
 	ECP_ZZZ Q;
-    FP12_YYY lv;
+    FP12_YYY lv,lv2;
 #if PAIRING_FRIENDLY_ZZZ==BN
     ECP2_ZZZ KA;
     FP2_YYY X;
@@ -157,22 +291,7 @@
 #endif
 #endif
 
-    BIG_XXX_rcopy(x,CURVE_Bnx_ZZZ);
-
-#if PAIRING_FRIENDLY_ZZZ==BN
-    BIG_XXX_pmul(n,x,6);
-#if SIGN_OF_X_ZZZ==POSITIVEX
-    BIG_XXX_inc(n,2);
-#else
-    BIG_XXX_dec(n,2);
-#endif
-#else
-    BIG_XXX_copy(n,x);
-#endif
-
-    BIG_XXX_norm(n);
-    BIG_XXX_pmul(n3,n,3);
-    BIG_XXX_norm(n3);
+	nb=PAIR_ZZZ_nbits(n3,n);
 
 	ECP2_ZZZ_copy(&P,P1);
 	ECP_ZZZ_copy(&Q,Q1);
@@ -187,26 +306,25 @@
 	ECP2_ZZZ_copy(&NP,&P); ECP2_ZZZ_neg(&NP);
 
     FP12_YYY_one(r);
-    nb=BIG_XXX_nbits(n3);  //n
 
     /* Main Miller Loop */
     for (i=nb-2; i>=1; i--)   //0
     {
-        FP12_YYY_sqr(r,r);
+		FP12_YYY_sqr(r,r);
         PAIR_ZZZ_line(&lv,&A,&A,&Qx,&Qy);
-        FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-        bt=BIG_XXX_bit(n3,i)-BIG_XXX_bit(n,i);
+
+		bt=BIG_XXX_bit(n3,i)-BIG_XXX_bit(n,i); // bt=BIG_bit(n,i);
         if (bt==1)
         {
-
-            PAIR_ZZZ_line(&lv,&A,&P,&Qx,&Qy);
-            FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            PAIR_ZZZ_line(&lv2,&A,&P,&Qx,&Qy);
+            FP12_YYY_smul(&lv,&lv2);
         }
-        if (bt==-1)
-        {
-            PAIR_ZZZ_line(&lv,&A,&NP,&Qx,&Qy);
-            FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-        }
+		if (bt==-1)
+		{
+            PAIR_ZZZ_line(&lv2,&A,&NP,&Qx,&Qy);
+            FP12_YYY_smul(&lv,&lv2);
+		}
+		FP12_YYY_ssmul(r,&lv);
 
     }
 
@@ -217,17 +335,19 @@
 
     /* R-ate fixup required for BN curves */
 #if PAIRING_FRIENDLY_ZZZ==BN
-    ECP2_ZZZ_copy(&KA,&P);
-    ECP2_ZZZ_frob(&KA,&X);
+
 #if SIGN_OF_X_ZZZ==NEGATIVEX
     ECP2_ZZZ_neg(&A);
 #endif
+
+    ECP2_ZZZ_copy(&KA,&P);
+    ECP2_ZZZ_frob(&KA,&X);
     PAIR_ZZZ_line(&lv,&A,&KA,&Qx,&Qy);
-    FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
     ECP2_ZZZ_frob(&KA,&X);
     ECP2_ZZZ_neg(&KA);
-    PAIR_ZZZ_line(&lv,&A,&KA,&Qx,&Qy);
-    FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+    PAIR_ZZZ_line(&lv2,&A,&KA,&Qx,&Qy);
+	FP12_YYY_smul(&lv,&lv2);
+    FP12_YYY_ssmul(r,&lv);
 #endif
 }
 
@@ -239,7 +359,7 @@
     int i,nb,bt;
     ECP2_ZZZ A,B,NP,NR,P,R;
 	ECP_ZZZ Q,S;
-    FP12_YYY lv;
+    FP12_YYY lv,lv2;
 #if PAIRING_FRIENDLY_ZZZ==BN
     FP2_YYY X;
     ECP2_ZZZ K;
@@ -253,23 +373,7 @@
     FP2_YYY_norm(&X);
 #endif
 #endif
-
-    BIG_XXX_rcopy(x,CURVE_Bnx_ZZZ);
-
-#if PAIRING_FRIENDLY_ZZZ==BN
-    BIG_XXX_pmul(n,x,6);
-#if SIGN_OF_X_ZZZ==POSITIVEX
-    BIG_XXX_inc(n,2);
-#else
-    BIG_XXX_dec(n,2);
-#endif
-#else
-    BIG_XXX_copy(n,x);
-#endif
-
-    BIG_XXX_norm(n);
-    BIG_XXX_pmul(n3,n,3);
-    BIG_XXX_norm(n3);
+	nb=PAIR_ZZZ_nbits(n3,n);
 
 	ECP2_ZZZ_copy(&P,P1);
 	ECP_ZZZ_copy(&Q,Q1);
@@ -295,38 +399,32 @@
 	ECP2_ZZZ_copy(&NP,&P); ECP2_ZZZ_neg(&NP);
 	ECP2_ZZZ_copy(&NR,&R); ECP2_ZZZ_neg(&NR);
 
-
     FP12_YYY_one(r);
-    nb=BIG_XXX_nbits(n3);
 
     /* Main Miller Loop */
     for (i=nb-2; i>=1; i--)
     {
         FP12_YYY_sqr(r,r);
         PAIR_ZZZ_line(&lv,&A,&A,&Qx,&Qy);
-        FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+        PAIR_ZZZ_line(&lv2,&B,&B,&Sx,&Sy);
+		FP12_YYY_smul(&lv,&lv2);
+        FP12_YYY_ssmul(r,&lv);
 
-        PAIR_ZZZ_line(&lv,&B,&B,&Sx,&Sy);
-        FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-
-        bt=BIG_XXX_bit(n3,i)-BIG_XXX_bit(n,i);
+		bt=BIG_XXX_bit(n3,i)-BIG_XXX_bit(n,i); // bt=BIG_bit(n,i);
         if (bt==1)
         {
             PAIR_ZZZ_line(&lv,&A,&P,&Qx,&Qy);
-            FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-
-            PAIR_ZZZ_line(&lv,&B,&R,&Sx,&Sy);
-            FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            PAIR_ZZZ_line(&lv2,&B,&R,&Sx,&Sy);
+			FP12_YYY_smul(&lv,&lv2);
+            FP12_YYY_ssmul(r,&lv);
         }
-
-        if (bt==-1)
-        {
+		if (bt==-1)
+		{
             PAIR_ZZZ_line(&lv,&A,&NP,&Qx,&Qy);
-            FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
- 
-            PAIR_ZZZ_line(&lv,&B,&NR,&Sx,&Sy);
-            FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-        }
+            PAIR_ZZZ_line(&lv2,&B,&NR,&Sx,&Sy);
+			FP12_YYY_smul(&lv,&lv2);
+            FP12_YYY_ssmul(r,&lv);
+		}
 
     }
 
@@ -346,24 +444,21 @@
 
     ECP2_ZZZ_copy(&K,&P);
     ECP2_ZZZ_frob(&K,&X);
-
     PAIR_ZZZ_line(&lv,&A,&K,&Qx,&Qy);
-    FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
     ECP2_ZZZ_frob(&K,&X);
     ECP2_ZZZ_neg(&K);
-    PAIR_ZZZ_line(&lv,&A,&K,&Qx,&Qy);
-    FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+    PAIR_ZZZ_line(&lv2,&A,&K,&Qx,&Qy);
+	FP12_YYY_smul(&lv,&lv2);
+    FP12_YYY_ssmul(r,&lv);
 
     ECP2_ZZZ_copy(&K,&R);
     ECP2_ZZZ_frob(&K,&X);
-
     PAIR_ZZZ_line(&lv,&B,&K,&Sx,&Sy);
-    FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
     ECP2_ZZZ_frob(&K,&X);
     ECP2_ZZZ_neg(&K);
-    PAIR_ZZZ_line(&lv,&B,&K,&Sx,&Sy);
-    FP12_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-
+    PAIR_ZZZ_line(&lv2,&B,&K,&Sx,&Sy);
+	FP12_YYY_smul(&lv,&lv2);
+    FP12_YYY_ssmul(r,&lv);
 #endif
 }
 
diff --git a/version3/c/pair.h b/version3/c/pair.h
index 81b710c..f03b329 100644
--- a/version3/c/pair.h
+++ b/version3/c/pair.h
@@ -42,6 +42,16 @@
 extern const BIG_XXX CURVE_BB_ZZZ[4][4]; /**< BN curve constant for GS decomposition */
 
 /* Pairing function prototypes */
+
+/**	@brief Precompute line functions for n-pairing
+ *
+	@param r array of precomputed FP12 products of line functions
+	@param PV ECP2 instance, an element of G2
+	@param QV ECP instance, an element of G1
+
+ */
+extern void PAIR_ZZZ_another(FP12_YYY r[],ECP2_ZZZ* PV,ECP_ZZZ* QV);
+
 /**	@brief Calculate Miller loop for Optimal ATE pairing e(P,Q)
  *
 	@param r FP12 result of the pairing calculation e(P,Q)
@@ -98,6 +108,29 @@
  */
 extern int PAIR_ZZZ_GTmember(FP12_YYY *x);
 
+/**	@brief Prepare Ate parameter
+ *
+	@param n BIG parameter
+	@param n3 BIG paramter = 3*n
+	@return number of nits in n3
 
+ */
+extern int PAIR_ZZZ_nbits(BIG_XXX n3,BIG_XXX n);
+
+/**	@brief Initialise structure for multi-pairing
+ *
+	@param r FP12 array, to be initialised to 1
+
+ */
+extern void PAIR_ZZZ_initmp(FP12_YYY r[]);
+
+
+/**	@brief Miller loop
+ *
+ 	@param res FP12 result
+	@param r FP12 precomputed array of accumulated line functions
+
+ */
+extern void PAIR_ZZZ_miller(FP12_YYY *res,FP12_YYY r[]);
 
 #endif
diff --git a/version3/c/pair192.c b/version3/c/pair192.c
index 2e4f4e0..d543bbf 100644
--- a/version3/c/pair192.c
+++ b/version3/c/pair192.c
@@ -132,6 +132,100 @@
     }
 
     FP24_YYY_from_FP8s(v,&a,&b,&c);
+	v->type=FP_SPARSER;
+}
+
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+int PAIR_ZZZ_nbits(BIG_XXX n3,BIG_XXX n)
+{
+	BIG_XXX x;
+    BIG_XXX_rcopy(x,CURVE_Bnx_ZZZ);
+
+    BIG_XXX_copy(n,x);
+    BIG_XXX_norm(n);
+	BIG_XXX_pmul(n3,n,3);
+	BIG_XXX_norm(n3);
+
+    return BIG_XXX_nbits(n3);
+}
+
+/*
+	For multi-pairing, product of n pairings
+	1. Declare FP24 array of length number of bits in Ate parameter
+	2. Initialise this array by calling PAIR_initmp()
+	3. Accumulate each pairing by calling PAIR_another() n times
+	4. Call PAIR_miller()
+	5. Call final exponentiation PAIR_fexp()
+*/
+
+/* prepare for multi-pairing */
+void PAIR_ZZZ_initmp(FP24_YYY r[])
+{
+	int i;
+	for (i=ATE_BITS_ZZZ-1; i>=0; i--)
+		FP24_YYY_one(&r[i]);
+	return;
+}
+
+/* basic Miller loop */
+void PAIR_ZZZ_miller(FP24_YYY *res,FP24_YYY r[])
+{
+	int i;
+    FP24_YYY_one(res);
+	for (i=ATE_BITS_ZZZ-1; i>=1; i--)
+	{
+		FP24_YYY_sqr(res,res);
+		FP24_YYY_ssmul(res,&r[i]);
+	}
+
+#if SIGN_OF_X_ZZZ==NEGATIVEX
+    FP24_YYY_conj(res,res);
+#endif
+	FP24_YYY_ssmul(res,&r[0]);
+	return;
+}
+
+/* Accumulate another set of line functions for n-pairing */
+void PAIR_ZZZ_another(FP24_YYY r[],ECP4_ZZZ* PV,ECP_ZZZ* QV)
+{
+    int i,j,nb,bt;
+	BIG_XXX x,n,n3;
+    FP24_YYY lv,lv2;
+    ECP4_ZZZ A,NP,P;
+	ECP_ZZZ Q;
+	FP_YYY Qx,Qy;
+
+	nb=PAIR_ZZZ_nbits(n3,n);
+
+	ECP4_ZZZ_copy(&P,PV);
+	ECP_ZZZ_copy(&Q,QV);
+
+	ECP4_ZZZ_affine(&P);
+	ECP_ZZZ_affine(&Q);
+
+	FP_YYY_copy(&Qx,&(Q.x));
+	FP_YYY_copy(&Qy,&(Q.y));
+
+	ECP4_ZZZ_copy(&A,&P);
+	ECP4_ZZZ_copy(&NP,&P); ECP4_ZZZ_neg(&NP);
+
+	for (i=nb-2; i>=1; i--)
+	{
+		PAIR_ZZZ_line(&lv,&A,&A,&Qx,&Qy);
+
+		bt=BIG_XXX_bit(n3,i)-BIG_XXX_bit(n,i); // bt=BIG_bit(n,i);
+		if (bt==1)
+		{
+			PAIR_ZZZ_line(&lv2,&A,&P,&Qx,&Qy);
+			FP24_YYY_smul(&lv,&lv2);
+		}
+		if (bt==-1)
+		{
+			PAIR_ZZZ_line(&lv2,&A,&NP,&Qx,&Qy);
+			FP24_YYY_smul(&lv,&lv2);
+		}
+		FP24_YYY_ssmul(&r[i],&lv);
+	}
 }
 
 /* Optimal R-ate pairing r=e(P,Q) */
@@ -139,17 +233,12 @@
 {
     BIG_XXX x,n,n3;
 	FP_YYY Qx,Qy;
-    int i,j,nb,bt;
+    int i,nb,bt;
     ECP4_ZZZ A,NP,P;
 	ECP_ZZZ Q;
-    FP24_YYY lv;
+    FP24_YYY lv,lv2;
 
-    BIG_XXX_rcopy(x,CURVE_Bnx_ZZZ);
-
-    BIG_XXX_copy(n,x);
-
-	BIG_XXX_pmul(n3,n,3);
-	BIG_XXX_norm(n3);
+	nb=PAIR_ZZZ_nbits(n3,n);
 
 	ECP4_ZZZ_copy(&P,P1);
 	ECP_ZZZ_copy(&Q,Q1);
@@ -165,29 +254,25 @@
 	ECP4_ZZZ_copy(&NP,&P); ECP4_ZZZ_neg(&NP);
 
     FP24_YYY_one(r);
-    nb=BIG_XXX_nbits(n3);  // n3
 
-	j=0;
     /* Main Miller Loop */
     for (i=nb-2; i>=1; i--)
     {
-		j++;
 		FP24_YYY_sqr(r,r);
         PAIR_ZZZ_line(&lv,&A,&A,&Qx,&Qy);
-        FP24_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
 
-		bt= BIG_XXX_bit(n3,i)-BIG_XXX_bit(n,i);  
+		bt= BIG_XXX_bit(n3,i)-BIG_XXX_bit(n,i);  // BIG_bit(n,i); 
         if (bt==1)
         {
-            PAIR_ZZZ_line(&lv,&A,&P,&Qx,&Qy);
-            FP24_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            PAIR_ZZZ_line(&lv2,&A,&P,&Qx,&Qy);
+            FP24_YYY_smul(&lv,&lv2);
         }
 		if (bt==-1)
 		{
-            PAIR_ZZZ_line(&lv,&A,&NP,&Qx,&Qy);
-            FP24_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            PAIR_ZZZ_line(&lv2,&A,&NP,&Qx,&Qy);
+            FP24_YYY_smul(&lv,&lv2);
 		}
-
+        FP24_YYY_ssmul(r,&lv);
     }
 
 #if SIGN_OF_X_ZZZ==NEGATIVEX
@@ -204,13 +289,8 @@
     int i,nb,bt;
     ECP4_ZZZ A,B,NP,NR,P,R;
 	ECP_ZZZ Q,S;
-    FP24_YYY lv;
-
-    BIG_XXX_rcopy(x,CURVE_Bnx_ZZZ);
-    BIG_XXX_copy(n,x);
-
-	BIG_XXX_pmul(n3,n,3);
-	BIG_XXX_norm(n3);
+    FP24_YYY lv,lv2;
+	nb=PAIR_ZZZ_nbits(n3,n);
 
 	ECP4_ZZZ_copy(&P,P1);
 	ECP_ZZZ_copy(&Q,Q1);
@@ -224,7 +304,6 @@
 	ECP4_ZZZ_affine(&R);
 	ECP_ZZZ_affine(&S);
 
-
     FP_YYY_copy(&Qx,&(Q.x));
     FP_YYY_copy(&Qy,&(Q.y));
 
@@ -236,35 +315,31 @@
 	ECP4_ZZZ_copy(&NP,&P); ECP4_ZZZ_neg(&NP);
 	ECP4_ZZZ_copy(&NR,&R); ECP4_ZZZ_neg(&NR);
 
-
     FP24_YYY_one(r);
-    nb=BIG_XXX_nbits(n3);
 
     /* Main Miller Loop */
     for (i=nb-2; i>=1; i--)
     {
-        FP24_YYY_sqr(r,r);
+		FP24_YYY_sqr(r,r);
         PAIR_ZZZ_line(&lv,&A,&A,&Qx,&Qy);
-        FP24_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+        PAIR_ZZZ_line(&lv2,&B,&B,&Sx,&Sy);
+		FP24_YYY_smul(&lv,&lv2);
+        FP24_YYY_ssmul(r,&lv);
 
-        PAIR_ZZZ_line(&lv,&B,&B,&Sx,&Sy);
-        FP24_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-
-		bt=BIG_XXX_bit(n3,i)-BIG_XXX_bit(n,i); 
+		bt=BIG_XXX_bit(n3,i)-BIG_XXX_bit(n,i); // bt=BIG_bit(n,i);
         if (bt==1)
         {
             PAIR_ZZZ_line(&lv,&A,&P,&Qx,&Qy);
-            FP24_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-
-            PAIR_ZZZ_line(&lv,&B,&R,&Sx,&Sy);
-            FP24_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+			PAIR_ZZZ_line(&lv2,&B,&R,&Sx,&Sy);
+            FP24_YYY_smul(&lv,&lv2);
+            FP24_YYY_ssmul(r,&lv);
         }
 		if (bt==-1)
 		{
             PAIR_ZZZ_line(&lv,&A,&NP,&Qx,&Qy);
-            FP24_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-            PAIR_ZZZ_line(&lv,&B,&NR,&Sx,&Sy);
-            FP24_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+			PAIR_ZZZ_line(&lv2,&B,&NR,&Sx,&Sy);
+            FP24_YYY_smul(&lv,&lv2);
+            FP24_YYY_ssmul(r,&lv);
 		}
 	}
 
diff --git a/version3/c/pair192.h b/version3/c/pair192.h
index 2e50d51..a4fa7d3 100644
--- a/version3/c/pair192.h
+++ b/version3/c/pair192.h
@@ -17,6 +17,16 @@
 extern const BIG_XXX CURVE_BB_ZZZ[4][4]; /**< BN curve constant for GS decomposition */
 
 /* Pairing function prototypes */
+
+/**	@brief Precompute line functions for n-pairing
+ *
+	@param r array of precomputed FP24 products of line functions
+	@param PV ECP4 instance, an element of G2
+	@param QV ECP instance, an element of G1
+
+ */
+extern void PAIR_ZZZ_another(FP24_YYY r[],ECP4_ZZZ* PV,ECP_ZZZ* QV);
+
 /**	@brief Calculate Miller loop for Optimal ATE pairing e(P,Q)
  *
 	@param r FP24 result of the pairing calculation e(P,Q)
@@ -73,5 +83,30 @@
  */
 extern int PAIR_ZZZ_GTmember(FP24_YYY *x);
 
+/**	@brief Prepare Ate parameter
+ *
+	@param n BIG parameter
+	@param n3 BIG paramter = 3*n
+	@return number of nits in n3
+
+ */
+extern int PAIR_ZZZ_nbits(BIG_XXX n3,BIG_XXX n);
+
+/**	@brief Initialise structure for multi-pairing
+ *
+	@param r FP24 array, to be initialised to 1
+
+ */
+extern void PAIR_ZZZ_initmp(FP24_YYY r[]);
+
+
+/**	@brief Miller loop
+ *
+ 	@param res FP24 result
+	@param r FP24 precomputed array of accumulated line functions
+
+ */
+extern void PAIR_ZZZ_miller(FP24_YYY *res,FP24_YYY r[]);
+
 
 #endif
diff --git a/version3/c/pair256.c b/version3/c/pair256.c
index 2f71691..f378315 100644
--- a/version3/c/pair256.c
+++ b/version3/c/pair256.c
@@ -129,6 +129,100 @@
     }
 
     FP48_YYY_from_FP16s(v,&a,&b,&c);
+	v->type=FP_SPARSER;
+}
+
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+int PAIR_ZZZ_nbits(BIG_XXX n3,BIG_XXX n)
+{
+	BIG_XXX x;
+    BIG_XXX_rcopy(x,CURVE_Bnx_ZZZ);
+
+    BIG_XXX_copy(n,x);
+    BIG_XXX_norm(n);
+	BIG_XXX_pmul(n3,n,3);
+	BIG_XXX_norm(n3);
+
+    return BIG_XXX_nbits(n3);
+}
+
+/*
+	For multi-pairing, product of n pairings
+	1. Declare FP24 array of length number of bits in Ate parameter
+	2. Initialise this array by calling PAIR_initmp()
+	3. Accumulate each pairing by calling PAIR_another() n times
+	4. Call PAIR_miller()
+	5. Call final exponentiation PAIR_fexp()
+*/
+
+/* prepare for multi-pairing */
+void PAIR_ZZZ_initmp(FP48_YYY r[])
+{
+	int i;
+	for (i=ATE_BITS_ZZZ-1; i>=0; i--)
+		FP48_YYY_one(&r[i]);
+	return;
+}
+
+/* basic Miller loop */
+void PAIR_ZZZ_miller(FP48_YYY *res,FP48_YYY r[])
+{
+	int i;
+    FP48_YYY_one(res);
+	for (i=ATE_BITS_ZZZ-1; i>=1; i--)
+	{
+		FP48_YYY_sqr(res,res);
+		FP48_YYY_ssmul(res,&r[i]);
+	}
+
+#if SIGN_OF_X_ZZZ==NEGATIVEX
+    FP48_YYY_conj(res,res);
+#endif
+	FP48_YYY_ssmul(res,&r[0]);
+	return;
+}
+
+/* Accumulate another set of line functions for n-pairing */
+void PAIR_ZZZ_another(FP48_YYY r[],ECP8_ZZZ* PV,ECP_ZZZ* QV)
+{
+    int i,j,nb,bt;
+	BIG_XXX x,n,n3;
+    FP48_YYY lv,lv2;
+    ECP8_ZZZ A,NP,P;
+	ECP_ZZZ Q;
+	FP_YYY Qx,Qy;
+
+	nb=PAIR_ZZZ_nbits(n3,n);
+
+	ECP8_ZZZ_copy(&P,PV);
+	ECP_ZZZ_copy(&Q,QV);
+
+	ECP8_ZZZ_affine(&P);
+	ECP_ZZZ_affine(&Q);
+
+	FP_YYY_copy(&Qx,&(Q.x));
+	FP_YYY_copy(&Qy,&(Q.y));
+
+	ECP8_ZZZ_copy(&A,&P);
+	ECP8_ZZZ_copy(&NP,&P); ECP8_ZZZ_neg(&NP);
+
+	for (i=nb-2; i>=1; i--)
+	{
+		PAIR_ZZZ_line(&lv,&A,&A,&Qx,&Qy);
+
+		bt=BIG_XXX_bit(n3,i)-BIG_XXX_bit(n,i); // bt=BIG_bit(n,i);
+		if (bt==1)
+		{
+			PAIR_ZZZ_line(&lv2,&A,&P,&Qx,&Qy);
+			FP48_YYY_smul(&lv,&lv2);
+		}
+		if (bt==-1)
+		{
+			PAIR_ZZZ_line(&lv2,&A,&NP,&Qx,&Qy);
+			FP48_YYY_smul(&lv,&lv2);
+		}
+		FP48_YYY_ssmul(&r[i],&lv);
+	}
 }
 
 /* Optimal R-ate pairing r=e(P,Q) */
@@ -136,17 +230,12 @@
 {
     BIG_XXX x,n,n3;
 	FP_YYY Qx,Qy;
-    int i,j,nb,bt;
+    int i,nb,bt;
     ECP8_ZZZ A,NP,P;
 	ECP_ZZZ Q;
-    FP48_YYY lv;
+    FP48_YYY lv,lv2;
 
-    BIG_XXX_rcopy(x,CURVE_Bnx_ZZZ);
-
-    BIG_XXX_copy(n,x);
-
-	BIG_XXX_pmul(n3,n,3);
-	BIG_XXX_norm(n3);
+	nb=PAIR_ZZZ_nbits(n3,n);
 
 	ECP8_ZZZ_copy(&P,P1);
 	ECP_ZZZ_copy(&Q,Q1);
@@ -162,29 +251,25 @@
 	ECP8_ZZZ_copy(&NP,&P); ECP8_ZZZ_neg(&NP);
 
     FP48_YYY_one(r);
-    nb=BIG_XXX_nbits(n3);  // n3
 
-	j=0;
     /* Main Miller Loop */
     for (i=nb-2; i>=1; i--)
     {
-		j++;
 		FP48_YYY_sqr(r,r);
         PAIR_ZZZ_line(&lv,&A,&A,&Qx,&Qy);
-        FP48_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
 
-		bt= BIG_XXX_bit(n3,i)-BIG_XXX_bit(n,i); 
+		bt= BIG_XXX_bit(n3,i)-BIG_XXX_bit(n,i);  // BIG_bit(n,i); 
         if (bt==1)
         {
-            PAIR_ZZZ_line(&lv,&A,&P,&Qx,&Qy);
-            FP48_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            PAIR_ZZZ_line(&lv2,&A,&P,&Qx,&Qy);
+            FP48_YYY_smul(&lv,&lv2);
         }
 		if (bt==-1)
 		{
-            PAIR_ZZZ_line(&lv,&A,&NP,&Qx,&Qy);
-            FP48_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            PAIR_ZZZ_line(&lv2,&A,&NP,&Qx,&Qy);
+            FP48_YYY_smul(&lv,&lv2);
 		}
-
+        FP48_YYY_ssmul(r,&lv);
     }
 
 #if SIGN_OF_X_ZZZ==NEGATIVEX
@@ -201,13 +286,9 @@
     int i,nb,bt;
     ECP8_ZZZ A,B,NP,NR,P,R;
 	ECP_ZZZ Q,S;
-    FP48_YYY lv;
+    FP48_YYY lv,lv2;
 
-    BIG_XXX_rcopy(x,CURVE_Bnx_ZZZ);
-    BIG_XXX_copy(n,x);
-
-	BIG_XXX_pmul(n3,n,3);
-	BIG_XXX_norm(n3);
+	nb=PAIR_ZZZ_nbits(n3,n);
 
 	ECP8_ZZZ_copy(&P,P1);
 	ECP_ZZZ_copy(&Q,Q1);
@@ -232,35 +313,31 @@
 	ECP8_ZZZ_copy(&NP,&P); ECP8_ZZZ_neg(&NP);
 	ECP8_ZZZ_copy(&NR,&R); ECP8_ZZZ_neg(&NR);
 
-
     FP48_YYY_one(r);
-    nb=BIG_XXX_nbits(n3);
 
     /* Main Miller Loop */
     for (i=nb-2; i>=1; i--)
     {
         FP48_YYY_sqr(r,r);
         PAIR_ZZZ_line(&lv,&A,&A,&Qx,&Qy);
-        FP48_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+		PAIR_ZZZ_line(&lv2,&B,&B,&Sx,&Sy);
+        FP48_YYY_smul(&lv,&lv2);
+        FP48_YYY_ssmul(r,&lv);
 
-        PAIR_ZZZ_line(&lv,&B,&B,&Sx,&Sy);
-        FP48_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-
-		bt=BIG_XXX_bit(n3,i)-BIG_XXX_bit(n,i); 
+		bt=BIG_XXX_bit(n3,i)-BIG_XXX_bit(n,i); // bt=BIG_bit(n,i);
         if (bt==1)
         {
             PAIR_ZZZ_line(&lv,&A,&P,&Qx,&Qy);
-            FP48_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-
-            PAIR_ZZZ_line(&lv,&B,&R,&Sx,&Sy);
-            FP48_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            PAIR_ZZZ_line(&lv2,&B,&R,&Sx,&Sy);
+			FP48_YYY_smul(&lv,&lv2);
+            FP48_YYY_ssmul(r,&lv);
         }
 		if (bt==-1)
 		{
             PAIR_ZZZ_line(&lv,&A,&NP,&Qx,&Qy);
-            FP48_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-            PAIR_ZZZ_line(&lv,&B,&NR,&Sx,&Sy);
-            FP48_YYY_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            PAIR_ZZZ_line(&lv2,&B,&NR,&Sx,&Sy);
+            FP48_YYY_smul(&lv,&lv2);
+            FP48_YYY_ssmul(r,&lv);
 		}
 	}
 
diff --git a/version3/c/pair256.h b/version3/c/pair256.h
index d270f76..1971863 100644
--- a/version3/c/pair256.h
+++ b/version3/c/pair256.h
@@ -17,6 +17,16 @@
 extern const BIG_XXX CURVE_BB_ZZZ[4][4]; /**< BN curve constant for GS decomposition */
 
 /* Pairing function prototypes */
+
+/**	@brief Precompute line functions for n-pairing
+ *
+	@param r array of precomputed FP48 products of line functions
+	@param PV ECP8 instance, an element of G2
+	@param QV ECP instance, an element of G1
+
+ */
+extern void PAIR_ZZZ_another(FP48_YYY r[],ECP8_ZZZ* PV,ECP_ZZZ* QV);
+
 /**	@brief Calculate Miller loop for Optimal ATE pairing e(P,Q)
  *
 	@param r FP48 result of the pairing calculation e(P,Q)
@@ -73,5 +83,28 @@
  */
 extern int PAIR_ZZZ_GTmember(FP48_YYY *x);
 
+/**	@brief Prepare Ate parameter
+ *
+	@param n BIG parameter
+	@param n3 BIG paramter = 3*n
+	@return number of nits in n3
 
+ */
+extern int PAIR_ZZZ_nbits(BIG_XXX n3,BIG_XXX n);
+
+/**	@brief Initialise structure for multi-pairing
+ *
+	@param r FP48 array, to be initialised to 1
+
+ */
+extern void PAIR_ZZZ_initmp(FP48_YYY r[]);
+
+
+/**	@brief Miller loop
+ *
+ 	@param res FP48 result
+	@param r FP48 precomputed array of accumulated line functions
+
+ */
+extern void PAIR_ZZZ_miller(FP48_YYY *res,FP48_YYY r[]);
 #endif
diff --git a/version3/cpp/amcl.h b/version3/cpp/amcl.h
index b9cfea2..c05e0c1 100644
--- a/version3/cpp/amcl.h
+++ b/version3/cpp/amcl.h
@@ -52,6 +52,12 @@
 #define D_TYPE 0
 #define M_TYPE 1
 
+#define FP_ZERO 0
+#define FP_UNITY 1
+#define FP_SPARSER 2
+#define FP_SPARSE 3
+#define FP_DENSE 4
+
 /**
  * @brief SHA256 hash function instance */
 typedef struct
diff --git a/version3/cpp/benchtest_all.cpp b/version3/cpp/benchtest_all.cpp
index 9589f98..6044068 100644
--- a/version3/cpp/benchtest_all.cpp
+++ b/version3/cpp/benchtest_all.cpp
@@ -949,7 +949,7 @@
 		RSA_KEY_PAIR(RNG,65537,&priv,&pub,NULL,NULL);
 		iterations++;
 		elapsed=(clock()-start)/(double)CLOCKS_PER_SEC;
-    } while (elapsed<MIN_TIME || iterations<MIN_ITERS);
+    } while (elapsed<MIN_TIME || iterations<1);
     elapsed=1000.0*elapsed/iterations;
     printf("RSA gen - %8d iterations  ",iterations);
     printf(" %8.2lf ms per iteration\n",elapsed);
diff --git a/version3/cpp/bls.cpp b/version3/cpp/bls.cpp
index ded8b3c..2167e6d 100644
--- a/version3/cpp/bls.cpp
+++ b/version3/cpp/bls.cpp
@@ -73,7 +73,6 @@
 }
 
 /* Verify signature given message m, the signature SIG, and the public key W */
-
 int ZZZ::BLS_VERIFY(octet *SIG,char *m,octet *W)
 {
 	FP12 v;
@@ -84,8 +83,19 @@
 	ECP2_generator(&G);
 	ECP2_fromOctet(&PK,W);
 	ECP_neg(&D);
-    PAIR_double_ate(&v,&G,&D,&PK,&HM);
-    PAIR_fexp(&v);
+
+// Use new multi-pairing mechanism 
+
+	FP12 r[ATE_BITS_ZZZ];
+	PAIR_initmp(r);
+	PAIR_another(r,&G,&D);
+	PAIR_another(r,&PK,&HM);
+	PAIR_miller(&v,r);
+
+//.. or alternatively
+//    PAIR_double_ate(&v,&G,&D,&PK,&HM);
+
+	PAIR_fexp(&v);
     if (FP12_isunity(&v)) return BLS_OK;
 	return BLS_FAIL;
 }
diff --git a/version3/cpp/bls192.cpp b/version3/cpp/bls192.cpp
index 683405c..0a8a63c 100644
--- a/version3/cpp/bls192.cpp
+++ b/version3/cpp/bls192.cpp
@@ -73,7 +73,6 @@
 }
 
 /* Verify signature given message m, the signature SIG, and the public key W */
-
 int ZZZ::BLS_VERIFY(octet *SIG,char *m,octet *W)
 {
 	FP24 v;
@@ -84,7 +83,18 @@
 	ECP4_generator(&G);
 	ECP4_fromOctet(&PK,W);
 	ECP_neg(&D);
-    PAIR_double_ate(&v,&G,&D,&PK,&HM);
+
+// Use new multi-pairing mechanism 
+
+	FP24 r[ATE_BITS_ZZZ];
+	PAIR_initmp(r);
+	PAIR_another(r,&G,&D);
+	PAIR_another(r,&PK,&HM);
+	PAIR_miller(&v,r);
+
+//.. or alternatively
+//    PAIR_double_ate(&v,&G,&D,&PK,&HM);
+
     PAIR_fexp(&v);
     if (FP24_isunity(&v)) return BLS_OK;
 	return BLS_FAIL;
diff --git a/version3/cpp/bls256.cpp b/version3/cpp/bls256.cpp
index 24aacc2..77afbc7 100644
--- a/version3/cpp/bls256.cpp
+++ b/version3/cpp/bls256.cpp
@@ -73,7 +73,6 @@
 }
 
 /* Verify signature of message m, the signature SIG, and the public key W */
-
 int ZZZ::BLS_VERIFY(octet *SIG,char *m,octet *W)
 {
 	FP48 v;
@@ -84,7 +83,16 @@
 	ECP8_generator(&G);
 	ECP8_fromOctet(&PK,W);
 	ECP_neg(&D);
-    PAIR_double_ate(&v,&G,&D,&PK,&HM);
+// Use new multi-pairing mechanism 
+
+	FP48 r[ATE_BITS_ZZZ];
+	PAIR_initmp(r);
+	PAIR_another(r,&G,&D);
+	PAIR_another(r,&PK,&HM);
+	PAIR_miller(&v,r);
+
+//.. or alternatively
+//    PAIR_double_ate(&v,&G,&D,&PK,&HM);
     PAIR_fexp(&v);
     if (FP48_isunity(&v)) return BLS_OK;
 	return BLS_FAIL;
diff --git a/version3/cpp/config16.py b/version3/cpp/config16.py
index f683b38..4f842d5 100644
--- a/version3/cpp/config16.py
+++ b/version3/cpp/config16.py
@@ -72,7 +72,7 @@
 	replace(fnameh,"XXX",bd)
 	os.system("g++ -O3 -c "+fnamec)
 
-def curveset(tb,tf,tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,cs) :
+def curveset(tb,tf,tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,ab,cs) :
 	bd="B"+tb+"_"+base
 	fnameh="config_big_"+bd+".h"
 	os.system(copytext+" config_big.h "+fnameh)
@@ -108,6 +108,7 @@
 	replace(fnameh,"@ST@",stw)
 	replace(fnameh,"@SX@",sx)
 	replace(fnameh,"@CS@",cs)
+	replace(fnameh,"@AB@",ab)
 
 
 	fnamec="big_"+bd+".cpp"
@@ -196,8 +197,10 @@
 		os.system(copytext+" fp12.h "+fnameh)
 		replace(fnamec,"YYY",tf)
 		replace(fnamec,"XXX",bd)
+		replace(fnamec,"ZZZ",tc)
 		replace(fnameh,"YYY",tf)
 		replace(fnameh,"XXX",bd)
+		replace(fnameh,"ZZZ",tc)
 		os.system("g++ -O3 -c "+fnamec)
 
 		fnamec="ecp2_"+tc+".cpp"
@@ -291,7 +294,7 @@
 	selection.append(x)
 	ptr=ptr+1
 
-# curveset(big,field,curve,big_length_bytes,bits_in_base,modulus_bits,modulus_mod_8,modulus_type,curve_type,pairing_friendly)
+# curveset(big,field,curve,big_length_bytes,bits_in_base,modulus_bits,modulus_mod_8,modulus_type,curve_type,pairing_friendly,sextic twist,sign of x,ate bits,curve security)
 # for each curve give names for big, field and curve. In many cases the latter two will be the same. 
 # Typically "big" is the size in bits, always a multiple of 8, "field" describes the modulus, and "curve" is the common name for the elliptic curve   
 # big_length_bytes is "big" divided by 8
@@ -301,22 +304,23 @@
 # modulus_type is NOT_SPECIAL, or PSEUDO_MERSENNE, or MONTGOMERY_Friendly, or GENERALISED_MERSENNE (supported for GOLDILOCKS only)
 # curve_type is WEIERSTRASS, EDWARDS or MONTGOMERY
 # pairing_friendly is BN, BLS or NOT (if not pairing friendly)
+# ate bits is number of bits in Ate parameter (from romgen program)
 # if pairing friendly. M or D type twist, and sign of the family parameter x
 
 
 	if x==1:
-		curveset("256","F25519","ED25519","32","13","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","128")
+		curveset("256","F25519","ED25519","32","13","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","","128")
 		curve_selected=True
 	if x==2:
-		curveset("256","F256PME","NUMS256E","32","13","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","128")
+		curveset("256","F256PME","NUMS256E","32","13","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","","128")
 		curve_selected=True
 
 
 	if x==3:
-		curveset("256","BN254","BN254","32","13","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("256","BN254","BN254","32","13","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==4:
-		curveset("256","BN254CX","BN254CX","32","13","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("256","BN254CX","BN254CX","32","13","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 
 # rsaset(big,ring,big_length_bytes,bits_in_base,multiplier)
diff --git a/version3/cpp/config32.py b/version3/cpp/config32.py
index c4c1758..750cc95 100644
--- a/version3/cpp/config32.py
+++ b/version3/cpp/config32.py
@@ -72,7 +72,7 @@
 	replace(fnameh,"XXX",bd)
 	os.system("g++ -O3 -c "+fnamec)
 
-def curveset(tb,tf,tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,cs) :
+def curveset(tb,tf,tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,ab,cs) :
 	bd="B"+tb+"_"+base
 	fnameh="config_big_"+bd+".h"
 	os.system(copytext+" config_big.h "+fnameh)
@@ -108,6 +108,8 @@
 	replace(fnameh,"@ST@",stw)
 	replace(fnameh,"@SX@",sx)
 	replace(fnameh,"@CS@",cs)
+	replace(fnameh,"@AB@",ab)
+
 
 	fnamec="big_"+bd+".cpp"
 	fnameh="big_"+bd+".h"
@@ -196,8 +198,10 @@
 			os.system(copytext+" fp12.h "+fnameh)
 			replace(fnamec,"YYY",tf)
 			replace(fnamec,"XXX",bd)
+			replace(fnamec,"ZZZ",tc)
 			replace(fnameh,"YYY",tf)
 			replace(fnameh,"XXX",bd)
+			replace(fnameh,"ZZZ",tc)
 			os.system("g++ -O3 -c "+fnamec)
 
 			fnamec="ecp2_"+tc+".cpp"
@@ -276,8 +280,10 @@
 			os.system(copytext+" fp24.h "+fnameh)
 			replace(fnamec,"YYY",tf)
 			replace(fnamec,"XXX",bd)
+			replace(fnamec,"ZZZ",tc)
 			replace(fnameh,"YYY",tf)
 			replace(fnameh,"XXX",bd)
+			replace(fnameh,"ZZZ",tc)
 			os.system("g++ -O3 -c "+fnamec)
 
 			fnamec="ecp4_"+tc+".cpp"
@@ -383,8 +389,10 @@
 			os.system(copytext+" fp48.h "+fnameh)
 			replace(fnamec,"YYY",tf)
 			replace(fnamec,"XXX",bd)
+			replace(fnamec,"ZZZ",tc)
 			replace(fnameh,"YYY",tf)
 			replace(fnameh,"XXX",bd)
+			replace(fnameh,"ZZZ",tc)
 			os.system("g++ -O3 -c "+fnamec)
 
 
@@ -489,7 +497,7 @@
 	selection.append(x)
 	ptr=ptr+1
 
-# curveset(big,field,curve,big_length_bytes,bits_in_base,modulus_bits,modulus_mod_8,modulus_type,curve_type,pairing_friendly,sextic twist,sign of x,curve security)
+# curveset(big,field,curve,big_length_bytes,bits_in_base,modulus_bits,modulus_mod_8,modulus_type,curve_type,pairing_friendly,sextic twist,sign of x,ate bits,curve security)
 # for each curve give names for big, field and curve. In many cases the latter two will be the same. 
 # Typically "big" is the size in bits, always a multiple of 8, "field" describes the modulus, and "curve" is the common name for the elliptic curve   
 # big_length_bytes is "big" divided by 8
@@ -500,96 +508,97 @@
 # curve_type is WEIERSTRASS, EDWARDS or MONTGOMERY
 # pairing_friendly is BN, BLS or NOT (if not pairing friendly)
 # if pairing friendly. M or D type twist, and sign of the family parameter x
+# ate bits is number of bits in Ate parameter (from romgen program)
 # curve security is AES equiavlent, rounded up.
 
 	if x==1:
-		curveset("256","F25519","ED25519","32","29","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","128")
+		curveset("256","F25519","ED25519","32","29","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","","128")
 		curve_selected=True
 	if x==2:
-		curveset("256","F25519","C25519","32","29","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","","","128")
+		curveset("256","F25519","C25519","32","29","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","","","","128")
 		curve_selected=True
 	if x==3:
-		curveset("256","NIST256","NIST256","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","128")
+		curveset("256","NIST256","NIST256","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 	if x==4:
-		curveset("256","BRAINPOOL","BRAINPOOL","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","128")
+		curveset("256","BRAINPOOL","BRAINPOOL","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 	if x==5:
-		curveset("256","ANSSI","ANSSI","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","128")
+		curveset("256","ANSSI","ANSSI","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 
 	if x==6:
-		curveset("336","HIFIVE","HIFIVE","42","29","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","192")
+		curveset("336","HIFIVE","HIFIVE","42","29","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","","192")
 		curve_selected=True
 	if x==7:
-		curveset("448","GOLDILOCKS","GOLDILOCKS","56","29","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","","","256")
+		curveset("448","GOLDILOCKS","GOLDILOCKS","56","29","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","","","","256")
 		curve_selected=True
 	if x==8:
-		curveset("384","NIST384","NIST384","48","29","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","192")
+		curveset("384","NIST384","NIST384","48","29","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","192")
 		curve_selected=True
 	if x==9:
-		curveset("416","C41417","C41417","52","29","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","","","256")
+		curveset("416","C41417","C41417","52","29","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","","","","256")
 		curve_selected=True
 	if x==10:
-		curveset("528","NIST521","NIST521","66","28","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","256")
+		curveset("528","NIST521","NIST521","66","28","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","","256")
 		curve_selected=True
 
 	if x==11:
-		curveset("256","F256PMW","NUMS256W","32","28","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","128")
+		curveset("256","F256PMW","NUMS256W","32","28","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 	if x==12:
-		curveset("256","F256PME","NUMS256E","32","29","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","128")
+		curveset("256","F256PME","NUMS256E","32","29","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","","128")
 		curve_selected=True
 	if x==13:
-		curveset("384","F384PM","NUMS384W","48","29","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","192")
+		curveset("384","F384PM","NUMS384W","48","29","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","","192")
 		curve_selected=True
 	if x==14:
-		curveset("384","F384PM","NUMS384E","48","29","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","192")
+		curveset("384","F384PM","NUMS384E","48","29","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","","192")
 		curve_selected=True
 	if x==15:
-		curveset("512","F512PM","NUMS512W","64","29","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","256")
+		curveset("512","F512PM","NUMS512W","64","29","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","","256")
 		curve_selected=True
 	if x==16:
-		curveset("512","F512PM","NUMS512E","64","29","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","","","256")
+		curveset("512","F512PM","NUMS512E","64","29","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","","","","256")
 		curve_selected=True
 
 	if x==17:
-		curveset("256","SECP256K1","SECP256K1","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","128")
+		curveset("256","SECP256K1","SECP256K1","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 
 	if x==18:
-		curveset("256","BN254","BN254","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("256","BN254","BN254","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==19:
-		curveset("256","BN254CX","BN254CX","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("256","BN254CX","BN254CX","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==20:
-		curveset("384","BLS383","BLS383","48","29","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","128")
+		curveset("384","BLS383","BLS383","48","29","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","65","128")
 		pfcurve_selected=True
 
 	if x==21:
-		curveset("384","BLS381","BLS381","48","29","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128")
+		curveset("384","BLS381","BLS381","48","29","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","65","128")
 		pfcurve_selected=True
 
 
 	if x==22:
-		curveset("256","FP256BN","FP256BN","32","28","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","128")
+		curveset("256","FP256BN","FP256BN","32","28","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==23:
-		curveset("512","FP512BN","FP512BN","64","29","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","128")
+		curveset("512","FP512BN","FP512BN","64","29","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","130","128")
 		pfcurve_selected=True
 # https://eprint.iacr.org/2017/334.pdf
 	if x==24:
-		curveset("464","BLS461","BLS461","58","28","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128")
+		curveset("464","BLS461","BLS461","58","28","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","78","128")
 		pfcurve_selected=True
 
 	if x==25:
-		curveset("480","BLS24","BLS24","60","29","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","192")
+		curveset("480","BLS24","BLS24","60","29","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","49","192")
 		pfcurve_selected=True
 
 
 	if x==26:
-		curveset("560","BLS48","BLS48","70","29","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","256")
+		curveset("560","BLS48","BLS48","70","29","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","32","256")
 		pfcurve_selected=True
 
 
diff --git a/version3/cpp/config64.py b/version3/cpp/config64.py
index 7087427..630e23a 100644
--- a/version3/cpp/config64.py
+++ b/version3/cpp/config64.py
@@ -72,7 +72,7 @@
 	replace(fnameh,"XXX",bd)
 	os.system("g++ -O3 -c "+fnamec)
 
-def curveset(tb,tf,tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,cs) :
+def curveset(tb,tf,tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,ab,cs) :
 	bd="B"+tb+"_"+base
 	fnameh="config_big_"+bd+".h"
 	os.system(copytext+" config_big.h "+fnameh)
@@ -108,6 +108,7 @@
 	replace(fnameh,"@ST@",stw)
 	replace(fnameh,"@SX@",sx)
 	replace(fnameh,"@CS@",cs)
+	replace(fnameh,"@AB@",ab)
 
 
 	fnamec="big_"+bd+".cpp"
@@ -197,8 +198,10 @@
 			os.system(copytext+" fp12.h "+fnameh)
 			replace(fnamec,"YYY",tf)
 			replace(fnamec,"XXX",bd)
+			replace(fnamec,"ZZZ",tc)
 			replace(fnameh,"YYY",tf)
 			replace(fnameh,"XXX",bd)
+			replace(fnameh,"ZZZ",tc)
 			os.system("g++ -O3 -c "+fnamec)
 
 			fnamec="ecp2_"+tc+".cpp"
@@ -278,8 +281,10 @@
 			os.system(copytext+" fp24.h "+fnameh)
 			replace(fnamec,"YYY",tf)
 			replace(fnamec,"XXX",bd)
+			replace(fnamec,"ZZZ",tc)
 			replace(fnameh,"YYY",tf)
 			replace(fnameh,"XXX",bd)
+			replace(fnameh,"ZZZ",tc)
 			os.system("g++ -O3 -c "+fnamec)
 
 			fnamec="ecp4_"+tc+".cpp"
@@ -385,8 +390,10 @@
 			os.system(copytext+" fp48.h "+fnameh)
 			replace(fnamec,"YYY",tf)
 			replace(fnamec,"XXX",bd)
+			replace(fnamec,"ZZZ",tc)
 			replace(fnameh,"YYY",tf)
 			replace(fnameh,"XXX",bd)
+			replace(fnameh,"ZZZ",tc)
 			os.system("g++ -O3 -c "+fnamec)
 
 
@@ -489,7 +496,7 @@
 	selection.append(x)
 	ptr=ptr+1
 
-# curveset(big,field,curve,big_length_bytes,bits_in_base,modulus_bits,modulus_mod_8,modulus_type,curve_type,pairing_friendly,sextic twist,sign of x,curve security)
+# curveset(big,field,curve,big_length_bytes,bits_in_base,modulus_bits,modulus_mod_8,modulus_type,curve_type,pairing_friendly,sextic twist,sign of x,ate bits,curve security)
 # for each curve give names for big, field and curve. In many cases the latter two will be the same. 
 # Typically "big" is the size in bits, always a multiple of 8, "field" describes the modulus, and "curve" is the common name for the elliptic curve   
 # big_length_bytes is "big" divided by 8
@@ -500,95 +507,96 @@
 # curve_type is WEIERSTRASS, EDWARDS or MONTGOMERY
 # pairing_friendly is BN, BLS or NOT (if not pairing friendly)
 # if pairing friendly. M or D type twist, and sign of the family parameter x
-# curve security is AES equiavlent, rounded up.
+# ate bits is number of bits in Ate parameter (from romgen program)
+# curve security is AES equivalent, rounded up.
 
 
 	if x==1:
-		curveset("256","F25519","ED25519","32","56","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","128")
+		curveset("256","F25519","ED25519","32","56","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","","128")
 		curve_selected=True
 	if x==2:
-		curveset("256","F25519","C25519","32","56","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","","","128")
+		curveset("256","F25519","C25519","32","56","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","","","","128")
 		curve_selected=True
 	if x==3:
-		curveset("256","NIST256","NIST256","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","128")
+		curveset("256","NIST256","NIST256","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 	if x==4:
-		curveset("256","BRAINPOOL","BRAINPOOL","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","128")
+		curveset("256","BRAINPOOL","BRAINPOOL","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 	if x==5:
-		curveset("256","ANSSI","ANSSI","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","128")
+		curveset("256","ANSSI","ANSSI","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 
 	if x==6:
-		curveset("336","HIFIVE","HIFIVE","42","60","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","192")
+		curveset("336","HIFIVE","HIFIVE","42","60","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","","","","192")
 		curve_selected=True
 	if x==7:
-		curveset("448","GOLDILOCKS","GOLDILOCKS","56","58","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","","","256")
+		curveset("448","GOLDILOCKS","GOLDILOCKS","56","58","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","","","","256")
 		curve_selected=True
 	if x==8:
-		curveset("384","NIST384","NIST384","48","56","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","192")
+		curveset("384","NIST384","NIST384","48","56","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","192")
 		curve_selected=True
 	if x==9:
-		curveset("416","C41417","C41417","52","60","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","","","256")
+		curveset("416","C41417","C41417","52","60","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","","","","256")
 		curve_selected=True
 	if x==10:
-		curveset("528","NIST521","NIST521","66","60","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","256")
+		curveset("528","NIST521","NIST521","66","60","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","","256")
 		curve_selected=True
 
 	if x==11:
-		curveset("256","F256PMW","NUMS256W","32","56","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","128")
+		curveset("256","F256PMW","NUMS256W","32","56","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 	if x==12:
-		curveset("256","F256PME","NUMS256E","32","56","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","128")
+		curveset("256","F256PME","NUMS256E","32","56","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","","128")
 		curve_selected=True
 	if x==13:
-		curveset("384","F384PM","NUMS384W","48","56","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","192")
+		curveset("384","F384PM","NUMS384W","48","56","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","","192")
 		curve_selected=True
 	if x==14:
-		curveset("384","F384PM","NUMS384E","48","56","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","192")
+		curveset("384","F384PM","NUMS384E","48","56","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","","","","192")
 		curve_selected=True
 	if x==15:
-		curveset("512","F512PM","NUMS512W","64","56","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","256")
+		curveset("512","F512PM","NUMS512W","64","56","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","","","","256")
 		curve_selected=True
 	if x==16:
-		curveset("512","F512PM","NUMS512E","64","56","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","","","256")
+		curveset("512","F512PM","NUMS512E","64","56","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","","","","256")
 		curve_selected=True
 
 	if x==17:
-		curveset("256","SECP256K1","SECP256K1","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","128")
+		curveset("256","SECP256K1","SECP256K1","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","","","","128")
 		curve_selected=True
 
 	if x==18:
-		curveset("256","BN254","BN254","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("256","BN254","BN254","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==19:
-		curveset("256","BN254CX","BN254CX","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("256","BN254CX","BN254CX","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==20:
-		curveset("384","BLS383","BLS383","48","58","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","128")
+		curveset("384","BLS383","BLS383","48","58","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","65","128")
 		pfcurve_selected=True
 
 	if x==21:
-		curveset("384","BLS381","BLS381","48","58","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128")
+		curveset("384","BLS381","BLS381","48","58","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","65","128")
 		pfcurve_selected=True
 
 	if x==22:
-		curveset("256","FP256BN","FP256BN","32","56","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","128")
+		curveset("256","FP256BN","FP256BN","32","56","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==23:
-		curveset("512","FP512BN","FP512BN","64","60","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","128")
+		curveset("512","FP512BN","FP512BN","64","60","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","130","128")
 		pfcurve_selected=True
 # https://eprint.iacr.org/2017/334.pdf
 	if x==24:
-		curveset("464","BLS461","BLS461","58","60","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128")
+		curveset("464","BLS461","BLS461","58","60","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","78","128")
 		pfcurve_selected=True
 
 	if x==25:
-		curveset("480","BLS24","BLS24","60","56","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","192")
+		curveset("480","BLS24","BLS24","60","56","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","49","192")
 		pfcurve_selected=True
 
 	if x==26:
-		curveset("560","BLS48","BLS48","70","58","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","256")
+		curveset("560","BLS48","BLS48","70","58","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","32","256")
 		pfcurve_selected=True
 
 
@@ -604,6 +612,7 @@
 	if x==27:
 		#256 is slower but may allow reuse of 256-bit BIGs used for elliptic curve
 		#512 is faster.. but best is 1024
+		#rsaset("960","RSA15360","120","58","16")  
 		rsaset("1024","RSA2048","128","58","2")
 		#rsaset("512","RSA2048","64","60","4")
 		#rsaset("256","RSA2048","32","56","8")
diff --git a/version3/cpp/config_curve.h b/version3/cpp/config_curve.h
index 4f905d5..d690d93 100644
--- a/version3/cpp/config_curve.h
+++ b/version3/cpp/config_curve.h
@@ -21,6 +21,8 @@
 #define SEXTIC_TWIST_ZZZ @ST@
 #define SIGN_OF_X_ZZZ @SX@ 
 
+#define ATE_BITS_ZZZ @AB@
+
 #endif
 
 
@@ -40,7 +42,6 @@
 #endif
 
 
-
 namespace ZZZ_BIG=XXX;
 namespace ZZZ_FP=YYY;
 
diff --git a/version3/cpp/fp12.cpp b/version3/cpp/fp12.cpp
index d7a3cb3..20ceea0 100644
--- a/version3/cpp/fp12.cpp
+++ b/version3/cpp/fp12.cpp
@@ -22,6 +22,7 @@
 /* FP12 elements are of the form a+i.b+i^2.c */
 
 #include "fp12_YYY.h"
+#include "config_curve_ZZZ.h"
 
 using namespace XXX;
 
@@ -81,6 +82,7 @@
     FP4_copy(&(w->a),&(x->a));
     FP4_copy(&(w->b),&(x->b));
     FP4_copy(&(w->c),&(x->c));
+	w->type=x->type;
 }
 
 /* FP12 w=1 */
@@ -90,6 +92,7 @@
     FP4_one(&(w->a));
     FP4_zero(&(w->b));
     FP4_zero(&(w->c));
+	w->type=FP_UNITY;
 }
 
 /* return 1 if x==y, else 0 */
@@ -118,6 +121,7 @@
     FP4_copy(&(w->a),a);
     FP4_zero(&(w->b));
     FP4_zero(&(w->c));
+	w->type=FP_SPARSER;
 }
 
 /* Create FP12 from 3 FP4's */
@@ -127,6 +131,7 @@
     FP4_copy(&(w->a),a);
     FP4_copy(&(w->b),b);
     FP4_copy(&(w->c),c);
+	w->type=FP_DENSE;
 }
 
 /* Granger-Scott Unitary Squaring. This does not benefit from lazy reduction */
@@ -167,6 +172,7 @@
     FP4_add(&(w->b),&B,&(w->b));
     FP4_add(&(w->c),&C,&(w->c));
 
+	w->type=FP_DENSE;
     FP12_reduce(w);	    /* reduce here as in pow function repeated squarings would trigger multiple reductions */
 }
 
@@ -178,6 +184,12 @@
 
     FP4 A,B,C,D;
 
+	if (x->type<=FP_UNITY)
+	{
+		FP12_copy(w,x);
+		return;
+	}
+
     FP4_sqr(&A,&(x->a));
     FP4_mul(&B,&(x->b),&(x->c));
     FP4_add(&B,&B,&B);
@@ -209,78 +221,170 @@
     FP4_add(&(w->b),&C,&D);
     FP4_add(&(w->c),&(w->c),&A);
 
+	if (x->type==FP_SPARSER)
+		w->type=FP_SPARSE;
+	else
+		w->type=FP_DENSE;
     FP12_norm(w);
 }
 
-/* FP12 full multiplication w=w*y */
+// Use FP12_mul when both multiplicands are dense
+// Use FP12smul when it is known that both multiplicands are line functions
+// Use FP12ssmul when it is suspected that one or both multiplicands could have some sparsity
 
 
-/* SU= 896 */
 /* FP12 full multiplication w=w*y */
 void YYY::FP12_mul(FP12 *w,FP12 *y)
 {
-    FP4 z0,z1,z2,z3,t0,t1;
+	FP4 z0,z1,z2,z3,t0,t1;
 
-    FP4_mul(&z0,&(w->a),&(y->a));
-    FP4_mul(&z2,&(w->b),&(y->b));  //
+	FP4_mul(&z0,&(w->a),&(y->a));  // xa.ya   always 11x11
 
-    FP4_add(&t0,&(w->a),&(w->b));
-    FP4_add(&t1,&(y->a),&(y->b));  //
+	FP4_mul(&z2,&(w->b),&(y->b));  // xb.yb  could be 00x00 or 01x01 or or 10x10 or 11x00 or 11x10 or 11x01 or 11x11 
+
+	FP4_add(&t0,&(w->a),&(w->b));  // (xa+xb)
+	FP4_add(&t1,&(y->a),&(y->b));  // (ya+yb)
 
 	FP4_norm(&t0);
 	FP4_norm(&t1);
 
-    FP4_mul(&z1,&t0,&t1);
-    FP4_add(&t0,&(w->b),&(w->c));
-    FP4_add(&t1,&(y->b),&(y->c));  //
+	FP4_mul(&z1,&t0,&t1); // (xa+xb)(ya+yb)  always 11x11
+	FP4_add(&t0,&(w->b),&(w->c));  // (xb+xc)
+	FP4_add(&t1,&(y->b),&(y->c));  // (yb+yc)
 
 	FP4_norm(&t0);
 	FP4_norm(&t1);
 
-    FP4_mul(&z3,&t0,&t1);
+	FP4_mul(&z3,&t0,&t1);	// (xb+xc)(yb+yc)   could be anything...
+	FP4_neg(&t0,&z0);		// -(xa.ya)
+	FP4_neg(&t1,&z2);		// -(xb.yb)
 
-    FP4_neg(&t0,&z0);
-    FP4_neg(&t1,&z2);
+	FP4_add(&z1,&z1,&t0);  
+	FP4_add(&(w->b),&z1,&t1); // /wb = (xa+xb)(ya+yb) -(xa.ya) -(xb.yb)						= xa.yb + xb.ya
 
-    FP4_add(&z1,&z1,&t0);   // z1=z1-z0
-    FP4_add(&(w->b),&z1,&t1);
+	FP4_add(&z3,&z3,&t1);        // (xb+xc)(yb+yc) -(xb.yb)
+	FP4_add(&z2,&z2,&t0);        // (xb.yb) - (xa.ya)
 
-    FP4_add(&z3,&z3,&t1);        // z3=z3-z2
-    FP4_add(&z2,&z2,&t0);        // z2=z2-z0
-
-    FP4_add(&t0,&(w->a),&(w->c));
-    FP4_add(&t1,&(y->a),&(y->c));
+	FP4_add(&t0,&(w->a),&(w->c));  // (xa+xc)
+	FP4_add(&t1,&(y->a),&(y->c));  // (ya+yc)
 
 	FP4_norm(&t0);
 	FP4_norm(&t1);
 
-    FP4_mul(&t0,&t1,&t0);
-    FP4_add(&z2,&z2,&t0);
+	FP4_mul(&t0,&t1,&t0);	// (xa+xc)(ya+yc)    always 11x11
+	FP4_add(&z2,&z2,&t0);	// (xb.yb) - (xa.ya) + (xa+xc)(ya+yc)
 
-    FP4_mul(&t0,&(w->c),&(y->c));
-    FP4_neg(&t1,&t0);
+	FP4_mul(&t0,&(w->c),&(y->c)); // (xc.yc)  could be anything	
+	FP4_neg(&t1,&t0);			  // -(xc.yc) 
 
-    FP4_add(&(w->c),&z2,&t1);
-    FP4_add(&z3,&z3,&t1);
-    FP4_times_i(&t0);
-    FP4_add(&(w->b),&(w->b),&t0);
+	FP4_add(&(w->c),&z2,&t1);		// wc = (xb.yb) - (xa.ya) + (xa+xc)(ya+yc) - (xc.yc)	=  xb.yb + xc.ya + xa.yc
+	FP4_add(&z3,&z3,&t1);			// (xb+xc)(yb+yc) -(xb.yb) - (xc.yc)					=  xb.yc + xc.yb
+	FP4_times_i(&t0);				// i.(xc.yc)
+	FP4_add(&(w->b),&(w->b),&t0);   // wb = (xa+xb)(ya+yb) -(xa.ya) -(xb.yb) +i(xc.yc)
 	FP4_norm(&z3);
-    FP4_times_i(&z3);
-    FP4_add(&(w->a),&z0,&z3);
+	FP4_times_i(&z3);				// i[(xb+xc)(yb+yc) -(xb.yb) - (xc.yc)]					= i(xb.yc + xc.yb)
+	FP4_add(&(w->a),&z0,&z3);		// wa = xa.ya + i(xb.yc + xc.yb)
 
     FP12_norm(w);
+	w->type=FP_DENSE;
 }
 
-/* FP12 multiplication w=w*y */
-/* SU= 744 */
-/* catering for special case that arises from special form of ATE pairing line function */
-void YYY::FP12_smul(FP12 *w,FP12 *y,int type)
+/* FP12 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+void YYY::FP12_ssmul(FP12 *w,FP12 *y)
 {
-    FP4 z0,z1,z2,z3,t0,t1;
+	FP4 z0,z1,z2,z3,t0,t1;
+	if (w->type==FP_UNITY)
+	{
+		FP12_copy(w,y);
+		return;
+	}
+	if (y->type==FP_UNITY)
+		return;
 
-	if (type==D_TYPE)
-	{ // y->c is 0
+	if (y->type >= FP_SPARSE)
+	{
+		FP4_mul(&z0,&(w->a),&(y->a));  // xa.ya   always 11x11
 
+#if SEXTIC_TWIST_ZZZ == M_TYPE
+		if (y->type==FP_SPARSE || w->type==FP_SPARSE)
+		{
+			FP2_mul(&z2.b,&(w->b).b,&(y->b).b);
+			FP2_zero(&z2.a);
+			if (y->type!=FP_SPARSE)
+				FP2_mul(&z2.a,&(w->b).b,&(y->b).a);
+			if (w->type!=FP_SPARSE)
+				FP2_mul(&z2.a,&(w->b).a,&(y->b).b);
+			FP4_times_i(&z2);
+		}
+		else
+#endif
+			FP4_mul(&z2,&(w->b),&(y->b));  // xb.yb  could be 00x00 or 01x01 or or 10x10 or 11x00 or 11x10 or 11x01 or 11x11 
+
+		FP4_add(&t0,&(w->a),&(w->b));  // (xa+xb)
+		FP4_add(&t1,&(y->a),&(y->b));  // (ya+yb)
+
+		FP4_norm(&t0);
+		FP4_norm(&t1);
+
+		FP4_mul(&z1,&t0,&t1); // (xa+xb)(ya+yb)  always 11x11
+		FP4_add(&t0,&(w->b),&(w->c));  // (xb+xc)
+		FP4_add(&t1,&(y->b),&(y->c));  // (yb+yc)
+
+		FP4_norm(&t0);
+		FP4_norm(&t1);
+
+		FP4_mul(&z3,&t0,&t1);	// (xb+xc)(yb+yc)   could be anything...
+		FP4_neg(&t0,&z0);		// -(xa.ya)
+		FP4_neg(&t1,&z2);		// -(xb.yb)
+
+		FP4_add(&z1,&z1,&t0);  
+		FP4_add(&(w->b),&z1,&t1); // /wb = (xa+xb)(ya+yb) -(xa.ya) -(xb.yb)						= xa.yb + xb.ya
+
+		FP4_add(&z3,&z3,&t1);        // (xb+xc)(yb+yc) -(xb.yb)
+		FP4_add(&z2,&z2,&t0);        // (xb.yb) - (xa.ya)
+
+		FP4_add(&t0,&(w->a),&(w->c));  // (xa+xc)
+		FP4_add(&t1,&(y->a),&(y->c));  // (ya+yc)
+
+		FP4_norm(&t0);
+		FP4_norm(&t1);
+
+		FP4_mul(&t0,&t1,&t0);	// (xa+xc)(ya+yc)    always 11x11
+		FP4_add(&z2,&z2,&t0);	// (xb.yb) - (xa.ya) + (xa+xc)(ya+yc)
+
+#if SEXTIC_TWIST_ZZZ == D_TYPE
+		if (y->type==FP_SPARSE || w->type==FP_SPARSE)
+		{
+			FP2_mul(&t0.a,&(w->c).a,&(y->c).a);
+			FP2_zero(&t0.b);
+			if (y->type!=FP_SPARSE)
+				FP2_mul(&t0.b,&(w->c).a,&(y->c).b);
+			if (w->type!=FP_SPARSE)
+				FP2_mul(&t0.b,&(w->c).b,&(y->c).a);
+		}
+		else
+#endif
+			FP4_mul(&t0,&(w->c),&(y->c)); // (xc.yc)  could be anything
+			
+		FP4_neg(&t1,&t0);			  // -(xc.yc) 
+
+		FP4_add(&(w->c),&z2,&t1);		// wc = (xb.yb) - (xa.ya) + (xa+xc)(ya+yc) - (xc.yc)	=  xb.yb + xc.ya + xa.yc
+		FP4_add(&z3,&z3,&t1);			// (xb+xc)(yb+yc) -(xb.yb) - (xc.yc)					=  xb.yc + xc.yb
+		FP4_times_i(&t0);				// i.(xc.yc)
+		FP4_add(&(w->b),&(w->b),&t0);   // wb = (xa+xb)(ya+yb) -(xa.ya) -(xb.yb) +i(xc.yc)
+		FP4_norm(&z3);
+		FP4_times_i(&z3);				// i[(xb+xc)(yb+yc) -(xb.yb) - (xc.yc)]					= i(xb.yc + xc.yb)
+		FP4_add(&(w->a),&z0,&z3);		// wa = xa.ya + i(xb.yc + xc.yb)
+	} else {
+		if (w->type==FP_SPARSER)
+		{
+			FP12_smul(w,y);
+			return;
+		}
+ // dense by sparser - 13m 
+#if SEXTIC_TWIST_ZZZ == D_TYPE
 		FP4_copy(&z3,&(w->b));
 		FP4_mul(&z0,&(w->a),&(y->a));
 
@@ -306,7 +410,6 @@
 		FP4_add(&z2,&z2,&t0);        // z2=z2-z0
 
 		FP4_add(&t0,&(w->a),&(w->c));
-
 		FP4_norm(&t0);
 		FP4_norm(&z3);
 
@@ -315,10 +418,8 @@
 
 		FP4_times_i(&z3);
 		FP4_add(&(w->a),&z0,&z3);
-	}
-
-	if (type==M_TYPE)
-	{ // y->b is zero
+#endif
+#if SEXTIC_TWIST_ZZZ == M_TYPE
 		FP4_mul(&z0,&(w->a),&(y->a));
 		FP4_add(&t0,&(w->a),&(w->b));
 		FP4_norm(&t0);
@@ -334,7 +435,6 @@
 		FP4_add(&z1,&z1,&t0);   // z1=z1-z0
 
 		FP4_copy(&(w->b),&z1);
-
 		FP4_copy(&z2,&t0);
 
 		FP4_add(&t0,&(w->a),&(w->c));
@@ -358,10 +458,116 @@
 		FP4_norm(&z3);
 		FP4_times_i(&z3);
 		FP4_add(&(w->a),&z0,&z3);
+
+#endif
 	}
+	w->type=FP_DENSE;
     FP12_norm(w);
 }
 
+/* FP12 multiplication w=w*y */
+/* catering for special case that arises from special form of ATE pairing line function */
+/* w and y are both sparser line functions - cost = 6m */ 
+void YYY::FP12_smul(FP12 *w,FP12 *y)
+{
+	FP2 w1,w2,w3,ta,tb,tc,td,te,t;
+
+//	if (type==D_TYPE)
+//	{ 
+#if SEXTIC_TWIST_ZZZ == D_TYPE
+	FP2_mul(&w1,&(w->a).a,&(y->a).a); // A1.A2
+	FP2_mul(&w2,&(w->a).b,&(y->a).b); // B1.B2
+	FP2_mul(&w3,&(w->b).a,&(y->b).a); // C1.C2
+
+	FP2_add(&ta,&(w->a).a,&(w->a).b); // A1+B1
+	FP2_add(&tb,&(y->a).a,&(y->a).b); // A2+B2
+	FP2_norm(&ta);
+	FP2_norm(&tb);
+	FP2_mul(&tc,&ta,&tb);			// (A1+B1)(A2+B2)
+	FP2_add(&t,&w1,&w2);
+	FP2_neg(&t,&t);
+	FP2_add(&tc,&tc,&t);			// (A1+B1)(A2+B2)-A1.A2-B1*B2 =  (A1.B2+A2.B1)		
+				
+	FP2_add(&ta,&(w->a).a,&(w->b).a); // A1+C1
+	FP2_add(&tb,&(y->a).a,&(y->b).a); // A2+C2
+	FP2_norm(&ta);
+	FP2_norm(&tb);
+	FP2_mul(&td,&ta,&tb);			// (A1+C1)(A2+C2)
+	FP2_add(&t,&w1,&w3);
+	FP2_neg(&t,&t);
+	FP2_add(&td,&td,&t);			// (A1+C1)(A2+C2)-A1.A2-C1*C2 =  (A1.C2+A2.C1)		
+
+	FP2_add(&ta,&(w->a).b,&(w->b).a); // B1+C1
+	FP2_add(&tb,&(y->a).b,&(y->b).a); // B2+C2
+	FP2_norm(&ta);
+	FP2_norm(&tb);
+	FP2_mul(&te,&ta,&tb);			// (B1+C1)(B2+C2)
+	FP2_add(&t,&w2,&w3);
+	FP2_neg(&t,&t);
+	FP2_add(&te,&te,&t);			// (B1+C1)(B2+C2)-B1.B2-C1*C2 =  (B1.C2+B2.C1)		
+
+	FP2_mul_ip(&w2);
+	FP2_add(&w1,&w1,&w2);
+	FP4_from_FP2s(&(w->a),&w1,&tc);
+	FP4_from_FP2s(&(w->b),&td,&te); // only norm these 2
+	FP4_from_FP2(&(w->c),&w3);
+
+	FP4_norm(&(w->a));
+	FP4_norm(&(w->b));
+#endif
+//	} else { 
+#if SEXTIC_TWIST_ZZZ == M_TYPE
+	FP2_mul(&w1,&(w->a).a,&(y->a).a); // A1.A2
+	FP2_mul(&w2,&(w->a).b,&(y->a).b); // B1.B2
+	FP2_mul(&w3,&(w->c).b,&(y->c).b); // F1.F2
+
+	FP2_add(&ta,&(w->a).a,&(w->a).b); // A1+B1
+	FP2_add(&tb,&(y->a).a,&(y->a).b); // A2+B2
+	FP2_norm(&ta);
+	FP2_norm(&tb);
+	FP2_mul(&tc,&ta,&tb);			// (A1+B1)(A2+B2)
+	FP2_add(&t,&w1,&w2);
+	FP2_neg(&t,&t);
+	FP2_add(&tc,&tc,&t);			// (A1+B1)(A2+B2)-A1.A2-B1*B2 =  (A1.B2+A2.B1)		
+				
+	FP2_add(&ta,&(w->a).a,&(w->c).b); // A1+F1
+	FP2_add(&tb,&(y->a).a,&(y->c).b); // A2+F2
+	FP2_norm(&ta);
+	FP2_norm(&tb);
+	FP2_mul(&td,&ta,&tb);			// (A1+F1)(A2+F2)
+	FP2_add(&t,&w1,&w3);
+	FP2_neg(&t,&t);
+	FP2_add(&td,&td,&t);			// (A1+F1)(A2+F2)-A1.A2-F1*F2 =  (A1.F2+A2.F1)		
+
+	FP2_add(&ta,&(w->a).b,&(w->c).b); // B1+F1
+	FP2_add(&tb,&(y->a).b,&(y->c).b); // B2+F2
+	FP2_norm(&ta);
+	FP2_norm(&tb);
+	FP2_mul(&te,&ta,&tb);			// (B1+F1)(B2+F2)
+	FP2_add(&t,&w2,&w3);
+	FP2_neg(&t,&t);
+	FP2_add(&te,&te,&t);			// (B1+F1)(B2+F2)-B1.B2-F1*F2 =  (B1.F2+B2.F1)	
+
+	FP2_mul_ip(&w2);
+	FP2_add(&w1,&w1,&w2);
+	FP4_from_FP2s(&(w->a),&w1,&tc);
+
+	FP2_mul_ip(&w3);
+	FP2_norm(&w3);
+	FP4_from_FP2H(&(w->b),&w3);
+
+	FP2_norm(&te);
+	FP2_mul_ip(&te);
+	FP4_from_FP2s(&(w->c),&te,&td);
+
+	FP4_norm(&(w->a));
+	FP4_norm(&(w->c));
+#endif
+
+//	}
+	w->type=FP_SPARSE;
+}
+
 /* Set w=1/x */
 /* SU= 600 */
 void YYY::FP12_inv(FP12 *w,FP12 *x)
@@ -400,7 +606,7 @@
     FP4_mul(&(w->a),&f0,&f3);
     FP4_mul(&(w->b),&f1,&f3);
     FP4_mul(&(w->c),&f2,&f3);
-
+	w->type=FP_DENSE;
 }
 
 /* constant time powering by small integer of max length bts */
@@ -610,6 +816,7 @@
 
     FP4_pmul(&(w->b),&(w->b),f);
     FP4_pmul(&(w->c),&(w->c),&f2);
+	w->type=FP_DENSE;
 }
 
 /* SU= 8 */
@@ -715,11 +922,15 @@
     FP_nres(&(g->c.b.b),b);
 }
 
-/* Move b to a if d=1 */
+/* Move g to f
+if d=1 */
 void YYY::FP12_cmove(FP12 *f,FP12 *g,int d)
 {
     FP4_cmove(&(f->a),&(g->a),d);
     FP4_cmove(&(f->b),&(g->b),d);
     FP4_cmove(&(f->c),&(g->c),d);
+	d=~(d-1);
+	f->type^=(f->type^g->type)&d;
 }
 
+
diff --git a/version3/cpp/fp12.h b/version3/cpp/fp12.h
index 8157e8b..69bc87c 100644
--- a/version3/cpp/fp12.h
+++ b/version3/cpp/fp12.h
@@ -10,11 +10,19 @@
 	@brief FP12 Structure - towered over three FP4
 */
 
+#define FP12_ZERO 0
+#define FP12_ONE 1
+#define FP12_SPARSER 2
+#define FP12_SPARSE 3
+#define FP12_DENSE 4
+
+
 typedef struct
 {
     FP4 a; /**< first part of FP12 */
     FP4 b; /**< second part of FP12 */
     FP4 c; /**< third part of FP12 */
+	int type;
 } FP12;
 
 extern const XXX::BIG Fra; /**< real part of BN curve Frobenius Constant */
@@ -85,24 +93,33 @@
 	@param y FP12 instance
  */
 extern void FP12_sqr(FP12 *x,FP12 *y);
-/**	@brief Fast multiplication of an FP12 by an FP12 that arises from an ATE pairing line function
+/**	@brief Fast multiplication of two sparse FP12s that arises from ATE pairing line functions
  *
-	Here the multiplier has a special form that can be exploited
 	@param x FP12 instance, on exit = x*y
 	@param y FP12 instance, of special form
-	@param t D_TYPE or M_TYPE twist
  */
-extern void FP12_smul(FP12 *x,FP12 *y,int t);
-/**	@brief Multiplication of two FP12s
+extern void FP12_smul(FP12 *x,FP12 *y);
+
+/**	@brief Fast multiplication of what may be sparse multiplicands
+ *
+	@param x FP12 instance, on exit = x*y
+	@param y FP12 instance, of special form
+ */
+extern void FP12_ssmul(FP12 *x,FP12 *y);
+
+
+/**	@brief Full unconditional Multiplication of two FP12s
  *
 	@param x FP12 instance, on exit = x*y
 	@param y FP12 instance, the multiplier
  */
 extern void FP12_mul(FP12 *x,FP12 *y);
+
 /**	@brief Inverting an FP12
  *
 	@param x FP12 instance, on exit = 1/y
 	@param y FP12 instance
+	@param t D_TYPE or M_TYPE twist
  */
 extern void FP12_inv(FP12 *x,FP12 *y);
 /**	@brief Raises an FP12 to the power of a BIG
diff --git a/version3/cpp/fp2.cpp b/version3/cpp/fp2.cpp
index 1eb2809..e8fcfaf 100644
--- a/version3/cpp/fp2.cpp
+++ b/version3/cpp/fp2.cpp
@@ -24,6 +24,11 @@
 
 #include "fp2_YYY.h"
 
+//namespace YYY {
+//extern int fp2muls;
+//extern int fp2sqrs;
+//}
+
 using namespace XXX;
 
 /* test x==0 ? */
@@ -201,6 +206,8 @@
 	FP_norm(&(w->a));
 
     FP_mul(&(w->a),&w1,&(w->a));     /* w->a#2 w->a=1 w1&w2=6 w1*w2=2 */
+
+//	YYY::fp2sqrs++;
 }
 
 
@@ -242,6 +249,7 @@
 	BIG_dnorm(A); FP_mod(w->a.g,A);  w->a.XES=3;// may drift above 2p...
 	BIG_dnorm(E); FP_mod(w->b.g,E);  w->b.XES=2;
 
+//	YYY::fp2muls++;
 }
 
 /* output FP2 in hex format [a,b] */
diff --git a/version3/cpp/fp24.cpp b/version3/cpp/fp24.cpp
index 7c971e9..79cbdf8 100644
--- a/version3/cpp/fp24.cpp
+++ b/version3/cpp/fp24.cpp
@@ -17,11 +17,12 @@
 under the License.
 */
 
-/* AMCL Fp^12 functions */
+/* AMCL Fp^24 functions */
 /* SU=m, m is Stack Usage (no lazy )*/
 /* FP24 elements are of the form a+i.b+i^2.c */
 
 #include "fp24_YYY.h"
+#include "config_curve_ZZZ.h"
 
 using namespace XXX;
 
@@ -81,6 +82,7 @@
     FP8_copy(&(w->a),&(x->a));
     FP8_copy(&(w->b),&(x->b));
     FP8_copy(&(w->c),&(x->c));
+	w->type=x->type;
 }
 
 /* FP24 w=1 */
@@ -89,7 +91,9 @@
 {
     FP8_one(&(w->a));
     FP8_zero(&(w->b));
-    FP8_zero(&(w->c));
+    FP8_zero(&(w->c));	
+	w->type=FP_UNITY;
+
 }
 
 /* return 1 if x==y, else 0 */
@@ -118,6 +122,7 @@
     FP8_copy(&(w->a),a);
     FP8_zero(&(w->b));
     FP8_zero(&(w->c));
+	w->type=FP_SPARSER;
 }
 
 /* Create FP24 from 3 FP8's */
@@ -127,6 +132,7 @@
     FP8_copy(&(w->a),a);
     FP8_copy(&(w->b),b);
     FP8_copy(&(w->c),c);
+	w->type=FP_DENSE;
 }
 
 /* Granger-Scott Unitary Squaring. This does not benefit from lazy reduction */
@@ -166,6 +172,7 @@
     FP8_add(&(w->c),&(w->c),&(w->c));
     FP8_add(&(w->b),&B,&(w->b));
     FP8_add(&(w->c),&C,&(w->c));
+	w->type=FP_DENSE;
 
     FP24_reduce(w);	    /* reduce here as in pow function repeated squarings would trigger multiple reductions */
 }
@@ -178,6 +185,12 @@
 
     FP8 A,B,C,D;
 
+	if (x->type<=FP_UNITY)
+	{
+		FP24_copy(w,x);
+		return;
+	}
+
     FP8_sqr(&A,&(x->a));
     FP8_mul(&B,&(x->b),&(x->c));
     FP8_add(&B,&B,&B);
@@ -211,13 +224,19 @@
     FP8_add(&(w->b),&C,&D);
     FP8_add(&(w->c),&(w->c),&A);
 
+	if (x->type==FP_SPARSER)
+		w->type=FP_SPARSE;
+	else
+		w->type=FP_DENSE;
+
     FP24_norm(w);
 }
 
-/* FP24 full multiplication w=w*y */
+// Use FP24_mul when both multiplicands are dense
+// Use FP24smul when it is known that both multiplicands are line functions
+// Use FP24ssmul when it is suspected that one or both multiplicands could have some sparsity
 
 
-/* SU= 896 */
 /* FP24 full multiplication w=w*y */
 void YYY::FP24_mul(FP24 *w,FP24 *y)
 {
@@ -270,18 +289,105 @@
     FP8_add(&(w->a),&z0,&z3);
 
     FP24_norm(w);
+	w->type=FP_DENSE;
 }
 
-/* FP24 multiplication w=w*y */
-/* SU= 744 */
-/* catering for special case that arises from special form of ATE pairing line function */
-void YYY::FP24_smul(FP24 *w,FP24 *y,int type)
+/* FP24 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+void YYY::FP24_ssmul(FP24 *w,FP24 *y)
 {
-    FP8 z0,z1,z2,z3,t0,t1;
+	FP8 z0,z1,z2,z3,t0,t1;
+	if (w->type==FP_UNITY)
+	{
+		FP24_copy(w,y);
+		return;
+	}
+	if (y->type==FP_UNITY)
+		return;
 
-	if (type==D_TYPE)
-	{ // y->c is 0
+	if (y->type >= FP_SPARSE)
+	{
+		FP8_mul(&z0,&(w->a),&(y->a));  // xa.ya   always 11x11
 
+#if SEXTIC_TWIST_ZZZ == M_TYPE
+		if (y->type==FP_SPARSE || w->type==FP_SPARSE)
+		{
+			FP4_mul(&z2.b,&(w->b).b,&(y->b).b);
+			FP4_zero(&z2.a);
+			if (y->type!=FP_SPARSE)
+				FP4_mul(&z2.a,&(w->b).b,&(y->b).a);
+			if (w->type!=FP_SPARSE)
+				FP4_mul(&z2.a,&(w->b).a,&(y->b).b);
+			FP8_times_i(&z2);
+		}
+		else
+#endif 
+			FP8_mul(&z2,&(w->b),&(y->b));  // xb.yb  could be 00x00 or 01x01 or or 10x10 or 11x00 or 11x10 or 11x01 or 11x11 
+
+		FP8_add(&t0,&(w->a),&(w->b));  // (xa+xb)
+		FP8_add(&t1,&(y->a),&(y->b));  // (ya+yb)
+
+		FP8_norm(&t0);
+		FP8_norm(&t1);
+
+		FP8_mul(&z1,&t0,&t1); // (xa+xb)(ya+yb)  always 11x11
+		FP8_add(&t0,&(w->b),&(w->c));  // (xb+xc)
+		FP8_add(&t1,&(y->b),&(y->c));  // (yb+yc)
+
+		FP8_norm(&t0);
+		FP8_norm(&t1);
+
+		FP8_mul(&z3,&t0,&t1);	// (xb+xc)(yb+yc)   could be anything...
+		FP8_neg(&t0,&z0);		// -(xa.ya)
+		FP8_neg(&t1,&z2);		// -(xb.yb)
+
+		FP8_add(&z1,&z1,&t0);  
+		FP8_add(&(w->b),&z1,&t1); // /wb = (xa+xb)(ya+yb) -(xa.ya) -(xb.yb)						= xa.yb + xb.ya
+
+		FP8_add(&z3,&z3,&t1);        // (xb+xc)(yb+yc) -(xb.yb)
+		FP8_add(&z2,&z2,&t0);        // (xb.yb) - (xa.ya)
+
+		FP8_add(&t0,&(w->a),&(w->c));  // (xa+xc)
+		FP8_add(&t1,&(y->a),&(y->c));  // (ya+yc)
+
+		FP8_norm(&t0);
+		FP8_norm(&t1);
+
+		FP8_mul(&t0,&t1,&t0);	// (xa+xc)(ya+yc)    always 11x11
+		FP8_add(&z2,&z2,&t0);	// (xb.yb) - (xa.ya) + (xa+xc)(ya+yc)
+
+#if SEXTIC_TWIST_ZZZ == D_TYPE
+		if (y->type==FP_SPARSE || w->type==FP_SPARSE)
+		{
+			FP4_mul(&t0.a,&(w->c).a,&(y->c).a);
+			FP4_zero(&t0.b);
+			if (y->type!=FP_SPARSE)
+				FP4_mul(&t0.b,&(w->c).a,&(y->c).b);
+			if (w->type!=FP_SPARSE)
+				FP4_mul(&t0.b,&(w->c).b,&(y->c).a);
+		}
+		else
+#endif
+			FP8_mul(&t0,&(w->c),&(y->c)); // (xc.yc)  could be anything
+			
+		FP8_neg(&t1,&t0);			  // -(xc.yc) 
+
+		FP8_add(&(w->c),&z2,&t1);		// wc = (xb.yb) - (xa.ya) + (xa+xc)(ya+yc) - (xc.yc)	=  xb.yb + xc.ya + xa.yc
+		FP8_add(&z3,&z3,&t1);			// (xb+xc)(yb+yc) -(xb.yb) - (xc.yc)					=  xb.yc + xc.yb
+		FP8_times_i(&t0);				// i.(xc.yc)
+		FP8_add(&(w->b),&(w->b),&t0);   // wb = (xa+xb)(ya+yb) -(xa.ya) -(xb.yb) +i(xc.yc)
+		FP8_norm(&z3);
+		FP8_times_i(&z3);				// i[(xb+xc)(yb+yc) -(xb.yb) - (xc.yc)]					= i(xb.yc + xc.yb)
+		FP8_add(&(w->a),&z0,&z3);		// wa = xa.ya + i(xb.yc + xc.yb)
+	} else {
+		if (w->type==FP_SPARSER)
+		{
+			FP24_smul(w,y);
+			return;
+		}
+// dense by sparser - 13m 
+#if SEXTIC_TWIST_ZZZ == D_TYPE
 		FP8_copy(&z3,&(w->b));
 		FP8_mul(&z0,&(w->a),&(y->a));
 
@@ -307,7 +413,6 @@
 		FP8_add(&z2,&z2,&t0);        // z2=z2-z0
 
 		FP8_add(&t0,&(w->a),&(w->c));
-
 		FP8_norm(&t0);
 		FP8_norm(&z3);
 
@@ -316,10 +421,8 @@
 
 		FP8_times_i(&z3);
 		FP8_add(&(w->a),&z0,&z3);
-	}
-
-	if (type==M_TYPE)
-	{ // y->b is zero
+#endif
+#if SEXTIC_TWIST_ZZZ == M_TYPE
 		FP8_mul(&z0,&(w->a),&(y->a));
 		FP8_add(&t0,&(w->a),&(w->b));
 		FP8_norm(&t0);
@@ -335,7 +438,6 @@
 		FP8_add(&z1,&z1,&t0);   // z1=z1-z0
 
 		FP8_copy(&(w->b),&z1);
-
 		FP8_copy(&z2,&t0);
 
 		FP8_add(&t0,&(w->a),&(w->c));
@@ -359,10 +461,112 @@
 		FP8_norm(&z3);
 		FP8_times_i(&z3);
 		FP8_add(&(w->a),&z0,&z3);
+#endif
 	}
+	w->type=FP_DENSE;
     FP24_norm(w);
 }
 
+/* FP24 multiplication w=w*y */
+/* catering for special case that arises from special form of ATE pairing line function */
+/* w and y are both sparser line functions - cost = 6m */ 
+void YYY::FP24_smul(FP24 *w,FP24 *y)
+{
+	FP4 w1,w2,w3,ta,tb,tc,td,te,t;
+
+
+#if SEXTIC_TWIST_ZZZ == D_TYPE
+	FP4_mul(&w1,&(w->a).a,&(y->a).a); // A1.A2
+	FP4_mul(&w2,&(w->a).b,&(y->a).b); // B1.B2
+	FP4_mul(&w3,&(w->b).a,&(y->b).a); // C1.C2
+
+	FP4_add(&ta,&(w->a).a,&(w->a).b); // A1+B1
+	FP4_add(&tb,&(y->a).a,&(y->a).b); // A2+B2
+	FP4_norm(&ta);
+	FP4_norm(&tb);
+	FP4_mul(&tc,&ta,&tb);			// (A1+B1)(A2+B2)
+	FP4_add(&t,&w1,&w2);
+	FP4_neg(&t,&t);
+	FP4_add(&tc,&tc,&t);			// (A1+B1)(A2+B2)-A1.A2-B1*B2 =  (A1.B2+A2.B1)		
+				
+	FP4_add(&ta,&(w->a).a,&(w->b).a); // A1+C1
+	FP4_add(&tb,&(y->a).a,&(y->b).a); // A2+C2
+	FP4_norm(&ta);
+	FP4_norm(&tb);
+	FP4_mul(&td,&ta,&tb);			// (A1+C1)(A2+C2)
+	FP4_add(&t,&w1,&w3);
+	FP4_neg(&t,&t);
+	FP4_add(&td,&td,&t);			// (A1+C1)(A2+C2)-A1.A2-C1*C2 =  (A1.C2+A2.C1)		
+
+	FP4_add(&ta,&(w->a).b,&(w->b).a); // B1+C1
+	FP4_add(&tb,&(y->a).b,&(y->b).a); // B2+C2
+	FP4_norm(&ta);
+	FP4_norm(&tb);
+	FP4_mul(&te,&ta,&tb);			// (B1+C1)(B2+C2)
+	FP4_add(&t,&w2,&w3);
+	FP4_neg(&t,&t);
+	FP4_add(&te,&te,&t);			// (B1+C1)(B2+C2)-B1.B2-C1*C2 =  (B1.C2+B2.C1)		
+
+	FP4_times_i(&w2);
+	FP4_add(&w1,&w1,&w2);
+	FP8_from_FP4s(&(w->a),&w1,&tc);
+	FP8_from_FP4s(&(w->b),&td,&te); // only norm these 2
+	FP8_from_FP4(&(w->c),&w3);
+
+	FP8_norm(&(w->a));
+	FP8_norm(&(w->b));
+#endif
+#if SEXTIC_TWIST_ZZZ == M_TYPE
+	FP4_mul(&w1,&(w->a).a,&(y->a).a); // A1.A2
+	FP4_mul(&w2,&(w->a).b,&(y->a).b); // B1.B2
+	FP4_mul(&w3,&(w->c).b,&(y->c).b); // F1.F2
+
+	FP4_add(&ta,&(w->a).a,&(w->a).b); // A1+B1
+	FP4_add(&tb,&(y->a).a,&(y->a).b); // A2+B2
+	FP4_norm(&ta);
+	FP4_norm(&tb);
+	FP4_mul(&tc,&ta,&tb);			// (A1+B1)(A2+B2)
+	FP4_add(&t,&w1,&w2);
+	FP4_neg(&t,&t);
+	FP4_add(&tc,&tc,&t);			// (A1+B1)(A2+B2)-A1.A2-B1*B2 =  (A1.B2+A2.B1)		
+				
+	FP4_add(&ta,&(w->a).a,&(w->c).b); // A1+F1
+	FP4_add(&tb,&(y->a).a,&(y->c).b); // A2+F2
+	FP4_norm(&ta);
+	FP4_norm(&tb);
+	FP4_mul(&td,&ta,&tb);			// (A1+F1)(A2+F2)
+	FP4_add(&t,&w1,&w3);
+	FP4_neg(&t,&t);
+	FP4_add(&td,&td,&t);			// (A1+F1)(A2+F2)-A1.A2-F1*F2 =  (A1.F2+A2.F1)		
+
+	FP4_add(&ta,&(w->a).b,&(w->c).b); // B1+F1
+	FP4_add(&tb,&(y->a).b,&(y->c).b); // B2+F2
+	FP4_norm(&ta);
+	FP4_norm(&tb);
+	FP4_mul(&te,&ta,&tb);			// (B1+F1)(B2+F2)
+	FP4_add(&t,&w2,&w3);
+	FP4_neg(&t,&t);
+	FP4_add(&te,&te,&t);			// (B1+F1)(B2+F2)-B1.B2-F1*F2 =  (B1.F2+B2.F1)	
+
+	FP4_times_i(&w2);
+	FP4_add(&w1,&w1,&w2);
+	FP8_from_FP4s(&(w->a),&w1,&tc);
+
+	FP4_times_i(&w3);
+	FP4_norm(&w3);
+	FP8_from_FP4H(&(w->b),&w3);
+
+	FP4_norm(&te);
+	FP4_times_i(&te);
+	FP8_from_FP4s(&(w->c),&te,&td);
+
+	FP8_norm(&(w->a));
+	FP8_norm(&(w->c));
+#endif
+
+	w->type=FP_SPARSE;
+}
+
 /* Set w=1/x */
 /* SU= 600 */
 void YYY::FP24_inv(FP24 *w,FP24 *x)
@@ -400,7 +604,7 @@
     FP8_mul(&(w->a),&f0,&f3);
     FP8_mul(&(w->b),&f1,&f3);
     FP8_mul(&(w->c),&f2,&f3);
-
+	w->type=FP_DENSE;
 }
 
 /* constant time powering by small integer of max length bts */
@@ -659,6 +863,7 @@
 		FP8_qmul(&(w->b),&(w->b),f); FP8_times_i2(&(w->b));
 		FP8_qmul(&(w->c),&(w->c),&f2); FP8_times_i2(&(w->c)); FP8_times_i2(&(w->c));
 	}
+	w->type=FP_DENSE;
 }
 
 
@@ -824,4 +1029,6 @@
     FP8_cmove(&(f->a),&(g->a),d);
     FP8_cmove(&(f->b),&(g->b),d);
     FP8_cmove(&(f->c),&(g->c),d);
+	d=~(d-1);
+	f->type^=(f->type^g->type)&d;
 }
diff --git a/version3/cpp/fp24.h b/version3/cpp/fp24.h
index d7a0fa0..41639f0 100644
--- a/version3/cpp/fp24.h
+++ b/version3/cpp/fp24.h
@@ -10,11 +10,19 @@
 	@brief FP12 Structure - towered over three FP8
 */
 
+#define FP24_ZERO 0
+#define FP24_ONE 1
+#define FP24_SPARSER 2
+#define FP24_SPARSE 3
+#define FP24_DENSE 4
+
+
 typedef struct
 {
     FP8 a; /**< first part of FP12 */
     FP8 b; /**< second part of FP12 */
     FP8 c; /**< third part of FP12 */
+	int type;
 } FP24;
 
 extern const XXX::BIG Fra; /**< real part of BN curve Frobenius Constant */
@@ -85,20 +93,27 @@
 	@param y FP24 instance
  */
 extern void FP24_sqr(FP24 *x,FP24 *y);
-/**	@brief Fast multiplication of an FP24 by an FP24 that arises from an ATE pairing line function
+
+/**	@brief Fast multiplication of two sparse FP24s that arises from ATE pairing line functions
  *
-	Here the multiplier has a special form that can be exploited
 	@param x FP24 instance, on exit = x*y
 	@param y FP24 instance, of special form
-	@param t D_TYPE or M_TYPE twist
  */
-extern void FP24_smul(FP24 *x,FP24 *y,int t);
-/**	@brief Multiplication of two FP24s
+extern void FP24_smul(FP24 *x,FP24 *y);
+
+/**	@brief Fast multiplication of what may be sparse multiplicands
+ *
+	@param x FP24 instance, on exit = x*y
+	@param y FP24 instance, of special form
+ */
+extern void FP24_ssmul(FP24 *x,FP24 *y);
+/**	@brief Full unconditional Multiplication of two FP24s
  *
 	@param x FP24 instance, on exit = x*y
 	@param y FP24 instance, the multiplier
  */
 extern void FP24_mul(FP24 *x,FP24 *y);
+
 /**	@brief Inverting an FP24
  *
 	@param x FP24 instance, on exit = 1/y
diff --git a/version3/cpp/fp4.h b/version3/cpp/fp4.h
index ffd728c..d8cdc9d 100644
--- a/version3/cpp/fp4.h
+++ b/version3/cpp/fp4.h
@@ -127,6 +127,7 @@
  */
 extern void FP4_pmul(FP4 *x,FP4 *y,FP2 *a);
 
+
 /**	@brief Multiplication of an FP4 by an FP
  *
 	@param x FP4 instance, on exit = y*a
diff --git a/version3/cpp/fp48.cpp b/version3/cpp/fp48.cpp
index 0e66774..7f0ff85 100644
--- a/version3/cpp/fp48.cpp
+++ b/version3/cpp/fp48.cpp
@@ -22,6 +22,7 @@
 /* FP48 elements are of the form a+i.b+i^2.c */
 
 #include "fp48_YYY.h"
+#include "config_curve_ZZZ.h"
 
 using namespace XXX;
 
@@ -82,6 +83,7 @@
     FP16_copy(&(w->a),&(x->a));
     FP16_copy(&(w->b),&(x->b));
     FP16_copy(&(w->c),&(x->c));
+	w->type=x->type;
 }
 
 /* FP48 w=1 */
@@ -91,6 +93,7 @@
     FP16_one(&(w->a));
     FP16_zero(&(w->b));
     FP16_zero(&(w->c));
+	w->type=FP_UNITY;
 }
 
 /* return 1 if x==y, else 0 */
@@ -119,6 +122,7 @@
     FP16_copy(&(w->a),a);
     FP16_zero(&(w->b));
     FP16_zero(&(w->c));
+	w->type=FP_SPARSER;
 }
 
 /* Create FP48 from 3 FP16's */
@@ -128,6 +132,7 @@
     FP16_copy(&(w->a),a);
     FP16_copy(&(w->b),b);
     FP16_copy(&(w->c),c);
+	w->type=FP_DENSE;
 }
 
 /* Granger-Scott Unitary Squaring. This does not benefit from lazy reduction */
@@ -167,6 +172,7 @@
     FP16_add(&(w->c),&(w->c),&(w->c));
     FP16_add(&(w->b),&B,&(w->b));
     FP16_add(&(w->c),&C,&(w->c));
+	w->type=FP_DENSE;
 
     FP48_reduce(w);	    /* reduce here as in pow function repeated squarings would trigger multiple reductions */
 }
@@ -179,6 +185,12 @@
 
     FP16 A,B,C,D;
 
+	if (x->type<=FP_UNITY)
+	{
+		FP48_copy(w,x);
+		return;
+	}
+
     FP16_sqr(&A,&(x->a));
     FP16_mul(&B,&(x->b),&(x->c));
     FP16_add(&B,&B,&B);
@@ -212,6 +224,11 @@
     FP16_add(&(w->b),&C,&D);
     FP16_add(&(w->c),&(w->c),&A);
 
+	if (x->type==FP_SPARSER)
+		w->type=FP_SPARSE;
+	else
+		w->type=FP_DENSE;
+
     FP48_norm(w);
 }
 
@@ -271,18 +288,105 @@
     FP16_add(&(w->a),&z0,&z3);
 
     FP48_norm(w);
+	w->type=FP_DENSE;
 }
 
-/* FP48 multiplication w=w*y */
-/* SU= 744 */
-/* catering for special case that arises from special form of ATE pairing line function */
-void YYY::FP48_smul(FP48 *w,FP48 *y,int type)
+/* FP48 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+void YYY::FP48_ssmul(FP48 *w,FP48 *y)
 {
-    FP16 z0,z1,z2,z3,t0,t1;
+	FP16 z0,z1,z2,z3,t0,t1;
+	if (w->type==FP_UNITY)
+	{
+		FP48_copy(w,y);
+		return;
+	}
+	if (y->type==FP_UNITY)
+		return;
 
-	if (type==D_TYPE)
-	{ // y->c is 0
+	if (y->type >= FP_SPARSE)
+	{
+		FP16_mul(&z0,&(w->a),&(y->a));  // xa.ya   always 11x11
 
+#if SEXTIC_TWIST_ZZZ == M_TYPE
+		if (y->type==FP_SPARSE || w->type==FP_SPARSE)
+		{
+			FP8_mul(&z2.b,&(w->b).b,&(y->b).b);
+			FP8_zero(&z2.a);
+			if (y->type!=FP_SPARSE)
+				FP8_mul(&z2.a,&(w->b).b,&(y->b).a);
+			if (w->type!=FP_SPARSE)
+				FP8_mul(&z2.a,&(w->b).a,&(y->b).b);
+			FP16_times_i(&z2);
+		}
+		else
+#endif 
+			FP16_mul(&z2,&(w->b),&(y->b));  // xb.yb  could be 00x00 or 01x01 or or 10x10 or 11x00 or 11x10 or 11x01 or 11x11 
+
+		FP16_add(&t0,&(w->a),&(w->b));  // (xa+xb)
+		FP16_add(&t1,&(y->a),&(y->b));  // (ya+yb)
+
+		FP16_norm(&t0);
+		FP16_norm(&t1);
+
+		FP16_mul(&z1,&t0,&t1); // (xa+xb)(ya+yb)  always 11x11
+		FP16_add(&t0,&(w->b),&(w->c));  // (xb+xc)
+		FP16_add(&t1,&(y->b),&(y->c));  // (yb+yc)
+
+		FP16_norm(&t0);
+		FP16_norm(&t1);
+
+		FP16_mul(&z3,&t0,&t1);	// (xb+xc)(yb+yc)   could be anything...
+		FP16_neg(&t0,&z0);		// -(xa.ya)
+		FP16_neg(&t1,&z2);		// -(xb.yb)
+
+		FP16_add(&z1,&z1,&t0);  
+		FP16_add(&(w->b),&z1,&t1); // /wb = (xa+xb)(ya+yb) -(xa.ya) -(xb.yb)						= xa.yb + xb.ya
+
+		FP16_add(&z3,&z3,&t1);        // (xb+xc)(yb+yc) -(xb.yb)
+		FP16_add(&z2,&z2,&t0);        // (xb.yb) - (xa.ya)
+
+		FP16_add(&t0,&(w->a),&(w->c));  // (xa+xc)
+		FP16_add(&t1,&(y->a),&(y->c));  // (ya+yc)
+
+		FP16_norm(&t0);
+		FP16_norm(&t1);
+
+		FP16_mul(&t0,&t1,&t0);	// (xa+xc)(ya+yc)    always 11x11
+		FP16_add(&z2,&z2,&t0);	// (xb.yb) - (xa.ya) + (xa+xc)(ya+yc)
+
+#if SEXTIC_TWIST_ZZZ == D_TYPE
+		if (y->type==FP_SPARSE || w->type==FP_SPARSE)
+		{
+			FP8_mul(&t0.a,&(w->c).a,&(y->c).a);
+			FP8_zero(&t0.b);
+			if (y->type!=FP_SPARSE)
+				FP8_mul(&t0.b,&(w->c).a,&(y->c).b);
+			if (w->type!=FP_SPARSE)
+				FP8_mul(&t0.b,&(w->c).b,&(y->c).a);
+		}
+		else
+#endif
+			FP16_mul(&t0,&(w->c),&(y->c)); // (xc.yc)  could be anything
+			
+		FP16_neg(&t1,&t0);			  // -(xc.yc) 
+
+		FP16_add(&(w->c),&z2,&t1);		// wc = (xb.yb) - (xa.ya) + (xa+xc)(ya+yc) - (xc.yc)	=  xb.yb + xc.ya + xa.yc
+		FP16_add(&z3,&z3,&t1);			// (xb+xc)(yb+yc) -(xb.yb) - (xc.yc)					=  xb.yc + xc.yb
+		FP16_times_i(&t0);				// i.(xc.yc)
+		FP16_add(&(w->b),&(w->b),&t0);   // wb = (xa+xb)(ya+yb) -(xa.ya) -(xb.yb) +i(xc.yc)
+		FP16_norm(&z3);
+		FP16_times_i(&z3);				// i[(xb+xc)(yb+yc) -(xb.yb) - (xc.yc)]					= i(xb.yc + xc.yb)
+		FP16_add(&(w->a),&z0,&z3);		// wa = xa.ya + i(xb.yc + xc.yb)
+	} else {
+		if (w->type==FP_SPARSER)
+		{
+			FP48_smul(w,y);
+			return;
+		}
+// dense by sparser - 13m 
+#if SEXTIC_TWIST_ZZZ == D_TYPE
 		FP16_copy(&z3,&(w->b));
 		FP16_mul(&z0,&(w->a),&(y->a));
 
@@ -308,7 +412,6 @@
 		FP16_add(&z2,&z2,&t0);        // z2=z2-z0
 
 		FP16_add(&t0,&(w->a),&(w->c));
-
 		FP16_norm(&t0);
 		FP16_norm(&z3);
 
@@ -317,10 +420,8 @@
 
 		FP16_times_i(&z3);
 		FP16_add(&(w->a),&z0,&z3);
-	}
-
-	if (type==M_TYPE)
-	{ // y->b is zero
+#endif
+#if SEXTIC_TWIST_ZZZ == M_TYPE
 		FP16_mul(&z0,&(w->a),&(y->a));
 		FP16_add(&t0,&(w->a),&(w->b));
 		FP16_norm(&t0);
@@ -336,7 +437,6 @@
 		FP16_add(&z1,&z1,&t0);   // z1=z1-z0
 
 		FP16_copy(&(w->b),&z1);
-
 		FP16_copy(&z2,&t0);
 
 		FP16_add(&t0,&(w->a),&(w->c));
@@ -360,10 +460,112 @@
 		FP16_norm(&z3);
 		FP16_times_i(&z3);
 		FP16_add(&(w->a),&z0,&z3);
+#endif
 	}
+	w->type=FP_DENSE;
     FP48_norm(w);
 }
 
+/* FP48 multiplication w=w*y */
+/* catering for special case that arises from special form of ATE pairing line function */
+/* w and y are both sparser line functions - cost = 6m */ 
+void YYY::FP48_smul(FP48 *w,FP48 *y)
+{
+	FP8 w1,w2,w3,ta,tb,tc,td,te,t;
+
+
+#if SEXTIC_TWIST_ZZZ == D_TYPE
+	FP8_mul(&w1,&(w->a).a,&(y->a).a); // A1.A2
+	FP8_mul(&w2,&(w->a).b,&(y->a).b); // B1.B2
+	FP8_mul(&w3,&(w->b).a,&(y->b).a); // C1.C2
+
+	FP8_add(&ta,&(w->a).a,&(w->a).b); // A1+B1
+	FP8_add(&tb,&(y->a).a,&(y->a).b); // A2+B2
+	FP8_norm(&ta);
+	FP8_norm(&tb);
+	FP8_mul(&tc,&ta,&tb);			// (A1+B1)(A2+B2)
+	FP8_add(&t,&w1,&w2);
+	FP8_neg(&t,&t);
+	FP8_add(&tc,&tc,&t);			// (A1+B1)(A2+B2)-A1.A2-B1*B2 =  (A1.B2+A2.B1)		
+				
+	FP8_add(&ta,&(w->a).a,&(w->b).a); // A1+C1
+	FP8_add(&tb,&(y->a).a,&(y->b).a); // A2+C2
+	FP8_norm(&ta);
+	FP8_norm(&tb);
+	FP8_mul(&td,&ta,&tb);			// (A1+C1)(A2+C2)
+	FP8_add(&t,&w1,&w3);
+	FP8_neg(&t,&t);
+	FP8_add(&td,&td,&t);			// (A1+C1)(A2+C2)-A1.A2-C1*C2 =  (A1.C2+A2.C1)		
+
+	FP8_add(&ta,&(w->a).b,&(w->b).a); // B1+C1
+	FP8_add(&tb,&(y->a).b,&(y->b).a); // B2+C2
+	FP8_norm(&ta);
+	FP8_norm(&tb);
+	FP8_mul(&te,&ta,&tb);			// (B1+C1)(B2+C2)
+	FP8_add(&t,&w2,&w3);
+	FP8_neg(&t,&t);
+	FP8_add(&te,&te,&t);			// (B1+C1)(B2+C2)-B1.B2-C1*C2 =  (B1.C2+B2.C1)		
+
+	FP8_times_i(&w2);
+	FP8_add(&w1,&w1,&w2);
+	FP16_from_FP8s(&(w->a),&w1,&tc);
+	FP16_from_FP8s(&(w->b),&td,&te); // only norm these 2
+	FP16_from_FP8(&(w->c),&w3);
+
+	FP16_norm(&(w->a));
+	FP16_norm(&(w->b));
+#endif
+#if SEXTIC_TWIST_ZZZ == M_TYPE
+	FP8_mul(&w1,&(w->a).a,&(y->a).a); // A1.A2
+	FP8_mul(&w2,&(w->a).b,&(y->a).b); // B1.B2
+	FP8_mul(&w3,&(w->c).b,&(y->c).b); // F1.F2
+
+	FP8_add(&ta,&(w->a).a,&(w->a).b); // A1+B1
+	FP8_add(&tb,&(y->a).a,&(y->a).b); // A2+B2
+	FP8_norm(&ta);
+	FP8_norm(&tb);
+	FP8_mul(&tc,&ta,&tb);			// (A1+B1)(A2+B2)
+	FP8_add(&t,&w1,&w2);
+	FP8_neg(&t,&t);
+	FP8_add(&tc,&tc,&t);			// (A1+B1)(A2+B2)-A1.A2-B1*B2 =  (A1.B2+A2.B1)		
+				
+	FP8_add(&ta,&(w->a).a,&(w->c).b); // A1+F1
+	FP8_add(&tb,&(y->a).a,&(y->c).b); // A2+F2
+	FP8_norm(&ta);
+	FP8_norm(&tb);
+	FP8_mul(&td,&ta,&tb);			// (A1+F1)(A2+F2)
+	FP8_add(&t,&w1,&w3);
+	FP8_neg(&t,&t);
+	FP8_add(&td,&td,&t);			// (A1+F1)(A2+F2)-A1.A2-F1*F2 =  (A1.F2+A2.F1)		
+
+	FP8_add(&ta,&(w->a).b,&(w->c).b); // B1+F1
+	FP8_add(&tb,&(y->a).b,&(y->c).b); // B2+F2
+	FP8_norm(&ta);
+	FP8_norm(&tb);
+	FP8_mul(&te,&ta,&tb);			// (B1+F1)(B2+F2)
+	FP8_add(&t,&w2,&w3);
+	FP8_neg(&t,&t);
+	FP8_add(&te,&te,&t);			// (B1+F1)(B2+F2)-B1.B2-F1*F2 =  (B1.F2+B2.F1)	
+
+	FP8_times_i(&w2);
+	FP8_add(&w1,&w1,&w2);
+	FP16_from_FP8s(&(w->a),&w1,&tc);
+
+	FP8_times_i(&w3);
+	FP8_norm(&w3);
+	FP16_from_FP8H(&(w->b),&w3);
+
+	FP8_norm(&te);
+	FP8_times_i(&te);
+	FP16_from_FP8s(&(w->c),&te,&td);
+
+	FP16_norm(&(w->a));
+	FP16_norm(&(w->c));
+#endif
+
+	w->type=FP_SPARSE;
+}
+
 /* Set w=1/x */
 /* SU= 600 */
 void YYY::FP48_inv(FP48 *w,FP48 *x)
@@ -401,7 +603,7 @@
     FP16_mul(&(w->a),&f0,&f3);
     FP16_mul(&(w->b),&f1,&f3);
     FP16_mul(&(w->c),&f2,&f3);
-
+	w->type=FP_DENSE;
 }
 
 /* constant time powering by small integer of max length bts */
@@ -726,8 +928,8 @@
   
 		FP16_qmul(&(w->b),&(w->b),f); FP16_times_i4(&(w->b)); FP16_times_i2(&(w->b)); 
 		FP16_qmul(&(w->c),&(w->c),&f2); FP16_times_i4(&(w->c)); FP16_times_i4(&(w->c)); FP16_times_i4(&(w->c)); 
-
 	}
+	w->type=FP_DENSE;
 }
 
 /* SU= 8 */
@@ -1030,5 +1232,7 @@
     FP16_cmove(&(f->a),&(g->a),d);
     FP16_cmove(&(f->b),&(g->b),d);
     FP16_cmove(&(f->c),&(g->c),d);
+	d=~(d-1);
+	f->type^=(f->type^g->type)&d;
 }
 
diff --git a/version3/cpp/fp48.h b/version3/cpp/fp48.h
index 64be9e9..407ed17 100644
--- a/version3/cpp/fp48.h
+++ b/version3/cpp/fp48.h
@@ -10,11 +10,19 @@
 	@brief FP12 Structure - towered over three FP16
 */
 
+#define FP48_ZERO 0
+#define FP48_ONE 1
+#define FP48_SPARSER 2
+#define FP48_SPARSE 3
+#define FP48_DENSE 4
+
+
 typedef struct
 {
     FP16 a; /**< first part of FP12 */
     FP16 b; /**< second part of FP12 */
     FP16 c; /**< third part of FP12 */
+	int type;
 } FP48;
 
 extern const XXX::BIG Fra; /**< real part of BN curve Frobenius Constant */
@@ -85,15 +93,20 @@
 	@param y FP48 instance
  */
 extern void FP48_sqr(FP48 *x,FP48 *y);
-/**	@brief Fast multiplication of an FP48 by an FP48 that arises from an ATE pairing line function
+/**	@brief Fast multiplication of two sparse FP24s that arises from ATE pairing line functions
  *
-	Here the multiplier has a special form that can be exploited
 	@param x FP48 instance, on exit = x*y
 	@param y FP48 instance, of special form
-	@param t D_TYPE or M_TYPE twist
  */
-extern void FP48_smul(FP48 *x,FP48 *y,int t);
-/**	@brief Multiplication of two FP48s
+extern void FP48_smul(FP48 *x,FP48 *y);
+
+/**	@brief Fast multiplication of what may be sparse multiplicands
+ *
+	@param x FP48 instance, on exit = x*y
+	@param y FP48 instance, of special form
+ */
+extern void FP48_ssmul(FP48 *x,FP48 *y);
+/**	@brief Full unconditional Multiplication of two FP24s
  *
 	@param x FP48 instance, on exit = x*y
 	@param y FP48 instance, the multiplier
diff --git a/version3/cpp/pair.cpp b/version3/cpp/pair.cpp
index ff37216..3cad837 100644
--- a/version3/cpp/pair.cpp
+++ b/version3/cpp/pair.cpp
@@ -48,7 +48,7 @@
 
 
 		FP2_copy(&YZ,&YY);		//FP2 YZ=new FP2(YY);        //Y 
-		FP2_mul(&YZ,&YZ,&ZZ);		//YZ.mul(ZZ);                //YZ
+		FP2_mul(&YZ,&YZ,&ZZ);	//YZ.mul(ZZ);                //YZ
 		FP2_sqr(&XX,&XX);		//XX.sqr();	               //X^2
 		FP2_sqr(&YY,&YY);		//YY.sqr();	               //Y^2
 		FP2_sqr(&ZZ,&ZZ);		//ZZ.sqr();			       //Z^2
@@ -57,8 +57,8 @@
 		FP2_neg(&YZ,&YZ);		//YZ.neg(); 
 		FP2_norm(&YZ);			//YZ.norm();       //-4YZ
 
-		FP2_imul(&XX,&XX,6);					//6X^2
-		FP2_pmul(&XX,&XX,Qx);	               //6X^2.Xs
+		FP2_imul(&XX,&XX,6);				//6X^2
+		FP2_pmul(&XX,&XX,Qx);	            //6X^2.Xs
 
 		FP2_imul(&ZZ,&ZZ,3*CURVE_B_I);	//3Bz^2 
 
@@ -99,7 +99,7 @@
 		FP2_copy(&Y1,&(A->y));		//FP2 Y1=new FP2(A.gety());    // Y1
 		FP2_copy(&T1,&(A->z));		//FP2 T1=new FP2(A.getz());    // Z1
 			
-		FP2_copy(&T2,&T1);		//FP2 T2=new FP2(A.getz());    // Z1
+		FP2_copy(&T2,&T1);			//FP2 T2=new FP2(A.getz());    // Z1
 
 		FP2_mul(&T1,&T1,&(B->y));	//T1.mul(B.gety());    // T1=Z1.Y2 
 		FP2_mul(&T2,&T2,&(B->x));	//T2.mul(B.getx());    // T2=Z1.X2
@@ -136,13 +136,146 @@
 		FP4_zero(&b);
 		FP4_from_FP2H(&c,&Y1);		//b=new FP4(Y1);
 #endif
-		ECP2_add(A,B);			//A.add(B);
+		ECP2_add(A,B);				//A.add(B);
     }
 
     FP12_from_FP4s(v,&a,&b,&c);
+	v->type=FP_SPARSER;
 }
 
-/* Optimal R-ate pairing r=e(P,Q) */
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+int ZZZ::PAIR_nbits(BIG n3,BIG n)
+{
+	BIG x;
+    BIG_rcopy(x,CURVE_Bnx);
+
+#if PAIRING_FRIENDLY_ZZZ==BN
+    BIG_pmul(n,x,6);
+#if SIGN_OF_X_ZZZ==POSITIVEX
+	BIG_inc(n,2);
+#else
+    BIG_dec(n,2);
+#endif
+
+#else
+    BIG_copy(n,x);
+#endif
+
+    BIG_norm(n);
+	BIG_pmul(n3,n,3);
+	BIG_norm(n3);
+
+    return BIG_nbits(n3);
+}
+
+/*
+	For multi-pairing, product of n pairings
+	1. Declare FP12 array of length number of bits in Ate parameter
+	2. Initialise this array by calling PAIR_initmp()
+	3. Accumulate each pairing by calling PAIR_another() n times
+	4. Call PAIR_miller()
+	5. Call final exponentiation PAIR_fexp()
+*/
+
+/* prepare for multi-pairing */
+void ZZZ::PAIR_initmp(FP12 r[])
+{
+	int i;
+	for (i=ATE_BITS_ZZZ-1; i>=0; i--)
+		FP12_one(&r[i]);
+	return;
+}
+
+/* basic Miller loop */
+void ZZZ::PAIR_miller(FP12 *res,FP12 r[])
+{
+	int i;
+    FP12_one(res);
+	for (i=ATE_BITS_ZZZ-1; i>=1; i--)
+	{
+		FP12_sqr(res,res);
+		FP12_ssmul(res,&r[i]);
+	}
+
+#if SIGN_OF_X_ZZZ==NEGATIVEX
+    FP12_conj(res,res);
+#endif
+	FP12_ssmul(res,&r[0]);
+	return;
+}
+
+/* Accumulate another set of line functions for n-pairing */
+void ZZZ::PAIR_another(FP12 r[],ECP2* PV,ECP* QV)
+{
+    int i,j,nb,bt;
+	BIG x,n,n3;
+    FP12 lv,lv2;
+    ECP2 A,NP,P;
+	ECP Q;
+	FP Qx,Qy;
+#if PAIRING_FRIENDLY_ZZZ==BN
+	ECP2 K;
+    FP2 X;
+    FP_rcopy(&Qx,Fra);
+    FP_rcopy(&Qy,Frb);
+    FP2_from_FPs(&X,&Qx,&Qy);
+#if SEXTIC_TWIST_ZZZ==M_TYPE
+	FP2_inv(&X,&X);
+	FP2_norm(&X);
+#endif
+#endif
+
+	nb=PAIR_nbits(n3,n);
+
+	ECP2_copy(&P,PV);
+	ECP_copy(&Q,QV);
+
+	ECP2_affine(&P);
+	ECP_affine(&Q);
+
+	FP_copy(&Qx,&(Q.x));
+	FP_copy(&Qy,&(Q.y));
+
+	ECP2_copy(&A,&P);
+	ECP2_copy(&NP,&P); ECP2_neg(&NP);
+
+	for (i=nb-2; i>=1; i--)
+	{
+		PAIR_line(&lv,&A,&A,&Qx,&Qy);
+
+		bt=BIG_bit(n3,i)-BIG_bit(n,i); // bt=BIG_bit(n,i);
+		if (bt==1)
+		{
+			PAIR_line(&lv2,&A,&P,&Qx,&Qy);
+			FP12_smul(&lv,&lv2);
+		}
+		if (bt==-1)
+		{
+			PAIR_line(&lv2,&A,&NP,&Qx,&Qy);
+			FP12_smul(&lv,&lv2);
+		}
+		FP12_ssmul(&r[i],&lv);
+	}
+
+#if PAIRING_FRIENDLY_ZZZ==BN
+
+#if SIGN_OF_X_ZZZ==NEGATIVEX
+	ECP2_neg(&A);
+#endif
+
+	ECP2_copy(&K,&P);
+	ECP2_frob(&K,&X);
+	PAIR_line(&lv,&A,&K,&Qx,&Qy);
+	ECP2_frob(&K,&X);
+	ECP2_neg(&K);
+	PAIR_line(&lv2,&A,&K,&Qx,&Qy);
+	FP12_smul(&lv,&lv2);
+	FP12_ssmul(&r[0],&lv);
+
+#endif
+}
+
+/* Optimal single R-ate pairing r=e(P,Q) */
 void ZZZ::PAIR_ate(FP12 *r,ECP2 *P1,ECP *Q1)
 {
     BIG x,n,n3;
@@ -150,7 +283,7 @@
     int i,nb,bt;
     ECP2 A,NP,P;
 	ECP Q;
-    FP12 lv;
+    FP12 lv,lv2;
 #if PAIRING_FRIENDLY_ZZZ==BN
     ECP2 KA;
     FP2 X;
@@ -166,23 +299,7 @@
 
 #endif
 
-    BIG_rcopy(x,CURVE_Bnx);
-
-#if PAIRING_FRIENDLY_ZZZ==BN
-    BIG_pmul(n,x,6);
-#if SIGN_OF_X_ZZZ==POSITIVEX
-	BIG_inc(n,2);
-#else
-    BIG_dec(n,2);
-#endif
-
-#else
-    BIG_copy(n,x);
-#endif
-
-    BIG_norm(n);
-	BIG_pmul(n3,n,3);
-	BIG_norm(n3);
+	nb=PAIR_nbits(n3,n);
 
 	ECP2_copy(&P,P1);
 	ECP_copy(&Q,Q1);
@@ -190,56 +307,53 @@
 	ECP2_affine(&P);
 	ECP_affine(&Q);
 
-
     FP_copy(&Qx,&(Q.x));
     FP_copy(&Qy,&(Q.y));
 
     ECP2_copy(&A,&P);
-
 	ECP2_copy(&NP,&P); ECP2_neg(&NP);
 
     FP12_one(r);
-    nb=BIG_nbits(n3);
 
     /* Main Miller Loop */
     for (i=nb-2; i>=1; i--)
     {
 		FP12_sqr(r,r);
         PAIR_line(&lv,&A,&A,&Qx,&Qy);
-        FP12_smul(r,&lv,SEXTIC_TWIST_ZZZ);
 
 		bt=BIG_bit(n3,i)-BIG_bit(n,i); // bt=BIG_bit(n,i);
         if (bt==1)
         {
-            PAIR_line(&lv,&A,&P,&Qx,&Qy);
-            FP12_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            PAIR_line(&lv2,&A,&P,&Qx,&Qy);
+            FP12_smul(&lv,&lv2);
         }
 		if (bt==-1)
 		{
-			//ECP2_neg(P);
-            PAIR_line(&lv,&A,&NP,&Qx,&Qy);
-            FP12_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-			//ECP2_neg(P);
+            PAIR_line(&lv2,&A,&NP,&Qx,&Qy);
+            FP12_smul(&lv,&lv2);
 		}
-
+		FP12_ssmul(r,&lv);
     }
 
 #if SIGN_OF_X_ZZZ==NEGATIVEX
     FP12_conj(r,r);
 #endif
+
     /* R-ate fixup required for BN curves */
 #if PAIRING_FRIENDLY_ZZZ==BN
-    ECP2_copy(&KA,&P);
-    ECP2_frob(&KA,&X);
+
 #if SIGN_OF_X_ZZZ==NEGATIVEX
     ECP2_neg(&A);
 #endif
+
+    ECP2_copy(&KA,&P);
+    ECP2_frob(&KA,&X);
     PAIR_line(&lv,&A,&KA,&Qx,&Qy);
-    FP12_smul(r,&lv,SEXTIC_TWIST_ZZZ);
     ECP2_frob(&KA,&X);
     ECP2_neg(&KA);
-    PAIR_line(&lv,&A,&KA,&Qx,&Qy);
-    FP12_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+    PAIR_line(&lv2,&A,&KA,&Qx,&Qy);
+	FP12_smul(&lv,&lv2);
+    FP12_ssmul(r,&lv);
 #endif
 }
 
@@ -252,7 +366,7 @@
     int i,nb,bt;
     ECP2 A,B,NP,NR,P,R;
 	ECP Q,S;
-    FP12 lv;
+    FP12 lv,lv2;
 #if PAIRING_FRIENDLY_ZZZ==BN
     ECP2 K;
     FP2 X;
@@ -267,22 +381,7 @@
 #endif
 
 #endif
-    BIG_rcopy(x,CURVE_Bnx);
-
-#if PAIRING_FRIENDLY_ZZZ==BN
-    BIG_pmul(n,x,6);
-#if SIGN_OF_X_ZZZ==POSITIVEX
-	BIG_inc(n,2);
-#else
-    BIG_dec(n,2);
-#endif
-#else
-    BIG_copy(n,x);
-#endif
-
-    BIG_norm(n);
-	BIG_pmul(n3,n,3);
-	BIG_norm(n3);
+	nb=PAIR_nbits(n3,n);
 
 	ECP2_copy(&P,P1);
 	ECP_copy(&Q,Q1);
@@ -309,33 +408,30 @@
 	ECP2_copy(&NR,&R); ECP2_neg(&NR);
 
     FP12_one(r);
-    nb=BIG_nbits(n3);
 
     /* Main Miller Loop */
     for (i=nb-2; i>=1; i--)
     {
         FP12_sqr(r,r);
         PAIR_line(&lv,&A,&A,&Qx,&Qy);
-        FP12_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-
-        PAIR_line(&lv,&B,&B,&Sx,&Sy);
-        FP12_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+        PAIR_line(&lv2,&B,&B,&Sx,&Sy);
+		FP12_smul(&lv,&lv2);
+        FP12_ssmul(r,&lv);
 
 		bt=BIG_bit(n3,i)-BIG_bit(n,i); // bt=BIG_bit(n,i);
         if (bt==1)
         {
             PAIR_line(&lv,&A,&P,&Qx,&Qy);
-            FP12_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-
-            PAIR_line(&lv,&B,&R,&Sx,&Sy);
-            FP12_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            PAIR_line(&lv2,&B,&R,&Sx,&Sy);
+			FP12_smul(&lv,&lv2);
+            FP12_ssmul(r,&lv);
         }
 		if (bt==-1)
 		{
             PAIR_line(&lv,&A,&NP,&Qx,&Qy);
-            FP12_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-            PAIR_line(&lv,&B,&NR,&Sx,&Sy);
-            FP12_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            PAIR_line(&lv2,&B,&NR,&Sx,&Sy);
+			FP12_smul(&lv,&lv2);
+            FP12_ssmul(r,&lv);
 		}
 	}
 
@@ -353,20 +449,20 @@
     ECP2_copy(&K,&P);
     ECP2_frob(&K,&X);
     PAIR_line(&lv,&A,&K,&Qx,&Qy);
-    FP12_smul(r,&lv,SEXTIC_TWIST_ZZZ);
     ECP2_frob(&K,&X);
     ECP2_neg(&K);
-    PAIR_line(&lv,&A,&K,&Qx,&Qy);
-    FP12_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+    PAIR_line(&lv2,&A,&K,&Qx,&Qy);
+	FP12_smul(&lv,&lv2);
+    FP12_ssmul(r,&lv);
+
     ECP2_copy(&K,&R);
     ECP2_frob(&K,&X);
-
     PAIR_line(&lv,&B,&K,&Sx,&Sy);
-    FP12_smul(r,&lv,SEXTIC_TWIST_ZZZ);
     ECP2_frob(&K,&X);
     ECP2_neg(&K);
-    PAIR_line(&lv,&B,&K,&Sx,&Sy);
-    FP12_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+    PAIR_line(&lv2,&B,&K,&Sx,&Sy);
+	FP12_smul(&lv,&lv2);
+    FP12_ssmul(r,&lv);
 #endif
 }
 
diff --git a/version3/cpp/pair.h b/version3/cpp/pair.h
index 0d6d179..9585597 100644
--- a/version3/cpp/pair.h
+++ b/version3/cpp/pair.h
@@ -19,6 +19,17 @@
 extern const XXX::BIG CURVE_BB[4][4]; /**< BN curve constant for GS decomposition */
 
 /* Pairing function prototypes */
+
+/**	@brief Precompute line functions for n-pairing
+ *
+	@param r array of precomputed FP12 products of line functions
+	@param PV ECP2 instance, an element of G2
+	@param QV ECP instance, an element of G1
+
+ */
+extern void PAIR_another(YYY::FP12 r[],ECP2* PV,ECP* QV);
+
+
 /**	@brief Calculate Miller loop for Optimal ATE pairing e(P,Q)
  *
 	@param r FP12 result of the pairing calculation e(P,Q)
@@ -70,11 +81,35 @@
 /**	@brief Tests FP12 for membership of GT
  *
 	@param x FP12 instance
-	@return 1 if x is in GT, else return 0
 
  */
 extern int PAIR_GTmember(YYY::FP12 *x);
 
+/**	@brief Prepare Ate parameter
+ *
+	@param n BIG parameter
+	@param n3 BIG paramter = 3*n
+	@return number of nits in n3
+
+ */
+extern int PAIR_nbits(XXX::BIG n3,XXX::BIG n);
+
+/**	@brief Initialise structure for multi-pairing
+ *
+	@param r FP12 array, to be initialised to 1
+
+ */
+extern void PAIR_initmp(YYY::FP12 r[]);
+
+
+/**	@brief Miller loop
+ *
+ 	@param res FP12 result
+	@param r FP12 precomputed array of accumulated line functions
+
+ */
+extern void PAIR_miller(YYY::FP12 *res,YYY::FP12 r[]);
+
 }
 
 #endif
diff --git a/version3/cpp/pair192.cpp b/version3/cpp/pair192.cpp
index 4d6303c..2d673fd 100644
--- a/version3/cpp/pair192.cpp
+++ b/version3/cpp/pair192.cpp
@@ -140,6 +140,101 @@
     }
 
     FP24_from_FP8s(v,&a,&b,&c);
+	v->type=FP_SPARSER;
+}
+
+
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+int ZZZ::PAIR_nbits(BIG n3,BIG n)
+{
+	BIG x;
+    BIG_rcopy(x,CURVE_Bnx);
+
+    BIG_copy(n,x);
+    BIG_norm(n);
+	BIG_pmul(n3,n,3);
+	BIG_norm(n3);
+
+    return BIG_nbits(n3);
+}
+
+/*
+	For multi-pairing, product of n pairings
+	1. Declare FP24 array of length number of bits in Ate parameter
+	2. Initialise this array by calling PAIR_initmp()
+	3. Accumulate each pairing by calling PAIR_another() n times
+	4. Call PAIR_miller()
+	5. Call final exponentiation PAIR_fexp()
+*/
+
+/* prepare for multi-pairing */
+void ZZZ::PAIR_initmp(FP24 r[])
+{
+	int i;
+	for (i=ATE_BITS_ZZZ-1; i>=0; i--)
+		FP24_one(&r[i]);
+	return;
+}
+
+/* basic Miller loop */
+void ZZZ::PAIR_miller(FP24 *res,FP24 r[])
+{
+	int i;
+    FP24_one(res);
+	for (i=ATE_BITS_ZZZ-1; i>=1; i--)
+	{
+		FP24_sqr(res,res);
+		FP24_ssmul(res,&r[i]);
+	}
+
+#if SIGN_OF_X_ZZZ==NEGATIVEX
+    FP24_conj(res,res);
+#endif
+	FP24_ssmul(res,&r[0]);
+	return;
+}
+
+/* Accumulate another set of line functions for n-pairing */
+void ZZZ::PAIR_another(FP24 r[],ECP4* PV,ECP* QV)
+{
+    int i,j,nb,bt;
+	BIG x,n,n3;
+    FP24 lv,lv2;
+    ECP4 A,NP,P;
+	ECP Q;
+	FP Qx,Qy;
+
+	nb=PAIR_nbits(n3,n);
+
+	ECP4_copy(&P,PV);
+	ECP_copy(&Q,QV);
+
+	ECP4_affine(&P);
+	ECP_affine(&Q);
+
+	FP_copy(&Qx,&(Q.x));
+	FP_copy(&Qy,&(Q.y));
+
+	ECP4_copy(&A,&P);
+	ECP4_copy(&NP,&P); ECP4_neg(&NP);
+
+	for (i=nb-2; i>=1; i--)
+	{
+		PAIR_line(&lv,&A,&A,&Qx,&Qy);
+
+		bt=BIG_bit(n3,i)-BIG_bit(n,i); // bt=BIG_bit(n,i);
+		if (bt==1)
+		{
+			PAIR_line(&lv2,&A,&P,&Qx,&Qy);
+			FP24_smul(&lv,&lv2);
+		}
+		if (bt==-1)
+		{
+			PAIR_line(&lv2,&A,&NP,&Qx,&Qy);
+			FP24_smul(&lv,&lv2);
+		}
+		FP24_ssmul(&r[i],&lv);
+	}
 }
 
 /* Optimal R-ate pairing r=e(P,Q) */
@@ -147,17 +242,12 @@
 {
     BIG x,n,n3;
 	FP Qx,Qy;
-    int i,j,nb,bt;
+    int i,nb,bt;
     ECP4 A,NP,P;
 	ECP Q;
-    FP24 lv;
+    FP24 lv,lv2;
 
-    BIG_rcopy(x,CURVE_Bnx);
-
-    BIG_copy(n,x);
-
-	BIG_pmul(n3,n,3);
-	BIG_norm(n3);
+	nb=PAIR_nbits(n3,n);
 
 	ECP4_copy(&P,P1);
 	ECP_copy(&Q,Q1);
@@ -169,32 +259,28 @@
     FP_copy(&Qy,&(Q.y));
 
     ECP4_copy(&A,&P);
-
 	ECP4_copy(&NP,&P); ECP4_neg(&NP);
 
     FP24_one(r);
-    nb=BIG_nbits(n3);  // n3
 
-	j=0;
     /* Main Miller Loop */
     for (i=nb-2; i>=1; i--)
     {
-		j++;
 		FP24_sqr(r,r);
         PAIR_line(&lv,&A,&A,&Qx,&Qy);
-        FP24_smul(r,&lv,SEXTIC_TWIST_ZZZ);
 
 		bt= BIG_bit(n3,i)-BIG_bit(n,i);  // BIG_bit(n,i); 
         if (bt==1)
         {
-            PAIR_line(&lv,&A,&P,&Qx,&Qy);
-            FP24_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            PAIR_line(&lv2,&A,&P,&Qx,&Qy);
+            FP24_smul(&lv,&lv2);
         }
 		if (bt==-1)
 		{
-            PAIR_line(&lv,&A,&NP,&Qx,&Qy);
-            FP24_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            PAIR_line(&lv2,&A,&NP,&Qx,&Qy);
+            FP24_smul(&lv,&lv2);
 		}
+        FP24_ssmul(r,&lv);
 
     }
 
@@ -212,13 +298,8 @@
     int i,nb,bt;
     ECP4 A,B,NP,NR,P,R;
 	ECP Q,S;
-    FP24 lv;
-
-    BIG_rcopy(x,CURVE_Bnx);
-    BIG_copy(n,x);
-
-	BIG_pmul(n3,n,3);
-	BIG_norm(n3);
+    FP24 lv,lv2;
+	nb=PAIR_nbits(n3,n);
 
 	ECP4_copy(&P,P1);
 	ECP_copy(&Q,Q1);
@@ -246,33 +327,30 @@
 
 
     FP24_one(r);
-    nb=BIG_nbits(n3);
 
     /* Main Miller Loop */
     for (i=nb-2; i>=1; i--)
     {
         FP24_sqr(r,r);
         PAIR_line(&lv,&A,&A,&Qx,&Qy);
-        FP24_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-
-        PAIR_line(&lv,&B,&B,&Sx,&Sy);
-        FP24_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+        PAIR_line(&lv2,&B,&B,&Sx,&Sy);
+		FP24_smul(&lv,&lv2);
+        FP24_ssmul(r,&lv);
 
 		bt=BIG_bit(n3,i)-BIG_bit(n,i); // bt=BIG_bit(n,i);
         if (bt==1)
         {
             PAIR_line(&lv,&A,&P,&Qx,&Qy);
-            FP24_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-
-            PAIR_line(&lv,&B,&R,&Sx,&Sy);
-            FP24_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+			PAIR_line(&lv2,&B,&R,&Sx,&Sy);
+            FP24_smul(&lv,&lv2);
+            FP24_ssmul(r,&lv);
         }
 		if (bt==-1)
 		{
             PAIR_line(&lv,&A,&NP,&Qx,&Qy);
-            FP24_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-            PAIR_line(&lv,&B,&NR,&Sx,&Sy);
-            FP24_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+			PAIR_line(&lv2,&B,&NR,&Sx,&Sy);
+            FP24_smul(&lv,&lv2);
+            FP24_ssmul(r,&lv);
 		}
 	}
 
diff --git a/version3/cpp/pair192.h b/version3/cpp/pair192.h
index 09baec4..df9792a 100644
--- a/version3/cpp/pair192.h
+++ b/version3/cpp/pair192.h
@@ -75,6 +75,41 @@
  */
 extern int PAIR_GTmember(YYY::FP24 *x);
 
+
+/**	@brief Precompute line functions for n-pairing
+ *
+	@param r array of precomputed FP24 products of line functions
+	@param PV ECP4 instance, an element of G2
+	@param QV ECP instance, an element of G1
+
+ */
+extern void PAIR_another(YYY::FP24 r[],ECP4* PV,ECP* QV);
+
+/**	@brief Prepare Ate parameter
+ *
+	@param n BIG parameter
+	@param n3 BIG paramter = 3*n
+	@return number of nits in n3
+
+ */
+extern int PAIR_nbits(XXX::BIG n3,XXX::BIG n);
+
+/**	@brief Initialise structure for multi-pairing
+ *
+	@param r FP24 array, to be initialised to 1
+
+ */
+extern void PAIR_initmp(YYY::FP24 r[]);
+
+
+/**	@brief Miller loop
+ *
+ 	@param res FP24 result
+	@param r FP24 precomputed array of accumulated line functions
+
+ */
+extern void PAIR_miller(YYY::FP24 *res,YYY::FP24 r[]);
+
 }
 
 #endif
diff --git a/version3/cpp/pair256.cpp b/version3/cpp/pair256.cpp
index ce07f29..e5719e4 100644
--- a/version3/cpp/pair256.cpp
+++ b/version3/cpp/pair256.cpp
@@ -139,6 +139,100 @@
     }
 
     FP48_from_FP16s(v,&a,&b,&c);
+	v->type=FP_SPARSER;
+}
+
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+int ZZZ::PAIR_nbits(BIG n3,BIG n)
+{
+	BIG x;
+    BIG_rcopy(x,CURVE_Bnx);
+
+    BIG_copy(n,x);
+    BIG_norm(n);
+	BIG_pmul(n3,n,3);
+	BIG_norm(n3);
+
+    return BIG_nbits(n3);
+}
+
+/*
+	For multi-pairing, product of n pairings
+	1. Declare FP24 array of length number of bits in Ate parameter
+	2. Initialise this array by calling PAIR_initmp()
+	3. Accumulate each pairing by calling PAIR_another() n times
+	4. Call PAIR_miller()
+	5. Call final exponentiation PAIR_fexp()
+*/
+
+/* prepare for multi-pairing */
+void ZZZ::PAIR_initmp(FP48 r[])
+{
+	int i;
+	for (i=ATE_BITS_ZZZ-1; i>=0; i--)
+		FP48_one(&r[i]);
+	return;
+}
+
+/* basic Miller loop */
+void ZZZ::PAIR_miller(FP48 *res,FP48 r[])
+{
+	int i;
+    FP48_one(res);
+	for (i=ATE_BITS_ZZZ-1; i>=1; i--)
+	{
+		FP48_sqr(res,res);
+		FP48_ssmul(res,&r[i]);
+	}
+
+#if SIGN_OF_X_ZZZ==NEGATIVEX
+    FP48_conj(res,res);
+#endif
+	FP48_ssmul(res,&r[0]);
+	return;
+}
+
+/* Accumulate another set of line functions for n-pairing */
+void ZZZ::PAIR_another(FP48 r[],ECP8* PV,ECP* QV)
+{
+    int i,j,nb,bt;
+	BIG x,n,n3;
+    FP48 lv,lv2;
+    ECP8 A,NP,P;
+	ECP Q;
+	FP Qx,Qy;
+
+	nb=PAIR_nbits(n3,n);
+
+	ECP8_copy(&P,PV);
+	ECP_copy(&Q,QV);
+
+	ECP8_affine(&P);
+	ECP_affine(&Q);
+
+	FP_copy(&Qx,&(Q.x));
+	FP_copy(&Qy,&(Q.y));
+
+	ECP8_copy(&A,&P);
+	ECP8_copy(&NP,&P); ECP8_neg(&NP);
+
+	for (i=nb-2; i>=1; i--)
+	{
+		PAIR_line(&lv,&A,&A,&Qx,&Qy);
+
+		bt=BIG_bit(n3,i)-BIG_bit(n,i); // bt=BIG_bit(n,i);
+		if (bt==1)
+		{
+			PAIR_line(&lv2,&A,&P,&Qx,&Qy);
+			FP48_smul(&lv,&lv2);
+		}
+		if (bt==-1)
+		{
+			PAIR_line(&lv2,&A,&NP,&Qx,&Qy);
+			FP48_smul(&lv,&lv2);
+		}
+		FP48_ssmul(&r[i],&lv);
+	}
 }
 
 /* Optimal R-ate pairing r=e(P,Q) */
@@ -146,17 +240,12 @@
 {
     BIG x,n,n3;
 	FP Qx,Qy;
-    int i,j,nb,bt;
+    int i,nb,bt;
     ECP8 A,NP,P;
 	ECP Q;
-    FP48 lv;
+    FP48 lv,lv2;
 
-    BIG_rcopy(x,CURVE_Bnx);
-
-    BIG_copy(n,x);
-
-	BIG_pmul(n3,n,3);
-	BIG_norm(n3);
+	nb=PAIR_nbits(n3,n);
 
 	ECP8_copy(&P,P1);
 	ECP_copy(&Q,Q1);
@@ -169,32 +258,28 @@
     FP_copy(&Qy,&(Q.y));
 
     ECP8_copy(&A,&P);
-
 	ECP8_copy(&NP,&P); ECP8_neg(&NP);
 
     FP48_one(r);
-    nb=BIG_nbits(n3);  // n3
 
-	j=0;
     /* Main Miller Loop */
     for (i=nb-2; i>=1; i--)
     {
-		j++;
 		FP48_sqr(r,r);
         PAIR_line(&lv,&A,&A,&Qx,&Qy);
-        FP48_smul(r,&lv,SEXTIC_TWIST_ZZZ);
 
 		bt= BIG_bit(n3,i)-BIG_bit(n,i);  // BIG_bit(n,i); 
         if (bt==1)
         {
-            PAIR_line(&lv,&A,&P,&Qx,&Qy);
-            FP48_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            PAIR_line(&lv2,&A,&P,&Qx,&Qy);
+            FP48_smul(&lv,&lv2);
         }
 		if (bt==-1)
 		{
-            PAIR_line(&lv,&A,&NP,&Qx,&Qy);
-            FP48_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            PAIR_line(&lv2,&A,&NP,&Qx,&Qy);
+            FP48_smul(&lv,&lv2);
 		}
+        FP48_ssmul(r,&lv);
 
     }
 
@@ -212,13 +297,8 @@
     int i,nb,bt;
     ECP8 A,B,NP,NR,P,R;
 	ECP Q,S;
-    FP48 lv;
-
-    BIG_rcopy(x,CURVE_Bnx);
-    BIG_copy(n,x);
-
-	BIG_pmul(n3,n,3);
-	BIG_norm(n3);
+    FP48 lv,lv2;
+	nb=PAIR_nbits(n3,n);
 
 	ECP8_copy(&P,P1);
 	ECP_copy(&Q,Q1);
@@ -245,33 +325,30 @@
 	ECP8_copy(&NR,&R); ECP8_neg(&NR);
 
     FP48_one(r);
-    nb=BIG_nbits(n3);
 
     /* Main Miller Loop */
     for (i=nb-2; i>=1; i--)
     {
         FP48_sqr(r,r);
         PAIR_line(&lv,&A,&A,&Qx,&Qy);
-        FP48_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-
-        PAIR_line(&lv,&B,&B,&Sx,&Sy);
-        FP48_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+		PAIR_line(&lv2,&B,&B,&Sx,&Sy);
+        FP48_smul(&lv,&lv2);
+        FP48_ssmul(r,&lv);
 
 		bt=BIG_bit(n3,i)-BIG_bit(n,i); // bt=BIG_bit(n,i);
         if (bt==1)
         {
             PAIR_line(&lv,&A,&P,&Qx,&Qy);
-            FP48_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-
-            PAIR_line(&lv,&B,&R,&Sx,&Sy);
-            FP48_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            PAIR_line(&lv2,&B,&R,&Sx,&Sy);
+			FP48_smul(&lv,&lv2);
+            FP48_ssmul(r,&lv);
         }
 		if (bt==-1)
 		{
             PAIR_line(&lv,&A,&NP,&Qx,&Qy);
-            FP48_smul(r,&lv,SEXTIC_TWIST_ZZZ);
-            PAIR_line(&lv,&B,&NR,&Sx,&Sy);
-            FP48_smul(r,&lv,SEXTIC_TWIST_ZZZ);
+            PAIR_line(&lv2,&B,&NR,&Sx,&Sy);
+            FP48_smul(&lv,&lv2);
+            FP48_ssmul(r,&lv);
 		}
 	}
 
diff --git a/version3/cpp/pair256.h b/version3/cpp/pair256.h
index 353b30a..e5e5fc5 100644
--- a/version3/cpp/pair256.h
+++ b/version3/cpp/pair256.h
@@ -75,6 +75,40 @@
  */
 extern int PAIR_GTmember(YYY::FP48 *x);
 
+/**	@brief Precompute line functions for n-pairing
+ *
+	@param r array of precomputed FP48 products of line functions
+	@param PV ECP8 instance, an element of G2
+	@param QV ECP instance, an element of G1
+
+ */
+extern void PAIR_another(YYY::FP48 r[],ECP8* PV,ECP* QV);
+
+/**	@brief Prepare Ate parameter
+ *
+	@param n BIG parameter
+	@param n3 BIG paramter = 3*n
+	@return number of nits in n3
+
+ */
+extern int PAIR_nbits(XXX::BIG n3,XXX::BIG n);
+
+/**	@brief Initialise structure for multi-pairing
+ *
+	@param r FP48 array, to be initialised to 1
+
+ */
+extern void PAIR_initmp(YYY::FP48 r[]);
+
+
+/**	@brief Miller loop
+ *
+ 	@param res FP48 result
+	@param r FP48 precomputed array of accumulated line functions
+
+ */
+extern void PAIR_miller(YYY::FP48 *res,YYY::FP48 r[]);
+
 }
 
 #endif
diff --git a/version3/cpp/rsa_support.h b/version3/cpp/rsa_support.h
index 3bec69f..3530c56 100644
--- a/version3/cpp/rsa_support.h
+++ b/version3/cpp/rsa_support.h
@@ -5,7 +5,7 @@
 
 namespace amcl {
 
-#define MAX_RSA_BYTES 512 // Maximum of 4096
+#define MAX_RSA_BYTES 1920 // Maximum of 4096
 
 /**	@brief RSA Key Pair Generator
  *
diff --git a/version3/cpp/testall.cpp b/version3/cpp/testall.cpp
index 51e9e6d..9f57b17 100644
--- a/version3/cpp/testall.cpp
+++ b/version3/cpp/testall.cpp
@@ -1567,8 +1567,9 @@
 
     printf("Decrypting test string\n");
     RSA_DECRYPT(&priv,&C,&ML);   // ... and then decrypt it 
-
+	printf("Decrypted\n");
     OAEP_DECODE(HASH_TYPE_RSA_RSA2048,NULL,&ML);    // decode it 
+	printf("Decoded\n");
     OCT_output_string(&ML);
 
     printf("Signing message\n");
diff --git a/version3/go/BLS.go b/version3/go/BLS.go
index b9d5538..db4db41 100644
--- a/version3/go/BLS.go
+++ b/version3/go/BLS.go
@@ -74,7 +74,16 @@
 	G := ECP2_generator()
 	PK := ECP2_fromBytes(W)
 	D.neg()
-	v := Ate2(G, D, PK, HM)
+
+// Use new multi-pairing mechanism 
+	r:=initmp()
+	another(r,G,D)
+	another(r,PK,HM)
+	v:=miller(r)
+
+//.. or alternatively
+//	v := Ate2(G, D, PK, HM)
+
 	v = Fexp(v)
 	if v.Isunity() {
 		return BLS_OK
diff --git a/version3/go/BLS192.go b/version3/go/BLS192.go
index a3898d1..999d0ae 100644
--- a/version3/go/BLS192.go
+++ b/version3/go/BLS192.go
@@ -74,7 +74,16 @@
 	G := ECP4_generator()
 	PK := ECP4_fromBytes(W)
 	D.neg()
-	v := Ate2(G, D, PK, HM)
+
+// Use new multi-pairing mechanism 
+	r:=initmp()
+	another(r,G,D)
+	another(r,PK,HM)
+	v:=miller(r)
+
+//.. or alternatively
+//	v := Ate2(G, D, PK, HM)
+
 	v = Fexp(v)
 	if v.Isunity() {
 		return BLS_OK
diff --git a/version3/go/BLS256.go b/version3/go/BLS256.go
index e17a9d7..74519ad 100644
--- a/version3/go/BLS256.go
+++ b/version3/go/BLS256.go
@@ -74,7 +74,16 @@
 	G := ECP8_generator()
 	PK := ECP8_fromBytes(W)
 	D.neg()
-	v := Ate2(G, D, PK, HM)
+
+// Use new multi-pairing mechanism 
+	r:=initmp()
+	another(r,G,D)
+	another(r,PK,HM)
+	v:=miller(r)
+
+//.. or alternatively
+//	v := Ate2(G, D, PK, HM)
+
 	v = Fexp(v)
 	if v.Isunity() {
 		return BLS_OK
diff --git a/version3/go/CONFIG_CURVE.go b/version3/go/CONFIG_CURVE.go
index 51edf40..794711e 100644
--- a/version3/go/CONFIG_CURVE.go
+++ b/version3/go/CONFIG_CURVE.go
@@ -14,6 +14,13 @@
 const D_TYPE int = 0
 const M_TYPE int = 1
 
+// Sparsity
+const FP_ZERO int = 0
+const FP_ONE int = 1
+const FP_SPARSER int = 2
+const FP_SPARSE int = 3
+const FP_DENSE int = 4
+
 // Pairing x parameter sign
 const POSITIVEX int = 0
 const NEGATIVEX int = 1
@@ -27,6 +34,7 @@
 
 const SEXTIC_TWIST int = @ST@
 const SIGN_OF_X int = @SX@
+const ATE_BITS int = @AB@
 
 // associated hash function and AES key size
 
diff --git a/version3/go/FP12.go b/version3/go/FP12.go
index d8a5b99..60af7c6 100644
--- a/version3/go/FP12.go
+++ b/version3/go/FP12.go
@@ -28,6 +28,7 @@
 	a *FP4
 	b *FP4
 	c *FP4
+	stype int
 }
 
 /* Constructors */
@@ -36,6 +37,7 @@
 	F.a = NewFP4copy(d)
 	F.b = NewFP4int(0)
 	F.c = NewFP4int(0)
+	F.stype=FP_SPARSER
 	return F
 }
 
@@ -44,6 +46,11 @@
 	F.a = NewFP4int(d)
 	F.b = NewFP4int(0)
 	F.c = NewFP4int(0)
+//	if d==1 {
+//		F.stype=FP_ONE
+//	} else {
+//		F.stype=FP_SPARSER
+//	}
 	return F
 }
 
@@ -52,6 +59,7 @@
 	F.a = NewFP4copy(d)
 	F.b = NewFP4copy(e)
 	F.c = NewFP4copy(f)
+	F.stype=FP_DENSE
 	return F
 }
 
@@ -60,6 +68,7 @@
 	F.a = NewFP4copy(x.a)
 	F.b = NewFP4copy(x.b)
 	F.c = NewFP4copy(x.c)
+	F.stype=x.stype
 	return F
 }
 
@@ -87,6 +96,8 @@
 	F.a.cmove(g.a, d)
 	F.b.cmove(g.b, d)
 	F.c.cmove(g.c, d)
+	d=^(d-1)
+	F.stype^=(F.stype^g.stype)&d
 }
 
 /* Constant time select from pre-computed table */
@@ -142,6 +153,7 @@
 	F.a.copy(x.a)
 	F.b.copy(x.b)
 	F.c.copy(x.c)
+	F.stype=x.stype
 }
 
 /* set this=1 */
@@ -149,6 +161,7 @@
 	F.a.one()
 	F.b.zero()
 	F.c.zero()
+	F.stype=FP_ONE
 }
 
 /* this=conj(this) */
@@ -197,11 +210,16 @@
 	F.b.add(B)
 	F.c.add(C)
 	F.reduce()
+	F.stype=FP_DENSE
 
 }
 
 /* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
 func (F *FP12) sqr() {
+	if F.stype==FP_ONE {
+		return
+	}
+
 	A := NewFP4copy(F.a)
 	B := NewFP4copy(F.b)
 	C := NewFP4copy(F.c)
@@ -237,6 +255,11 @@
 	F.b.copy(C)
 	F.b.add(D)
 	F.c.add(A)
+	if F.stype==FP_SPARSER {
+		F.stype=FP_SPARSE
+	} else {
+		F.stype=FP_DENSE
+	}
 	F.norm()
 }
 
@@ -304,113 +327,303 @@
 	z3.times_i()
 	F.a.copy(z0)
 	F.a.add(z3)
+	F.stype=FP_DENSE
 	F.norm()
 }
 
-/* Special case of multiplication arises from special form of ATE pairing line function */
-func (F *FP12) smul(y *FP12, twist int) {
-	if twist == D_TYPE {
-		z0 := NewFP4copy(F.a)
-		z2 := NewFP4copy(F.b)
-		z3 := NewFP4copy(F.b)
-		t0 := NewFP4int(0)
-		t1 := NewFP4copy(y.a)
 
-		z0.mul(y.a)
-		z2.pmul(y.b.real())
-		F.b.add(F.a)
-		t1.real().add(y.b.real())
-
-		t1.norm()
-		F.b.norm()
-		F.b.mul(t1)
-		z3.add(F.c)
-		z3.norm()
-		z3.pmul(y.b.real())
-
-		t0.copy(z0)
-		t0.neg()
-		t1.copy(z2)
-		t1.neg()
-
-		F.b.add(t0)
-		F.b.add(t1)
-		z3.add(t1)
-		z3.norm()
-		z2.add(t0)
-
-		t0.copy(F.a)
-		t0.add(F.c)
-		t0.norm()
-		t0.mul(y.a)
-		F.c.copy(z2)
-		F.c.add(t0)
-
-		z3.times_i()
-		F.a.copy(z0)
-		F.a.add(z3)
+/* FP12 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+func (F *FP12) ssmul(y *FP12) {
+	if F.stype==FP_ONE {
+		F.Copy(y)
+		return
 	}
-	if twist == M_TYPE {
-		z0 := NewFP4copy(F.a)
-		z1 := NewFP4int(0)
-		z2 := NewFP4int(0)
-		z3 := NewFP4int(0)
-		t0 := NewFP4copy(F.a)
-		t1 := NewFP4int(0)
-
+	if y.stype==FP_ONE {
+		return
+	}
+	if y.stype>=FP_SPARSE {
+		z0:=NewFP4copy(F.a)
+		z1:=NewFP4int(0)
+		z2:=NewFP4int(0)
+		z3:=NewFP4int(0)
 		z0.mul(y.a)
-		t0.add(F.b)
-		t0.norm()
 
-		z1.copy(t0)
-		z1.mul(y.a)
-		t0.copy(F.b)
-		t0.add(F.c)
-		t0.norm()
+		if SEXTIC_TWIST==M_TYPE {
+			if y.stype==FP_SPARSE || F.stype==FP_SPARSE {
+				z2.getb().copy(F.b.getb())
+				z2.getb().mul(y.b.getb())
+				z2.geta().zero()
+				if y.stype!=FP_SPARSE {
+					z2.geta().copy(F.b.getb())
+					z2.geta().mul(y.b.geta())
+				}
+				if F.stype!=FP_SPARSE {
+					z2.geta().copy(F.b.geta())
+					z2.geta().mul(y.b.getb())
+				}
+				z2.times_i()
+			} else {
+				z2.copy(F.b)
+				z2.mul(y.b)
+			}
+		} else {
+			z2.copy(F.b)
+			z2.mul(y.b)
+		}
+		t0:=NewFP4copy(F.a)
+		t1:=NewFP4copy(y.a)
+		t0.add(F.b); t0.norm();
+		t1.add(y.b); t1.norm()
 
-		z3.copy(t0)
-		z3.pmul(y.c.getb())
-		z3.times_i()
+		z1.copy(t0); z1.mul(t1)
+		t0.copy(F.b); t0.add(F.c); t0.norm()
+		t1.copy(y.b); t1.add(y.c); t1.norm()
 
-		t0.copy(z0)
-		t0.neg()
+		z3.copy(t0); z3.mul(t1)
+
+		t0.copy(z0); t0.neg()
+		t1.copy(z2); t1.neg()
 
 		z1.add(t0)
-		F.b.copy(z1)
-		z2.copy(t0)
+		F.b.copy(z1); F.b.add(t1)
 
-		t0.copy(F.a)
-		t0.add(F.c)
-		t1.copy(y.a)
-		t1.add(y.c)
+		z3.add(t1)
+		z2.add(t0)
 
-		t0.norm()
-		t1.norm()
-
+		t0.copy(F.a); t0.add(F.c); t0.norm()
+		t1.copy(y.a); t1.add(y.c); t1.norm()
+	
 		t0.mul(t1)
 		z2.add(t0)
 
-		t0.copy(F.c)
+		if SEXTIC_TWIST==D_TYPE {
+			if y.stype==FP_SPARSE || F.stype==FP_SPARSE {
+				t0.geta().copy(F.c.geta())
+				t0.geta().mul(y.c.geta())
+				t0.getb().zero()
+				if y.stype!=FP_SPARSE {
+					t0.getb().copy(F.c.geta())
+					t0.getb().mul(y.c.getb())
+				}
+				if F.stype!=FP_SPARSE {
+					t0.getb().copy(F.c.getb())
+					t0.getb().mul(y.c.geta())
+				}
+			} else {
+				t0.copy(F.c)
+				t0.mul(y.c)
+			}
+		} else {
+			t0.copy(F.c)
+			t0.mul(y.c)
+		}
+		t1.copy(t0); t1.neg()
 
-		t0.pmul(y.c.getb())
-		t0.times_i()
-
-		t1.copy(t0)
-		t1.neg()
-
-		F.c.copy(z2)
-		F.c.add(t1)
+		F.c.copy(z2); F.c.add(t1)
 		z3.add(t1)
 		t0.times_i()
 		F.b.add(t0)
 		z3.norm()
 		z3.times_i()
-		F.a.copy(z0)
-		F.a.add(z3)
+		F.a.copy(z0); F.a.add(z3);
+	} else {
+		if F.stype==FP_SPARSER {
+			F.smul(y)
+			return
+		}
+		if SEXTIC_TWIST==D_TYPE { // dense by sparser - 13m 
+			z0:=NewFP4copy(F.a)
+			z2:=NewFP4copy(F.b)
+			z3:=NewFP4copy(F.b)
+			t0:=NewFP4int(0)
+			t1:=NewFP4copy(y.a)
+			z0.mul(y.a)
+			z2.pmul(y.b.real())
+			F.b.add(F.a)
+			t1.real().add(y.b.real())
+
+			t1.norm()
+			F.b.norm()
+			F.b.mul(t1)
+			z3.add(F.c)
+			z3.norm()
+			z3.pmul(y.b.real())
+
+			t0.copy(z0); t0.neg()
+			t1.copy(z2); t1.neg()
+
+			F.b.add(t0)
+
+			F.b.add(t1)
+			z3.add(t1)
+			z2.add(t0)
+
+			t0.copy(F.a); t0.add(F.c); t0.norm()
+			z3.norm()
+			t0.mul(y.a)
+			F.c.copy(z2); F.c.add(t0)
+
+			z3.times_i()
+			F.a.copy(z0); F.a.add(z3)
+		}
+		if SEXTIC_TWIST==M_TYPE {
+			z0:=NewFP4copy(F.a)
+			z1:=NewFP4int(0)
+			z2:=NewFP4int(0)
+			z3:=NewFP4int(0)
+			t0:=NewFP4copy(F.a)
+			t1:=NewFP4int(0)
+		
+			z0.mul(y.a)
+			t0.add(F.b); t0.norm()
+
+			z1.copy(t0); z1.mul(y.a)
+			t0.copy(F.b); t0.add(F.c)
+			t0.norm()
+
+			z3.copy(t0)
+			z3.pmul(y.c.getb())
+			z3.times_i()
+
+			t0.copy(z0); t0.neg()
+			z1.add(t0)
+			F.b.copy(z1)
+			z2.copy(t0)
+
+			t0.copy(F.a); t0.add(F.c); t0.norm()
+			t1.copy(y.a); t1.add(y.c); t1.norm()
+
+			t0.mul(t1)
+			z2.add(t0)
+			t0.copy(F.c)
+			
+			t0.pmul(y.c.getb())
+			t0.times_i()
+			t1.copy(t0); t1.neg()
+
+			F.c.copy(z2); F.c.add(t1)
+			z3.add(t1)
+			t0.times_i()
+			F.b.add(t0)
+			z3.norm()
+			z3.times_i()
+			F.a.copy(z0); F.a.add(z3)
+		}	
 	}
+	F.stype=FP_DENSE
 	F.norm()
 }
 
+
+/* Special case of multiplication arises from special form of ATE pairing line function */
+func (F *FP12) smul(y *FP12) {
+	if SEXTIC_TWIST==D_TYPE {	
+		w1:=NewFP2copy(F.a.geta())
+		w2:=NewFP2copy(F.a.getb())
+		w3:=NewFP2copy(F.b.geta())
+
+		w1.mul(y.a.geta())
+		w2.mul(y.a.getb())
+		w3.mul(y.b.geta())
+
+		ta:=NewFP2copy(F.a.geta())
+		tb:=NewFP2copy(y.a.geta())
+		ta.add(F.a.getb()); ta.norm()
+		tb.add(y.a.getb()); tb.norm()
+		tc:=NewFP2copy(ta)
+		tc.mul(tb);
+		t:=NewFP2copy(w1)
+		t.add(w2)
+		t.neg()
+		tc.add(t)
+
+		ta.copy(F.a.geta()); ta.add(F.b.geta()); ta.norm()
+		tb.copy(y.a.geta()); tb.add(y.b.geta()); tb.norm()
+		td:=NewFP2copy(ta)
+		td.mul(tb)
+		t.copy(w1)
+		t.add(w3)
+		t.neg()
+		td.add(t)
+
+		ta.copy(F.a.getb()); ta.add(F.b.geta()); ta.norm()
+		tb.copy(y.a.getb()); tb.add(y.b.geta()); tb.norm()
+		te:=NewFP2copy(ta)
+		te.mul(tb)
+		t.copy(w2)
+		t.add(w3)
+		t.neg()
+		te.add(t)
+
+		w2.mul_ip()
+		w1.add(w2)
+
+		F.a.geta().copy(w1); F.a.getb().copy(tc)
+		F.b.geta().copy(td); F.b.getb().copy(te)
+		F.c.geta().copy(w3); F.c.getb().zero()
+
+		F.a.norm()
+		F.b.norm()
+	} else {
+		w1:=NewFP2copy(F.a.geta())
+		w2:=NewFP2copy(F.a.getb())
+		w3:=NewFP2copy(F.c.getb())
+
+		w1.mul(y.a.geta())
+		w2.mul(y.a.getb())
+		w3.mul(y.c.getb())
+
+		ta:=NewFP2copy(F.a.geta())
+		tb:=NewFP2copy(y.a.geta())
+		ta.add(F.a.getb()); ta.norm()
+		tb.add(y.a.getb()); tb.norm()
+		tc:=NewFP2copy(ta)
+		tc.mul(tb);
+		t:=NewFP2copy(w1)
+		t.add(w2)
+		t.neg()
+		tc.add(t)
+
+		ta.copy(F.a.geta()); ta.add(F.c.getb()); ta.norm()
+		tb.copy(y.a.geta()); tb.add(y.c.getb()); tb.norm()
+		td:=NewFP2copy(ta)
+		td.mul(tb)
+		t.copy(w1)
+		t.add(w3)
+		t.neg()
+		td.add(t)
+
+		ta.copy(F.a.getb()); ta.add(F.c.getb()); ta.norm()
+		tb.copy(y.a.getb()); tb.add(y.c.getb()); tb.norm()
+		te:=NewFP2copy(ta)
+		te.mul(tb)
+		t.copy(w2)
+		t.add(w3)
+		t.neg()
+		te.add(t)
+
+		w2.mul_ip()
+		w1.add(w2)
+		F.a.geta().copy(w1); F.a.getb().copy(tc)
+
+		w3.mul_ip()
+		w3.norm()
+		F.b.geta().zero(); F.b.getb().copy(w3)
+
+		te.norm()
+		te.mul_ip()
+		F.c.geta().copy(te)
+		F.c.getb().copy(td)
+
+		F.a.norm()
+		F.c.norm()
+
+	}
+	F.stype=FP_SPARSE;
+}
+
 /* this=1/this */
 func (F *FP12) Inverse() {
 	f0 := NewFP4copy(F.a)
@@ -456,6 +669,7 @@
 	F.b.mul(f3)
 	F.c.copy(f2)
 	F.c.mul(f3)
+	F.stype=FP_DENSE
 }
 
 /* this=this^p using Frobenius */
@@ -472,6 +686,7 @@
 
 	F.b.pmul(f)
 	F.c.pmul(f2)
+	F.stype=FP_DENSE
 }
 
 /* trace function */
diff --git a/version3/go/FP24.go b/version3/go/FP24.go
index da505ea..32bae65 100644
--- a/version3/go/FP24.go
+++ b/version3/go/FP24.go
@@ -28,6 +28,7 @@
 	a *FP8
 	b *FP8
 	c *FP8
+	stype int
 }
 
 /* Constructors */
@@ -36,6 +37,7 @@
 	F.a = NewFP8copy(d)
 	F.b = NewFP8int(0)
 	F.c = NewFP8int(0)
+	F.stype=FP_SPARSER
 	return F
 }
 
@@ -44,6 +46,11 @@
 	F.a = NewFP8int(d)
 	F.b = NewFP8int(0)
 	F.c = NewFP8int(0)
+	if d==1 {
+		F.stype=FP_ONE
+	} else {
+		F.stype=FP_SPARSER
+	}
 	return F
 }
 
@@ -52,6 +59,7 @@
 	F.a = NewFP8copy(d)
 	F.b = NewFP8copy(e)
 	F.c = NewFP8copy(f)
+	F.stype=FP_DENSE
 	return F
 }
 
@@ -60,6 +68,7 @@
 	F.a = NewFP8copy(x.a)
 	F.b = NewFP8copy(x.b)
 	F.c = NewFP8copy(x.c)
+	F.stype=x.stype
 	return F
 }
 
@@ -87,6 +96,8 @@
 	F.a.cmove(g.a, d)
 	F.b.cmove(g.b, d)
 	F.c.cmove(g.c, d)
+	d=^(d-1)
+	F.stype^=(F.stype^g.stype)&d
 }
 
 /* Constant time select from pre-computed table */
@@ -142,6 +153,7 @@
 	F.a.copy(x.a)
 	F.b.copy(x.b)
 	F.c.copy(x.c)
+	F.stype=x.stype
 }
 
 /* set this=1 */
@@ -149,6 +161,7 @@
 	F.a.one()
 	F.b.zero()
 	F.c.zero()
+	F.stype=FP_ONE
 }
 
 /* this=conj(this) */
@@ -197,11 +210,14 @@
 	F.b.add(B)
 	F.c.add(C)
 	F.reduce()
-
+	F.stype=FP_DENSE
 }
 
 /* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
 func (F *FP24) sqr() {
+	if F.stype==FP_ONE {
+		return
+	}
 	A := NewFP8copy(F.a)
 	B := NewFP8copy(F.b)
 	C := NewFP8copy(F.c)
@@ -237,6 +253,11 @@
 	F.b.copy(C)
 	F.b.add(D)
 	F.c.add(A)
+	if F.stype==FP_SPARSER {
+		F.stype=FP_SPARSE
+	} else {
+		F.stype=FP_DENSE
+	}
 	F.norm()
 }
 
@@ -305,114 +326,302 @@
 	z3.times_i()
 	F.a.copy(z0)
 	F.a.add(z3)
+	F.stype=FP_DENSE
 	F.norm()
 }
 
-/* Special case of multiplication arises from special form of ATE pairing line function */
-func (F *FP24) smul(y *FP24, twist int) {
-	if twist == D_TYPE {
-		z0 := NewFP8copy(F.a)
-		z2 := NewFP8copy(F.b)
-		z3 := NewFP8copy(F.b)
-		t0 := NewFP8int(0)
-		t1 := NewFP8copy(y.a)
-
-		z0.mul(y.a)
-		z2.pmul(y.b.real())
-		F.b.add(F.a)
-		t1.real().add(y.b.real())
-
-		t1.norm()
-		F.b.norm()
-		F.b.mul(t1)
-		z3.add(F.c)
-		z3.norm()
-		z3.pmul(y.b.real())
-
-		t0.copy(z0)
-		t0.neg()
-		t1.copy(z2)
-		t1.neg()
-
-		F.b.add(t0)
-
-		F.b.add(t1)
-		z3.add(t1)
-		z3.norm()
-		z2.add(t0)
-
-		t0.copy(F.a)
-		t0.add(F.c)
-		t0.norm()
-		t0.mul(y.a)
-		F.c.copy(z2)
-		F.c.add(t0)
-
-		z3.times_i()
-		F.a.copy(z0)
-		F.a.add(z3)
+/* FP24 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+func (F *FP24) ssmul(y *FP24) {
+	if F.stype==FP_ONE {
+		F.Copy(y)
+		return
 	}
-	if twist == M_TYPE {
-		z0 := NewFP8copy(F.a)
-		z1 := NewFP8int(0)
-		z2 := NewFP8int(0)
-		z3 := NewFP8int(0)
-		t0 := NewFP8copy(F.a)
-		t1 := NewFP8int(0)
-
+	if y.stype==FP_ONE {
+		return
+	}
+	if y.stype>=FP_SPARSE {
+		z0:=NewFP8copy(F.a)
+		z1:=NewFP8int(0)
+		z2:=NewFP8int(0)
+		z3:=NewFP8int(0)
 		z0.mul(y.a)
-		t0.add(F.b)
-		t0.norm()
 
-		z1.copy(t0)
-		z1.mul(y.a)
-		t0.copy(F.b)
-		t0.add(F.c)
-		t0.norm()
+		if SEXTIC_TWIST==M_TYPE {
+			if y.stype==FP_SPARSE || F.stype==FP_SPARSE {
+				z2.getb().copy(F.b.getb())
+				z2.getb().mul(y.b.getb())
+				z2.geta().zero()
+				if y.stype!=FP_SPARSE {
+					z2.geta().copy(F.b.getb())
+					z2.geta().mul(y.b.geta())
+				}
+				if F.stype!=FP_SPARSE {
+					z2.geta().copy(F.b.geta())
+					z2.geta().mul(y.b.getb())
+				}
+				z2.times_i()
+			} else {
+				z2.copy(F.b)
+				z2.mul(y.b)
+			}
+		} else {
+			z2.copy(F.b)
+			z2.mul(y.b)
+		}
+		t0:=NewFP8copy(F.a)
+		t1:=NewFP8copy(y.a)
+		t0.add(F.b); t0.norm();
+		t1.add(y.b); t1.norm()
 
-		z3.copy(t0)
-		z3.pmul(y.c.getb())
-		z3.times_i()
+		z1.copy(t0); z1.mul(t1)
+		t0.copy(F.b); t0.add(F.c); t0.norm()
+		t1.copy(y.b); t1.add(y.c); t1.norm()
 
-		t0.copy(z0)
-		t0.neg()
+		z3.copy(t0); z3.mul(t1)
+
+		t0.copy(z0); t0.neg()
+		t1.copy(z2); t1.neg()
 
 		z1.add(t0)
-		F.b.copy(z1)
-		z2.copy(t0)
+		F.b.copy(z1); F.b.add(t1)
 
-		t0.copy(F.a)
-		t0.add(F.c)
-		t1.copy(y.a)
-		t1.add(y.c)
+		z3.add(t1)
+		z2.add(t0)
 
-		t0.norm()
-		t1.norm()
-
+		t0.copy(F.a); t0.add(F.c); t0.norm()
+		t1.copy(y.a); t1.add(y.c); t1.norm()
+	
 		t0.mul(t1)
 		z2.add(t0)
 
-		t0.copy(F.c)
+		if SEXTIC_TWIST==D_TYPE {
+			if y.stype==FP_SPARSE || F.stype==FP_SPARSE {
+				t0.geta().copy(F.c.geta())
+				t0.geta().mul(y.c.geta())
+				t0.getb().zero()
+				if y.stype!=FP_SPARSE {
+					t0.getb().copy(F.c.geta())
+					t0.getb().mul(y.c.getb())
+				}
+				if F.stype!=FP_SPARSE {
+					t0.getb().copy(F.c.getb())
+					t0.getb().mul(y.c.geta())
+				}
+			} else {
+				t0.copy(F.c)
+				t0.mul(y.c)
+			}
+		} else {
+			t0.copy(F.c)
+			t0.mul(y.c)
+		}
+		t1.copy(t0); t1.neg()
 
-		t0.pmul(y.c.getb())
-		t0.times_i()
-
-		t1.copy(t0)
-		t1.neg()
-
-		F.c.copy(z2)
-		F.c.add(t1)
+		F.c.copy(z2); F.c.add(t1)
 		z3.add(t1)
 		t0.times_i()
 		F.b.add(t0)
 		z3.norm()
 		z3.times_i()
-		F.a.copy(z0)
-		F.a.add(z3)
+		F.a.copy(z0); F.a.add(z3);
+	} else {
+		if F.stype==FP_SPARSER {
+			F.smul(y)
+			return
+		}
+		if SEXTIC_TWIST==D_TYPE { // dense by sparser - 13m 
+			z0:=NewFP8copy(F.a)
+			z2:=NewFP8copy(F.b)
+			z3:=NewFP8copy(F.b)
+			t0:=NewFP8int(0)
+			t1:=NewFP8copy(y.a)
+			z0.mul(y.a)
+			z2.pmul(y.b.real())
+			F.b.add(F.a)
+			t1.real().add(y.b.real())
+
+			t1.norm()
+			F.b.norm()
+			F.b.mul(t1)
+			z3.add(F.c)
+			z3.norm()
+			z3.pmul(y.b.real())
+
+			t0.copy(z0); t0.neg()
+			t1.copy(z2); t1.neg()
+
+			F.b.add(t0)
+
+			F.b.add(t1)
+			z3.add(t1)
+			z2.add(t0)
+
+			t0.copy(F.a); t0.add(F.c); t0.norm()
+			z3.norm()
+			t0.mul(y.a)
+			F.c.copy(z2); F.c.add(t0)
+
+			z3.times_i()
+			F.a.copy(z0); F.a.add(z3)
+		}
+		if SEXTIC_TWIST==M_TYPE {
+			z0:=NewFP8copy(F.a)
+			z1:=NewFP8int(0)
+			z2:=NewFP8int(0)
+			z3:=NewFP8int(0)
+			t0:=NewFP8copy(F.a)
+			t1:=NewFP8int(0)
+		
+			z0.mul(y.a)
+			t0.add(F.b); t0.norm()
+
+			z1.copy(t0); z1.mul(y.a)
+			t0.copy(F.b); t0.add(F.c)
+			t0.norm()
+
+			z3.copy(t0)
+			z3.pmul(y.c.getb())
+			z3.times_i()
+
+			t0.copy(z0); t0.neg()
+			z1.add(t0)
+			F.b.copy(z1)
+			z2.copy(t0)
+
+			t0.copy(F.a); t0.add(F.c); t0.norm()
+			t1.copy(y.a); t1.add(y.c); t1.norm()
+
+			t0.mul(t1)
+			z2.add(t0)
+			t0.copy(F.c)
+			
+			t0.pmul(y.c.getb())
+			t0.times_i()
+			t1.copy(t0); t1.neg()
+
+			F.c.copy(z2); F.c.add(t1)
+			z3.add(t1)
+			t0.times_i()
+			F.b.add(t0)
+			z3.norm()
+			z3.times_i()
+			F.a.copy(z0); F.a.add(z3)
+		}	
 	}
+	F.stype=FP_DENSE
 	F.norm()
 }
 
+
+/* Special case of multiplication arises from special form of ATE pairing line function */
+func (F *FP24) smul(y *FP24) {
+	if SEXTIC_TWIST==D_TYPE {	
+		w1:=NewFP4copy(F.a.geta())
+		w2:=NewFP4copy(F.a.getb())
+		w3:=NewFP4copy(F.b.geta())
+
+		w1.mul(y.a.geta())
+		w2.mul(y.a.getb())
+		w3.mul(y.b.geta())
+
+		ta:=NewFP4copy(F.a.geta())
+		tb:=NewFP4copy(y.a.geta())
+		ta.add(F.a.getb()); ta.norm()
+		tb.add(y.a.getb()); tb.norm()
+		tc:=NewFP4copy(ta)
+		tc.mul(tb);
+		t:=NewFP4copy(w1)
+		t.add(w2)
+		t.neg()
+		tc.add(t)
+
+		ta.copy(F.a.geta()); ta.add(F.b.geta()); ta.norm()
+		tb.copy(y.a.geta()); tb.add(y.b.geta()); tb.norm()
+		td:=NewFP4copy(ta)
+		td.mul(tb)
+		t.copy(w1)
+		t.add(w3)
+		t.neg()
+		td.add(t)
+
+		ta.copy(F.a.getb()); ta.add(F.b.geta()); ta.norm()
+		tb.copy(y.a.getb()); tb.add(y.b.geta()); tb.norm()
+		te:=NewFP4copy(ta)
+		te.mul(tb)
+		t.copy(w2)
+		t.add(w3)
+		t.neg()
+		te.add(t)
+
+		w2.times_i()
+		w1.add(w2)
+
+		F.a.geta().copy(w1); F.a.getb().copy(tc)
+		F.b.geta().copy(td); F.b.getb().copy(te)
+		F.c.geta().copy(w3); F.c.getb().zero()
+
+		F.a.norm()
+		F.b.norm()
+	} else {
+		w1:=NewFP4copy(F.a.geta())
+		w2:=NewFP4copy(F.a.getb())
+		w3:=NewFP4copy(F.c.getb())
+
+		w1.mul(y.a.geta())
+		w2.mul(y.a.getb())
+		w3.mul(y.c.getb())
+
+		ta:=NewFP4copy(F.a.geta())
+		tb:=NewFP4copy(y.a.geta())
+		ta.add(F.a.getb()); ta.norm()
+		tb.add(y.a.getb()); tb.norm()
+		tc:=NewFP4copy(ta)
+		tc.mul(tb);
+		t:=NewFP4copy(w1)
+		t.add(w2)
+		t.neg()
+		tc.add(t)
+
+		ta.copy(F.a.geta()); ta.add(F.c.getb()); ta.norm()
+		tb.copy(y.a.geta()); tb.add(y.c.getb()); tb.norm()
+		td:=NewFP4copy(ta)
+		td.mul(tb)
+		t.copy(w1)
+		t.add(w3)
+		t.neg()
+		td.add(t)
+
+		ta.copy(F.a.getb()); ta.add(F.c.getb()); ta.norm()
+		tb.copy(y.a.getb()); tb.add(y.c.getb()); tb.norm()
+		te:=NewFP4copy(ta)
+		te.mul(tb)
+		t.copy(w2)
+		t.add(w3)
+		t.neg()
+		te.add(t)
+
+		w2.times_i()
+		w1.add(w2)
+		F.a.geta().copy(w1); F.a.getb().copy(tc)
+
+		w3.times_i()
+		w3.norm()
+		F.b.geta().zero(); F.b.getb().copy(w3)
+
+		te.norm()
+		te.times_i()
+		F.c.geta().copy(te)
+		F.c.getb().copy(td)
+
+		F.a.norm()
+		F.c.norm()
+
+	}
+	F.stype=FP_SPARSE;
+}
+
 /* this=1/this */
 func (F *FP24) Inverse() {
 	f0 := NewFP8copy(F.a)
@@ -458,6 +667,7 @@
 	F.b.mul(f3)
 	F.c.copy(f2)
 	F.c.mul(f3)
+	F.stype=FP_DENSE
 }
 
 /* this=this^p using Frobenius */
@@ -482,6 +692,7 @@
 		F.c.times_i2()
 		F.c.times_i2()
 	}
+	F.stype=FP_DENSE
 }
 
 /* trace function */
diff --git a/version3/go/FP48.go b/version3/go/FP48.go
index 05517da..481e21a 100644
--- a/version3/go/FP48.go
+++ b/version3/go/FP48.go
@@ -28,6 +28,7 @@
 	a *FP16
 	b *FP16
 	c *FP16
+	stype int
 }
 
 /* Constructors */
@@ -36,6 +37,7 @@
 	F.a = NewFP16copy(d)
 	F.b = NewFP16int(0)
 	F.c = NewFP16int(0)
+	F.stype=FP_SPARSER
 	return F
 }
 
@@ -44,6 +46,11 @@
 	F.a = NewFP16int(d)
 	F.b = NewFP16int(0)
 	F.c = NewFP16int(0)
+	if d==1 {
+		F.stype=FP_ONE
+	} else {
+		F.stype=FP_SPARSER
+	}
 	return F
 }
 
@@ -52,6 +59,7 @@
 	F.a = NewFP16copy(d)
 	F.b = NewFP16copy(e)
 	F.c = NewFP16copy(f)
+	F.stype=FP_DENSE
 	return F
 }
 
@@ -60,6 +68,7 @@
 	F.a = NewFP16copy(x.a)
 	F.b = NewFP16copy(x.b)
 	F.c = NewFP16copy(x.c)
+	F.stype=x.stype
 	return F
 }
 
@@ -87,6 +96,8 @@
 	F.a.cmove(g.a, d)
 	F.b.cmove(g.b, d)
 	F.c.cmove(g.c, d)
+	d=^(d-1)
+	F.stype^=(F.stype^g.stype)&d
 }
 
 /* Constant time select from pre-computed table */
@@ -142,10 +153,12 @@
 	F.a.copy(x.a)
 	F.b.copy(x.b)
 	F.c.copy(x.c)
+	F.stype=x.stype
 }
 
 /* set this=1 */
 func (F *FP48) one() {
+	F.stype=FP_ONE
 	F.a.one()
 	F.b.zero()
 	F.c.zero()
@@ -197,11 +210,14 @@
 	F.b.add(B)
 	F.c.add(C)
 	F.reduce()
-
+	F.stype=FP_DENSE
 }
 
 /* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
 func (F *FP48) sqr() {
+	if F.stype==FP_ONE {
+		return
+	}
 	A := NewFP16copy(F.a)
 	B := NewFP16copy(F.b)
 	C := NewFP16copy(F.c)
@@ -237,6 +253,11 @@
 	F.b.copy(C)
 	F.b.add(D)
 	F.c.add(A)
+	if F.stype==FP_SPARSER {
+		F.stype=FP_SPARSE
+	} else {
+		F.stype=FP_DENSE
+	}
 	F.norm()
 }
 
@@ -305,113 +326,302 @@
 	z3.times_i()
 	F.a.copy(z0)
 	F.a.add(z3)
+	F.stype=FP_DENSE
 	F.norm()
 }
 
-/* Special case of multiplication arises from special form of ATE pairing line function */
-func (F *FP48) smul(y *FP48, twist int) {
-	if twist == D_TYPE {
-		z0 := NewFP16copy(F.a)
-		z2 := NewFP16copy(F.b)
-		z3 := NewFP16copy(F.b)
-		t0 := NewFP16int(0)
-		t1 := NewFP16copy(y.a)
-
-		z0.mul(y.a)
-		z2.pmul(y.b.real())
-		F.b.add(F.a)
-		t1.real().add(y.b.real())
-
-		t1.norm()
-		F.b.norm()
-		F.b.mul(t1)
-		z3.add(F.c)
-		z3.norm()
-		z3.pmul(y.b.real())
-
-		t0.copy(z0)
-		t0.neg()
-		t1.copy(z2)
-		t1.neg()
-
-		F.b.add(t0)
-		F.b.add(t1)
-		z3.add(t1)
-		z3.norm()
-		z2.add(t0)
-
-		t0.copy(F.a)
-		t0.add(F.c)
-		t0.norm()
-		t0.mul(y.a)
-		F.c.copy(z2)
-		F.c.add(t0)
-
-		z3.times_i()
-		F.a.copy(z0)
-		F.a.add(z3)
+/* FP48 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+func (F *FP48) ssmul(y *FP48) {
+	if F.stype==FP_ONE {
+		F.Copy(y)
+		return
 	}
-	if twist == M_TYPE {
-		z0 := NewFP16copy(F.a)
-		z1 := NewFP16int(0)
-		z2 := NewFP16int(0)
-		z3 := NewFP16int(0)
-		t0 := NewFP16copy(F.a)
-		t1 := NewFP16int(0)
-
+	if y.stype==FP_ONE {
+		return
+	}
+	if y.stype>=FP_SPARSE {
+		z0:=NewFP16copy(F.a)
+		z1:=NewFP16int(0)
+		z2:=NewFP16int(0)
+		z3:=NewFP16int(0)
 		z0.mul(y.a)
-		t0.add(F.b)
-		t0.norm()
 
-		z1.copy(t0)
-		z1.mul(y.a)
-		t0.copy(F.b)
-		t0.add(F.c)
-		t0.norm()
+		if SEXTIC_TWIST==M_TYPE {
+			if y.stype==FP_SPARSE || F.stype==FP_SPARSE {
+				z2.getb().copy(F.b.getb())
+				z2.getb().mul(y.b.getb())
+				z2.geta().zero()
+				if y.stype!=FP_SPARSE {
+					z2.geta().copy(F.b.getb())
+					z2.geta().mul(y.b.geta())
+				}
+				if F.stype!=FP_SPARSE {
+					z2.geta().copy(F.b.geta())
+					z2.geta().mul(y.b.getb())
+				}
+				z2.times_i()
+			} else {
+				z2.copy(F.b)
+				z2.mul(y.b)
+			}
+		} else {
+			z2.copy(F.b)
+			z2.mul(y.b)
+		}
+		t0:=NewFP16copy(F.a)
+		t1:=NewFP16copy(y.a)
+		t0.add(F.b); t0.norm();
+		t1.add(y.b); t1.norm()
 
-		z3.copy(t0)
-		z3.pmul(y.c.getb())
-		z3.times_i()
+		z1.copy(t0); z1.mul(t1)
+		t0.copy(F.b); t0.add(F.c); t0.norm()
+		t1.copy(y.b); t1.add(y.c); t1.norm()
 
-		t0.copy(z0)
-		t0.neg()
+		z3.copy(t0); z3.mul(t1)
+
+		t0.copy(z0); t0.neg()
+		t1.copy(z2); t1.neg()
 
 		z1.add(t0)
-		F.b.copy(z1)
-		z2.copy(t0)
+		F.b.copy(z1); F.b.add(t1)
 
-		t0.copy(F.a)
-		t0.add(F.c)
-		t1.copy(y.a)
-		t1.add(y.c)
+		z3.add(t1)
+		z2.add(t0)
 
-		t0.norm()
-		t1.norm()
-
+		t0.copy(F.a); t0.add(F.c); t0.norm()
+		t1.copy(y.a); t1.add(y.c); t1.norm()
+	
 		t0.mul(t1)
 		z2.add(t0)
 
-		t0.copy(F.c)
+		if SEXTIC_TWIST==D_TYPE {
+			if y.stype==FP_SPARSE || F.stype==FP_SPARSE {
+				t0.geta().copy(F.c.geta())
+				t0.geta().mul(y.c.geta())
+				t0.getb().zero()
+				if y.stype!=FP_SPARSE {
+					t0.getb().copy(F.c.geta())
+					t0.getb().mul(y.c.getb())
+				}
+				if F.stype!=FP_SPARSE {
+					t0.getb().copy(F.c.getb())
+					t0.getb().mul(y.c.geta())
+				}
+			} else {
+				t0.copy(F.c)
+				t0.mul(y.c)
+			}
+		} else {
+			t0.copy(F.c)
+			t0.mul(y.c)
+		}
+		t1.copy(t0); t1.neg()
 
-		t0.pmul(y.c.getb())
-		t0.times_i()
-
-		t1.copy(t0)
-		t1.neg()
-
-		F.c.copy(z2)
-		F.c.add(t1)
+		F.c.copy(z2); F.c.add(t1)
 		z3.add(t1)
 		t0.times_i()
 		F.b.add(t0)
 		z3.norm()
 		z3.times_i()
-		F.a.copy(z0)
-		F.a.add(z3)
+		F.a.copy(z0); F.a.add(z3);
+	} else {
+		if F.stype==FP_SPARSER {
+			F.smul(y)
+			return
+		}
+		if SEXTIC_TWIST==D_TYPE { // dense by sparser - 13m 
+			z0:=NewFP16copy(F.a)
+			z2:=NewFP16copy(F.b)
+			z3:=NewFP16copy(F.b)
+			t0:=NewFP16int(0)
+			t1:=NewFP16copy(y.a)
+			z0.mul(y.a)
+			z2.pmul(y.b.real())
+			F.b.add(F.a)
+			t1.real().add(y.b.real())
+
+			t1.norm()
+			F.b.norm()
+			F.b.mul(t1)
+			z3.add(F.c)
+			z3.norm()
+			z3.pmul(y.b.real())
+
+			t0.copy(z0); t0.neg()
+			t1.copy(z2); t1.neg()
+
+			F.b.add(t0)
+
+			F.b.add(t1)
+			z3.add(t1)
+			z2.add(t0)
+
+			t0.copy(F.a); t0.add(F.c); t0.norm()
+			z3.norm()
+			t0.mul(y.a)
+			F.c.copy(z2); F.c.add(t0)
+
+			z3.times_i()
+			F.a.copy(z0); F.a.add(z3)
+		}
+		if SEXTIC_TWIST==M_TYPE {
+			z0:=NewFP16copy(F.a)
+			z1:=NewFP16int(0)
+			z2:=NewFP16int(0)
+			z3:=NewFP16int(0)
+			t0:=NewFP16copy(F.a)
+			t1:=NewFP16int(0)
+		
+			z0.mul(y.a)
+			t0.add(F.b); t0.norm()
+
+			z1.copy(t0); z1.mul(y.a)
+			t0.copy(F.b); t0.add(F.c)
+			t0.norm()
+
+			z3.copy(t0)
+			z3.pmul(y.c.getb())
+			z3.times_i()
+
+			t0.copy(z0); t0.neg()
+			z1.add(t0)
+			F.b.copy(z1)
+			z2.copy(t0)
+
+			t0.copy(F.a); t0.add(F.c); t0.norm()
+			t1.copy(y.a); t1.add(y.c); t1.norm()
+
+			t0.mul(t1)
+			z2.add(t0)
+			t0.copy(F.c)
+			
+			t0.pmul(y.c.getb())
+			t0.times_i()
+			t1.copy(t0); t1.neg()
+
+			F.c.copy(z2); F.c.add(t1)
+			z3.add(t1)
+			t0.times_i()
+			F.b.add(t0)
+			z3.norm()
+			z3.times_i()
+			F.a.copy(z0); F.a.add(z3)
+		}	
 	}
+	F.stype=FP_DENSE
 	F.norm()
 }
 
+
+/* Special case of multiplication arises from special form of ATE pairing line function */
+func (F *FP48) smul(y *FP48) {
+	if SEXTIC_TWIST==D_TYPE {	
+		w1:=NewFP8copy(F.a.geta())
+		w2:=NewFP8copy(F.a.getb())
+		w3:=NewFP8copy(F.b.geta())
+
+		w1.mul(y.a.geta())
+		w2.mul(y.a.getb())
+		w3.mul(y.b.geta())
+
+		ta:=NewFP8copy(F.a.geta())
+		tb:=NewFP8copy(y.a.geta())
+		ta.add(F.a.getb()); ta.norm()
+		tb.add(y.a.getb()); tb.norm()
+		tc:=NewFP8copy(ta)
+		tc.mul(tb);
+		t:=NewFP8copy(w1)
+		t.add(w2)
+		t.neg()
+		tc.add(t)
+
+		ta.copy(F.a.geta()); ta.add(F.b.geta()); ta.norm()
+		tb.copy(y.a.geta()); tb.add(y.b.geta()); tb.norm()
+		td:=NewFP8copy(ta)
+		td.mul(tb)
+		t.copy(w1)
+		t.add(w3)
+		t.neg()
+		td.add(t)
+
+		ta.copy(F.a.getb()); ta.add(F.b.geta()); ta.norm()
+		tb.copy(y.a.getb()); tb.add(y.b.geta()); tb.norm()
+		te:=NewFP8copy(ta)
+		te.mul(tb)
+		t.copy(w2)
+		t.add(w3)
+		t.neg()
+		te.add(t)
+
+		w2.times_i()
+		w1.add(w2)
+
+		F.a.geta().copy(w1); F.a.getb().copy(tc)
+		F.b.geta().copy(td); F.b.getb().copy(te)
+		F.c.geta().copy(w3); F.c.getb().zero()
+
+		F.a.norm()
+		F.b.norm()
+	} else {
+		w1:=NewFP8copy(F.a.geta())
+		w2:=NewFP8copy(F.a.getb())
+		w3:=NewFP8copy(F.c.getb())
+
+		w1.mul(y.a.geta())
+		w2.mul(y.a.getb())
+		w3.mul(y.c.getb())
+
+		ta:=NewFP8copy(F.a.geta())
+		tb:=NewFP8copy(y.a.geta())
+		ta.add(F.a.getb()); ta.norm()
+		tb.add(y.a.getb()); tb.norm()
+		tc:=NewFP8copy(ta)
+		tc.mul(tb);
+		t:=NewFP8copy(w1)
+		t.add(w2)
+		t.neg()
+		tc.add(t)
+
+		ta.copy(F.a.geta()); ta.add(F.c.getb()); ta.norm()
+		tb.copy(y.a.geta()); tb.add(y.c.getb()); tb.norm()
+		td:=NewFP8copy(ta)
+		td.mul(tb)
+		t.copy(w1)
+		t.add(w3)
+		t.neg()
+		td.add(t)
+
+		ta.copy(F.a.getb()); ta.add(F.c.getb()); ta.norm()
+		tb.copy(y.a.getb()); tb.add(y.c.getb()); tb.norm()
+		te:=NewFP8copy(ta)
+		te.mul(tb)
+		t.copy(w2)
+		t.add(w3)
+		t.neg()
+		te.add(t)
+
+		w2.times_i()
+		w1.add(w2)
+		F.a.geta().copy(w1); F.a.getb().copy(tc)
+
+		w3.times_i()
+		w3.norm()
+		F.b.geta().zero(); F.b.getb().copy(w3)
+
+		te.norm()
+		te.times_i()
+		F.c.geta().copy(te)
+		F.c.getb().copy(td)
+
+		F.a.norm()
+		F.c.norm()
+
+	}
+	F.stype=FP_SPARSE;
+}
+
 /* this=1/this */
 func (F *FP48) Inverse() {
 	f0 := NewFP16copy(F.a)
@@ -457,6 +667,7 @@
 	F.b.mul(f3)
 	F.c.copy(f2)
 	F.c.mul(f3)
+	F.stype=FP_DENSE
 }
 
 /* this=this^p using Frobenius */
@@ -485,6 +696,7 @@
 		F.c.times_i4()
 		F.c.times_i4()
 	}
+	F.stype=FP_DENSE
 }
 
 /* trace function */
diff --git a/version3/go/PAIR.go b/version3/go/PAIR.go
index 35a3d8d..acacc69 100644
--- a/version3/go/PAIR.go
+++ b/version3/go/PAIR.go
@@ -123,38 +123,135 @@
 		A.Add(B)
 	}
 
-	return NewFP12fp4s(a, b, c)
+	r:=NewFP12fp4s(a, b, c)
+	r.stype=FP_SPARSER
+	return r
+}
+
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+func lbits(n3 *BIG,n *BIG) int {
+	n.copy(NewBIGints(CURVE_Bnx))
+	if CURVE_PAIRING_TYPE==BN {
+		n.pmul(6)
+		if SIGN_OF_X==POSITIVEX {
+			n.inc(2)
+		} else {
+			n.dec(2)
+		}
+	}
+
+	n.norm()
+	n3.copy(n)
+	n3.pmul(3)
+	n3.norm()
+	return n3.nbits()
+}
+
+/* prepare for multi-pairing */
+func initmp() []*FP12 {
+	var r []*FP12
+	for i:=ATE_BITS-1; i>=0; i-- {
+		r=append(r,NewFP12int(1))
+	}
+	return r
+}
+
+/* basic Miller loop */
+func miller(r []*FP12) *FP12 {
+	res:=NewFP12int(1);
+	for i:=ATE_BITS-1; i>=1; i-- {
+		res.sqr()
+		res.ssmul(r[i]);
+	}
+
+	if SIGN_OF_X==NEGATIVEX {
+		res.conj()
+	}
+	res.ssmul(r[0])
+	return res
+}
+
+/* Accumulate another set of line functions for n-pairing */
+func another(r []*FP12,P1 *ECP2,Q1 *ECP) {
+	f:=NewFP2bigs(NewBIGints(Fra), NewBIGints(Frb))
+	n:=NewBIG()
+	n3:=NewBIG()
+	K:=NewECP2();
+	var lv,lv2 *FP12
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+	P:=NewECP2()
+	P.Copy(P1)
+	Q:=NewECP()
+	Q.Copy(Q1)
+
+	P.Affine()
+	Q.Affine()
+
+	if CURVE_PAIRING_TYPE==BN {
+		if SEXTIC_TWIST==M_TYPE {
+			f.inverse()
+			f.norm()
+		}
+	}
+
+	Qx := NewFPcopy(Q.getx())
+	Qy := NewFPcopy(Q.gety())
+
+	A:=NewECP2()
+	A.Copy(P)
+
+	MP:=NewECP2()
+	MP.Copy(P); MP.neg()
+
+	nb:=lbits(n3,n)
+
+	for i:=nb-2;i>=1;i-- {
+		lv=line(A,A,Qx,Qy)
+
+		bt:=n3.bit(i)-n.bit(i)
+		if bt==1 {
+			lv2=line(A,P,Qx,Qy)
+			lv.smul(lv2)
+		}
+		if bt==-1 {
+			lv2=line(A,MP,Qx,Qy)
+			lv.smul(lv2)
+		}
+		r[i].ssmul(lv)
+	}
+
+/* R-ate fixup required for BN curves */
+	if CURVE_PAIRING_TYPE==BN {
+		if SIGN_OF_X==NEGATIVEX {
+			A.neg()
+		}
+		K.Copy(P)
+		K.frob(f)
+		lv=line(A,K,Qx,Qy)
+		K.frob(f)
+		K.neg()
+		lv2=line(A,K,Qx,Qy)
+		lv.smul(lv2)
+		r[0].ssmul(lv)
+	} 
 }
 
 /* Optimal R-ate pairing */
 func Ate(P1 *ECP2, Q1 *ECP) *FP12 {
 	f := NewFP2bigs(NewBIGints(Fra), NewBIGints(Frb))
-	x := NewBIGints(CURVE_Bnx)
-	n := NewBIGcopy(x)
+	n:=NewBIG()
+	n3:=NewBIG()
 	K := NewECP2()
-	var lv *FP12
+	var lv,lv2 *FP12
 
 	if CURVE_PAIRING_TYPE == BN {
 		if SEXTIC_TWIST == M_TYPE {
 			f.inverse()
 			f.norm()
 		}
-		n.pmul(6)
-		if SIGN_OF_X == POSITIVEX {
-			n.inc(2)
-		} else {
-			n.dec(2)
-		}
-	} else {
-		n.copy(x)
 	}
 
-	n.norm()
-
-	n3 := NewBIGcopy(n)
-	n3.pmul(3)
-	n3.norm()
-
 	P := NewECP2()
 	P.Copy(P1)
 	P.Affine()
@@ -174,21 +271,21 @@
 	NP.Copy(P)
 	NP.neg()
 
-	nb := n3.nbits()
+	nb:=lbits(n3,n)
 
 	for i := nb - 2; i >= 1; i-- {
 		r.sqr()
 		lv = line(A, A, Qx, Qy)
-		r.smul(lv, SEXTIC_TWIST)
 		bt := n3.bit(i) - n.bit(i)
 		if bt == 1 {
-			lv = line(A, P, Qx, Qy)
-			r.smul(lv, SEXTIC_TWIST)
+			lv2 = line(A, P, Qx, Qy)
+			lv.smul(lv2)
 		}
 		if bt == -1 {
-			lv = line(A, NP, Qx, Qy)
-			r.smul(lv, SEXTIC_TWIST)
+			lv2 = line(A, NP, Qx, Qy)
+			lv.smul(lv2)
 		}
+		r.ssmul(lv)
 	}
 
 	if SIGN_OF_X == NEGATIVEX {
@@ -205,11 +302,11 @@
 		K.Copy(P)
 		K.frob(f)
 		lv = line(A, K, Qx, Qy)
-		r.smul(lv, SEXTIC_TWIST)
 		K.frob(f)
 		K.neg()
-		lv = line(A, K, Qx, Qy)
-		r.smul(lv, SEXTIC_TWIST)
+		lv2 = line(A, K, Qx, Qy)
+		lv.smul(lv2)
+		r.ssmul(lv)
 	}
 
 	return r
@@ -218,32 +315,18 @@
 /* Optimal R-ate double pairing e(P,Q).e(R,S) */
 func Ate2(P1 *ECP2, Q1 *ECP, R1 *ECP2, S1 *ECP) *FP12 {
 	f := NewFP2bigs(NewBIGints(Fra), NewBIGints(Frb))
-	x := NewBIGints(CURVE_Bnx)
-	n := NewBIGcopy(x)
+	n:=NewBIG()
+	n3:=NewBIG()
 	K := NewECP2()
-	var lv *FP12
+	var lv,lv2 *FP12
 
 	if CURVE_PAIRING_TYPE == BN {
 		if SEXTIC_TWIST == M_TYPE {
 			f.inverse()
 			f.norm()
 		}
-		n.pmul(6)
-		if SIGN_OF_X == POSITIVEX {
-			n.inc(2)
-		} else {
-			n.dec(2)
-		}
-	} else {
-		n.copy(x)
 	}
 
-	n.norm()
-
-	n3 := NewBIGcopy(n)
-	n3.pmul(3)
-	n3.norm()
-
 	P := NewECP2()
 	P.Copy(P1)
 	P.Affine()
@@ -275,26 +358,26 @@
 	NR.Copy(R)
 	NR.neg()
 
-	nb := n3.nbits()
+	nb:=lbits(n3,n)
 
 	for i := nb - 2; i >= 1; i-- {
 		r.sqr()
 		lv = line(A, A, Qx, Qy)
-		r.smul(lv, SEXTIC_TWIST)
-		lv = line(B, B, Sx, Sy)
-		r.smul(lv, SEXTIC_TWIST)
+		lv2 = line(B, B, Sx, Sy)
+		lv.smul(lv2)
+		r.ssmul(lv)
 		bt := n3.bit(i) - n.bit(i)
 		if bt == 1 {
 			lv = line(A, P, Qx, Qy)
-			r.smul(lv, SEXTIC_TWIST)
-			lv = line(B, R, Sx, Sy)
-			r.smul(lv, SEXTIC_TWIST)
+			lv2 = line(B, R, Sx, Sy)
+			lv.smul(lv2)
+			r.ssmul(lv)
 		}
 		if bt == -1 {
 			lv = line(A, NP, Qx, Qy)
-			r.smul(lv, SEXTIC_TWIST)
-			lv = line(B, NR, Sx, Sy)
-			r.smul(lv, SEXTIC_TWIST)
+			lv2 = line(B, NR, Sx, Sy)
+			lv.smul(lv2)
+			r.ssmul(lv)
 		}
 	}
 
@@ -312,21 +395,19 @@
 		K.frob(f)
 
 		lv = line(A, K, Qx, Qy)
-		r.smul(lv, SEXTIC_TWIST)
 		K.frob(f)
 		K.neg()
-		lv = line(A, K, Qx, Qy)
-		r.smul(lv, SEXTIC_TWIST)
-
+		lv2 = line(A, K, Qx, Qy)
+		lv.smul(lv2)
+		r.ssmul(lv)
 		K.Copy(R)
 		K.frob(f)
-
 		lv = line(B, K, Sx, Sy)
-		r.smul(lv, SEXTIC_TWIST)
 		K.frob(f)
 		K.neg()
-		lv = line(B, K, Sx, Sy)
-		r.smul(lv, SEXTIC_TWIST)
+		lv2 = line(B, K, Sx, Sy)
+		lv.smul(lv2)
+		r.ssmul(lv)
 	}
 
 	return r
diff --git a/version3/go/PAIR192.go b/version3/go/PAIR192.go
index fe92256..a22877f 100644
--- a/version3/go/PAIR192.go
+++ b/version3/go/PAIR192.go
@@ -123,18 +123,92 @@
 		A.Add(B)
 	}
 
-	return NewFP24fp8s(a, b, c)
+	r:=NewFP24fp8s(a, b, c)
+	r.stype=FP_SPARSER
+	return r
 }
 
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+func lbits(n3 *BIG,n *BIG) int {
+	n.copy(NewBIGints(CURVE_Bnx))
+	n3.copy(n)
+	n3.pmul(3)
+	n3.norm()
+	return n3.nbits()
+}
+
+/* prepare for multi-pairing */
+func initmp() []*FP24 {
+	var r []*FP24
+	for i:=ATE_BITS-1; i>=0; i-- {
+		r=append(r,NewFP24int(1))
+	}
+	return r
+}
+
+/* basic Miller loop */
+func miller(r []*FP24) *FP24 {
+	res:=NewFP24int(1);
+	for i:=ATE_BITS-1; i>=1; i-- {
+		res.sqr()
+		res.ssmul(r[i]);
+	}
+
+	if SIGN_OF_X==NEGATIVEX {
+		res.conj()
+	}
+	res.ssmul(r[0])
+	return res
+}
+
+/* Accumulate another set of line functions for n-pairing */
+func another(r []*FP24,P1 *ECP4,Q1 *ECP) {
+	n:=NewBIG()
+	n3:=NewBIG()
+	var lv,lv2 *FP24
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+	P:=NewECP4()
+	P.Copy(P1)
+	Q:=NewECP()
+	Q.Copy(Q1)
+
+	P.Affine()
+	Q.Affine()
+
+	Qx := NewFPcopy(Q.getx())
+	Qy := NewFPcopy(Q.gety())
+
+	A:=NewECP4()
+	A.Copy(P)
+
+	MP:=NewECP4()
+	MP.Copy(P); MP.neg()
+
+	nb:=lbits(n3,n)
+
+	for i:=nb-2;i>=1;i-- {
+		lv=line(A,A,Qx,Qy)
+
+		bt:=n3.bit(i)-n.bit(i)
+		if bt==1 {
+			lv2=line(A,P,Qx,Qy)
+			lv.smul(lv2)
+		}
+		if bt==-1 {
+			lv2=line(A,MP,Qx,Qy)
+			lv.smul(lv2)
+		}
+		r[i].ssmul(lv)
+	}
+}
+
+
 /* Optimal R-ate pairing */
 func Ate(P1 *ECP4, Q1 *ECP) *FP24 {
-	x := NewBIGints(CURVE_Bnx)
-	n := NewBIGcopy(x)
-	var lv *FP24
-
-	n3 := NewBIGcopy(n)
-	n3.pmul(3)
-	n3.norm()
+	n:=NewBIG()
+	n3:=NewBIG()
+	var lv,lv2 *FP24
 
 	P := NewECP4()
 	P.Copy(P1)
@@ -154,21 +228,22 @@
 	NP.Copy(P)
 	NP.neg()
 
-	nb := n3.nbits()
+	nb:=lbits(n3,n)
 
 	for i := nb - 2; i >= 1; i-- {
 		r.sqr()
 		lv = line(A, A, Qx, Qy)
-		r.smul(lv, SEXTIC_TWIST)
+		
 		bt := n3.bit(i) - n.bit(i)
 		if bt == 1 {
-			lv = line(A, P, Qx, Qy)
-			r.smul(lv, SEXTIC_TWIST)
+			lv2 = line(A, P, Qx, Qy)
+			lv.smul(lv2)
 		}
 		if bt == -1 {
-			lv = line(A, NP, Qx, Qy)
-			r.smul(lv, SEXTIC_TWIST)
+			lv2 = line(A, NP, Qx, Qy)
+			lv.smul(lv2)
 		}
+		r.ssmul(lv)
 	}
 
 	if SIGN_OF_X == NEGATIVEX {
@@ -180,13 +255,9 @@
 
 /* Optimal R-ate double pairing e(P,Q).e(R,S) */
 func Ate2(P1 *ECP4, Q1 *ECP, R1 *ECP4, S1 *ECP) *FP24 {
-	x := NewBIGints(CURVE_Bnx)
-	n := NewBIGcopy(x)
-	var lv *FP24
-
-	n3 := NewBIGcopy(n)
-	n3.pmul(3)
-	n3.norm()
+	n:=NewBIG()
+	n3:=NewBIG()
+	var lv,lv2 *FP24
 
 	P := NewECP4()
 	P.Copy(P1)
@@ -219,26 +290,26 @@
 	NR.Copy(R)
 	NR.neg()
 
-	nb := n3.nbits()
+	nb := lbits(n3,n)
 
 	for i := nb - 2; i >= 1; i-- {
 		r.sqr()
 		lv = line(A, A, Qx, Qy)
-		r.smul(lv, SEXTIC_TWIST)
-		lv = line(B, B, Sx, Sy)
-		r.smul(lv, SEXTIC_TWIST)
+		lv2 = line(B, B, Sx, Sy)
+		lv.smul(lv2)
+		r.ssmul(lv)
 		bt := n3.bit(i) - n.bit(i)
 		if bt == 1 {
 			lv = line(A, P, Qx, Qy)
-			r.smul(lv, SEXTIC_TWIST)
-			lv = line(B, R, Sx, Sy)
-			r.smul(lv, SEXTIC_TWIST)
+			lv2 = line(B, R, Sx, Sy)
+			lv.smul(lv2)
+			r.ssmul(lv)
 		}
 		if bt == -1 {
 			lv = line(A, NP, Qx, Qy)
-			r.smul(lv, SEXTIC_TWIST)
-			lv = line(B, NR, Sx, Sy)
-			r.smul(lv, SEXTIC_TWIST)
+			lv2 = line(B, NR, Sx, Sy)
+			lv.smul(lv2)
+			r.ssmul(lv)
 		}
 	}
 
diff --git a/version3/go/PAIR256.go b/version3/go/PAIR256.go
index 05e2c21..fcd8821 100644
--- a/version3/go/PAIR256.go
+++ b/version3/go/PAIR256.go
@@ -123,18 +123,91 @@
 		A.Add(B)
 	}
 
-	return NewFP48fp16s(a, b, c)
+	r:=NewFP48fp16s(a, b, c)
+	r.stype=FP_SPARSER
+	return r
+}
+
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+func lbits(n3 *BIG,n *BIG) int {
+	n.copy(NewBIGints(CURVE_Bnx))
+	n3.copy(n)
+	n3.pmul(3)
+	n3.norm()
+	return n3.nbits()
+}
+
+/* prepare for multi-pairing */
+func initmp() []*FP48 {
+	var r []*FP48
+	for i:=ATE_BITS-1; i>=0; i-- {
+		r=append(r,NewFP48int(1))
+	}
+	return r
+}
+
+/* basic Miller loop */
+func miller(r []*FP48) *FP48 {
+	res:=NewFP48int(1);
+	for i:=ATE_BITS-1; i>=1; i-- {
+		res.sqr()
+		res.ssmul(r[i]);
+	}
+
+	if SIGN_OF_X==NEGATIVEX {
+		res.conj()
+	}
+	res.ssmul(r[0])
+	return res
+}
+
+/* Accumulate another set of line functions for n-pairing */
+func another(r []*FP48,P1 *ECP8,Q1 *ECP) {
+	n:=NewBIG()
+	n3:=NewBIG()
+	var lv,lv2 *FP48
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+	P:=NewECP8()
+	P.Copy(P1)
+	Q:=NewECP()
+	Q.Copy(Q1)
+
+	P.Affine()
+	Q.Affine()
+
+	Qx := NewFPcopy(Q.getx())
+	Qy := NewFPcopy(Q.gety())
+
+	A:=NewECP8()
+	A.Copy(P)
+
+	MP:=NewECP8()
+	MP.Copy(P); MP.neg()
+
+	nb:=lbits(n3,n)
+
+	for i:=nb-2;i>=1;i-- {
+		lv=line(A,A,Qx,Qy)
+
+		bt:=n3.bit(i)-n.bit(i)
+		if bt==1 {
+			lv2=line(A,P,Qx,Qy)
+			lv.smul(lv2)
+		}
+		if bt==-1 {
+			lv2=line(A,MP,Qx,Qy)
+			lv.smul(lv2)
+		}
+		r[i].ssmul(lv)
+	}
 }
 
 /* Optimal R-ate pairing */
 func Ate(P1 *ECP8, Q1 *ECP) *FP48 {
-	x := NewBIGints(CURVE_Bnx)
-	n := NewBIGcopy(x)
-	var lv *FP48
-
-	n3 := NewBIGcopy(n)
-	n3.pmul(3)
-	n3.norm()
+	n:=NewBIG()
+	n3:=NewBIG()
+	var lv,lv2 *FP48
 
 	P := NewECP8()
 	P.Copy(P1)
@@ -154,21 +227,22 @@
 	NP.Copy(P)
 	NP.neg()
 
-	nb := n3.nbits()
+	nb:=lbits(n3,n)
 
 	for i := nb - 2; i >= 1; i-- {
 		r.sqr()
 		lv = line(A, A, Qx, Qy)
-		r.smul(lv, SEXTIC_TWIST)
+		
 		bt := n3.bit(i) - n.bit(i)
 		if bt == 1 {
-			lv = line(A, P, Qx, Qy)
-			r.smul(lv, SEXTIC_TWIST)
+			lv2 = line(A, P, Qx, Qy)
+			lv.smul(lv2)
 		}
 		if bt == -1 {
-			lv = line(A, NP, Qx, Qy)
-			r.smul(lv, SEXTIC_TWIST)
+			lv2 = line(A, NP, Qx, Qy)
+			lv.smul(lv2)
 		}
+		r.ssmul(lv)
 	}
 
 	if SIGN_OF_X == NEGATIVEX {
@@ -180,13 +254,9 @@
 
 /* Optimal R-ate double pairing e(P,Q).e(R,S) */
 func Ate2(P1 *ECP8, Q1 *ECP, R1 *ECP8, S1 *ECP) *FP48 {
-	x := NewBIGints(CURVE_Bnx)
-	n := NewBIGcopy(x)
-	var lv *FP48
-
-	n3 := NewBIGcopy(n)
-	n3.pmul(3)
-	n3.norm()
+	n:=NewBIG()
+	n3:=NewBIG()
+	var lv,lv2 *FP48
 
 	P := NewECP8()
 	P.Copy(P1)
@@ -219,26 +289,26 @@
 	NR.Copy(R)
 	NR.neg()
 
-	nb := n3.nbits()
+	nb := lbits(n3,n)
 
 	for i := nb - 2; i >= 1; i-- {
 		r.sqr()
 		lv = line(A, A, Qx, Qy)
-		r.smul(lv, SEXTIC_TWIST)
-		lv = line(B, B, Sx, Sy)
-		r.smul(lv, SEXTIC_TWIST)
+		lv2 = line(B, B, Sx, Sy)
+		lv.smul(lv2)
+		r.ssmul(lv)
 		bt := n3.bit(i) - n.bit(i)
 		if bt == 1 {
 			lv = line(A, P, Qx, Qy)
-			r.smul(lv, SEXTIC_TWIST)
-			lv = line(B, R, Sx, Sy)
-			r.smul(lv, SEXTIC_TWIST)
+			lv2 = line(B, R, Sx, Sy)
+			lv.smul(lv2)
+			r.ssmul(lv)
 		}
 		if bt == -1 {
 			lv = line(A, NP, Qx, Qy)
-			r.smul(lv, SEXTIC_TWIST)
-			lv = line(B, NR, Sx, Sy)
-			r.smul(lv, SEXTIC_TWIST)
+			lv2 = line(B, NR, Sx, Sy)
+			lv.smul(lv2)
+			r.ssmul(lv)
 		}
 	}
 
diff --git a/version3/go/config32.py b/version3/go/config32.py
index 02d4fdb..3bb75ca 100644
--- a/version3/go/config32.py
+++ b/version3/go/config32.py
@@ -63,7 +63,7 @@
 
 	replace(fpath+"CONFIG_FF.go","@ML@",ml);
 
-def curveset(tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,cs) :
+def curveset(tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,ab,cs) :
 	global deltext,slashtext,copytext
 	global cptr,chosen
 
@@ -116,6 +116,7 @@
 
 	replace(fpath+"CONFIG_CURVE.go","@ST@",stw)
 	replace(fpath+"CONFIG_CURVE.go","@SX@",sx)
+	replace(fpath+"CONFIG_CURVE.go","@AB@",ab)
 
 	if cs == "128" :
 		replace(fpath+"CONFIG_CURVE.go","@HT@","32")
@@ -251,7 +252,7 @@
 	selection.append(x)
 	ptr=ptr+1
 
-# curveset(curve,big_length_bytes,bits_in_base,modulus_bits,modulus_mod_8,modulus_type,curve_type,pairing_friendly,curve security)
+# curveset(curve,big_length_bytes,bits_in_base,modulus_bits,modulus_mod_8,modulus_type,curve_type,pairing_friendly,ate_bits,curve security)
 # where "curve" is the common name for the elliptic curve
 # big_length_bytes is the modulus size rounded up to a number of bytes
 # bits_in_base gives the number base used for 32 bit architectures, as n where the base is 2^n
@@ -260,95 +261,96 @@
 # modulus_type is NOT_SPECIAL, or PSEUDO_MERSENNE, or MONTGOMERY_Friendly, or GENERALISED_MERSENNE (supported for GOLDILOCKS only)
 # curve_type is WEIERSTRASS, EDWARDS or MONTGOMERY
 # pairing_friendly is BN, BLS or NOT (if not pairing friendly
+# ate bits is number of bits in Ate parameter (from romgen program)
 # curve security is AES equiavlent, rounded up.
 
 
 	if x==1:
-		curveset("ED25519","32","29","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","128")
+		curveset("ED25519","32","29","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==2:
-		curveset("C25519","32","29","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","NOT","NOT","128")
+		curveset("C25519","32","29","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==3:
-		curveset("NIST256","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("NIST256","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==4:
-		curveset("BRAINPOOL","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("BRAINPOOL","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==5:
-		curveset("ANSSI","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("ANSSI","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 
 	if x==6:
-		curveset("HIFIVE","42","29","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","192")
+		curveset("HIFIVE","42","29","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==7:
-		curveset("GOLDILOCKS","56","29","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","NOT","NOT","256")
+		curveset("GOLDILOCKS","56","29","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 	if x==8:
-		curveset("NIST384","48","29","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","192")
+		curveset("NIST384","48","29","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==9:
-		curveset("C41417","52","29","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","256")
+		curveset("C41417","52","29","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 	if x==10:
-		curveset("NIST521","66","28","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","256")
+		curveset("NIST521","66","28","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 
 	if x==11:
-		curveset("NUMS256W","32","28","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("NUMS256W","32","28","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==12:
-		curveset("NUMS256E","32","29","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","128")
+		curveset("NUMS256E","32","29","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==13:
-		curveset("NUMS384W","48","29","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","192")
+		curveset("NUMS384W","48","29","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==14:
-		curveset("NUMS384E","48","29","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","192")
+		curveset("NUMS384E","48","29","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==15:
-		curveset("NUMS512W","64","29","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","256")
+		curveset("NUMS512W","64","29","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 	if x==16:
-		curveset("NUMS512E","64","29","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","256")
+		curveset("NUMS512E","64","29","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 
 	if x==17:
-		curveset("SECP256K1","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("SECP256K1","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 
 	if x==18:
-		curveset("BN254","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("BN254","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==19:
-		curveset("BN254CX","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("BN254CX","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==20:
-		curveset("BLS383","48","29","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","128")
+		curveset("BLS383","48","29","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","65","128")
 		pfcurve_selected=True
 
 	if x==21:
-		curveset("BLS381","48","29","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128")
+		curveset("BLS381","48","29","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","65","128")
 		pfcurve_selected=True
 
 	if x==22:
-		curveset("FP256BN","32","28","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","128")
+		curveset("FP256BN","32","28","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==23:
-		curveset("FP512BN","64","29","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","128")
+		curveset("FP512BN","64","29","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","130","128")
 		pfcurve_selected=True
 # https://eprint.iacr.org/2017/334.pdf
 	if x==24:
-		curveset("BLS461","58","28","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128")
+		curveset("BLS461","58","28","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","78","128")
 		pfcurve_selected=True
 
 	if x==25:
-		curveset("BLS24","60","29","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","192")
+		curveset("BLS24","60","29","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","49","192")
 		pfcurve_selected=True
 
 	if x==26:
-		curveset("BLS48","70","29","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","256")
+		curveset("BLS48","70","29","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","32","256")
 		pfcurve_selected=True
 
 
diff --git a/version3/go/config64.py b/version3/go/config64.py
index cec591d..2ceea37 100644
--- a/version3/go/config64.py
+++ b/version3/go/config64.py
@@ -63,7 +63,7 @@
 
 	replace(fpath+"CONFIG_FF.go","@ML@",ml);
 
-def curveset(tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,cs) :
+def curveset(tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,ab,cs) :
 	global deltext,slashtext,copytext
 	global cptr,chosen
 
@@ -116,6 +116,7 @@
 
 	replace(fpath+"CONFIG_CURVE.go","@ST@",stw)
 	replace(fpath+"CONFIG_CURVE.go","@SX@",sx)
+	replace(fpath+"CONFIG_CURVE.go","@AB@",ab)
 
 	if cs == "128" :
 		replace(fpath+"CONFIG_CURVE.go","@HT@","32")
@@ -251,7 +252,7 @@
 	selection.append(x)
 	ptr=ptr+1
 
-# curveset(curve,big_length_bytes,bits_in_base,modulus_bits,modulus_mod_8,modulus_type,curve_type,pairing_friendly,curve security)
+# curveset(big,field,curve,big_length_bytes,bits_in_base,modulus_bits,modulus_mod_8,modulus_type,curve_type,pairing_friendly,sextic twist,sign of x,ate bits,curve security)
 # where "curve" is the common name for the elliptic curve
 # big_length_bytes is the modulus size rounded up to a number of bytes
 # bits_in_base gives the number base used for 64 bit architectures, as n where the base is 2^n
@@ -260,96 +261,97 @@
 # modulus_type is NOT_SPECIAL, or PSEUDO_MERSENNE, or MONTGOMERY_Friendly, or GENERALISED_MERSENNE (supported for GOLDILOCKS only)
 # curve_type is WEIERSTRASS, EDWARDS or MONTGOMERY
 # pairing_friendly is BN, BLS or NOT (if not pairing friendly
+# ate bits is number of bits in Ate parameter (from romgen program)
 # curve security is AES equiavlent, rounded up.
 
 
 	if x==1:
-		curveset("ED25519","32","56","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","128")
+		curveset("ED25519","32","56","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==2:
-		curveset("C25519","32","56","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","NOT","NOT","128")
+		curveset("C25519","32","56","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==3:
-		curveset("NIST256","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("NIST256","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==4:
-		curveset("BRAINPOOL","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("BRAINPOOL","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==5:
-		curveset("ANSSI","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("ANSSI","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 
 	if x==6:
-		curveset("HIFIVE","42","60","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","192")
+		curveset("HIFIVE","42","60","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==7:
-		curveset("GOLDILOCKS","56","58","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","NOT","NOT","256")
+		curveset("GOLDILOCKS","56","58","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 	if x==8:
-		curveset("NIST384","48","56","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","192")
+		curveset("NIST384","48","56","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==9:
-		curveset("C41417","52","60","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","256")
+		curveset("C41417","52","60","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 	if x==10:
-		curveset("NIST521","66","60","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","256")
+		curveset("NIST521","66","60","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 
 	if x==11:
-		curveset("NUMS256W","32","56","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("NUMS256W","32","56","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==12:
-		curveset("NUMS256E","32","56","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","128")
+		curveset("NUMS256E","32","56","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==13:
-		curveset("NUMS384W","48","58","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","192")
+		curveset("NUMS384W","48","58","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==14:
-		curveset("NUMS384E","48","56","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","192")
+		curveset("NUMS384E","48","56","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==15:
-		curveset("NUMS512W","64","60","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","256")
+		curveset("NUMS512W","64","60","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 	if x==16:
-		curveset("NUMS512E","64","60","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","256")
+		curveset("NUMS512E","64","60","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 
 	if x==17:
-		curveset("SECP256K1","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("SECP256K1","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 
 	if x==18:
-		curveset("BN254","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("BN254","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==19:
-		curveset("BN254CX","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("BN254CX","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==20:
-		curveset("BLS383","48","58","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","128")
+		curveset("BLS383","48","58","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","65","128")
 		pfcurve_selected=True
 
 	if x==21:
-		curveset("BLS381","48","58","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128")
+		curveset("BLS381","48","58","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","65","128")
 		pfcurve_selected=True
 
 
 	if x==22:
-		curveset("FP256BN","32","56","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","128")
+		curveset("FP256BN","32","56","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==23:
-		curveset("FP512BN","64","60","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","128")
+		curveset("FP512BN","64","60","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","130","128")
 		pfcurve_selected=True
 # https://eprint.iacr.org/2017/334.pdf
 	if x==24:
-		curveset("BLS461","58","60","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128")
+		curveset("BLS461","58","60","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","78","128")
 		pfcurve_selected=True
 
 	if x==25:
-		curveset("BLS24","60","56","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","192")
+		curveset("BLS24","60","56","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","49","192")
 		pfcurve_selected=True
 
 	if x==26:
-		curveset("BLS48","70","58","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","256")
+		curveset("BLS48","70","58","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","42","256")
 		pfcurve_selected=True
 
 
diff --git a/version3/java/BLS.java b/version3/java/BLS.java
index dd6f57e..ac4191b 100644
--- a/version3/java/BLS.java
+++ b/version3/java/BLS.java
@@ -79,7 +79,16 @@
 		ECP2 G=ECP2.generator();
 		ECP2 PK=ECP2.fromBytes(W);
 		D.neg();
-		FP12 v=PAIR.ate2(G,D,PK,HM);
+
+// Use new multi-pairing mechanism 
+		FP12[] r=PAIR.initmp();
+		PAIR.another(r,G,D);
+		PAIR.another(r,PK,HM);
+		FP12 v=PAIR.miller(r);
+
+//.. or alternatively
+//		FP12 v=PAIR.ate2(G,D,PK,HM);
+
 		v=PAIR.fexp(v);
 		if (v.isunity())
 			return BLS_OK;
diff --git a/version3/java/BLS192.java b/version3/java/BLS192.java
index 0a2c463..a55d183 100644
--- a/version3/java/BLS192.java
+++ b/version3/java/BLS192.java
@@ -79,7 +79,16 @@
 		ECP4 G=ECP4.generator();
 		ECP4 PK=ECP4.fromBytes(W);
 		D.neg();
-		FP24 v=PAIR192.ate2(G,D,PK,HM);
+
+// Use new multi-pairing mechanism 
+		FP24[] r=PAIR192.initmp();
+		PAIR192.another(r,G,D);
+		PAIR192.another(r,PK,HM);
+		FP24 v=PAIR192.miller(r);
+
+//.. or alternatively
+//		FP24 v=PAIR192.ate2(G,D,PK,HM);
+
 		v=PAIR192.fexp(v);
 		if (v.isunity())
 			return BLS_OK;
diff --git a/version3/java/BLS256.java b/version3/java/BLS256.java
index 8f2e444..8b902f4 100644
--- a/version3/java/BLS256.java
+++ b/version3/java/BLS256.java
@@ -79,7 +79,16 @@
 		ECP8 G=ECP8.generator();
 		ECP8 PK=ECP8.fromBytes(W);
 		D.neg();
-		FP48 v=PAIR256.ate2(G,D,PK,HM);
+
+// Use new multi-pairing mechanism 
+		FP48[] r=PAIR256.initmp();
+		PAIR256.another(r,G,D);
+		PAIR256.another(r,PK,HM);
+		FP48 v=PAIR256.miller(r);
+
+//.. or alternatively
+//		FP48 v=PAIR256.ate2(G,D,PK,HM);
+
 		v=PAIR256.fexp(v);
 		if (v.isunity())
 			return BLS_OK;
diff --git a/version3/java/CONFIG_CURVE.java b/version3/java/CONFIG_CURVE.java
index 232e654..b6bd5ce 100644
--- a/version3/java/CONFIG_CURVE.java
+++ b/version3/java/CONFIG_CURVE.java
@@ -16,6 +16,8 @@
 	public static final int CURVE_PAIRING_TYPE=@PF@;
 	public static final int SEXTIC_TWIST=@ST@;
 	public static final int SIGN_OF_X=@SX@;
+	public static final int ATE_BITS= @AB@;
+
 
 	public static final int SHA256=32;
 	public static final int SHA384=48;
diff --git a/version3/java/FP12.java b/version3/java/FP12.java
index bc3b6a3..551037c 100644
--- a/version3/java/FP12.java
+++ b/version3/java/FP12.java
@@ -23,9 +23,26 @@
 package org.apache.milagro.amcl.XXX;
 
 public final class FP12 {
+	public static final int ZERO=0;
+	public static final int ONE=1;
+	public static final int SPARSER=2;
+	public static final int SPARSE=3;
+	public static final int DENSE=4;
+
 	private final FP4 a;
 	private final FP4 b;
 	private final FP4 c;
+	private int type;
+
+	public void settype(int a)
+	{
+		type=a;
+	}
+
+	public int gettype()
+	{
+		return type;
+	}
 /* reduce all components of this mod Modulus */
 	public void reduce()
 	{
@@ -51,6 +68,8 @@
 		a.cmove(g.a,d);
 		b.cmove(g.b,d);
 		c.cmove(g.c,d);		
+		d=~(d-1);
+		type^=(type^g.type)&d;
 	}
 
 
@@ -116,6 +135,7 @@
 		a.copy(x.a);
 		b.copy(x.b);
 		c.copy(x.c);
+		type=x.type;
 	}
 /* set this=1 */
 	public void one()
@@ -123,6 +143,7 @@
 		a.one();
 		b.zero();
 		c.zero();
+		type=ONE;
 	}
 /* this=conj(this) */
 	public void conj()
@@ -137,6 +158,7 @@
 		a=new FP4(d);
 		b=new FP4(0);
 		c=new FP4(0);
+		type=SPARSER;
 	}
 
 	public FP12(int d)
@@ -144,6 +166,10 @@
 		a=new FP4(d);
 		b=new FP4(0);
 		c=new FP4(0);
+		if (d==1)
+			type=ONE;
+		else
+			type=SPARSER;
 	}
 
 	public FP12(FP4 d,FP4 e,FP4 f)
@@ -151,6 +177,7 @@
 		a=new FP4(d);
 		b=new FP4(e);
 		c=new FP4(f);
+		type=DENSE;
 	}
 
 	public FP12(FP12 x)
@@ -158,6 +185,7 @@
 		a=new FP4(x.a);
 		b=new FP4(x.b);
 		c=new FP4(x.c);
+		type=x.type;
 	}
 
 /* Granger-Scott Unitary Squaring */
@@ -196,12 +224,16 @@
 		c.add(c);
 		b.add(B);
 		c.add(C);
+		type=DENSE;
 		reduce();
 	}
 
 /* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
 	public void sqr()
 	{
+		if (type==ONE)
+			return;
+
 		FP4 A=new FP4(a);
 		FP4 B=new FP4(b);
 		FP4 C=new FP4(c);
@@ -236,6 +268,10 @@
 
 		b.copy(C); b.add(D);
 		c.add(A);
+		if (type==SPARSER)
+			type=SPARSE;
+		else
+			type=DENSE;
 		norm();
 	}
 
@@ -297,9 +333,320 @@
 		z3.times_i();
 		a.copy(z0); a.add(z3);
 		norm();
+		type=DENSE;
 	}
 
-/* Special case of multiplication arises from special form of ATE pairing line function */
+/* FP12 multiplication w=w*y */
+/* catering for special case that arises from special form of ATE pairing line function */
+/* w and y are both sparser line functions - cost = 6m */ 
+	public void smul(FP12 y)
+	{
+		if (CONFIG_CURVE.SEXTIC_TWIST==CONFIG_CURVE.D_TYPE)
+		{	
+			FP2 w1=new FP2(a.geta());
+			FP2 w2=new FP2(a.getb());
+			FP2 w3=new FP2(b.geta());
+
+			w1.mul(y.a.geta());
+			w2.mul(y.a.getb());
+			w3.mul(y.b.geta());
+
+			FP2 ta=new FP2(a.geta());
+			FP2 tb=new FP2(y.a.geta());
+			ta.add(a.getb()); ta.norm();
+			tb.add(y.a.getb()); tb.norm();
+			FP2 tc=new FP2(ta);
+			tc.mul(tb);
+			FP2 t=new FP2(w1);
+			t.add(w2);
+			t.neg();
+			tc.add(t);
+
+			ta.copy(a.geta()); ta.add(b.geta()); ta.norm();
+			tb.copy(y.a.geta()); tb.add(y.b.geta()); tb.norm();
+			FP2 td=new FP2(ta);
+			td.mul(tb);
+			t.copy(w1);
+			t.add(w3);
+			t.neg();
+			td.add(t);
+
+			ta.copy(a.getb()); ta.add(b.geta()); ta.norm();
+			tb.copy(y.a.getb()); tb.add(y.b.geta()); tb.norm();
+			FP2 te=new FP2(ta);
+			te.mul(tb);
+			t.copy(w2);
+			t.add(w3);
+			t.neg();
+			te.add(t);
+
+			w2.mul_ip();
+			w1.add(w2);
+
+			a.geta().copy(w1); a.getb().copy(tc);
+			b.geta().copy(td); b.getb().copy(te);
+			c.geta().copy(w3); c.getb().zero();
+
+			a.norm();
+			b.norm();
+
+		} else {
+			FP2 w1=new FP2(a.geta());
+			FP2 w2=new FP2(a.getb());
+			FP2 w3=new FP2(c.getb());
+
+			w1.mul(y.a.geta());
+			w2.mul(y.a.getb());
+			w3.mul(y.c.getb());
+
+			FP2 ta=new FP2(a.geta());
+			FP2 tb=new FP2(y.a.geta());
+			ta.add(a.getb()); ta.norm();
+			tb.add(y.a.getb()); tb.norm();
+			FP2 tc=new FP2(ta);
+			tc.mul(tb);
+			FP2 t=new FP2(w1);
+			t.add(w2);
+			t.neg();
+			tc.add(t);
+
+			ta.copy(a.geta()); ta.add(c.getb()); ta.norm();
+			tb.copy(y.a.geta()); tb.add(y.c.getb()); tb.norm();
+			FP2 td=new FP2(ta);
+			td.mul(tb);
+			t.copy(w1);
+			t.add(w3);
+			t.neg();
+			td.add(t);
+
+			ta.copy(a.getb()); ta.add(c.getb()); ta.norm();
+			tb.copy(y.a.getb()); tb.add(y.c.getb()); tb.norm();
+			FP2 te=new FP2(ta);
+			te.mul(tb);
+			t.copy(w2);
+			t.add(w3);
+			t.neg();
+			te.add(t);
+
+			w2.mul_ip();
+			w1.add(w2);
+			a.geta().copy(w1); a.getb().copy(tc);
+
+			w3.mul_ip();
+			w3.norm();
+			b.geta().zero(); b.getb().copy(w3);
+
+			te.norm();
+			te.mul_ip();
+			c.geta().copy(te);
+			c.getb().copy(td);
+
+			a.norm();
+			c.norm();
+
+		}
+		type=SPARSE;
+	}
+
+/* FP12 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+	public void ssmul(FP12 y)
+	{
+		if (type==ONE)
+		{
+			copy(y);
+			return;
+		}
+		if (y.type==ONE)
+			return;
+
+		if (y.type>=SPARSE)
+		{
+			FP4 z0=new FP4(a);
+			FP4 z1=new FP4(0);
+			FP4 z2=new FP4(0);
+			FP4 z3=new FP4(0);
+			z0.mul(y.a);
+
+			if (CONFIG_CURVE.SEXTIC_TWIST==CONFIG_CURVE.M_TYPE)
+			{
+				if (y.type==SPARSE || type==SPARSE)
+				{
+					z2.getb().copy(b.getb());
+					z2.getb().mul(y.b.getb());
+					z2.geta().zero();
+					if (y.type!=SPARSE)
+					{
+						z2.geta().copy(b.getb());
+						z2.geta().mul(y.b.geta());
+					}
+					if (type!=SPARSE)
+					{
+						z2.geta().copy(b.geta());
+						z2.geta().mul(y.b.getb());
+					}
+					z2.times_i();
+				} else {
+					z2.copy(b);
+					z2.mul(y.b);
+				}
+			} else {
+				z2.copy(b);
+				z2.mul(y.b);
+			}
+			FP4 t0=new FP4(a);
+			FP4 t1=new FP4(y.a);
+			t0.add(b); t0.norm();
+			t1.add(y.b); t1.norm();
+
+			z1.copy(t0); z1.mul(t1);
+			t0.copy(b); t0.add(c); t0.norm();
+			t1.copy(y.b); t1.add(y.c); t1.norm();
+
+			z3.copy(t0); z3.mul(t1);
+
+			t0.copy(z0); t0.neg();
+			t1.copy(z2); t1.neg();
+
+			z1.add(t0);
+			b.copy(z1); b.add(t1);
+
+			z3.add(t1);
+			z2.add(t0);
+
+			t0.copy(a); t0.add(c); t0.norm();
+			t1.copy(y.a); t1.add(y.c); t1.norm();
+	
+			t0.mul(t1);
+			z2.add(t0);
+
+			if (CONFIG_CURVE.SEXTIC_TWIST==CONFIG_CURVE.D_TYPE)
+			{
+				if (y.type==SPARSE || type==SPARSE)
+				{
+					t0.geta().copy(c.geta());
+					t0.geta().mul(y.c.geta());
+					t0.getb().zero();
+					if (y.type!=SPARSE)
+					{
+						t0.getb().copy(c.geta());
+						t0.getb().mul(y.c.getb());
+					}
+					if (type!=SPARSE)
+					{
+						t0.getb().copy(c.getb());
+						t0.getb().mul(y.c.geta());
+					}
+				} else {
+					t0.copy(c);
+					t0.mul(y.c);
+				}
+			} else {
+				t0.copy(c);
+				t0.mul(y.c);
+			}
+			t1.copy(t0); t1.neg();
+
+			c.copy(z2); c.add(t1);
+			z3.add(t1);
+			t0.times_i();
+			b.add(t0);
+			z3.norm();
+			z3.times_i();
+			a.copy(z0); a.add(z3);
+		} else {
+			if (type==SPARSER)
+			{
+				smul(y);
+				return;
+			}
+			if (CONFIG_CURVE.SEXTIC_TWIST==CONFIG_CURVE.D_TYPE)
+			{ // dense by sparser - 13m 
+				FP4 z0=new FP4(a);
+				FP4 z2=new FP4(b);
+				FP4 z3=new FP4(b);
+				FP4 t0=new FP4(0);
+				FP4 t1=new FP4(y.a);
+				z0.mul(y.a);
+				z2.pmul(y.b.real());
+				b.add(a);
+				t1.real().add(y.b.real());
+
+				t1.norm();
+				b.norm();
+				b.mul(t1);
+				z3.add(c);
+				z3.norm();
+				z3.pmul(y.b.real());
+
+				t0.copy(z0); t0.neg();
+				t1.copy(z2); t1.neg();
+
+				b.add(t0);
+
+				b.add(t1);
+				z3.add(t1);
+				z2.add(t0);
+
+				t0.copy(a); t0.add(c); t0.norm();
+				z3.norm();
+				t0.mul(y.a);
+				c.copy(z2); c.add(t0);
+
+				z3.times_i();
+				a.copy(z0); a.add(z3);
+			}
+			if (CONFIG_CURVE.SEXTIC_TWIST==CONFIG_CURVE.M_TYPE)
+			{
+				FP4 z0=new FP4(a);
+				FP4 z1=new FP4(0);
+				FP4 z2=new FP4(0);
+				FP4 z3=new FP4(0);
+				FP4 t0=new FP4(a);
+				FP4 t1=new FP4(0);
+		
+				z0.mul(y.a);
+				t0.add(b); t0.norm();
+
+				z1.copy(t0); z1.mul(y.a);
+				t0.copy(b); t0.add(c);
+				t0.norm();
+
+				z3.copy(t0); 
+				z3.pmul(y.c.getb());
+				z3.times_i();
+
+				t0.copy(z0); t0.neg();
+				z1.add(t0);
+				b.copy(z1); 
+				z2.copy(t0);
+
+				t0.copy(a); t0.add(c); t0.norm();
+				t1.copy(y.a); t1.add(y.c); t1.norm();
+
+				t0.mul(t1);
+				z2.add(t0);
+				t0.copy(c); 
+			
+				t0.pmul(y.c.getb());
+				t0.times_i();
+				t1.copy(t0); t1.neg();
+
+				c.copy(z2); c.add(t1);
+				z3.add(t1);
+				t0.times_i();
+				b.add(t0);
+				z3.norm();
+				z3.times_i();
+				a.copy(z0); a.add(z3);
+			}	
+		}
+		type=DENSE;
+		norm();
+	}
+
+/* Special case of multiplication arises from special form of ATE pairing line function 
 	public void smul(FP12 y,int type)
 	{
 		if (type==CONFIG_CURVE.D_TYPE)
@@ -392,7 +739,7 @@
 		}
 		norm();
 	}
-
+*/
 /* this=1/this */
 	public void inverse()
 	{
@@ -432,6 +779,7 @@
 		a.copy(f0); a.mul(f3);
 		b.copy(f1); b.mul(f3);
 		c.copy(f2); c.mul(f3);
+		type=DENSE;
 	}
 
 /* this=this^p using Frobenius */
@@ -449,6 +797,7 @@
 
 		b.pmul(f);
 		c.pmul(f2);
+		type=DENSE;
 	}
 
 /* trace function */
diff --git a/version3/java/FP24.java b/version3/java/FP24.java
index c968d3c..9338ea6 100644
--- a/version3/java/FP24.java
+++ b/version3/java/FP24.java
@@ -23,9 +23,26 @@
 package org.apache.milagro.amcl.XXX;
 
 public final class FP24 {
+	public static final int ZERO=0;
+	public static final int ONE=1;
+	public static final int SPARSER=2;
+	public static final int SPARSE=3;
+	public static final int DENSE=4;
+
 	private final FP8 a;
 	private final FP8 b;
 	private final FP8 c;
+	private int type;
+
+	public void settype(int a)
+	{
+		type=a;
+	}
+
+	public int gettype()
+	{
+		return type;
+	}
 /* reduce all components of this mod Modulus */
 	public void reduce()
 	{
@@ -51,6 +68,8 @@
 		a.cmove(g.a,d);
 		b.cmove(g.b,d);
 		c.cmove(g.c,d);		
+		d=~(d-1);
+		type^=(type^g.type)&d;
 	}
 
 
@@ -115,6 +134,7 @@
 		a.copy(x.a);
 		b.copy(x.b);
 		c.copy(x.c);
+		type=x.type;
 	}
 /* set this=1 */
 	public void one()
@@ -122,6 +142,7 @@
 		a.one();
 		b.zero();
 		c.zero();
+		type=ONE;
 	}
 /* this=conj(this) */
 	public void conj()
@@ -136,6 +157,7 @@
 		a=new FP8(d);
 		b=new FP8(0);
 		c=new FP8(0);
+		type=SPARSER;
 	}
 
 	public FP24(int d)
@@ -143,6 +165,10 @@
 		a=new FP8(d);
 		b=new FP8(0);
 		c=new FP8(0);
+		if (d==1)
+			type=ONE;
+		else
+			type=SPARSER;
 	}
 
 	public FP24(FP8 d,FP8 e,FP8 f)
@@ -150,6 +176,7 @@
 		a=new FP8(d);
 		b=new FP8(e);
 		c=new FP8(f);
+		type=DENSE;
 	}
 
 	public FP24(FP24 x)
@@ -157,6 +184,7 @@
 		a=new FP8(x.a);
 		b=new FP8(x.b);
 		c=new FP8(x.c);
+		type=x.type;
 	}
 
 /* Granger-Scott Unitary Squaring */
@@ -195,12 +223,16 @@
 		c.add(c);
 		b.add(B);
 		c.add(C);
+		type=DENSE;
 		reduce();
 	}
 
 /* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
 	public void sqr()
 	{
+		if (type==ONE)
+			return;
+
 		FP8 A=new FP8(a);
 		FP8 B=new FP8(b);
 		FP8 C=new FP8(c);
@@ -235,7 +267,10 @@
 
 		b.copy(C); b.add(D);
 		c.add(A);
-
+		if (type==SPARSER)
+			type=SPARSE;
+		else
+			type=DENSE;
 		norm();
 	}
 
@@ -297,90 +332,220 @@
 		z3.times_i();
 		a.copy(z0); a.add(z3);
 		norm();
-
+		type=DENSE;
 	}
 
-/* Special case of multiplication arises from special form of ATE pairing line function */
-	public void smul(FP24 y,int type)
+
+/* FP24 multiplication w=w*y */
+/* catering for special case that arises from special form of ATE pairing line function */
+/* w and y are both sparser line functions - cost = 6m */ 
+	public void smul(FP24 y)
 	{
-		if (type==CONFIG_CURVE.D_TYPE)
-		{
-			FP8 z0=new FP8(a);
-			FP8 z2=new FP8(b);
-			FP8 z3=new FP8(b);
-			FP8 t0=new FP8(0);
-			FP8 t1=new FP8(y.a);
-			z0.mul(y.a);
-			z2.pmul(y.b.real());
-			b.add(a);
-			t1.real().add(y.b.real());
+		if (CONFIG_CURVE.SEXTIC_TWIST==CONFIG_CURVE.D_TYPE)
+		{	
+			FP4 w1=new FP4(a.geta());
+			FP4 w2=new FP4(a.getb());
+			FP4 w3=new FP4(b.geta());
 
-			t1.norm();
+			w1.mul(y.a.geta());
+			w2.mul(y.a.getb());
+			w3.mul(y.b.geta());
+
+			FP4 ta=new FP4(a.geta());
+			FP4 tb=new FP4(y.a.geta());
+			ta.add(a.getb()); ta.norm();
+			tb.add(y.a.getb()); tb.norm();
+			FP4 tc=new FP4(ta);
+			tc.mul(tb);
+			FP4 t=new FP4(w1);
+			t.add(w2);
+			t.neg();
+			tc.add(t);
+
+			ta.copy(a.geta()); ta.add(b.geta()); ta.norm();
+			tb.copy(y.a.geta()); tb.add(y.b.geta()); tb.norm();
+			FP4 td=new FP4(ta);
+			td.mul(tb);
+			t.copy(w1);
+			t.add(w3);
+			t.neg();
+			td.add(t);
+
+			ta.copy(a.getb()); ta.add(b.geta()); ta.norm();
+			tb.copy(y.a.getb()); tb.add(y.b.geta()); tb.norm();
+			FP4 te=new FP4(ta);
+			te.mul(tb);
+			t.copy(w2);
+			t.add(w3);
+			t.neg();
+			te.add(t);
+
+			w2.times_i();
+			w1.add(w2);
+
+			a.geta().copy(w1); a.getb().copy(tc);
+			b.geta().copy(td); b.getb().copy(te);
+			c.geta().copy(w3); c.getb().zero();
+
+			a.norm();
 			b.norm();
-			b.mul(t1);
-			z3.add(c);
-			z3.norm();
-			z3.pmul(y.b.real());
 
-			t0.copy(z0); t0.neg();
-			t1.copy(z2); t1.neg();
+		} else {
+			FP4 w1=new FP4(a.geta());
+			FP4 w2=new FP4(a.getb());
+			FP4 w3=new FP4(c.getb());
 
-			b.add(t0);
+			w1.mul(y.a.geta());
+			w2.mul(y.a.getb());
+			w3.mul(y.c.getb());
 
-			b.add(t1);
-			z3.add(t1);
-			z2.add(t0);
+			FP4 ta=new FP4(a.geta());
+			FP4 tb=new FP4(y.a.geta());
+			ta.add(a.getb()); ta.norm();
+			tb.add(y.a.getb()); tb.norm();
+			FP4 tc=new FP4(ta);
+			tc.mul(tb);
+			FP4 t=new FP4(w1);
+			t.add(w2);
+			t.neg();
+			tc.add(t);
 
-			t0.copy(a); t0.add(c);
-			t0.norm();
-			z3.norm();
-			t0.mul(y.a);
-			c.copy(z2); c.add(t0);
+			ta.copy(a.geta()); ta.add(c.getb()); ta.norm();
+			tb.copy(y.a.geta()); tb.add(y.c.getb()); tb.norm();
+			FP4 td=new FP4(ta);
+			td.mul(tb);
+			t.copy(w1);
+			t.add(w3);
+			t.neg();
+			td.add(t);
 
-			z3.times_i();
-			a.copy(z0); a.add(z3);
+			ta.copy(a.getb()); ta.add(c.getb()); ta.norm();
+			tb.copy(y.a.getb()); tb.add(y.c.getb()); tb.norm();
+			FP4 te=new FP4(ta);
+			te.mul(tb);
+			t.copy(w2);
+			t.add(w3);
+			t.neg();
+			te.add(t);
+
+			w2.times_i();
+			w1.add(w2);
+			a.geta().copy(w1); a.getb().copy(tc);
+
+			w3.times_i();
+			w3.norm();
+			b.geta().zero(); b.getb().copy(w3);
+
+			te.norm();
+			te.times_i();
+			c.geta().copy(te);
+			c.getb().copy(td);
+
+			a.norm();
+			c.norm();
+
 		}
-		if (type==CONFIG_CURVE.M_TYPE)
+		type=SPARSE;
+	}
+
+/* FP24 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+	public void ssmul(FP24 y)
+	{
+		if (type==ONE)
+		{
+			copy(y);
+			return;
+		}
+		if (y.type==ONE)
+			return;
+
+		if (y.type>=SPARSE)
 		{
 			FP8 z0=new FP8(a);
 			FP8 z1=new FP8(0);
 			FP8 z2=new FP8(0);
 			FP8 z3=new FP8(0);
-			FP8 t0=new FP8(a);
-			FP8 t1=new FP8(0);
-		
 			z0.mul(y.a);
-			t0.add(b);
-			t0.norm();
 
-			z1.copy(t0); z1.mul(y.a);
-			t0.copy(b); t0.add(c);
-			t0.norm();
+			if (CONFIG_CURVE.SEXTIC_TWIST==CONFIG_CURVE.M_TYPE)
+			{
+				if (y.type==SPARSE || type==SPARSE)
+				{
+					z2.getb().copy(b.getb());
+					z2.getb().mul(y.b.getb());
+					z2.geta().zero();
+					if (y.type!=SPARSE)
+					{
+						z2.geta().copy(b.getb());
+						z2.geta().mul(y.b.geta());
+					}
+					if (type!=SPARSE)
+					{
+						z2.geta().copy(b.geta());
+						z2.geta().mul(y.b.getb());
+					}
+					z2.times_i();
+				} else {
+					z2.copy(b);
+					z2.mul(y.b);
+				}
+			} else {
+				z2.copy(b);
+				z2.mul(y.b);
+			}
+			FP8 t0=new FP8(a);
+			FP8 t1=new FP8(y.a);
+			t0.add(b); t0.norm();
+			t1.add(y.b); t1.norm();
 
-			z3.copy(t0); 
-			z3.pmul(y.c.getb());
-			z3.times_i();
+			z1.copy(t0); z1.mul(t1);
+			t0.copy(b); t0.add(c); t0.norm();
+			t1.copy(y.b); t1.add(y.c); t1.norm();
+
+			z3.copy(t0); z3.mul(t1);
 
 			t0.copy(z0); t0.neg();
+			t1.copy(z2); t1.neg();
 
 			z1.add(t0);
-			b.copy(z1); 
-			z2.copy(t0);
+			b.copy(z1); b.add(t1);
 
-			t0.copy(a); t0.add(c);
-			t1.copy(y.a); t1.add(y.c);
+			z3.add(t1);
+			z2.add(t0);
 
-			t0.norm();
-			t1.norm();
+			t0.copy(a); t0.add(c); t0.norm();
+			t1.copy(y.a); t1.add(y.c); t1.norm();
 	
 			t0.mul(t1);
 			z2.add(t0);
 
-			t0.copy(c); 
-			
-			t0.pmul(y.c.getb());
-			t0.times_i();
-
+			if (CONFIG_CURVE.SEXTIC_TWIST==CONFIG_CURVE.D_TYPE)
+			{
+				if (y.type==SPARSE || type==SPARSE)
+				{
+					t0.geta().copy(c.geta());
+					t0.geta().mul(y.c.geta());
+					t0.getb().zero();
+					if (y.type!=SPARSE)
+					{
+						t0.getb().copy(c.geta());
+						t0.getb().mul(y.c.getb());
+					}
+					if (type!=SPARSE)
+					{
+						t0.getb().copy(c.getb());
+						t0.getb().mul(y.c.geta());
+					}
+				} else {
+					t0.copy(c);
+					t0.mul(y.c);
+				}
+			} else {
+				t0.copy(c);
+				t0.mul(y.c);
+			}
 			t1.copy(t0); t1.neg();
 
 			c.copy(z2); c.add(t1);
@@ -390,7 +555,94 @@
 			z3.norm();
 			z3.times_i();
 			a.copy(z0); a.add(z3);
+		} else {
+			if (type==SPARSER)
+			{
+				smul(y);
+				return;
+			}
+			if (CONFIG_CURVE.SEXTIC_TWIST==CONFIG_CURVE.D_TYPE)
+			{ // dense by sparser - 13m 
+				FP8 z0=new FP8(a);
+				FP8 z2=new FP8(b);
+				FP8 z3=new FP8(b);
+				FP8 t0=new FP8(0);
+				FP8 t1=new FP8(y.a);
+				z0.mul(y.a);
+				z2.pmul(y.b.real());
+				b.add(a);
+				t1.real().add(y.b.real());
+
+				t1.norm();
+				b.norm();
+				b.mul(t1);
+				z3.add(c);
+				z3.norm();
+				z3.pmul(y.b.real());
+
+				t0.copy(z0); t0.neg();
+				t1.copy(z2); t1.neg();
+
+				b.add(t0);
+
+				b.add(t1);
+				z3.add(t1);
+				z2.add(t0);
+
+				t0.copy(a); t0.add(c); t0.norm();
+				z3.norm();
+				t0.mul(y.a);
+				c.copy(z2); c.add(t0);
+
+				z3.times_i();
+				a.copy(z0); a.add(z3);
+			}
+			if (CONFIG_CURVE.SEXTIC_TWIST==CONFIG_CURVE.M_TYPE)
+			{
+				FP8 z0=new FP8(a);
+				FP8 z1=new FP8(0);
+				FP8 z2=new FP8(0);
+				FP8 z3=new FP8(0);
+				FP8 t0=new FP8(a);
+				FP8 t1=new FP8(0);
+		
+				z0.mul(y.a);
+				t0.add(b); t0.norm();
+
+				z1.copy(t0); z1.mul(y.a);
+				t0.copy(b); t0.add(c);
+				t0.norm();
+
+				z3.copy(t0); 
+				z3.pmul(y.c.getb());
+				z3.times_i();
+
+				t0.copy(z0); t0.neg();
+				z1.add(t0);
+				b.copy(z1); 
+				z2.copy(t0);
+
+				t0.copy(a); t0.add(c); t0.norm();
+				t1.copy(y.a); t1.add(y.c); t1.norm();
+
+				t0.mul(t1);
+				z2.add(t0);
+				t0.copy(c); 
+			
+				t0.pmul(y.c.getb());
+				t0.times_i();
+				t1.copy(t0); t1.neg();
+
+				c.copy(z2); c.add(t1);
+				z3.add(t1);
+				t0.times_i();
+				b.add(t0);
+				z3.norm();
+				z3.times_i();
+				a.copy(z0); a.add(z3);
+			}	
 		}
+		type=DENSE;
 		norm();
 	}
 
@@ -433,6 +685,7 @@
 		a.copy(f0); a.mul(f3);
 		b.copy(f1); b.mul(f3);
 		c.copy(f2); c.mul(f3);
+		type=DENSE;
 	}
 
 /* this=this^p using Frobenius */
@@ -455,6 +708,7 @@
 			b.qmul(f); b.times_i2();
 			c.qmul(f2); c.times_i2(); c.times_i2();
 		}
+		type=DENSE;
 	}
 
 /* trace function */
@@ -467,7 +721,7 @@
 		return t;
 	}
 
-/* convert from byte array to FP12 */
+/* convert from byte array to FP24 */
 	public static FP24 fromBytes(byte[] w)
 	{
 		BIG a,b;
diff --git a/version3/java/FP48.java b/version3/java/FP48.java
index b1959bc..e0dc3c4 100644
--- a/version3/java/FP48.java
+++ b/version3/java/FP48.java
@@ -23,9 +23,27 @@
 package org.apache.milagro.amcl.XXX;
 
 public final class FP48 {
+	public static final int ZERO=0;
+	public static final int ONE=1;
+	public static final int SPARSER=2;
+	public static final int SPARSE=3;
+	public static final int DENSE=4;
+
 	private final FP16 a;
 	private final FP16 b;
 	private final FP16 c;
+	private int type;
+
+	public void settype(int a)
+	{
+		type=a;
+	}
+
+	public int gettype()
+	{
+		return type;
+	}
+
 /* reduce all components of this mod Modulus */
 	public void reduce()
 	{
@@ -51,6 +69,8 @@
 		a.cmove(g.a,d);
 		b.cmove(g.b,d);
 		c.cmove(g.c,d);		
+		d=~(d-1);
+		type^=(type^g.type)&d;
 	}
 
 
@@ -115,6 +135,7 @@
 		a.copy(x.a);
 		b.copy(x.b);
 		c.copy(x.c);
+		type=x.type;
 	}
 /* set this=1 */
 	public void one()
@@ -122,6 +143,7 @@
 		a.one();
 		b.zero();
 		c.zero();
+		type=ONE;
 	}
 /* this=conj(this) */
 	public void conj()
@@ -136,6 +158,7 @@
 		a=new FP16(d);
 		b=new FP16(0);
 		c=new FP16(0);
+		type=SPARSER;
 	}
 
 	public FP48(int d)
@@ -143,6 +166,10 @@
 		a=new FP16(d);
 		b=new FP16(0);
 		c=new FP16(0);
+		if (d==1)
+			type=ONE;
+		else
+			type=SPARSER;
 	}
 
 	public FP48(FP16 d,FP16 e,FP16 f)
@@ -150,6 +177,7 @@
 		a=new FP16(d);
 		b=new FP16(e);
 		c=new FP16(f);
+		type=DENSE;
 	}
 
 	public FP48(FP48 x)
@@ -157,6 +185,7 @@
 		a=new FP16(x.a);
 		b=new FP16(x.b);
 		c=new FP16(x.c);
+		type=x.type;
 	}
 
 /* Granger-Scott Unitary Squaring */
@@ -195,12 +224,16 @@
 		c.add(c);
 		b.add(B);
 		c.add(C);
+		type=DENSE;
 		reduce();
 	}
 
 /* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
 	public void sqr()
 	{
+		if (type==ONE)
+			return;
+
 		FP16 A=new FP16(a);
 		FP16 B=new FP16(b);
 		FP16 C=new FP16(c);
@@ -235,7 +268,10 @@
 
 		b.copy(C); b.add(D);
 		c.add(A);
-
+		if (type==SPARSER)
+			type=SPARSE;
+		else
+			type=DENSE;
 		norm();
 	}
 
@@ -297,90 +333,219 @@
 		z3.times_i();
 		a.copy(z0); a.add(z3);
 		norm();
-
+		type=DENSE;
 	}
 
-/* Special case of multiplication arises from special form of ATE pairing line function */
-	public void smul(FP48 y,int type)
+/* FP48 multiplication w=w*y */
+/* catering for special case that arises from special form of ATE pairing line function */
+/* w and y are both sparser line functions - cost = 6m */ 
+	public void smul(FP48 y)
 	{
-		if (type==CONFIG_CURVE.D_TYPE)
-		{
-			FP16 z0=new FP16(a);
-			FP16 z2=new FP16(b);
-			FP16 z3=new FP16(b);
-			FP16 t0=new FP16(0);
-			FP16 t1=new FP16(y.a);
-			z0.mul(y.a);
-			z2.pmul(y.b.real());
-			b.add(a);
-			t1.real().add(y.b.real());
+		if (CONFIG_CURVE.SEXTIC_TWIST==CONFIG_CURVE.D_TYPE)
+		{	
+			FP8 w1=new FP8(a.geta());
+			FP8 w2=new FP8(a.getb());
+			FP8 w3=new FP8(b.geta());
 
-			t1.norm();
+			w1.mul(y.a.geta());
+			w2.mul(y.a.getb());
+			w3.mul(y.b.geta());
+
+			FP8 ta=new FP8(a.geta());
+			FP8 tb=new FP8(y.a.geta());
+			ta.add(a.getb()); ta.norm();
+			tb.add(y.a.getb()); tb.norm();
+			FP8 tc=new FP8(ta);
+			tc.mul(tb);
+			FP8 t=new FP8(w1);
+			t.add(w2);
+			t.neg();
+			tc.add(t);
+
+			ta.copy(a.geta()); ta.add(b.geta()); ta.norm();
+			tb.copy(y.a.geta()); tb.add(y.b.geta()); tb.norm();
+			FP8 td=new FP8(ta);
+			td.mul(tb);
+			t.copy(w1);
+			t.add(w3);
+			t.neg();
+			td.add(t);
+
+			ta.copy(a.getb()); ta.add(b.geta()); ta.norm();
+			tb.copy(y.a.getb()); tb.add(y.b.geta()); tb.norm();
+			FP8 te=new FP8(ta);
+			te.mul(tb);
+			t.copy(w2);
+			t.add(w3);
+			t.neg();
+			te.add(t);
+
+			w2.times_i();
+			w1.add(w2);
+
+			a.geta().copy(w1); a.getb().copy(tc);
+			b.geta().copy(td); b.getb().copy(te);
+			c.geta().copy(w3); c.getb().zero();
+
+			a.norm();
 			b.norm();
-			b.mul(t1);
-			z3.add(c);
-			z3.norm();
-			z3.pmul(y.b.real());
 
-			t0.copy(z0); t0.neg();
-			t1.copy(z2); t1.neg();
+		} else {
+			FP8 w1=new FP8(a.geta());
+			FP8 w2=new FP8(a.getb());
+			FP8 w3=new FP8(c.getb());
 
-			b.add(t0);
+			w1.mul(y.a.geta());
+			w2.mul(y.a.getb());
+			w3.mul(y.c.getb());
 
-			b.add(t1);
-			z3.add(t1);
-			z2.add(t0);
+			FP8 ta=new FP8(a.geta());
+			FP8 tb=new FP8(y.a.geta());
+			ta.add(a.getb()); ta.norm();
+			tb.add(y.a.getb()); tb.norm();
+			FP8 tc=new FP8(ta);
+			tc.mul(tb);
+			FP8 t=new FP8(w1);
+			t.add(w2);
+			t.neg();
+			tc.add(t);
 
-			t0.copy(a); t0.add(c);
-			t0.norm();
-			z3.norm();
-			t0.mul(y.a);
-			c.copy(z2); c.add(t0);
+			ta.copy(a.geta()); ta.add(c.getb()); ta.norm();
+			tb.copy(y.a.geta()); tb.add(y.c.getb()); tb.norm();
+			FP8 td=new FP8(ta);
+			td.mul(tb);
+			t.copy(w1);
+			t.add(w3);
+			t.neg();
+			td.add(t);
 
-			z3.times_i();
-			a.copy(z0); a.add(z3);
+			ta.copy(a.getb()); ta.add(c.getb()); ta.norm();
+			tb.copy(y.a.getb()); tb.add(y.c.getb()); tb.norm();
+			FP8 te=new FP8(ta);
+			te.mul(tb);
+			t.copy(w2);
+			t.add(w3);
+			t.neg();
+			te.add(t);
+
+			w2.times_i();
+			w1.add(w2);
+			a.geta().copy(w1); a.getb().copy(tc);
+
+			w3.times_i();
+			w3.norm();
+			b.geta().zero(); b.getb().copy(w3);
+
+			te.norm();
+			te.times_i();
+			c.geta().copy(te);
+			c.getb().copy(td);
+
+			a.norm();
+			c.norm();
+
 		}
-		if (type==CONFIG_CURVE.M_TYPE)
+		type=SPARSE;
+	}
+
+/* FP48 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+	public void ssmul(FP48 y)
+	{
+		if (type==ONE)
+		{
+			copy(y);
+			return;
+		}
+		if (y.type==ONE)
+			return;
+
+		if (y.type>=SPARSE)
 		{
 			FP16 z0=new FP16(a);
 			FP16 z1=new FP16(0);
 			FP16 z2=new FP16(0);
 			FP16 z3=new FP16(0);
-			FP16 t0=new FP16(a);
-			FP16 t1=new FP16(0);
-		
 			z0.mul(y.a);
-			t0.add(b);
-			t0.norm();
 
-			z1.copy(t0); z1.mul(y.a);
-			t0.copy(b); t0.add(c);
-			t0.norm();
+			if (CONFIG_CURVE.SEXTIC_TWIST==CONFIG_CURVE.M_TYPE)
+			{
+				if (y.type==SPARSE || type==SPARSE)
+				{
+					z2.getb().copy(b.getb());
+					z2.getb().mul(y.b.getb());
+					z2.geta().zero();
+					if (y.type!=SPARSE)
+					{
+						z2.geta().copy(b.getb());
+						z2.geta().mul(y.b.geta());
+					}
+					if (type!=SPARSE)
+					{
+						z2.geta().copy(b.geta());
+						z2.geta().mul(y.b.getb());
+					}
+					z2.times_i();
+				} else {
+					z2.copy(b);
+					z2.mul(y.b);
+				}
+			} else {
+				z2.copy(b);
+				z2.mul(y.b);
+			}
+			FP16 t0=new FP16(a);
+			FP16 t1=new FP16(y.a);
+			t0.add(b); t0.norm();
+			t1.add(y.b); t1.norm();
 
-			z3.copy(t0); 
-			z3.pmul(y.c.getb());
-			z3.times_i();
+			z1.copy(t0); z1.mul(t1);
+			t0.copy(b); t0.add(c); t0.norm();
+			t1.copy(y.b); t1.add(y.c); t1.norm();
+
+			z3.copy(t0); z3.mul(t1);
 
 			t0.copy(z0); t0.neg();
+			t1.copy(z2); t1.neg();
 
 			z1.add(t0);
-			b.copy(z1); 
-			z2.copy(t0);
+			b.copy(z1); b.add(t1);
 
-			t0.copy(a); t0.add(c);
-			t1.copy(y.a); t1.add(y.c);
+			z3.add(t1);
+			z2.add(t0);
 
-			t0.norm();
-			t1.norm();
+			t0.copy(a); t0.add(c); t0.norm();
+			t1.copy(y.a); t1.add(y.c); t1.norm();
 	
 			t0.mul(t1);
 			z2.add(t0);
 
-			t0.copy(c); 
-			
-			t0.pmul(y.c.getb());
-			t0.times_i();
-
+			if (CONFIG_CURVE.SEXTIC_TWIST==CONFIG_CURVE.D_TYPE)
+			{
+				if (y.type==SPARSE || type==SPARSE)
+				{
+					t0.geta().copy(c.geta());
+					t0.geta().mul(y.c.geta());
+					t0.getb().zero();
+					if (y.type!=SPARSE)
+					{
+						t0.getb().copy(c.geta());
+						t0.getb().mul(y.c.getb());
+					}
+					if (type!=SPARSE)
+					{
+						t0.getb().copy(c.getb());
+						t0.getb().mul(y.c.geta());
+					}
+				} else {
+					t0.copy(c);
+					t0.mul(y.c);
+				}
+			} else {
+				t0.copy(c);
+				t0.mul(y.c);
+			}
 			t1.copy(t0); t1.neg();
 
 			c.copy(z2); c.add(t1);
@@ -390,10 +555,96 @@
 			z3.norm();
 			z3.times_i();
 			a.copy(z0); a.add(z3);
+		} else {
+			if (type==SPARSER)
+			{
+				smul(y);
+				return;
+			}
+			if (CONFIG_CURVE.SEXTIC_TWIST==CONFIG_CURVE.D_TYPE)
+			{ // dense by sparser - 13m 
+				FP16 z0=new FP16(a);
+				FP16 z2=new FP16(b);
+				FP16 z3=new FP16(b);
+				FP16 t0=new FP16(0);
+				FP16 t1=new FP16(y.a);
+				z0.mul(y.a);
+				z2.pmul(y.b.real());
+				b.add(a);
+				t1.real().add(y.b.real());
+
+				t1.norm();
+				b.norm();
+				b.mul(t1);
+				z3.add(c);
+				z3.norm();
+				z3.pmul(y.b.real());
+
+				t0.copy(z0); t0.neg();
+				t1.copy(z2); t1.neg();
+
+				b.add(t0);
+
+				b.add(t1);
+				z3.add(t1);
+				z2.add(t0);
+
+				t0.copy(a); t0.add(c); t0.norm();
+				z3.norm();
+				t0.mul(y.a);
+				c.copy(z2); c.add(t0);
+
+				z3.times_i();
+				a.copy(z0); a.add(z3);
+			}
+			if (CONFIG_CURVE.SEXTIC_TWIST==CONFIG_CURVE.M_TYPE)
+			{
+				FP16 z0=new FP16(a);
+				FP16 z1=new FP16(0);
+				FP16 z2=new FP16(0);
+				FP16 z3=new FP16(0);
+				FP16 t0=new FP16(a);
+				FP16 t1=new FP16(0);
+		
+				z0.mul(y.a);
+				t0.add(b); t0.norm();
+
+				z1.copy(t0); z1.mul(y.a);
+				t0.copy(b); t0.add(c);
+				t0.norm();
+
+				z3.copy(t0); 
+				z3.pmul(y.c.getb());
+				z3.times_i();
+
+				t0.copy(z0); t0.neg();
+				z1.add(t0);
+				b.copy(z1); 
+				z2.copy(t0);
+
+				t0.copy(a); t0.add(c); t0.norm();
+				t1.copy(y.a); t1.add(y.c); t1.norm();
+
+				t0.mul(t1);
+				z2.add(t0);
+				t0.copy(c); 
+			
+				t0.pmul(y.c.getb());
+				t0.times_i();
+				t1.copy(t0); t1.neg();
+
+				c.copy(z2); c.add(t1);
+				z3.add(t1);
+				t0.times_i();
+				b.add(t0);
+				z3.norm();
+				z3.times_i();
+				a.copy(z0); a.add(z3);
+			}	
 		}
+		type=DENSE;
 		norm();
 	}
-
 /* this=1/this */
 	public void inverse()
 	{
@@ -433,6 +684,7 @@
 		a.copy(f0); a.mul(f3);
 		b.copy(f1); b.mul(f3);
 		c.copy(f2); c.mul(f3);
+		type=DENSE;
 	}
 
 /* this=this^p using Frobenius */
@@ -456,6 +708,7 @@
 			b.qmul(f); b.times_i4(); b.times_i2(); 
 			c.qmul(f2); c.times_i4(); c.times_i4(); c.times_i4(); 
 		}
+		type=DENSE;
 	}
 
 /* trace function */
@@ -468,7 +721,7 @@
 		return t;
 	}
 
-/* convert from byte array to FP12 */
+/* convert from byte array to FP48 */
 	public static FP48 fromBytes(byte[] w)
 	{
 		BIG a,b;
diff --git a/version3/java/PAIR.java b/version3/java/PAIR.java
index 9f79ad2..2ad5e7c 100644
--- a/version3/java/PAIR.java
+++ b/version3/java/PAIR.java
@@ -123,18 +123,68 @@
 			}
 			A.add(B);
 		}
-
-		return new FP12(a,b,c);
+		FP12 r=new FP12(a,b,c);
+		r.settype(FP12.SPARSE);
+		return r;
 	}
 
-/* Optimal R-ate pairing */
-	public static FP12 ate(ECP2 P1,ECP Q1)
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+	public static int lbits(BIG n3,BIG n)
 	{
+		n.copy(new BIG(ROM.CURVE_Bnx));
+		if (CONFIG_CURVE.CURVE_PAIRING_TYPE==CONFIG_CURVE.BN)
+		{
+			n.pmul(6);
+			if (CONFIG_CURVE.SIGN_OF_X==CONFIG_CURVE.POSITIVEX)
+			{
+				n.inc(2);
+			} else {
+				n.dec(2);
+			}
+		}
+		
+		n.norm();
+		n3.copy(n);
+		n3.pmul(3);
+		n3.norm();
+		return n3.nbits();
+	}
+
+/* prepare for multi-pairing */
+	public static FP12[] initmp()
+	{
+		FP12[] r=new FP12[CONFIG_CURVE.ATE_BITS];
+		for (int i=CONFIG_CURVE.ATE_BITS-1; i>=0; i--)
+			r[i]=new FP12(1);
+		return r;
+	}
+
+/* basic Miller loop */
+	public static FP12 miller(FP12[] r)
+	{
+		FP12 res=new FP12(1);
+		for (int i=CONFIG_CURVE.ATE_BITS-1; i>=1; i--)
+		{
+			res.sqr();
+			res.ssmul(r[i]); 
+		}
+
+		if (CONFIG_CURVE.SIGN_OF_X==CONFIG_CURVE.NEGATIVEX)
+			res.conj();
+		res.ssmul(r[0]);
+
+		return res;
+	}
+
+/* Accumulate another set of line functions for n-pairing */
+	public static void another(FP12[] r,ECP2 P1,ECP Q1)
+	{
+
 		FP2 f;
-		BIG x=new BIG(ROM.CURVE_Bnx);
-		BIG n=new BIG(x);
+		BIG n=new BIG(0);
+		BIG n3=new BIG(0);
 		ECP2 K=new ECP2();
-		FP12 lv;
+		FP12 lv,lv2;
 		int bt;
 
 // P is needed in affine form for line function, Q for (Qx,Qy) extraction
@@ -152,21 +202,81 @@
 				f.inverse();
 				f.norm();
 			}
-			n.pmul(6);
-			if (CONFIG_CURVE.SIGN_OF_X==CONFIG_CURVE.POSITIVEX)
+		}
+
+		FP Qx=new FP(Q.getx());
+		FP Qy=new FP(Q.gety());
+
+		ECP2 A=new ECP2();
+		A.copy(P);
+
+		ECP2 MP=new ECP2();
+		MP.copy(P); MP.neg();
+
+		int nb=lbits(n3,n);
+
+		for (int i=nb-2;i>=1;i--)
+		{
+			lv=line(A,A,Qx,Qy);
+
+			bt=n3.bit(i)-n.bit(i); 
+			if (bt==1)
 			{
-				n.inc(2);
-			} else {
-				n.dec(2);
+				lv2=line(A,P,Qx,Qy);
+				lv.smul(lv2);
+			}
+			if (bt==-1)
+			{
+				lv2=line(A,MP,Qx,Qy);
+				lv.smul(lv2);
+			}
+			r[i].ssmul(lv);
+		}
+
+/* R-ate fixup required for BN curves */
+		if (CONFIG_CURVE.CURVE_PAIRING_TYPE==CONFIG_CURVE.BN)
+		{
+			if (CONFIG_CURVE.SIGN_OF_X==CONFIG_CURVE.NEGATIVEX)
+			{
+				A.neg();
+			}
+			K.copy(P);
+			K.frob(f);
+			lv=line(A,K,Qx,Qy);
+			K.frob(f);
+			K.neg();
+			lv2=line(A,K,Qx,Qy);
+			lv.smul(lv2);
+			r[0].ssmul(lv);
+		} 
+	}
+
+/* Optimal R-ate pairing */
+	public static FP12 ate(ECP2 P1,ECP Q1)
+	{
+		FP2 f;
+		BIG n=new BIG(0);
+		BIG n3=new BIG(0);
+		ECP2 K=new ECP2();
+		FP12 lv,lv2;
+		int bt;
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+		ECP2 P=new ECP2(P1);
+		ECP Q=new ECP(Q1);
+
+		P.affine();
+		Q.affine();
+
+		if (CONFIG_CURVE.CURVE_PAIRING_TYPE==CONFIG_CURVE.BN)
+		{
+			f=new FP2(new BIG(ROM.Fra),new BIG(ROM.Frb));
+			if (CONFIG_CURVE.SEXTIC_TWIST==CONFIG_CURVE.M_TYPE)
+			{
+				f.inverse();
+				f.norm();
 			}
 		}
-		else
-			n.copy(x);
-		n.norm();
-		
-		BIG n3=new BIG(n);
-		n3.pmul(3);
-		n3.norm();
 
 		FP Qx=new FP(Q.getx());
 		FP Qy=new FP(Q.gety());
@@ -178,25 +288,25 @@
 		ECP2 MP=new ECP2();
 		MP.copy(P); MP.neg();
 
-		int nb=n3.nbits();
+		int nb=lbits(n3,n);
 
 		for (int i=nb-2;i>=1;i--)
 		{
 			r.sqr();
 			lv=line(A,A,Qx,Qy);
-			r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
 
 			bt=n3.bit(i)-n.bit(i); 
 			if (bt==1)
 			{
-				lv=line(A,P,Qx,Qy);
-				r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
+				lv2=line(A,P,Qx,Qy);
+				lv.smul(lv2);
 			}
 			if (bt==-1)
 			{
-				lv=line(A,MP,Qx,Qy);
-				r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
+				lv2=line(A,MP,Qx,Qy);
+				lv.smul(lv2);
 			}
+			r.ssmul(lv);
 		}
 
 		if (CONFIG_CURVE.SIGN_OF_X==CONFIG_CURVE.NEGATIVEX)
@@ -214,11 +324,11 @@
 			K.copy(P);
 			K.frob(f);
 			lv=line(A,K,Qx,Qy);
-			r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
 			K.frob(f);
 			K.neg();
-			lv=line(A,K,Qx,Qy);
-			r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
+			lv2=line(A,K,Qx,Qy);
+			lv.smul(lv2);
+			r.ssmul(lv);
 		} 
 		return r;
 	}
@@ -227,10 +337,10 @@
 	public static FP12 ate2(ECP2 P1,ECP Q1,ECP2 R1,ECP S1)
 	{
 		FP2 f;
-		BIG x=new BIG(ROM.CURVE_Bnx);
-		BIG n=new BIG(x);
+		BIG n=new BIG(0);
+		BIG n3=new BIG(0);
 		ECP2 K=new ECP2();
-		FP12 lv;
+		FP12 lv,lv2;
 		int bt;
 
 		ECP2 P=new ECP2(P1);
@@ -253,21 +363,7 @@
 				f.inverse();
 				f.norm();
 			}
-			n.pmul(6); 
-			if (CONFIG_CURVE.SIGN_OF_X==CONFIG_CURVE.POSITIVEX)
-			{
-				n.inc(2);
-			} else {
-				n.dec(2);
-			}
 		}
-		else
-			n.copy(x);
-		n.norm();
-
-		BIG n3=new BIG(n);
-		n3.pmul(3);
-		n3.norm();
 
 		FP Qx=new FP(Q.getx());
 		FP Qy=new FP(Q.gety());
@@ -286,32 +382,30 @@
 		ECP2 MR=new ECP2();
 		MR.copy(R); MR.neg();
 
-
-		int nb=n3.nbits();
+		int nb=lbits(n3,n);
 
 		for (int i=nb-2;i>=1;i--)
 		{
 			r.sqr();
 			lv=line(A,A,Qx,Qy);
-			r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
-
-			lv=line(B,B,Sx,Sy);
-			r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
+			lv2=line(B,B,Sx,Sy);
+			lv.smul(lv2);
+			r.ssmul(lv);
 
 			bt=n3.bit(i)-n.bit(i);
 			if (bt==1)
 			{
 				lv=line(A,P,Qx,Qy);
-				r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
-				lv=line(B,R,Sx,Sy);
-				r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
+				lv2=line(B,R,Sx,Sy);
+				lv.smul(lv2);
+				r.ssmul(lv);
 			}
 			if (bt==-1)
 			{
 				lv=line(A,MP,Qx,Qy);
-				r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
-				lv=line(B,MR,Sx,Sy);
-				r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
+				lv2=line(B,MR,Sx,Sy);
+				lv.smul(lv2);
+				r.ssmul(lv);
 			}
 		}
 
@@ -334,19 +428,19 @@
 			K.frob(f);
 
 			lv=line(A,K,Qx,Qy);
-			r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
 			K.frob(f);
 			K.neg();
-			lv=line(A,K,Qx,Qy);
-			r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
+			lv2=line(A,K,Qx,Qy);
+			lv.smul(lv2);
+			r.ssmul(lv);
 			K.copy(R);
 			K.frob(f);
 			lv=line(B,K,Sx,Sy);
-			r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
 			K.frob(f);
 			K.neg();
-			lv=line(B,K,Sx,Sy);
-			r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
+			lv2=line(B,K,Sx,Sy);
+			lv.smul(lv2);
+			r.ssmul(lv);
 		}
 		return r;
 	}
diff --git a/version3/java/PAIR192.java b/version3/java/PAIR192.java
index d9ce24a..9b9f975 100644
--- a/version3/java/PAIR192.java
+++ b/version3/java/PAIR192.java
@@ -124,16 +124,101 @@
 			}
 			A.add(B);
 		}
-		return new FP24(a,b,c);
+		FP24 r=new FP24(a,b,c);
+		r.settype(FP24.SPARSE);
+		return r;
+	}
+
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+	public static int lbits(BIG n3,BIG n)
+	{
+		n.copy(new BIG(ROM.CURVE_Bnx));
+		n3.copy(n);
+		n3.pmul(3);
+		n3.norm();
+		return n3.nbits();
+	}
+
+/* prepare for multi-pairing */
+	public static FP24[] initmp()
+	{
+		FP24[] r=new FP24[CONFIG_CURVE.ATE_BITS];
+		for (int i=CONFIG_CURVE.ATE_BITS-1; i>=0; i--)
+			r[i]=new FP24(1);
+		return r;
+	}
+
+/* basic Miller loop */
+	public static FP24 miller(FP24[] r)
+	{
+		FP24 res=new FP24(1);
+		for (int i=CONFIG_CURVE.ATE_BITS-1; i>=1; i--)
+		{
+			res.sqr();
+			res.ssmul(r[i]); 
+		}
+
+		if (CONFIG_CURVE.SIGN_OF_X==CONFIG_CURVE.NEGATIVEX)
+			res.conj();
+		res.ssmul(r[0]);
+
+		return res;
+	}
+
+/* Accumulate another set of line functions for n-pairing */
+	public static void another(FP24[] r,ECP4 P1,ECP Q1)
+	{
+		FP2 f;
+		BIG n=new BIG(0);
+		BIG n3=new BIG(0);
+		FP24 lv,lv2;
+		int bt;
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+		ECP4 P=new ECP4(P1);
+		ECP Q=new ECP(Q1);
+
+		P.affine();
+		Q.affine();
+
+		FP Qx=new FP(Q.getx());
+		FP Qy=new FP(Q.gety());
+
+		ECP4 A=new ECP4();
+		A.copy(P);
+
+		ECP4 MP=new ECP4();
+		MP.copy(P); MP.neg();
+
+		int nb=lbits(n3,n);
+
+		for (int i=nb-2;i>=1;i--)
+		{
+			lv=line(A,A,Qx,Qy);
+
+			bt=n3.bit(i)-n.bit(i); 
+			if (bt==1)
+			{
+				lv2=line(A,P,Qx,Qy);
+				lv.smul(lv2);
+			}
+			if (bt==-1)
+			{
+				lv2=line(A,MP,Qx,Qy);
+				lv.smul(lv2);
+			}
+			r[i].ssmul(lv);
+		}
+
 	}
 
 /* Optimal R-ate pairing */
 	public static FP24 ate(ECP4 P1,ECP Q1)
 	{
 		FP2 f;
-		BIG x=new BIG(ROM.CURVE_Bnx);
-		BIG n=new BIG(x);
-		FP24 lv;
+		BIG n=new BIG(0);
+		BIG n3=new BIG(0);
+		FP24 lv,lv2;
 		int bt;
 		
 		ECP4 P=new ECP4(P1);
@@ -142,10 +227,6 @@
 		P.affine();
 		Q.affine();
 
-		BIG n3=new BIG(n);
-		n3.pmul(3);
-		n3.norm();
-
 		FP Qx=new FP(Q.getx());
 		FP Qy=new FP(Q.gety());
 
@@ -156,25 +237,25 @@
 		ECP4 MP=new ECP4();
 		MP.copy(P); MP.neg();
 
-		int nb=n3.nbits();
+		int nb=lbits(n3,n);
 
 		for (int i=nb-2;i>=1;i--)
 		{
 			r.sqr();
 			lv=line(A,A,Qx,Qy);
-			r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
 
 			bt=n3.bit(i)-n.bit(i); 
 			if (bt==1)
 			{
-				lv=line(A,P,Qx,Qy);
-				r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
+				lv2=line(A,P,Qx,Qy);
+				lv.smul(lv2);
 			}
 			if (bt==-1)
 			{
-				lv=line(A,MP,Qx,Qy);
-				r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
+				lv2=line(A,MP,Qx,Qy);
+				lv.smul(lv2);
 			}
+			r.ssmul(lv);
 		}
 
 		if (CONFIG_CURVE.SIGN_OF_X==CONFIG_CURVE.NEGATIVEX)
@@ -189,9 +270,9 @@
 	public static FP24 ate2(ECP4 P1,ECP Q1,ECP4 R1,ECP S1)
 	{
 		FP2 f;
-		BIG x=new BIG(ROM.CURVE_Bnx);
-		BIG n=new BIG(x);
-		FP24 lv;
+		BIG n=new BIG(0);
+		BIG n3=new BIG(0);
+		FP24 lv,lv2;
 		int bt;
 
 		ECP4 P=new ECP4(P1);
@@ -206,11 +287,6 @@
 		R.affine();
 		S.affine();
 
-
-		BIG n3=new BIG(n);
-		n3.pmul(3);
-		n3.norm();
-
 		FP Qx=new FP(Q.getx());
 		FP Qy=new FP(Q.gety());
 		FP Sx=new FP(S.getx());
@@ -229,31 +305,30 @@
 		MR.copy(R); MR.neg();
 
 
-		int nb=n3.nbits();
+		int nb=lbits(n3,n);
 
 		for (int i=nb-2;i>=1;i--)
 		{
 			r.sqr();
 			lv=line(A,A,Qx,Qy);
-			r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
+			lv2=line(B,B,Sx,Sy);
+			lv.smul(lv2);
+			r.ssmul(lv);
 
-			lv=line(B,B,Sx,Sy);
-			r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
-
-			bt=n3.bit(i)-n.bit(i); // bt=n.bit(i);
+			bt=n3.bit(i)-n.bit(i);
 			if (bt==1)
 			{
 				lv=line(A,P,Qx,Qy);
-				r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
-				lv=line(B,R,Sx,Sy);
-				r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
+				lv2=line(B,R,Sx,Sy);
+				lv.smul(lv2);
+				r.ssmul(lv);
 			}
 			if (bt==-1)
 			{
 				lv=line(A,MP,Qx,Qy);
-				r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
-				lv=line(B,MR,Sx,Sy);
-				r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
+				lv2=line(B,MR,Sx,Sy);
+				lv.smul(lv2);
+				r.ssmul(lv);
 			}
 		}
 
diff --git a/version3/java/PAIR256.java b/version3/java/PAIR256.java
index 33c5ceb..d327975 100644
--- a/version3/java/PAIR256.java
+++ b/version3/java/PAIR256.java
@@ -123,16 +123,102 @@
 			}
 			A.add(B);
 		}
-		return new FP48(a,b,c);
+		FP48 r=new FP48(a,b,c);
+		r.settype(FP48.SPARSE);
+		return r;
 	}
 
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+	public static int lbits(BIG n3,BIG n)
+	{
+		n.copy(new BIG(ROM.CURVE_Bnx));
+		n3.copy(n);
+		n3.pmul(3);
+		n3.norm();
+		return n3.nbits();
+	}
+
+/* prepare for multi-pairing */
+	public static FP48[] initmp()
+	{
+		FP48[] r=new FP48[CONFIG_CURVE.ATE_BITS];
+		for (int i=CONFIG_CURVE.ATE_BITS-1; i>=0; i--)
+			r[i]=new FP48(1);
+		return r;
+	}
+
+/* basic Miller loop */
+	public static FP48 miller(FP48[] r)
+	{
+		FP48 res=new FP48(1);
+		for (int i=CONFIG_CURVE.ATE_BITS-1; i>=1; i--)
+		{
+			res.sqr();
+			res.ssmul(r[i]); 
+		}
+
+		if (CONFIG_CURVE.SIGN_OF_X==CONFIG_CURVE.NEGATIVEX)
+			res.conj();
+		res.ssmul(r[0]);
+
+		return res;
+	}
+
+/* Accumulate another set of line functions for n-pairing */
+	public static void another(FP48[] r,ECP8 P1,ECP Q1)
+	{
+		FP2 f;
+		BIG n=new BIG(0);
+		BIG n3=new BIG(0);
+		FP48 lv,lv2;
+		int bt;
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+		ECP8 P=new ECP8(P1);
+		ECP Q=new ECP(Q1);
+
+		P.affine();
+		Q.affine();
+
+		FP Qx=new FP(Q.getx());
+		FP Qy=new FP(Q.gety());
+
+		ECP8 A=new ECP8();
+		A.copy(P);
+
+		ECP8 MP=new ECP8();
+		MP.copy(P); MP.neg();
+
+		int nb=lbits(n3,n);
+
+		for (int i=nb-2;i>=1;i--)
+		{
+			lv=line(A,A,Qx,Qy);
+
+			bt=n3.bit(i)-n.bit(i); 
+			if (bt==1)
+			{
+				lv2=line(A,P,Qx,Qy);
+				lv.smul(lv2);
+			}
+			if (bt==-1)
+			{
+				lv2=line(A,MP,Qx,Qy);
+				lv.smul(lv2);
+			}
+			r[i].ssmul(lv);
+		}
+
+	}
+
+
 /* Optimal R-ate pairing */
 	public static FP48 ate(ECP8 P1,ECP Q1)
 	{
 		FP2 f;
-		BIG x=new BIG(ROM.CURVE_Bnx);
-		BIG n=new BIG(x);
-		FP48 lv;
+		BIG n=new BIG(0);
+		BIG n3=new BIG(0);
+		FP48 lv,lv2;
 		int bt;
 		
 		ECP8 P=new ECP8(P1);
@@ -141,11 +227,6 @@
 		P.affine();
 		Q.affine();
 
-
-		BIG n3=new BIG(n);
-		n3.pmul(3);
-		n3.norm();
-
 		FP Qx=new FP(Q.getx());
 		FP Qy=new FP(Q.gety());
 
@@ -156,25 +237,25 @@
 		ECP8 MP=new ECP8();
 		MP.copy(P); MP.neg();
 
-		int nb=n3.nbits();
+		int nb=lbits(n3,n);
 
 		for (int i=nb-2;i>=1;i--)
 		{
 			r.sqr();
 			lv=line(A,A,Qx,Qy);
-			r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
 
 			bt=n3.bit(i)-n.bit(i); 
 			if (bt==1)
 			{
-				lv=line(A,P,Qx,Qy);
-				r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
+				lv2=line(A,P,Qx,Qy);
+				lv.smul(lv2);
 			}
 			if (bt==-1)
 			{
-				lv=line(A,MP,Qx,Qy);
-				r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
+				lv2=line(A,MP,Qx,Qy);
+				lv.smul(lv2);
 			}
+			r.ssmul(lv);
 		}
 
 		if (CONFIG_CURVE.SIGN_OF_X==CONFIG_CURVE.NEGATIVEX)
@@ -189,9 +270,9 @@
 	public static FP48 ate2(ECP8 P1,ECP Q1,ECP8 R1,ECP S1)
 	{
 		FP2 f;
-		BIG x=new BIG(ROM.CURVE_Bnx);
-		BIG n=new BIG(x);
-		FP48 lv;
+		BIG n=new BIG(0);
+		BIG n3=new BIG(0);
+		FP48 lv,lv2;
 		int bt;
 
 		ECP8 P=new ECP8(P1);
@@ -206,10 +287,6 @@
 		R.affine();
 		S.affine();
 
-		BIG n3=new BIG(n);
-		n3.pmul(3);
-		n3.norm();
-
 		FP Qx=new FP(Q.getx());
 		FP Qy=new FP(Q.gety());
 		FP Sx=new FP(S.getx());
@@ -227,32 +304,30 @@
 		ECP8 MR=new ECP8();
 		MR.copy(R); MR.neg();
 
-
-		int nb=n3.nbits();
+		int nb=lbits(n3,n);
 
 		for (int i=nb-2;i>=1;i--)
 		{
 			r.sqr();
 			lv=line(A,A,Qx,Qy);
-			r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
-
-			lv=line(B,B,Sx,Sy);
-			r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
+			lv2=line(B,B,Sx,Sy);
+			lv.smul(lv2);
+			r.ssmul(lv);
 
 			bt=n3.bit(i)-n.bit(i);
 			if (bt==1)
 			{
 				lv=line(A,P,Qx,Qy);
-				r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
-				lv=line(B,R,Sx,Sy);
-				r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
+				lv2=line(B,R,Sx,Sy);
+				lv.smul(lv2);
+				r.ssmul(lv);
 			}
 			if (bt==-1)
 			{
 				lv=line(A,MP,Qx,Qy);
-				r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
-				lv=line(B,MR,Sx,Sy);
-				r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST);
+				lv2=line(B,MR,Sx,Sy);
+				lv.smul(lv2);
+				r.ssmul(lv);
 			}
 		}
 
diff --git a/version3/java/config32.py b/version3/java/config32.py
index c599d59..847c694 100644
--- a/version3/java/config32.py
+++ b/version3/java/config32.py
@@ -77,7 +77,7 @@
 	replace(fpath+"CONFIG_FF.java","@ML@",ml);
 
 
-def curveset(tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,cs) :
+def curveset(tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,ab,cs) :
 	global deltext,slashtext,copytext
 	global cptr,chosen
 
@@ -133,6 +133,7 @@
 
 	replace(fpath+"CONFIG_CURVE.java","@ST@",stw)
 	replace(fpath+"CONFIG_CURVE.java","@SX@",sx)
+	replace(fpath+"CONFIG_CURVE.java","@AB@",ab)
 
 	if cs == "128" :
 		replace(fpath+"CONFIG_CURVE.java","@HT@","32")
@@ -297,91 +298,91 @@
 
 
 	if x==1:
-		curveset("ED25519","32","29","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","128")
+		curveset("ED25519","32","29","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==2:
-		curveset("C25519","32","29","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","NOT","NOT","128")
+		curveset("C25519","32","29","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==3:
-		curveset("NIST256","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")   # Change to 28
+		curveset("NIST256","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")   # Change to 28
 		curve_selected=True
 	if x==4:
-		curveset("BRAINPOOL","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128") # Change to 28
+		curveset("BRAINPOOL","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128") # Change to 28
 		curve_selected=True
 	if x==5:
-		curveset("ANSSI","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128") # Change to 28
+		curveset("ANSSI","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128") # Change to 28
 		curve_selected=True
 
 	if x==6:
-		curveset("HIFIVE","42","29","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","192")
+		curveset("HIFIVE","42","29","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==7:
-		curveset("GOLDILOCKS","56","29","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","NOT","NOT","256")
+		curveset("GOLDILOCKS","56","29","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 	if x==8:
-		curveset("NIST384","48","29","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","192")  # change to 29
+		curveset("NIST384","48","29","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","192")  # change to 29
 		curve_selected=True
 	if x==9:
-		curveset("C41417","52","29","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","256")
+		curveset("C41417","52","29","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 	if x==10:
-		curveset("NIST521","66","28","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","256")
+		curveset("NIST521","66","28","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 
 	if x==11:
-		curveset("NUMS256W","32","28","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("NUMS256W","32","28","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==12:
-		curveset("NUMS256E","32","29","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","128")
+		curveset("NUMS256E","32","29","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==13:
-		curveset("NUMS384W","48","29","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","192")
+		curveset("NUMS384W","48","29","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==14:
-		curveset("NUMS384E","48","29","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","192")
+		curveset("NUMS384E","48","29","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==15:
-		curveset("NUMS512W","64","29","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","256")
+		curveset("NUMS512W","64","29","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 	if x==16:
-		curveset("NUMS512E","64","29","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","256")
+		curveset("NUMS512E","64","29","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 	if x==17:
-		curveset("SECP256K1","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")   # Change to 28
+		curveset("SECP256K1","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")   # Change to 28
 		curve_selected=True
 
 	if x==18:
-		curveset("BN254","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")  
+		curveset("BN254","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")  
 		pfcurve_selected=True
 	if x==19:
-		curveset("BN254CX","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")  
+		curveset("BN254CX","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")  
 		pfcurve_selected=True
 	if x==20:
-		curveset("BLS383","48","29","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","128") 
+		curveset("BLS383","48","29","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","65","128") 
 		pfcurve_selected=True
 
 	if x==21:
-		curveset("BLS381","48","29","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128") 
+		curveset("BLS381","48","29","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","65","128") 
 		pfcurve_selected=True
 
 
 	if x==22:
-		curveset("FP256BN","32","28","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","128") 
+		curveset("FP256BN","32","28","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","66","128") 
 		pfcurve_selected=True
 	if x==23:
-		curveset("FP512BN","64","29","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","128")
+		curveset("FP512BN","64","29","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","130","128")
 		pfcurve_selected=True
 # https://eprint.iacr.org/2017/334.pdf
 	if x==24:
-		curveset("BLS461","58","28","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128")
+		curveset("BLS461","58","28","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","78","128")
 		pfcurve_selected=True
 
 
 	if x==25:
-		curveset("BLS24","60","29","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","192")
+		curveset("BLS24","60","29","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","49","192")
 		pfcurve_selected=True
 	if x==26:
-		curveset("BLS48","70","29","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","256")
+		curveset("BLS48","70","29","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","32","256")
 		pfcurve_selected=True
 
 
diff --git a/version3/java/config64.py b/version3/java/config64.py
index 96ffb76..7a8acb6 100644
--- a/version3/java/config64.py
+++ b/version3/java/config64.py
@@ -78,7 +78,7 @@
 	replace(fpath+"CONFIG_FF.java","@ML@",ml);
 
 
-def curveset(tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,cs) :
+def curveset(tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,ab,cs) :
 	global deltext,slashtext,copytext
 	global cptr,chosen
 
@@ -133,6 +133,7 @@
 	replace(fpath+"CONFIG_CURVE.java","@PF@",pf)
 	replace(fpath+"CONFIG_CURVE.java","@ST@",stw)
 	replace(fpath+"CONFIG_CURVE.java","@SX@",sx)
+	replace(fpath+"CONFIG_CURVE.java","@AB@",ab)
 
 	if cs == "128" :
 		replace(fpath+"CONFIG_CURVE.java","@HT@","32")
@@ -298,90 +299,90 @@
 
 
 	if x==1:
-		curveset("ED25519","32","56","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","128")
+		curveset("ED25519","32","56","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==2:
-		curveset("C25519","32","56","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","NOT","NOT","128")
+		curveset("C25519","32","56","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==3:
-		curveset("NIST256","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("NIST256","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==4:
-		curveset("BRAINPOOL","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("BRAINPOOL","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==5:
-		curveset("ANSSI","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("ANSSI","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 
 	if x==6:
-		curveset("HIFIVE","42","60","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","192")
+		curveset("HIFIVE","42","60","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==7:
-		curveset("GOLDILOCKS","56","58","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","NOT","NOT","256")   # change to 58
+		curveset("GOLDILOCKS","56","58","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","256")   # change to 58
 		curve_selected=True
 	if x==8:
-		curveset("NIST384","48","56","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","192")
+		curveset("NIST384","48","56","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==9:
-		curveset("C41417","52","60","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","256")
+		curveset("C41417","52","60","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 	if x==10:
-		curveset("NIST521","66","60","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","256")
+		curveset("NIST521","66","60","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 
 	if x==11:
-		curveset("NUMS256W","32","56","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("NUMS256W","32","56","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==12:
-		curveset("NUMS256E","32","56","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","128")
+		curveset("NUMS256E","32","56","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==13:
-		curveset("NUMS384W","48","58","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","192")
+		curveset("NUMS384W","48","58","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==14:
-		curveset("NUMS384E","48","56","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","192")
+		curveset("NUMS384E","48","56","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==15:
-		curveset("NUMS512W","64","60","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","256")
+		curveset("NUMS512W","64","60","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 	if x==16:
-		curveset("NUMS512E","64","56","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","256")
+		curveset("NUMS512E","64","56","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 	if x==17:
-		curveset("SECP256K1","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("SECP256K1","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 
 	if x==18:
-		curveset("BN254","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("BN254","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==19:
-		curveset("BN254CX","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("BN254CX","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==20:
-		curveset("BLS383","48","58","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","128")  # change to 58
+		curveset("BLS383","48","58","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","65","128")  # change to 58
 		pfcurve_selected=True
 
 	if x==21:
-		curveset("BLS381","48","58","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128")  # change to 58
+		curveset("BLS381","48","58","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","65","128")  # change to 58
 		pfcurve_selected=True
 
 	if x==22:
-		curveset("FP256BN","32","56","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","128")  
+		curveset("FP256BN","32","56","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","66","128")  
 		pfcurve_selected=True
 	if x==23:
-		curveset("FP512BN","64","60","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","128")
+		curveset("FP512BN","64","60","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","130","128")
 		pfcurve_selected=True
 # https://eprint.iacr.org/2017/334.pdf
 	if x==24:
-		curveset("BLS461","58","60","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128")
+		curveset("BLS461","58","60","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","78","128")
 		pfcurve_selected=True
 
 	if x==25:
-		curveset("BLS24","60","56","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","192")
+		curveset("BLS24","60","56","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","49","192")
 		pfcurve_selected=True
 
 	if x==26:
-		curveset("BLS48","70","58","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","256")
+		curveset("BLS48","70","58","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","32","256")
 		pfcurve_selected=True
 
 
diff --git a/version3/js/bls.js b/version3/js/bls.js
index dec35d4..6346b1d 100644
--- a/version3/js/bls.js
+++ b/version3/js/bls.js
@@ -100,10 +100,20 @@
 			var G=ctx.ECP2.generator();
 			var PK=ctx.ECP2.fromBytes(W);
 			D.neg();
-			var v=ctx.PAIR.ate2(G,D,PK,HM);
+
+// Use new multi-pairing mechanism 
+			var r=ctx.PAIR.initmp();
+			ctx.PAIR.another(r,G,D);
+			ctx.PAIR.another(r,PK,HM);
+			var v=ctx.PAIR.miller(r);
+
+//.. or alternatively
+//			var v=ctx.PAIR.ate2(G,D,PK,HM);
+
+			v=ctx.PAIR.fexp(v);
 			if (v.isunity())
 				return this.BLS_OK;
-			return this.BLS_OK;
+			return this.BLS_FAIL;
 		}
     };
 
diff --git a/version3/js/bls192.js b/version3/js/bls192.js
index 6712348..5bb92c7 100644
--- a/version3/js/bls192.js
+++ b/version3/js/bls192.js
@@ -100,10 +100,19 @@
 			var G=ctx.ECP4.generator();
 			var PK=ctx.ECP4.fromBytes(W);
 			D.neg();
-			var v=ctx.PAIR192.ate2(G,D,PK,HM);
+
+// Use new multi-pairing mechanism 
+			var r=ctx.PAIR192.initmp();
+			ctx.PAIR192.another(r,G,D);
+			ctx.PAIR192.another(r,PK,HM);
+			var v=ctx.PAIR192.miller(r);
+
+//.. or alternatively
+//			var v=ctx.PAIR192.ate2(G,D,PK,HM);
+			v=ctx.PAIR192.fexp(v);
 			if (v.isunity())
 				return this.BLS_OK;
-			return this.BLS_OK;
+			return this.BLS_FAIL;
 		}
     };
 
diff --git a/version3/js/bls256.js b/version3/js/bls256.js
index fcf507c..9fa8f36 100644
--- a/version3/js/bls256.js
+++ b/version3/js/bls256.js
@@ -100,10 +100,20 @@
 			var G=ctx.ECP8.generator();
 			var PK=ctx.ECP8.fromBytes(W);
 			D.neg();
-			var v=ctx.PAIR256.ate2(G,D,PK,HM);
+
+// Use new multi-pairing mechanism 
+			var r=ctx.PAIR256.initmp();
+			ctx.PAIR256.another(r,G,D);
+			ctx.PAIR256.another(r,PK,HM);
+			var v=ctx.PAIR256.miller(r);
+
+//.. or alternatively
+//			var v=ctx.PAIR256.ate2(G,D,PK,HM);
+
+			v=ctx.PAIR256.fexp(v);
 			if (v.isunity())
 				return this.BLS_OK;
-			return this.BLS_OK;
+			return this.BLS_FAIL;
 		}
     };
 
diff --git a/version3/js/ctx.js b/version3/js/ctx.js
index 4588eec..d8cdbb0 100644
--- a/version3/js/ctx.js
+++ b/version3/js/ctx.js
@@ -38,6 +38,7 @@
             "@PF": 0,       /* Pairing Friendly */
             "@ST": 0,       /* Sextic Twist Type */
             "@SX": 0,       /* Sign of x parameter */
+            "@AB": 0,       /* ATE parameter size */
             "@HT": 32,      /* Hash output size */
 			"@SH": 9,       /* Maximum field excess */
             "@AK": 16       /* AES key size */
@@ -56,6 +57,7 @@
             "@PF": 0,
             "@ST": 0,
             "@SX": 0,
+            "@AB": 0,
             "@HT": 32,
 			"@SH": 9, 
             "@AK": 16
@@ -75,6 +77,7 @@
             "@PF": 0,
             "@ST": 0,
             "@SX": 0,
+            "@AB": 0,
             "@HT": 32,
 			"@SH": 8, 
             "@AK": 16
@@ -93,6 +96,7 @@
             "@PF": 0,
             "@ST": 0,
             "@SX": 0,
+            "@AB": 0,
             "@HT": 32,
 			"@SH": 8, 
             "@AK": 16
@@ -111,6 +115,7 @@
             "@PF": 0,
             "@ST": 0,
             "@SX": 0,
+            "@AB": 0,
             "@HT": 48,
 			"@SH": 7, 
             "@AK": 24
@@ -129,6 +134,7 @@
             "@PF": 0,
             "@ST": 0,
             "@SX": 0,
+            "@AB": 0,
             "@HT": 32,
 			"@SH": 8, 
             "@AK": 16
@@ -147,6 +153,7 @@
             "@PF": 0,
             "@ST": 0,
             "@SX": 0,
+            "@AB": 0,
             "@HT": 32,
 			"@SH": 8, 
             "@AK": 16
@@ -165,6 +172,7 @@
             "@PF": 0,
             "@ST": 0,
             "@SX": 0,
+            "@AB": 0,
             "@HT": 48,
 			"@SH": 9, 
             "@AK": 24
@@ -183,6 +191,7 @@
             "@PF": 0,
             "@ST": 0,
             "@SX": 0,
+            "@AB": 0,
             "@HT": 64,
 			"@SH": 11, 
             "@AK": 32
@@ -201,6 +210,7 @@
             "@PF": 0,
             "@ST": 0,
             "@SX": 0,
+            "@AB": 0,
             "@HT": 64,
 			"@SH": 4, 
             "@AK": 32
@@ -219,6 +229,7 @@
             "@PF": 0,
             "@ST": 0,
             "@SX": 0,
+            "@AB": 0,
             "@HT": 64,
 			"@SH": 8, 
             "@AK": 32
@@ -237,6 +248,7 @@
             "@PF": 0,
             "@ST": 0,
             "@SX": 0,
+            "@AB": 0,
             "@HT": 32,
 			"@SH": 8, 
             "@AK": 16
@@ -255,6 +267,7 @@
             "@PF": 0,
             "@ST": 0,
             "@SX": 0,
+            "@AB": 0,
             "@HT": 32,
 			"@SH": 8, 
             "@AK": 16
@@ -273,6 +286,7 @@
             "@PF": 0,
             "@ST": 0,
             "@SX": 0,
+            "@AB": 0,
             "@HT": 48,
 			"@SH": 7, 
             "@AK": 24
@@ -291,6 +305,7 @@
             "@PF": 0,
             "@ST": 0,
             "@SX": 0,
+            "@AB": 0,
             "@HT": 48,
 			"@SH": 7, 
             "@AK": 24
@@ -309,6 +324,7 @@
             "@PF": 0,
             "@ST": 0,
             "@SX": 0,
+            "@AB": 0,
             "@HT": 64,
 			"@SH": 11, 
             "@AK": 32
@@ -327,6 +343,7 @@
             "@PF": 0,
             "@ST": 0,
             "@SX": 0,
+            "@AB": 0,
             "@HT": 64,
 			"@SH": 11, 
             "@AK": 32
@@ -345,6 +362,7 @@
             "@PF": 1,
             "@ST": 1,
             "@SX": 1,
+            "@AB": 66,
             "@HT": 32,
 			"@SH": 8, 
             "@AK": 16
@@ -363,6 +381,7 @@
             "@PF": 1,
             "@ST": 1,
             "@SX": 0,
+            "@AB": 130,
             "@HT": 32,
 			"@SH": 11, 
             "@AK": 16
@@ -381,6 +400,7 @@
             "@PF": 1,
             "@ST": 0,
             "@SX": 1,
+            "@AB": 66,
             "@HT": 32,
 			"@SH": 10, 
             "@AK": 16
@@ -399,6 +419,7 @@
             "@PF": 1,
             "@ST": 0,
             "@SX": 1,
+            "@AB": 66,
             "@HT": 32,
 			"@SH": 10, 
             "@AK": 16
@@ -417,6 +438,7 @@
             "@PF": 2,
             "@ST": 1,
             "@SX": 0,
+            "@AB": 65,
             "@HT": 32,
 			"@SH": 8, 
             "@AK": 16
@@ -435,6 +457,7 @@
             "@PF": 3,
             "@ST": 1,
             "@SX": 0,
+            "@AB": 49,
             "@HT": 48,
 			"@SH": 4, 
             "@AK": 24
@@ -453,6 +476,7 @@
             "@PF": 4,
             "@ST": 1,
             "@SX": 0,
+            "@AB": 32,
             "@HT": 64,
 			"@SH": 11, 
             "@AK": 32
@@ -471,6 +495,7 @@
             "@PF": 2,
             "@ST": 1,
             "@SX": 1,
+            "@AB": 65,
             "@HT": 32,
 			"@SH": 10, 
             "@AK": 16
@@ -489,6 +514,7 @@
             "@PF": 2,
             "@ST": 1,
             "@SX": 1,
+            "@AB": 78,
             "@HT": 32,
 			"@SH": 11, 
             "@AK": 16
@@ -543,7 +569,6 @@
     prepareModule("HASH512");
     prepareModule("SHA3");
     prepareModule("RAND");
-    //prepareModule("NewHope");
     prepareModule("NHS");
 
     if (typeof input_parameter === "undefined") {
diff --git a/version3/js/ecp.js b/version3/js/ecp.js
index 53d4605..c593e00 100644
--- a/version3/js/ecp.js
+++ b/version3/js/ecp.js
@@ -48,6 +48,7 @@
     ECP.CURVE_PAIRING_TYPE = ctx.config["@PF"];
     ECP.SEXTIC_TWIST = ctx.config["@ST"];
     ECP.SIGN_OF_X = ctx.config["@SX"];
+    ECP.ATE_BITS = ctx.config["@AB"];
 
     ECP.HASH_TYPE = ctx.config["@HT"];
     ECP.AESKEY = ctx.config["@AK"];
diff --git a/version3/js/fp.js b/version3/js/fp.js
index 588e551..2b939a2 100644
--- a/version3/js/fp.js
+++ b/version3/js/fp.js
@@ -39,6 +39,12 @@
     FP.GENERALISED_MERSENNE = 2;
     FP.MONTGOMERY_FRIENDLY = 3;
 
+	FP.ZERO = 0;
+	FP.ONE = 1;
+	FP.SPARSER = 2;
+	FP.SPARSE = 3;
+	FP.DENSE= 4;
+
     FP.MODBITS = ctx.config["@NBT"];
     FP.MOD8 = ctx.config["@M8"];
     FP.MODTYPE = ctx.config["@MT"];
diff --git a/version3/js/fp12.js b/version3/js/fp12.js
index f87c43e..e013e42 100644
--- a/version3/js/fp12.js
+++ b/version3/js/fp12.js
@@ -26,15 +26,27 @@
 
     /* general purpose constructor */
     var FP12 = function(d, e, f) {
-        if (d instanceof FP12) {
-            this.a = new ctx.FP4(d.a);
-            this.b = new ctx.FP4(d.b);
-            this.c = new ctx.FP4(d.c);
-        } else {
-            this.a = new ctx.FP4(d);
-            this.b = new ctx.FP4(e);
-            this.c = new ctx.FP4(f);
-        }
+		if (!isNaN(d))
+		{
+			this.a = new ctx.FP4(d);
+			this.b = new ctx.FP4(0);
+			this.c = new ctx.FP4(0);
+			if (d==1) this.stype=ctx.FP.ONE;
+			else this.stype=ctx.FP.SPARSER;
+		}
+		else
+		{
+			if (d instanceof FP12) {
+				this.a = new ctx.FP4(d.a);
+				this.b = new ctx.FP4(d.b);
+				this.c = new ctx.FP4(d.c);
+			} else {
+				this.a = new ctx.FP4(d);
+				this.b = new ctx.FP4(e);
+				this.c = new ctx.FP4(f);
+			}
+			this.stype=ctx.FP.DENSE;
+		}
     };
 
     FP12.prototype = {
@@ -69,6 +81,8 @@
             this.a.cmove(g.a, d);
             this.b.cmove(g.b, d);
             this.c.cmove(g.c, d);
+			d=~(d-1);
+			this.stype^=(this.stype^g.stype)&d;
         },
 
 
@@ -95,6 +109,14 @@
             this.cmove(invf, (m & 1));
         },
 
+		settype: function(w) {
+			this.stype=w;
+		},
+
+		gettype: function() {
+			return this.stype;
+		},
+
         /* extract a from this */
         geta: function() {
             return this.a;
@@ -120,6 +142,7 @@
             this.a.copy(x.a);
             this.b.copy(x.b);
             this.c.copy(x.c);
+			this.stype=x.stype;
         },
 
         /* set this=1 */
@@ -127,6 +150,7 @@
             this.a.one();
             this.b.zero();
             this.c.zero();
+			this.stype=ctx.FP.ONE;
         },
 
         /* this=conj(this) */
@@ -141,6 +165,7 @@
             this.a.copy(d);
             this.b.copy(e);
             this.c.copy(f);
+			this.stype=ctx.FP.DENSE;
         },
 
         /* set this from one ctx.FP4 */
@@ -148,6 +173,7 @@
             this.a.copy(d);
             this.b.zero();
             this.c.zero();
+			this.stype=ctx.FP.SPARSER
         },
 
         /* Granger-Scott Unitary Squaring */
@@ -185,11 +211,15 @@
             this.c.add(this.c);
             this.b.add(B);
             this.c.add(C);
+			this.stype=ctx.FP.DENSE;
             this.reduce();
         },
 
         /* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
         sqr: function() {
+			if (this.stype==ctx.FP.ONE)
+				return;
+
             var A = new ctx.FP4(this.a), 
                 B = new ctx.FP4(this.b), 
                 C = new ctx.FP4(this.c), 
@@ -220,7 +250,10 @@
             this.b.copy(C);
             this.b.add(D);
             this.c.add(A);
-
+			if (this.stype==ctx.FP.SPARSER)
+				this.stype=ctx.FP.SPARSE;
+			else
+				this.stype=ctx.FP.DENSE;
             this.norm();
         },
 
@@ -291,111 +324,317 @@
             z3.times_i();
             this.a.copy(z0);
             this.a.add(z3);
-
+			this.stype=ctx.FP.DENSE;
             this.norm();
         },
 
-        /* Special case this*=y that arises from special form of ATE pairing line function */
-        smul: function(y, twist) {
-            var z0, z1, z2, z3, t0, t1;
+/* FP12 multiplication w=w*y */
+/* catering for special case that arises from special form of ATE pairing line function */
+/* w and y are both sparser line functions - cost = 6m */ 
+		smul: function(y) {
+			if (ctx.ECP.SEXTIC_TWIST==ctx.ECP.D_TYPE)
+			{	
+				var w1=new ctx.FP2(this.a.geta());
+				var w2=new ctx.FP2(this.a.getb());
+				var w3=new ctx.FP2(this.b.geta());
 
-            if (twist == ctx.ECP.D_TYPE) {
+				w1.mul(y.a.geta());
+				w2.mul(y.a.getb());
+				w3.mul(y.b.geta());
 
-                z0 = new ctx.FP4(this.a); 
-                z2 = new ctx.FP4(this.b); 
-                z3 = new ctx.FP4(this.b); 
-                t0 = new ctx.FP4(0);
-                t1 = new ctx.FP4(y.a); 
+				var ta=new ctx.FP2(this.a.geta());
+				var tb=new ctx.FP2(y.a.geta());
+				ta.add(this.a.getb()); ta.norm();
+				tb.add(y.a.getb()); tb.norm();
+				var tc=new ctx.FP2(ta);
+				tc.mul(tb);
+				var t=new ctx.FP2(w1);
+				t.add(w2);
+				t.neg();
+				tc.add(t);
 
-                z0.mul(y.a);
-                z2.pmul(y.b.real());
-                this.b.add(this.a);
-                t1.real().add(y.b.real());
+				ta.copy(this.a.geta()); ta.add(this.b.geta()); ta.norm();
+				tb.copy(y.a.geta()); tb.add(y.b.geta()); tb.norm();
+				var td=new ctx.FP2(ta);
+				td.mul(tb);
+				t.copy(w1);
+				t.add(w3);
+				t.neg();
+				td.add(t);
 
-                this.b.norm();
-                t1.norm();
+				ta.copy(this.a.getb()); ta.add(this.b.geta()); ta.norm();
+				tb.copy(y.a.getb()); tb.add(y.b.geta()); tb.norm();
+				var te=new ctx.FP2(ta);
+				te.mul(tb);
+				t.copy(w2);
+				t.add(w3);
+				t.neg();
+				te.add(t);
 
-                this.b.mul(t1);
-                z3.add(this.c);
-                z3.norm();
-                z3.pmul(y.b.real());
+				w2.mul_ip();
+				w1.add(w2);
 
-                t0.copy(z0);
-                t0.neg();
-                t1.copy(z2);
-                t1.neg();
+				this.a.geta().copy(w1); this.a.getb().copy(tc);
+				this.b.geta().copy(td); this.b.getb().copy(te);
+				this.c.geta().copy(w3); this.c.getb().zero();
 
-                this.b.add(t0);
+				this.a.norm();
+				this.b.norm();
 
-                this.b.add(t1);
-                z3.add(t1);
-                z2.add(t0);
+			} else {
+				var w1=new ctx.FP2(this.a.geta());
+				var w2=new ctx.FP2(this.a.getb());
+				var w3=new ctx.FP2(this.c.getb());
 
-                t0.copy(this.a);
-                t0.add(this.c);
-                t0.norm();
-                t0.mul(y.a);
-                this.c.copy(z2);
-                this.c.add(t0);
+				w1.mul(y.a.geta());
+				w2.mul(y.a.getb());
+				w3.mul(y.c.getb());
 
-                z3.times_i();
-                this.a.copy(z0);
-                this.a.add(z3);
-            }
+				var ta=new ctx.FP2(this.a.geta());
+				var tb=new ctx.FP2(y.a.geta());
+				ta.add(this.a.getb()); ta.norm();
+				tb.add(y.a.getb()); tb.norm();
+				var tc=new ctx.FP2(ta);
+				tc.mul(tb);
+				var t=new ctx.FP2(w1);
+				t.add(w2);
+				t.neg();
+				tc.add(t);
 
-            if (twist == ctx.ECP.M_TYPE) {
-                z0=new ctx.FP4(this.a);
-                z1=new ctx.FP4(0);
-                z2=new ctx.FP4(0);
-                z3=new ctx.FP4(0);
-                t0=new ctx.FP4(this.a);
-                t1=new ctx.FP4(0);
+				ta.copy(this.a.geta()); ta.add(this.c.getb()); ta.norm();
+				tb.copy(y.a.geta()); tb.add(y.c.getb()); tb.norm();
+				var td=new ctx.FP2(ta);
+				td.mul(tb);
+				t.copy(w1);
+				t.add(w3);
+				t.neg();
+				td.add(t);
 
-                z0.mul(y.a);
-                t0.add(this.b);
-                t0.norm();
+				ta.copy(this.a.getb()); ta.add(this.c.getb()); ta.norm();
+				tb.copy(y.a.getb()); tb.add(y.c.getb()); tb.norm();
+				var te=new ctx.FP2(ta);
+				te.mul(tb);
+				t.copy(w2);
+				t.add(w3);
+				t.neg();
+				te.add(t);
 
-                z1.copy(t0); z1.mul(y.a);
-                t0.copy(this.b); t0.add(this.c);
-                t0.norm();
+				w2.mul_ip();
+				w1.add(w2);
+				this.a.geta().copy(w1); this.a.getb().copy(tc);
 
-                z3.copy(t0); 
-                z3.pmul(y.c.getb());
-                z3.times_i();
+				w3.mul_ip();
+				w3.norm();
+				this.b.geta().zero(); this.b.getb().copy(w3);
 
-                t0.copy(z0); t0.neg();
+				te.norm();
+				te.mul_ip();
+				this.c.geta().copy(te);
+				this.c.getb().copy(td);
 
-                z1.add(t0);
-                this.b.copy(z1);
-                z2.copy(t0);
+				this.a.norm();
+				this.c.norm();
 
-                t0.copy(this.a); t0.add(this.c);
-                t1.copy(y.a); t1.add(y.c);
+			}
+			this.stype=ctx.FP.SPARSE;
+		},
 
-                t0.norm();
-                t1.norm();
+/* FP12 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+		ssmul: function(y) {
+			if (this.stype==ctx.FP.ONE)
+			{
+				this.copy(y);
+				return;
+			}
+			if (y.stype==ctx.FP.ONE)
+				return;
 
-                t0.mul(t1);
-                z2.add(t0);
+			if (y.stype>=ctx.FP.SPARSE)
+			{
+				var z0=new ctx.FP4(this.a);
+				var z1=new ctx.FP4(0);
+				var z2=new ctx.FP4(0);
+				var z3=new ctx.FP4(0);
+				z0.mul(y.a);
 
-                t0.copy(this.c);
+				if (ctx.ECP.SEXTIC_TWIST==ctx.ECP.M_TYPE)
+				{
+					if (y.stype==ctx.FP.SPARSE || this.stype==ctx.FP.SPARSE)
+					{
+						z2.getb().copy(this.b.getb());
+						z2.getb().mul(y.b.getb());
+						z2.geta().zero();
+						if (y.stype!=ctx.FP.SPARSE)
+						{
+							z2.geta().copy(this.b.getb());
+							z2.geta().mul(y.b.geta());
+						}
+						if (this.stype!=ctx.FP.SPARSE)
+						{
+							z2.geta().copy(this.b.geta());
+							z2.geta().mul(y.b.getb());
+						}
+						z2.times_i();
+					} else {
+						z2.copy(this.b);
+						z2.mul(y.b);
+					}
+				} else {
+					z2.copy(this.b);
+					z2.mul(y.b);
+				}
+				var t0=new ctx.FP4(this.a);
+				var t1=new ctx.FP4(y.a);
+				t0.add(this.b); t0.norm();
+				t1.add(y.b); t1.norm();
 
-                t0.pmul(y.c.getb());
-                t0.times_i();
+				z1.copy(t0); z1.mul(t1);
+				t0.copy(this.b); t0.add(this.c); t0.norm();
+				t1.copy(y.b); t1.add(y.c); t1.norm();
 
-                t1.copy(t0); t1.neg();
+				z3.copy(t0); z3.mul(t1);
 
-                this.c.copy(z2); this.c.add(t1);
-                z3.add(t1);
-                t0.times_i();
-                this.b.add(t0);
-                z3.norm();
-                z3.times_i();
-                this.a.copy(z0); this.a.add(z3);
-            }
+				t0.copy(z0); t0.neg();
+				t1.copy(z2); t1.neg();
 
-            this.norm();
-        },
+				z1.add(t0);
+				this.b.copy(z1); this.b.add(t1);
+
+				z3.add(t1);
+				z2.add(t0);
+
+				t0.copy(this.a); t0.add(this.c); t0.norm();
+				t1.copy(y.a); t1.add(y.c); t1.norm();
+	
+				t0.mul(t1);
+				z2.add(t0);
+
+				if (ctx.ECP.SEXTIC_TWIST==ctx.ECP.D_TYPE)
+				{
+					if (y.stype==ctx.FP.SPARSE || this.stype==ctx.FP.SPARSE)
+					{
+						t0.geta().copy(this.c.geta());
+						t0.geta().mul(y.c.geta());
+						t0.getb().zero();
+						if (y.stype!=ctx.FP.SPARSE)
+						{
+							t0.getb().copy(this.c.geta());
+							t0.getb().mul(y.c.getb());
+						}
+						if (this.stype!=ctx.FP.SPARSE)
+						{
+							t0.getb().copy(this.c.getb());
+							t0.getb().mul(y.c.geta());
+						}
+					} else {
+						t0.copy(this.c);
+						t0.mul(y.c);
+					}
+				} else {
+					t0.copy(this.c);
+					t0.mul(y.c);
+				}
+				t1.copy(t0); t1.neg();
+
+				this.c.copy(z2); this.c.add(t1);
+				z3.add(t1);
+				t0.times_i();
+				this.b.add(t0);
+				z3.norm();
+				z3.times_i();
+				this.a.copy(z0); this.a.add(z3);
+			} else {
+				if (this.stype==ctx.FP.SPARSER)
+				{
+					this.smul(y);
+					return;
+				}
+				if (ctx.ECP.SEXTIC_TWIST==ctx.ECP.D_TYPE)
+				{ // dense by sparser - 13m 
+					var z0=new ctx.FP4(this.a);
+					var z2=new ctx.FP4(this.b);
+					var z3=new ctx.FP4(this.b);
+					var t0=new ctx.FP4(0);
+					var t1=new ctx.FP4(y.a);
+					z0.mul(y.a);
+					z2.pmul(y.b.real());
+					this.b.add(this.a);
+					t1.real().add(y.b.real());
+
+					t1.norm();
+					this.b.norm();
+					this.b.mul(t1);
+					z3.add(this.c);
+					z3.norm();
+					z3.pmul(y.b.real());
+
+					t0.copy(z0); t0.neg();
+					t1.copy(z2); t1.neg();
+
+					this.b.add(t0);
+
+					this.b.add(t1);
+					z3.add(t1);
+					z2.add(t0);
+
+					t0.copy(this.a); t0.add(this.c); t0.norm();
+					z3.norm();
+					t0.mul(y.a);
+					this.c.copy(z2); this.c.add(t0);
+
+					z3.times_i();
+					this.a.copy(z0); this.a.add(z3);
+				}
+				if (ctx.ECP.SEXTIC_TWIST==ctx.ECP.M_TYPE)
+				{
+					var z0=new ctx.FP4(this.a);
+					var z1=new ctx.FP4(0);
+					var z2=new ctx.FP4(0);
+					var z3=new ctx.FP4(0);
+					var t0=new ctx.FP4(this.a);
+					var t1=new ctx.FP4(0);
+		
+					z0.mul(y.a);
+					t0.add(this.b); t0.norm();
+
+					z1.copy(t0); z1.mul(y.a);
+					t0.copy(this.b); t0.add(this.c);
+					t0.norm();
+
+					z3.copy(t0); 
+					z3.pmul(y.c.getb());
+					z3.times_i();
+
+					t0.copy(z0); t0.neg();
+					z1.add(t0);
+					this.b.copy(z1); 
+					z2.copy(t0);
+
+					t0.copy(this.a); t0.add(this.c); t0.norm();
+					t1.copy(y.a); t1.add(y.c); t1.norm();
+
+					t0.mul(t1);
+					z2.add(t0);
+					t0.copy(this.c); 
+			
+					t0.pmul(y.c.getb());
+					t0.times_i();
+					t1.copy(t0); t1.neg();
+
+					this.c.copy(z2); this.c.add(t1);
+					z3.add(t1);
+					t0.times_i();
+					this.b.add(t0);
+					z3.norm();
+					z3.times_i();
+					this.a.copy(z0); this.a.add(z3);
+				}	
+			}
+			this.stype=ctx.FP.DENSE;
+			this.norm();
+		},
 
         /* this=1/this */
         inverse: function() {
@@ -441,6 +680,7 @@
             this.b.mul(f3);
             this.c.copy(f2);
             this.c.mul(f3);
+			this.stype=ctx.FP.DENSE;
         },
 
         /* this=this^p, where p=Modulus, using Frobenius */
@@ -457,6 +697,7 @@
 
             this.b.pmul(f);
             this.c.pmul(f2);
+			this.stype=ctx.FP.DENSE;
         },
 
         /* trace function */
diff --git a/version3/js/fp24.js b/version3/js/fp24.js
index 28f424d..609d00e 100644
--- a/version3/js/fp24.js
+++ b/version3/js/fp24.js
@@ -26,15 +26,27 @@
 
     /* general purpose constructor */
     var FP24 = function(d, e, f) {
-        if (d instanceof FP24) {
-            this.a = new ctx.FP8(d.a);
-            this.b = new ctx.FP8(d.b);
-            this.c = new ctx.FP8(d.c);
-        } else {
-            this.a = new ctx.FP8(d);
-            this.b = new ctx.FP8(e);
-            this.c = new ctx.FP8(f);
-        }
+		if (!isNaN(d))
+		{
+			this.a = new ctx.FP8(d);
+			this.b = new ctx.FP8(0);
+			this.c = new ctx.FP8(0);
+			if (d==1) this.stype=ctx.FP.ONE;
+			else this.stype=ctx.FP.SPARSER;
+		}
+		else
+		{
+			if (d instanceof FP24) {
+				this.a = new ctx.FP8(d.a);
+				this.b = new ctx.FP8(d.b);
+				this.c = new ctx.FP8(d.c);
+			} else {
+				this.a = new ctx.FP8(d);
+				this.b = new ctx.FP8(e);
+				this.c = new ctx.FP8(f);
+			}
+			this.stype=ctx.FP.DENSE;
+		}
     };
 
     FP24.prototype = {
@@ -68,6 +80,8 @@
             this.a.cmove(g.a, d);
             this.b.cmove(g.b, d);
             this.c.cmove(g.c, d);
+			d=~(d-1);
+			this.stype^=(this.stype^g.stype)&d;
         },
 
         /* Constant time select from pre-computed table */
@@ -92,7 +106,14 @@
             invf.conj();
             this.cmove(invf, (m & 1));
         },
+		
+		settype: function(w) {
+			this.stype=w;
+		},
 
+		gettype: function() {
+			return this.stype;
+		},
         /* extract a from this */
         geta: function() {
             return this.a;
@@ -118,6 +139,7 @@
             this.a.copy(x.a);
             this.b.copy(x.b);
             this.c.copy(x.c);
+			this.stype=x.stype;
         },
 
         /* set this=1 */
@@ -125,6 +147,7 @@
             this.a.one();
             this.b.zero();
             this.c.zero();
+			this.stype=ctx.FP.ONE;
         },
 
         /* this=conj(this) */
@@ -139,6 +162,7 @@
             this.a.copy(d);
             this.b.copy(e);
             this.c.copy(f);
+			this.stype=ctx.FP.DENSE;
         },
 
         /* set this from one ctx.FP8 */
@@ -146,6 +170,7 @@
             this.a.copy(d);
             this.b.zero();
             this.c.zero();
+			this.stype=ctx.FP.SPARSER
         },
 
         /* Granger-Scott Unitary Squaring */
@@ -183,11 +208,15 @@
             this.c.add(this.c);
             this.b.add(B);
             this.c.add(C);
+			this.stype=ctx.FP.DENSE;
             this.reduce();
         },
 
         /* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
         sqr: function() {
+			if (this.stype==ctx.FP.ONE)
+				return;
+
             var A = new ctx.FP8(this.a), 
                 B = new ctx.FP8(this.b), 
                 C = new ctx.FP8(this.c), 
@@ -218,7 +247,10 @@
             this.b.copy(C);
             this.b.add(D);
             this.c.add(A);
-
+			if (this.stype==ctx.FP.SPARSER)
+				this.stype=ctx.FP.SPARSE;
+			else
+				this.stype=ctx.FP.DENSE;
             this.norm();
         },
 
@@ -290,110 +322,317 @@
             z3.times_i();
             this.a.copy(z0);
             this.a.add(z3);
-
+			this.stype=ctx.FP.DENSE;
             this.norm();
         },
 
-        /* Special case this*=y that arises from special form of ATE pairing line function */
-        smul: function(y, twist) {
-            var z0, z1, z2, z3, t0, t1;
+/* FP24 multiplication w=w*y */
+/* catering for special case that arises from special form of ATE pairing line function */
+/* w and y are both sparser line functions - cost = 6m */ 
+		smul: function(y) {
+			if (ctx.ECP.SEXTIC_TWIST==ctx.ECP.D_TYPE)
+			{	
+				var w1=new ctx.FP4(this.a.geta());
+				var w2=new ctx.FP4(this.a.getb());
+				var w3=new ctx.FP4(this.b.geta());
 
-            if (twist == ctx.ECP.D_TYPE) {
-                z0 = new ctx.FP8(this.a); 
-                z2 = new ctx.FP8(this.b); 
-                z3 = new ctx.FP8(this.b); 
-                t0 = new ctx.FP8(0);
-                t1 = new ctx.FP8(y.a); 
+				w1.mul(y.a.geta());
+				w2.mul(y.a.getb());
+				w3.mul(y.b.geta());
 
-                z0.mul(y.a);
-                z2.pmul(y.b.real());
-                this.b.add(this.a);
-                t1.real().add(y.b.real());
+				var ta=new ctx.FP4(this.a.geta());
+				var tb=new ctx.FP4(y.a.geta());
+				ta.add(this.a.getb()); ta.norm();
+				tb.add(y.a.getb()); tb.norm();
+				var tc=new ctx.FP4(ta);
+				tc.mul(tb);
+				var t=new ctx.FP4(w1);
+				t.add(w2);
+				t.neg();
+				tc.add(t);
 
-                this.b.norm();
-                t1.norm();
+				ta.copy(this.a.geta()); ta.add(this.b.geta()); ta.norm();
+				tb.copy(y.a.geta()); tb.add(y.b.geta()); tb.norm();
+				var td=new ctx.FP4(ta);
+				td.mul(tb);
+				t.copy(w1);
+				t.add(w3);
+				t.neg();
+				td.add(t);
 
-                this.b.mul(t1);
-                z3.add(this.c);
-                z3.norm();
-                z3.pmul(y.b.real());
+				ta.copy(this.a.getb()); ta.add(this.b.geta()); ta.norm();
+				tb.copy(y.a.getb()); tb.add(y.b.geta()); tb.norm();
+				var te=new ctx.FP4(ta);
+				te.mul(tb);
+				t.copy(w2);
+				t.add(w3);
+				t.neg();
+				te.add(t);
 
-                t0.copy(z0);
-                t0.neg();
-                t1.copy(z2);
-                t1.neg();
+				w2.times_i();
+				w1.add(w2);
 
-                this.b.add(t0);
+				this.a.geta().copy(w1); this.a.getb().copy(tc);
+				this.b.geta().copy(td); this.b.getb().copy(te);
+				this.c.geta().copy(w3); this.c.getb().zero();
 
-                this.b.add(t1);
-                z3.add(t1);
-                z2.add(t0);
+				this.a.norm();
+				this.b.norm();
 
-                t0.copy(this.a);
-                t0.add(this.c);
-                t0.norm();
-                t0.mul(y.a);
-                this.c.copy(z2);
-                this.c.add(t0);
+			} else {
+				var w1=new ctx.FP4(this.a.geta());
+				var w2=new ctx.FP4(this.a.getb());
+				var w3=new ctx.FP4(this.c.getb());
 
-                z3.times_i();
-                this.a.copy(z0);
-                this.a.add(z3);
-            }
+				w1.mul(y.a.geta());
+				w2.mul(y.a.getb());
+				w3.mul(y.c.getb());
 
-            if (twist == ctx.ECP.M_TYPE) {
-                z0=new ctx.FP8(this.a);
-                z1=new ctx.FP8(0);
-                z2=new ctx.FP8(0);
-                z3=new ctx.FP8(0);
-                t0=new ctx.FP8(this.a);
-                t1=new ctx.FP8(0);
+				var ta=new ctx.FP4(this.a.geta());
+				var tb=new ctx.FP4(y.a.geta());
+				ta.add(this.a.getb()); ta.norm();
+				tb.add(y.a.getb()); tb.norm();
+				var tc=new ctx.FP4(ta);
+				tc.mul(tb);
+				var t=new ctx.FP4(w1);
+				t.add(w2);
+				t.neg();
+				tc.add(t);
 
-                z0.mul(y.a);
-                t0.add(this.b);
-                t0.norm();
+				ta.copy(this.a.geta()); ta.add(this.c.getb()); ta.norm();
+				tb.copy(y.a.geta()); tb.add(y.c.getb()); tb.norm();
+				var td=new ctx.FP4(ta);
+				td.mul(tb);
+				t.copy(w1);
+				t.add(w3);
+				t.neg();
+				td.add(t);
 
-                z1.copy(t0); z1.mul(y.a);
-                t0.copy(this.b); t0.add(this.c);
-                t0.norm();
+				ta.copy(this.a.getb()); ta.add(this.c.getb()); ta.norm();
+				tb.copy(y.a.getb()); tb.add(y.c.getb()); tb.norm();
+				var te=new ctx.FP4(ta);
+				te.mul(tb);
+				t.copy(w2);
+				t.add(w3);
+				t.neg();
+				te.add(t);
 
-                z3.copy(t0); 
-                z3.pmul(y.c.getb());
-                z3.times_i();
+				w2.times_i();
+				w1.add(w2);
+				this.a.geta().copy(w1); this.a.getb().copy(tc);
 
-                t0.copy(z0); t0.neg();
+				w3.times_i();
+				w3.norm();
+				this.b.geta().zero(); this.b.getb().copy(w3);
 
-                z1.add(t0);
-                this.b.copy(z1);
-                z2.copy(t0);
+				te.norm();
+				te.times_i();
+				this.c.geta().copy(te);
+				this.c.getb().copy(td);
 
-                t0.copy(this.a); t0.add(this.c);
-                t1.copy(y.a); t1.add(y.c);
+				this.a.norm();
+				this.c.norm();
 
-                t0.norm();
-                t1.norm();
+			}
+			this.stype=ctx.FP.SPARSE;
+		},
 
-                t0.mul(t1);
-                z2.add(t0);
+/* FP24 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+		ssmul: function(y) {
+			if (this.stype==ctx.FP.ONE)
+			{
+				this.copy(y);
+				return;
+			}
+			if (y.stype==ctx.FP.ONE)
+				return;
 
-                t0.copy(this.c);
+			if (y.stype>=ctx.FP.SPARSE)
+			{
+				var z0=new ctx.FP8(this.a);
+				var z1=new ctx.FP8(0);
+				var z2=new ctx.FP8(0);
+				var z3=new ctx.FP8(0);
+				z0.mul(y.a);
 
-                t0.pmul(y.c.getb());
-                t0.times_i();
+				if (ctx.ECP.SEXTIC_TWIST==ctx.ECP.M_TYPE)
+				{
+					if (y.stype==ctx.FP.SPARSE || this.stype==ctx.FP.SPARSE)
+					{
+						z2.getb().copy(this.b.getb());
+						z2.getb().mul(y.b.getb());
+						z2.geta().zero();
+						if (y.stype!=ctx.FP.SPARSE)
+						{
+							z2.geta().copy(this.b.getb());
+							z2.geta().mul(y.b.geta());
+						}
+						if (this.stype!=ctx.FP.SPARSE)
+						{
+							z2.geta().copy(this.b.geta());
+							z2.geta().mul(y.b.getb());
+						}
+						z2.times_i();
+					} else {
+						z2.copy(this.b);
+						z2.mul(y.b);
+					}
+				} else {
+					z2.copy(this.b);
+					z2.mul(y.b);
+				}
+				var t0=new ctx.FP8(this.a);
+				var t1=new ctx.FP8(y.a);
+				t0.add(this.b); t0.norm();
+				t1.add(y.b); t1.norm();
 
-                t1.copy(t0); t1.neg();
+				z1.copy(t0); z1.mul(t1);
+				t0.copy(this.b); t0.add(this.c); t0.norm();
+				t1.copy(y.b); t1.add(y.c); t1.norm();
 
-                this.c.copy(z2); this.c.add(t1);
-                z3.add(t1);
-                t0.times_i();
-                this.b.add(t0);
-                z3.norm();
-                z3.times_i();
-                this.a.copy(z0); this.a.add(z3);
-            }
+				z3.copy(t0); z3.mul(t1);
 
-            this.norm();
-        },
+				t0.copy(z0); t0.neg();
+				t1.copy(z2); t1.neg();
+
+				z1.add(t0);
+				this.b.copy(z1); this.b.add(t1);
+
+				z3.add(t1);
+				z2.add(t0);
+
+				t0.copy(this.a); t0.add(this.c); t0.norm();
+				t1.copy(y.a); t1.add(y.c); t1.norm();
+	
+				t0.mul(t1);
+				z2.add(t0);
+
+				if (ctx.ECP.SEXTIC_TWIST==ctx.ECP.D_TYPE)
+				{
+					if (y.stype==ctx.FP.SPARSE || this.stype==ctx.FP.SPARSE)
+					{
+						t0.geta().copy(this.c.geta());
+						t0.geta().mul(y.c.geta());
+						t0.getb().zero();
+						if (y.stype!=ctx.FP.SPARSE)
+						{
+							t0.getb().copy(this.c.geta());
+							t0.getb().mul(y.c.getb());
+						}
+						if (this.stype!=ctx.FP.SPARSE)
+						{
+							t0.getb().copy(this.c.getb());
+							t0.getb().mul(y.c.geta());
+						}
+					} else {
+						t0.copy(this.c);
+						t0.mul(y.c);
+					}
+				} else {
+					t0.copy(this.c);
+					t0.mul(y.c);
+				}
+				t1.copy(t0); t1.neg();
+
+				this.c.copy(z2); this.c.add(t1);
+				z3.add(t1);
+				t0.times_i();
+				this.b.add(t0);
+				z3.norm();
+				z3.times_i();
+				this.a.copy(z0); this.a.add(z3);
+			} else {
+				if (this.stype==ctx.FP.SPARSER)
+				{
+					this.smul(y);
+					return;
+				}
+				if (ctx.ECP.SEXTIC_TWIST==ctx.ECP.D_TYPE)
+				{ // dense by sparser - 13m 
+					var z0=new ctx.FP8(this.a);
+					var z2=new ctx.FP8(this.b);
+					var z3=new ctx.FP8(this.b);
+					var t0=new ctx.FP8(0);
+					var t1=new ctx.FP8(y.a);
+					z0.mul(y.a);
+					z2.pmul(y.b.real());
+					this.b.add(this.a);
+					t1.real().add(y.b.real());
+
+					t1.norm();
+					this.b.norm();
+					this.b.mul(t1);
+					z3.add(this.c);
+					z3.norm();
+					z3.pmul(y.b.real());
+
+					t0.copy(z0); t0.neg();
+					t1.copy(z2); t1.neg();
+
+					this.b.add(t0);
+
+					this.b.add(t1);
+					z3.add(t1);
+					z2.add(t0);
+
+					t0.copy(this.a); t0.add(this.c); t0.norm();
+					z3.norm();
+					t0.mul(y.a);
+					this.c.copy(z2); this.c.add(t0);
+
+					z3.times_i();
+					this.a.copy(z0); this.a.add(z3);
+				}
+				if (ctx.ECP.SEXTIC_TWIST==ctx.ECP.M_TYPE)
+				{
+					var z0=new ctx.FP8(this.a);
+					var z1=new ctx.FP8(0);
+					var z2=new ctx.FP8(0);
+					var z3=new ctx.FP8(0);
+					var t0=new ctx.FP8(this.a);
+					var t1=new ctx.FP8(0);
+		
+					z0.mul(y.a);
+					t0.add(this.b); t0.norm();
+
+					z1.copy(t0); z1.mul(y.a);
+					t0.copy(this.b); t0.add(this.c);
+					t0.norm();
+
+					z3.copy(t0); 
+					z3.pmul(y.c.getb());
+					z3.times_i();
+
+					t0.copy(z0); t0.neg();
+					z1.add(t0);
+					this.b.copy(z1); 
+					z2.copy(t0);
+
+					t0.copy(this.a); t0.add(this.c); t0.norm();
+					t1.copy(y.a); t1.add(y.c); t1.norm();
+
+					t0.mul(t1);
+					z2.add(t0);
+					t0.copy(this.c); 
+			
+					t0.pmul(y.c.getb());
+					t0.times_i();
+					t1.copy(t0); t1.neg();
+
+					this.c.copy(z2); this.c.add(t1);
+					z3.add(t1);
+					t0.times_i();
+					this.b.add(t0);
+					z3.norm();
+					z3.times_i();
+					this.a.copy(z0); this.a.add(z3);
+				}	
+			}
+			this.stype=ctx.FP.DENSE;
+			this.norm();
+		},
 
         /* this=1/this */
         inverse: function() {
@@ -439,6 +678,7 @@
             this.b.mul(f3);
             this.c.copy(f2);
             this.c.mul(f3);
+			this.stype=ctx.FP.DENSE;
         },
 
         /* this=this^p, where p=Modulus, using Frobenius */
@@ -460,6 +700,7 @@
                 this.b.qmul(f); this.b.times_i2();
                 this.c.qmul(f2); this.c.times_i2(); this.c.times_i2();
             }
+			this.stype=ctx.FP.DENSE;
         },
 
         /* trace function */
diff --git a/version3/js/fp48.js b/version3/js/fp48.js
index 2aec58d..e5a4bea 100644
--- a/version3/js/fp48.js
+++ b/version3/js/fp48.js
@@ -26,15 +26,27 @@
 
     /* general purpose constructor */
     var FP48 = function(d, e, f) {
-        if (d instanceof FP48) {
-            this.a = new ctx.FP16(d.a);
-            this.b = new ctx.FP16(d.b);
-            this.c = new ctx.FP16(d.c);
-        } else {
-            this.a = new ctx.FP16(d);
-            this.b = new ctx.FP16(e);
-            this.c = new ctx.FP16(f);
-        }
+		if (!isNaN(d))
+		{
+			this.a = new ctx.FP16(d);
+			this.b = new ctx.FP16(0);
+			this.c = new ctx.FP16(0);
+			if (d==1) this.stype=ctx.FP.ONE;
+			else this.stype=ctx.FP.SPARSER;
+		}
+		else
+		{
+	        if (d instanceof FP48) {
+		        this.a = new ctx.FP16(d.a);
+			    this.b = new ctx.FP16(d.b);
+				this.c = new ctx.FP16(d.c);
+			} else {
+				this.a = new ctx.FP16(d);
+				this.b = new ctx.FP16(e);
+				this.c = new ctx.FP16(f);
+			}
+			this.stype=ctx.FP.DENSE;
+		}
     };
 
     FP48.prototype = {
@@ -68,6 +80,9 @@
             this.a.cmove(g.a, d);
             this.b.cmove(g.b, d);
             this.c.cmove(g.c, d);
+			d=~(d-1);
+			this.stype^=(this.stype^g.stype)&d;
+
         },
 
         /* Constant time select from pre-computed table */
@@ -93,6 +108,14 @@
             this.cmove(invf, (m & 1));
         },
 
+		settype: function(w) {
+			this.stype=w;
+		},
+
+		gettype: function() {
+			return this.stype;
+		},
+
         /* extract a from this */
         geta: function() {
             return this.a;
@@ -118,6 +141,7 @@
             this.a.copy(x.a);
             this.b.copy(x.b);
             this.c.copy(x.c);
+			this.stype=x.stype;
         },
 
         /* set this=1 */
@@ -125,6 +149,7 @@
             this.a.one();
             this.b.zero();
             this.c.zero();
+			this.stype=ctx.FP.ONE;
         },
 
         /* this=conj(this) */
@@ -139,6 +164,7 @@
             this.a.copy(d);
             this.b.copy(e);
             this.c.copy(f);
+			this.stype=ctx.FP.DENSE;
         },
 
         /* set this from one ctx.FP16 */
@@ -146,6 +172,7 @@
             this.a.copy(d);
             this.b.zero();
             this.c.zero();
+			this.stype=ctx.FP.SPARSER
         },
 
         /* Granger-Scott Unitary Squaring */
@@ -183,11 +210,15 @@
             this.c.add(this.c);
             this.b.add(B);
             this.c.add(C);
+			this.stype=ctx.FP.DENSE;
             this.reduce();
         },
 
         /* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
         sqr: function() {
+			if (this.stype==ctx.FP.ONE)
+				return;
+
             var A = new ctx.FP16(this.a), 
                 B = new ctx.FP16(this.b), 
                 C = new ctx.FP16(this.c), 
@@ -218,7 +249,10 @@
             this.b.copy(C);
             this.b.add(D);
             this.c.add(A);
-
+			if (this.stype==ctx.FP.SPARSER)
+				this.stype=ctx.FP.SPARSE;
+			else
+				this.stype=ctx.FP.DENSE;
             this.norm();
         },
 
@@ -290,110 +324,318 @@
             z3.times_i();
             this.a.copy(z0);
             this.a.add(z3);
-
+			this.stype=ctx.FP.DENSE;
             this.norm();
         },
 
-        /* Special case this*=y that arises from special form of ATE pairing line function */
-        smul: function(y, twist) {
-            var z0, z1, z2, z3, t0, t1;
+/* FP48 multiplication w=w*y */
+/* catering for special case that arises from special form of ATE pairing line function */
+/* w and y are both sparser line functions - cost = 6m */ 
+		smul: function(y) {
+			if (ctx.ECP.SEXTIC_TWIST==ctx.ECP.D_TYPE)
+			{	
+				var w1=new ctx.FP8(this.a.geta());
+				var w2=new ctx.FP8(this.a.getb());
+				var w3=new ctx.FP8(this.b.geta());
 
-            if (twist == ctx.ECP.D_TYPE) {
-                z0 = new ctx.FP16(this.a), 
-                z2 = new ctx.FP16(this.b), 
-                z3 = new ctx.FP16(this.b), 
-                t0 = new ctx.FP16(0),
-                t1 = new ctx.FP16(y.a);
+				w1.mul(y.a.geta());
+				w2.mul(y.a.getb());
+				w3.mul(y.b.geta());
 
-                z0.mul(y.a);
-                z2.pmul(y.b.real());
-                this.b.add(this.a);
-                t1.real().add(y.b.real());
+				var ta=new ctx.FP8(this.a.geta());
+				var tb=new ctx.FP8(y.a.geta());
+				ta.add(this.a.getb()); ta.norm();
+				tb.add(y.a.getb()); tb.norm();
+				var tc=new ctx.FP8(ta);
+				tc.mul(tb);
+				var t=new ctx.FP8(w1);
+				t.add(w2);
+				t.neg();
+				tc.add(t);
 
-                this.b.norm();
-                t1.norm();
+				ta.copy(this.a.geta()); ta.add(this.b.geta()); ta.norm();
+				tb.copy(y.a.geta()); tb.add(y.b.geta()); tb.norm();
+				var td=new ctx.FP8(ta);
+				td.mul(tb);
+				t.copy(w1);
+				t.add(w3);
+				t.neg();
+				td.add(t);
 
-                this.b.mul(t1);
-                z3.add(this.c);
-                z3.norm();
-                z3.pmul(y.b.real());
+				ta.copy(this.a.getb()); ta.add(this.b.geta()); ta.norm();
+				tb.copy(y.a.getb()); tb.add(y.b.geta()); tb.norm();
+				var te=new ctx.FP8(ta);
+				te.mul(tb);
+				t.copy(w2);
+				t.add(w3);
+				t.neg();
+				te.add(t);
 
-                t0.copy(z0);
-                t0.neg();
-                t1.copy(z2);
-                t1.neg();
+				w2.times_i();
+				w1.add(w2);
 
-                this.b.add(t0);
+				this.a.geta().copy(w1); this.a.getb().copy(tc);
+				this.b.geta().copy(td); this.b.getb().copy(te);
+				this.c.geta().copy(w3); this.c.getb().zero();
 
-                this.b.add(t1);
-                z3.add(t1);
-                z2.add(t0);
+				this.a.norm();
+				this.b.norm();
 
-                t0.copy(this.a);
-                t0.add(this.c);
-                t0.norm();
-                t0.mul(y.a);
-                this.c.copy(z2);
-                this.c.add(t0);
+			} else {
+				var w1=new ctx.FP8(this.a.geta());
+				var w2=new ctx.FP8(this.a.getb());
+				var w3=new ctx.FP8(this.c.getb());
 
-                z3.times_i();
-                this.a.copy(z0);
-                this.a.add(z3);
-            }
+				w1.mul(y.a.geta());
+				w2.mul(y.a.getb());
+				w3.mul(y.c.getb());
 
-            if (twist == ctx.ECP.M_TYPE) {
-                z0=new ctx.FP16(this.a);
-                z1=new ctx.FP16(0);
-                z2=new ctx.FP16(0);
-                z3=new ctx.FP16(0);
-                t0=new ctx.FP16(this.a);
-                t1=new ctx.FP16(0);
+				var ta=new ctx.FP8(this.a.geta());
+				var tb=new ctx.FP8(y.a.geta());
+				ta.add(this.a.getb()); ta.norm();
+				tb.add(y.a.getb()); tb.norm();
+				var tc=new ctx.FP8(ta);
+				tc.mul(tb);
+				var t=new ctx.FP8(w1);
+				t.add(w2);
+				t.neg();
+				tc.add(t);
 
-                z0.mul(y.a);
-                t0.add(this.b);
-                t0.norm();
+				ta.copy(this.a.geta()); ta.add(this.c.getb()); ta.norm();
+				tb.copy(y.a.geta()); tb.add(y.c.getb()); tb.norm();
+				var td=new ctx.FP8(ta);
+				td.mul(tb);
+				t.copy(w1);
+				t.add(w3);
+				t.neg();
+				td.add(t);
 
-                z1.copy(t0); z1.mul(y.a);
-                t0.copy(this.b); t0.add(this.c);
-                t0.norm();
+				ta.copy(this.a.getb()); ta.add(this.c.getb()); ta.norm();
+				tb.copy(y.a.getb()); tb.add(y.c.getb()); tb.norm();
+				var te=new ctx.FP8(ta);
+				te.mul(tb);
+				t.copy(w2);
+				t.add(w3);
+				t.neg();
+				te.add(t);
 
-                z3.copy(t0); //z3.mul(y.c);
-                z3.pmul(y.c.getb());
-                z3.times_i();
+				w2.times_i();
+				w1.add(w2);
+				this.a.geta().copy(w1); this.a.getb().copy(tc);
 
-                t0.copy(z0); t0.neg();
+				w3.times_i();
+				w3.norm();
+				this.b.geta().zero(); this.b.getb().copy(w3);
 
-                z1.add(t0);
-                this.b.copy(z1);
-                z2.copy(t0);
+				te.norm();
+				te.times_i();
+				this.c.geta().copy(te);
+				this.c.getb().copy(td);
 
-                t0.copy(this.a); t0.add(this.c);
-                t1.copy(y.a); t1.add(y.c);
+				this.a.norm();
+				this.c.norm();
 
-                t0.norm();
-                t1.norm();
+			}
+			this.stype=ctx.FP.SPARSE;
+		},
 
-                t0.mul(t1);
-                z2.add(t0);
+/* FP48 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+		ssmul: function(y) {
+			if (this.stype==ctx.FP.ONE)
+			{
+				this.copy(y);
+				return;
+			}
+			if (y.stype==ctx.FP.ONE)
+				return;
 
-                t0.copy(this.c);
+			if (y.stype>=ctx.FP.SPARSE)
+			{
+				var z0=new ctx.FP16(this.a);
+				var z1=new ctx.FP16(0);
+				var z2=new ctx.FP16(0);
+				var z3=new ctx.FP16(0);
+				z0.mul(y.a);
 
-                t0.pmul(y.c.getb());
-                t0.times_i();
+				if (ctx.ECP.SEXTIC_TWIST==ctx.ECP.M_TYPE)
+				{
+					if (y.stype==ctx.FP.SPARSE || this.stype==ctx.FP.SPARSE)
+					{
+						z2.getb().copy(this.b.getb());
+						z2.getb().mul(y.b.getb());
+						z2.geta().zero();
+						if (y.stype!=ctx.FP.SPARSE)
+						{
+							z2.geta().copy(this.b.getb());
+							z2.geta().mul(y.b.geta());
+						}
+						if (this.stype!=ctx.FP.SPARSE)
+						{
+							z2.geta().copy(this.b.geta());
+							z2.geta().mul(y.b.getb());
+						}
+						z2.times_i();
+					} else {
+						z2.copy(this.b);
+						z2.mul(y.b);
+					}
+				} else {
+					z2.copy(this.b);
+					z2.mul(y.b);
+				}
+				var t0=new ctx.FP16(this.a);
+				var t1=new ctx.FP16(y.a);
+				t0.add(this.b); t0.norm();
+				t1.add(y.b); t1.norm();
 
-                t1.copy(t0); t1.neg();
+				z1.copy(t0); z1.mul(t1);
+				t0.copy(this.b); t0.add(this.c); t0.norm();
+				t1.copy(y.b); t1.add(y.c); t1.norm();
 
-                this.c.copy(z2); this.c.add(t1);
-                z3.add(t1);
-                t0.times_i();
-                this.b.add(t0);
-                z3.norm();
-                z3.times_i();
-                this.a.copy(z0); this.a.add(z3);
-            }
+				z3.copy(t0); z3.mul(t1);
 
-            this.norm();
-        },
+				t0.copy(z0); t0.neg();
+				t1.copy(z2); t1.neg();
+
+				z1.add(t0);
+				this.b.copy(z1); this.b.add(t1);
+
+				z3.add(t1);
+				z2.add(t0);
+
+				t0.copy(this.a); t0.add(this.c); t0.norm();
+				t1.copy(y.a); t1.add(y.c); t1.norm();
+	
+				t0.mul(t1);
+				z2.add(t0);
+
+				if (ctx.ECP.SEXTIC_TWIST==ctx.ECP.D_TYPE)
+				{
+					if (y.stype==ctx.FP.SPARSE || this.stype==ctx.FP.SPARSE)
+					{
+						t0.geta().copy(this.c.geta());
+						t0.geta().mul(y.c.geta());
+						t0.getb().zero();
+						if (y.stype!=ctx.FP.SPARSE)
+						{
+							t0.getb().copy(this.c.geta());
+							t0.getb().mul(y.c.getb());
+						}
+						if (this.stype!=ctx.FP.SPARSE)
+						{
+							t0.getb().copy(this.c.getb());
+							t0.getb().mul(y.c.geta());
+						}
+					} else {
+						t0.copy(this.c);
+						t0.mul(y.c);
+					}
+				} else {
+					t0.copy(this.c);
+					t0.mul(y.c);
+				}
+				t1.copy(t0); t1.neg();
+
+				this.c.copy(z2); this.c.add(t1);
+				z3.add(t1);
+				t0.times_i();
+				this.b.add(t0);
+				z3.norm();
+				z3.times_i();
+				this.a.copy(z0); this.a.add(z3);
+			} else {
+				if (this.stype==ctx.FP.SPARSER)
+				{
+					this.smul(y);
+					return;
+				}
+				if (ctx.ECP.SEXTIC_TWIST==ctx.ECP.D_TYPE)
+				{ // dense by sparser - 13m 
+					var z0=new ctx.FP16(this.a);
+					var z2=new ctx.FP16(this.b);
+					var z3=new ctx.FP16(this.b);
+					var t0=new ctx.FP16(0);
+					var t1=new ctx.FP16(y.a);
+					z0.mul(y.a);
+					z2.pmul(y.b.real());
+					this.b.add(this.a);
+					t1.real().add(y.b.real());
+
+					t1.norm();
+					this.b.norm();
+					this.b.mul(t1);
+					z3.add(this.c);
+					z3.norm();
+					z3.pmul(y.b.real());
+
+					t0.copy(z0); t0.neg();
+					t1.copy(z2); t1.neg();
+
+					this.b.add(t0);
+
+					this.b.add(t1);
+					z3.add(t1);
+					z2.add(t0);
+
+					t0.copy(this.a); t0.add(this.c); t0.norm();
+					z3.norm();
+					t0.mul(y.a);
+					this.c.copy(z2); this.c.add(t0);
+
+					z3.times_i();
+					this.a.copy(z0); this.a.add(z3);
+				}
+				if (ctx.ECP.SEXTIC_TWIST==ctx.ECP.M_TYPE)
+				{
+					var z0=new ctx.FP16(this.a);
+					var z1=new ctx.FP16(0);
+					var z2=new ctx.FP16(0);
+					var z3=new ctx.FP16(0);
+					var t0=new ctx.FP16(this.a);
+					var t1=new ctx.FP16(0);
+		
+					z0.mul(y.a);
+					t0.add(this.b); t0.norm();
+
+					z1.copy(t0); z1.mul(y.a);
+					t0.copy(this.b); t0.add(this.c);
+					t0.norm();
+
+					z3.copy(t0); 
+					z3.pmul(y.c.getb());
+					z3.times_i();
+
+					t0.copy(z0); t0.neg();
+					z1.add(t0);
+					this.b.copy(z1); 
+					z2.copy(t0);
+
+					t0.copy(this.a); t0.add(this.c); t0.norm();
+					t1.copy(y.a); t1.add(y.c); t1.norm();
+
+					t0.mul(t1);
+					z2.add(t0);
+					t0.copy(this.c); 
+			
+					t0.pmul(y.c.getb());
+					t0.times_i();
+					t1.copy(t0); t1.neg();
+
+					this.c.copy(z2); this.c.add(t1);
+					z3.add(t1);
+					t0.times_i();
+					this.b.add(t0);
+					z3.norm();
+					z3.times_i();
+					this.a.copy(z0); this.a.add(z3);
+				}	
+			}
+			this.stype=ctx.FP.DENSE;
+			this.norm();
+		},
+
 
         /* this=1/this */
         inverse: function() {
@@ -439,6 +681,7 @@
             this.b.mul(f3);
             this.c.copy(f2);
             this.c.mul(f3);
+			this.stype=ctx.FP.DENSE;
         },
 
         /* this=this^p, where p=Modulus, using Frobenius */
@@ -461,6 +704,7 @@
                 this.b.qmul(f); this.b.times_i4(); this.b.times_i2();
                 this.c.qmul(f2); this.c.times_i4(); this.c.times_i4(); this.c.times_i4();
             }
+			this.stype=ctx.FP.DENSE;
         },
 
         /* trace function */
diff --git a/version3/js/pair.js b/version3/js/pair.js
index 6e0c08e..1c229b1 100644
--- a/version3/js/pair.js
+++ b/version3/js/pair.js
@@ -122,19 +122,121 @@
             }
 
             r.set(a, b, c);
+			r.settype(ctx.FP.SPARSER);
 
             return r;
         },
 
+/* prepare for multi-pairing */
+		initmp: function() {
+			var r=[];
+			for (var i=0;i<ctx.ECP.ATE_BITS;i++)
+				r[i] = new ctx.FP12(1);
+			return r;
+		},
+
+/* basic Miller loop */
+		miller: function(r) {
+			var res=new ctx.FP12(1);
+			for (var i=ctx.ECP.ATE_BITS-1; i>=1; i--)
+			{
+				res.sqr();
+				res.ssmul(r[i]); 
+			}
+
+			if (ctx.ECP.SIGN_OF_X==ctx.ECP.NEGATIVEX)
+				res.conj();
+			res.ssmul(r[0]);
+
+			return res;
+		},
+
+/* Accumulate another set of line functions for n-pairing */
+		another: function(r,P1,Q1) {
+
+			var f;
+			var n=new ctx.BIG(0);
+			var n3=new ctx.BIG(0);
+			var K=new ctx.ECP2();
+			var lv,lv2;
+			var bt;
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+			var P=new ctx.ECP2(); P.copy(P1); P.affine();
+			var Q=new ctx.ECP(); Q.copy(Q1); Q.affine();
+
+			P.affine();
+			Q.affine();
+
+			if (ctx.ECP.CURVE_PAIRING_TYPE==ctx.ECP.BN)
+			{
+                var fa = new ctx.BIG(0);
+                fa.rcopy(ctx.ROM_FIELD.Fra);
+                var fb = new ctx.BIG(0);
+                fb.rcopy(ctx.ROM_FIELD.Frb);
+                f = new ctx.FP2(fa, fb); 
+				if (ctx.ECP.SEXTIC_TWIST==ctx.ECP.M_TYPE)
+				{
+					f.inverse();
+					f.norm();
+				}
+			}
+
+			var Qx=new ctx.FP(Q.getx());
+			var Qy=new ctx.FP(Q.gety());
+
+			var A=new ctx.ECP2();
+			A.copy(P);
+
+			var MP=new ctx.ECP2();
+			MP.copy(P); MP.neg();
+
+			var nb=PAIR.lbits(n3,n);
+
+			for (var i=nb-2;i>=1;i--)
+			{
+				lv=PAIR.line(A,A,Qx,Qy);
+
+				bt=n3.bit(i)-n.bit(i); 
+				if (bt==1)
+				{
+					lv2=PAIR.line(A,P,Qx,Qy);
+					lv.smul(lv2);
+				}
+				if (bt==-1)
+				{
+					lv2=PAIR.line(A,MP,Qx,Qy);
+					lv.smul(lv2);
+				}
+				r[i].ssmul(lv);
+			}
+
+/* R-ate fixup required for BN curves */
+			if (ctx.ECP.CURVE_PAIRING_TYPE==ctx.ECP.BN)
+			{
+				if (ctx.ECP.SIGN_OF_X==ctx.ECP.NEGATIVEX)
+				{
+					A.neg();
+				}
+				K.copy(P);
+				K.frob(f);
+				lv=PAIR.line(A,K,Qx,Qy);
+				K.frob(f);
+				K.neg();
+				lv2=PAIR.line(A,K,Qx,Qy);
+				lv.smul(lv2);
+				r[0].ssmul(lv);
+			}	 
+		},
+
         /* Optimal R-ate pairing */
         ate: function(P1, Q1) {
-            var fa, fb, f, x, n, n3, K, lv,
+            var fa, fb, f, x, n, n3, K, lv, lv2,
                 Qx, Qy, A, NP, r, nb, bt,
                 i;
 
-            x = new ctx.BIG(0);
-            x.rcopy(ctx.ROM_CURVE.CURVE_Bnx);
-            n = new ctx.BIG(x); 
+            n = new ctx.BIG(0);
+			n3 = new ctx.BIG(0);
             K = new ctx.ECP2();
 
             if (ctx.ECP.CURVE_PAIRING_TYPE == ctx.ECP.BN) {
@@ -149,21 +251,7 @@
                     f.inverse();
                     f.norm();
                 }
-
-                n.pmul(6);
-                if (ctx.ECP.SIGN_OF_X == ctx.ECP.POSITIVEX) {
-                    n.inc(2);
-                } else {
-                    n.dec(2);
-                }
-            } else {
-                n.copy(x);
             }
-            n.norm();
-
-            n3 = new ctx.BIG(n);
-            n3.pmul(3);
-            n3.norm();
 
 			var P=new ctx.ECP2(); P.copy(P1); P.affine();
 			var Q=new ctx.ECP(); Q.copy(Q1); Q.affine();
@@ -179,24 +267,22 @@
 			NP.copy(P);
 			NP.neg();
 
-            nb = n3.nbits();
+            nb = PAIR.lbits(n3,n);
 
             for (i = nb - 2; i >= 1; i--) {
                 r.sqr();
                 lv = PAIR.line(A, A, Qx, Qy);
-
-                r.smul(lv,ctx.ECP.SEXTIC_TWIST);
-
                 bt=n3.bit(i)-n.bit(i);
 
                 if (bt == 1) {
-                    lv = PAIR.line(A, P, Qx, Qy);
-                    r.smul(lv,ctx.ECP.SEXTIC_TWIST);
+                    lv2 = PAIR.line(A, P, Qx, Qy);
+                    lv.smul(lv2);
                 }
                 if (bt == -1) {
-                    lv = PAIR.line(A, NP, Qx, Qy);
-                    r.smul(lv,ctx.ECP.SEXTIC_TWIST);
+                    lv2 = PAIR.line(A, NP, Qx, Qy);
+                    lv.smul(lv2);
                 }
+                r.ssmul(lv);
             }
 
             if (ctx.ECP.SIGN_OF_X == ctx.ECP.NEGATIVEX) {
@@ -213,27 +299,25 @@
                 K.frob(f);
 
                 lv = PAIR.line(A, K, Qx, Qy);
-                r.smul(lv,ctx.ECP.SEXTIC_TWIST);
                 K.frob(f);
                 K.neg();
-                lv = PAIR.line(A, K, Qx, Qy);
-                r.smul(lv,ctx.ECP.SEXTIC_TWIST);
+                lv2 = PAIR.line(A, K, Qx, Qy);
+				lv.smul(lv2);
+                r.ssmul(lv);
             }
 
             return r;
         },
 
         /* Optimal R-ate double pairing e(P,Q).e(R,S) */
+	
         ate2: function(P1, Q1, R1, S1) {
-            var fa, fb, f, x, n, n3, K, lv,
+            var fa, fb, f, x, n, n3, K, lv, lv2,
                 Qx, Qy, Sx, Sy, A, B, NP,NR,r, nb, bt,
                 i;
 
-
-            x = new ctx.BIG(0);
-            x.rcopy(ctx.ROM_CURVE.CURVE_Bnx);
-
-            n = new ctx.BIG(x);
+            n = new ctx.BIG(0);
+			n3 = new ctx.BIG(0);
             K = new ctx.ECP2();
 
             if (ctx.ECP.CURVE_PAIRING_TYPE == ctx.ECP.BN) {
@@ -247,21 +331,7 @@
                     f.inverse();
                     f.norm();
                 }
-
-                n.pmul(6);
-                if (ctx.ECP.SIGN_OF_X == ctx.ECP.POSITIVEX) {
-                    n.inc(2);
-                } else {
-                    n.dec(2);
-                }
-            } else {
-                n.copy(x);
-            }
-            n.norm();
-
-            n3 = new ctx.BIG(n);
-            n3.pmul(3);
-            n3.norm();
+            } 
 
 			var P=new ctx.ECP2(); P.copy(P1); P.affine();
 			var Q=new ctx.ECP(); Q.copy(Q1); Q.affine();
@@ -289,28 +359,28 @@
 			NR.copy(R);
 			NR.neg();
 
-            nb = n3.nbits();
+            nb = PAIR.lbits(n3,n);
 
             for (i = nb - 2; i >= 1; i--) {
                 r.sqr();
                 lv = PAIR.line(A, A, Qx, Qy);
-                r.smul(lv,ctx.ECP.SEXTIC_TWIST);
-                lv = PAIR.line(B, B, Sx, Sy);
-                r.smul(lv,ctx.ECP.SEXTIC_TWIST);
+                lv2 = PAIR.line(B, B, Sx, Sy);
+				lv.smul(lv2);
+                r.ssmul(lv);
 
                 bt=n3.bit(i)-n.bit(i);
 
                 if (bt == 1) {
                     lv = PAIR.line(A, P, Qx, Qy);
-                    r.smul(lv,ctx.ECP.SEXTIC_TWIST);
-                    lv = PAIR.line(B, R, Sx, Sy);
-                    r.smul(lv,ctx.ECP.SEXTIC_TWIST);
+                    lv2 = PAIR.line(B, R, Sx, Sy);
+					lv.smul(lv2);
+                    r.ssmul(lv);
                 }
                 if (bt == -1) {
                     lv = PAIR.line(A, NP, Qx, Qy);
-                    r.smul(lv,ctx.ECP.SEXTIC_TWIST);
-                    lv = PAIR.line(B, NR, Sx, Sy);
-                    r.smul(lv,ctx.ECP.SEXTIC_TWIST);
+                    lv2 = PAIR.line(B, NR, Sx, Sy);
+					lv.smul(lv2);
+                    r.ssmul(lv);
                 }
             }
 
@@ -319,7 +389,7 @@
             }
 
 
-            /* R-ate fixup required for BN curves */
+            // R-ate fixup required for BN curves 
             if (ctx.ECP.CURVE_PAIRING_TYPE == ctx.ECP.BN) {
                 if (ctx.ECP.SIGN_OF_X == ctx.ECP.NEGATIVEX) {
                     A.neg();
@@ -329,21 +399,21 @@
                 K.frob(f);
 
                 lv = PAIR.line(A, K, Qx, Qy);
-                r.smul(lv,ctx.ECP.SEXTIC_TWIST);
                 K.frob(f);
                 K.neg();
-                lv = PAIR.line(A, K, Qx, Qy);
-                r.smul(lv,ctx.ECP.SEXTIC_TWIST);
+                lv2 = PAIR.line(A, K, Qx, Qy);
+				lv.smul(lv2);
+                r.ssmul(lv);
 
                 K.copy(R);
                 K.frob(f);
 
                 lv = PAIR.line(B, K, Sx, Sy);
-                r.smul(lv,ctx.ECP.SEXTIC_TWIST);
                 K.frob(f);
                 K.neg();
-                lv = PAIR.line(B, K, Sx, Sy);
-                r.smul(lv,ctx.ECP.SEXTIC_TWIST);
+                lv2 = PAIR.line(B, K, Sx, Sy);
+				lv.smul(lv2);
+                r.ssmul(lv);
             }
 
             return r;
@@ -491,6 +561,28 @@
         }
     };
 
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+	PAIR.lbits = function(n3,n)
+	{
+		n.rcopy(ctx.ROM_CURVE.CURVE_Bnx);
+		if (ctx.ECP.CURVE_PAIRING_TYPE==ctx.ECP.BN)
+		{
+			n.pmul(6);
+			if (ctx.ECP.SIGN_OF_X==ctx.ECP.POSITIVEX)
+			{
+				n.inc(2);
+			} else {
+				n.dec(2);
+			}
+		}
+		
+		n.norm();
+		n3.copy(n);
+		n3.pmul(3);
+		n3.norm();
+		return n3.nbits();
+	},
+
     /* GLV method */
     PAIR.glv = function(e) {
         var u = [],
diff --git a/version3/js/pair192.js b/version3/js/pair192.js
index ee33fb9..f915991 100644
--- a/version3/js/pair192.js
+++ b/version3/js/pair192.js
@@ -121,23 +121,89 @@
             }
 
             r.set(a, b, c);
-
+			r.settype(ctx.FP.SPARSER);
             return r;
         },
 
+/* prepare for multi-pairing */
+		initmp: function() {
+			var r=[];
+			for (var i=0;i<ctx.ECP.ATE_BITS;i++)
+				r[i] = new ctx.FP24(1);
+			return r;
+		},
+
+/* basic Miller loop */
+		miller: function(r) {
+			var res=new ctx.FP24(1);
+			for (var i=ctx.ECP.ATE_BITS-1; i>=1; i--)
+			{
+				res.sqr();
+				res.ssmul(r[i]); 
+			}
+
+			if (ctx.ECP.SIGN_OF_X==ctx.ECP.NEGATIVEX)
+				res.conj();
+			res.ssmul(r[0]);
+
+			return res;
+		},
+
+/* Accumulate another set of line functions for n-pairing */
+		another: function(r,P1,Q1) {
+
+			var f;
+			var n=new ctx.BIG(0);
+			var n3=new ctx.BIG(0);
+			var lv,lv2;
+			var bt;
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+			var P=new ctx.ECP4(); P.copy(P1); P.affine();
+			var Q=new ctx.ECP(); Q.copy(Q1); Q.affine();
+
+			P.affine();
+			Q.affine();
+
+			var Qx=new ctx.FP(Q.getx());
+			var Qy=new ctx.FP(Q.gety());
+
+			var A=new ctx.ECP4();
+			A.copy(P);
+
+			var MP=new ctx.ECP4();
+			MP.copy(P); MP.neg();
+
+			var nb=PAIR192.lbits(n3,n);
+
+			for (var i=nb-2;i>=1;i--)
+			{
+				lv=PAIR192.line(A,A,Qx,Qy);
+
+				bt=n3.bit(i)-n.bit(i); 
+				if (bt==1)
+				{
+					lv2=PAIR192.line(A,P,Qx,Qy);
+					lv.smul(lv2);
+				}
+				if (bt==-1)
+				{
+					lv2=PAIR192.line(A,MP,Qx,Qy);
+					lv.smul(lv2);
+				}
+				r[i].ssmul(lv);
+			} 
+		},
+
+
         /* Optimal R-ate pairing */
         ate: function(P1, Q1) {
-            var x, n, n3, lv,
+            var x, n, n3, lv, lv2,
                 Qx, Qy, A, NP, r, nb, bt,
                 i;
 
-            x = new ctx.BIG(0);
-            x.rcopy(ctx.ROM_CURVE.CURVE_Bnx);
-            n = new ctx.BIG(x);
-
-            n3 = new ctx.BIG(n);
-            n3.pmul(3);
-            n3.norm();
+            n = new ctx.BIG(0);
+			n3 = new ctx.BIG(0);
 
 			var P=new ctx.ECP4(); P.copy(P1); P.affine();
 			var Q=new ctx.ECP(); Q.copy(Q1); Q.affine();
@@ -154,24 +220,22 @@
 			NP.neg();
 
 
-            nb = n3.nbits();
+            nb = PAIR192.lbits(n3,n);
 
             for (i = nb - 2; i >= 1; i--) {
                 r.sqr();
                 lv = PAIR192.line(A, A, Qx, Qy);
-
-                r.smul(lv,ctx.ECP.SEXTIC_TWIST);
-
                 bt=n3.bit(i)-n.bit(i);
 
                 if (bt == 1) {
-                    lv = PAIR192.line(A, P, Qx, Qy);
-                    r.smul(lv,ctx.ECP.SEXTIC_TWIST);
+                    lv2 = PAIR192.line(A, P, Qx, Qy);
+                    lv.smul(lv2);
                 }
                 if (bt == -1) {
-                    lv = PAIR192.line(A, NP, Qx, Qy);
-                    r.smul(lv,ctx.ECP.SEXTIC_TWIST);
+                    lv2 = PAIR192.line(A, NP, Qx, Qy);
+                    lv.smul(lv2);
                 }
+                r.ssmul(lv);
             }
 
             if (ctx.ECP.SIGN_OF_X == ctx.ECP.NEGATIVEX) {
@@ -183,18 +247,13 @@
 
         /* Optimal R-ate double pairing e(P,Q).e(R,S) */
         ate2: function(P1, Q1, R1, S1) {
-            var x, n, n3, lv,
+            var x, n, n3, lv, lv2,
                 Qx, Qy, Sx, Sy, A, B, NP, NR, r, nb, bt,
                 i;
 
 
-            x = new ctx.BIG(0);
-            x.rcopy(ctx.ROM_CURVE.CURVE_Bnx);
-
-            n = new ctx.BIG(x); 
-            n3 = new ctx.BIG(n);
-            n3.pmul(3);
-            n3.norm();
+            n = new ctx.BIG(0);
+			n3 = new ctx.BIG(0);
 
 			var P=new ctx.ECP4(); P.copy(P1); P.affine();
 			var Q=new ctx.ECP(); Q.copy(Q1); Q.affine();
@@ -221,29 +280,28 @@
 			NR.copy(R);
 			NR.neg();
 
-
-            nb = n3.nbits();
+            nb = PAIR192.lbits(n3,n);
 
             for (i = nb - 2; i >= 1; i--) {
                 r.sqr();
                 lv = PAIR192.line(A, A, Qx, Qy);
-                r.smul(lv,ctx.ECP.SEXTIC_TWIST);
-                lv = PAIR192.line(B, B, Sx, Sy);
-                r.smul(lv,ctx.ECP.SEXTIC_TWIST);
+                lv2 = PAIR192.line(B, B, Sx, Sy);
+				lv.smul(lv2);
+                r.ssmul(lv);
 
                 bt=n3.bit(i)-n.bit(i);
 
                 if (bt == 1) {
                     lv = PAIR192.line(A, P, Qx, Qy);
-                    r.smul(lv,ctx.ECP.SEXTIC_TWIST);
-                    lv = PAIR192.line(B, R, Sx, Sy);
-                    r.smul(lv,ctx.ECP.SEXTIC_TWIST);
+                    lv2 = PAIR192.line(B, R, Sx, Sy);
+					lv.smul(lv2);
+                    r.ssmul(lv);
                 }
                 if (bt == -1) {
                     lv = PAIR192.line(A, NP, Qx, Qy);
-                    r.smul(lv,ctx.ECP.SEXTIC_TWIST);
-                    lv = PAIR192.line(B, NR, Sx, Sy);
-                    r.smul(lv,ctx.ECP.SEXTIC_TWIST);
+                    lv2 = PAIR192.line(B, NR, Sx, Sy);
+					lv.smul(lv2);
+                    r.ssmul(lv);
                 }
             }
 
@@ -353,6 +411,16 @@
         }
     };
 
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+	PAIR192.lbits = function(n3,n)
+	{
+		n.rcopy(ctx.ROM_CURVE.CURVE_Bnx);
+		n3.copy(n);
+		n3.pmul(3);
+		n3.norm();
+		return n3.nbits();
+	},
+
     /* GLV method */
     PAIR192.glv = function(e) {
         var u = [],
diff --git a/version3/js/pair256.js b/version3/js/pair256.js
index 2408fc0..9891a88 100644
--- a/version3/js/pair256.js
+++ b/version3/js/pair256.js
@@ -121,23 +121,89 @@
             }
 
             r.set(a, b, c);
-
+			r.settype(ctx.FP.SPARSER);
             return r;
         },
 
+/* prepare for multi-pairing */
+		initmp: function() {
+			var r=[];
+			for (var i=0;i<ctx.ECP.ATE_BITS;i++)
+				r[i] = new ctx.FP48(1);
+			return r;
+		},
+
+/* basic Miller loop */
+		miller: function(r) {
+			var res=new ctx.FP48(1);
+			for (var i=ctx.ECP.ATE_BITS-1; i>=1; i--)
+			{
+				res.sqr();
+				res.ssmul(r[i]); 
+			}
+
+			if (ctx.ECP.SIGN_OF_X==ctx.ECP.NEGATIVEX)
+				res.conj();
+			res.ssmul(r[0]);
+
+			return res;
+		},
+
+/* Accumulate another set of line functions for n-pairing */
+		another: function(r,P1,Q1) {
+
+			var f;
+			var n=new ctx.BIG(0);
+			var n3=new ctx.BIG(0);
+			var lv,lv2;
+			var bt;
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+			var P=new ctx.ECP8(); P.copy(P1); P.affine();
+			var Q=new ctx.ECP(); Q.copy(Q1); Q.affine();
+
+			P.affine();
+			Q.affine();
+
+			var Qx=new ctx.FP(Q.getx());
+			var Qy=new ctx.FP(Q.gety());
+
+			var A=new ctx.ECP8();
+			A.copy(P);
+
+			var MP=new ctx.ECP8();
+			MP.copy(P); MP.neg();
+
+			var nb=PAIR256.lbits(n3,n);
+
+			for (var i=nb-2;i>=1;i--)
+			{
+				lv=PAIR256.line(A,A,Qx,Qy);
+
+				bt=n3.bit(i)-n.bit(i); 
+				if (bt==1)
+				{
+					lv2=PAIR256.line(A,P,Qx,Qy);
+					lv.smul(lv2);
+				}
+				if (bt==-1)
+				{
+					lv2=PAIR256.line(A,MP,Qx,Qy);
+					lv.smul(lv2);
+				}
+				r[i].ssmul(lv);
+			} 
+		},
+
+
         /* Optimal R-ate pairing */
         ate: function(P1, Q1) {
-            var x, n, n3, lv,
+            var x, n, n3, lv, lv2,
                 Qx, Qy, A, NP, r, nb, bt,
                 i;
 
-            x = new ctx.BIG(0);
-            x.rcopy(ctx.ROM_CURVE.CURVE_Bnx);
-            n = new ctx.BIG(x); 
-
-            n3 = new ctx.BIG(n);
-            n3.pmul(3);
-            n3.norm();
+            n = new ctx.BIG(0);
+			n3 = new ctx.BIG(0);
 
 			var P=new ctx.ECP8(); P.copy(P1); P.affine();
 			var Q=new ctx.ECP(); Q.copy(Q1); Q.affine();
@@ -154,24 +220,22 @@
 			NP.neg();
 
 
-            nb = n3.nbits();
+            nb = PAIR256.lbits(n3,n);
 
             for (i = nb - 2; i >= 1; i--) {
                 r.sqr();
                 lv = PAIR256.line(A, A, Qx, Qy);
-
-                r.smul(lv,ctx.ECP.SEXTIC_TWIST);
-
                 bt=n3.bit(i)-n.bit(i);
 
                 if (bt == 1) {
-                    lv = PAIR256.line(A, P, Qx, Qy);
-                    r.smul(lv,ctx.ECP.SEXTIC_TWIST);
+                    lv2 = PAIR256.line(A, P, Qx, Qy);
+                    lv.smul(lv2);
                 }
                 if (bt == -1) {
-                    lv = PAIR256.line(A, NP, Qx, Qy);
-                    r.smul(lv,ctx.ECP.SEXTIC_TWIST);
+                    lv2 = PAIR256.line(A, NP, Qx, Qy);
+                    lv.smul(lv2);
                 }
+                r.ssmul(lv);
             }
 
             if (ctx.ECP.SIGN_OF_X == ctx.ECP.NEGATIVEX) {
@@ -183,18 +247,12 @@
 
         /* Optimal R-ate double pairing e(P,Q).e(R,S) */
         ate2: function(P1, Q1, R1, S1) {
-            var x, n, n3, lv,
+            var x, n, n3, lv, lv2,
                 Qx, Qy, Sx, Sy, A, B, NP, NR, r, nb, bt,
                 i;
 
-
-            x = new ctx.BIG(0);
-            x.rcopy(ctx.ROM_CURVE.CURVE_Bnx);
-
-            n = new ctx.BIG(x);
-            n3 = new ctx.BIG(n);
-            n3.pmul(3);
-            n3.norm();
+            n = new ctx.BIG(0);
+			n3 = new ctx.BIG(0);
 
 			var P=new ctx.ECP8(); P.copy(P1); P.affine();
 			var Q=new ctx.ECP(); Q.copy(Q1); Q.affine();
@@ -222,28 +280,28 @@
 			NR.neg();
 
 
-            nb = n3.nbits();
+            nb = PAIR256.lbits(n3,n);
 
             for (i = nb - 2; i >= 1; i--) {
                 r.sqr();
                 lv = PAIR256.line(A, A, Qx, Qy);
-                r.smul(lv,ctx.ECP.SEXTIC_TWIST);
-                lv = PAIR256.line(B, B, Sx, Sy);
-                r.smul(lv,ctx.ECP.SEXTIC_TWIST);
+                lv2 = PAIR256.line(B, B, Sx, Sy);
+				lv.smul(lv2);
+                r.ssmul(lv);
 
                 bt=n3.bit(i)-n.bit(i);
 
                 if (bt == 1) {
                     lv = PAIR256.line(A, P, Qx, Qy);
-                    r.smul(lv,ctx.ECP.SEXTIC_TWIST);
-                    lv = PAIR256.line(B, R, Sx, Sy);
-                    r.smul(lv,ctx.ECP.SEXTIC_TWIST);
+                    lv2 = PAIR256.line(B, R, Sx, Sy);
+					lv.smul(lv2);
+                    r.ssmul(lv);
                 }
                 if (bt == -1) {
                     lv = PAIR256.line(A, NP, Qx, Qy);
-                    r.smul(lv,ctx.ECP.SEXTIC_TWIST);
-                    lv = PAIR256.line(B, NR, Sx, Sy);
-                    r.smul(lv,ctx.ECP.SEXTIC_TWIST);
+                    lv2 = PAIR256.line(B, NR, Sx, Sy);
+					lv.smul(lv2);
+                    r.ssmul(lv);
                 }
             }
 
@@ -424,6 +482,16 @@
         }
     };
 
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+	PAIR256.lbits = function(n3,n)
+	{
+		n.rcopy(ctx.ROM_CURVE.CURVE_Bnx);
+		n3.copy(n);
+		n3.pmul(3);
+		n3.norm();
+		return n3.nbits();
+	},
+
     /* GLV method */
     PAIR256.glv = function(e) {
         var u = [],
diff --git a/version3/python/bls.py b/version3/python/bls.py
index 4f3672d..55d9181 100644
--- a/version3/python/bls.py
+++ b/version3/python/bls.py
@@ -53,9 +53,17 @@
     PK=ECp2()
     PK.fromBytes(W)
     D=-D
-    v = pair.double_miller(G, D, PK, HM)
-    v = pair.fexp(v)
 
+# Use new multi-pairing mechanism 
+    r=pair.initmp()
+    pair.another(r,G,D)
+    pair.another(r,PK,HM)
+    v=pair.miller(r)
+
+#.. or alternatively
+#    v = pair.double_ate(G, D, PK, HM)
+    
+    v = pair.fexp(v)
     if v.isone():
         return True
     return False
diff --git a/version3/python/fp12.py b/version3/python/fp12.py
index 11bfa81..dbc5cf3 100644
--- a/version3/python/fp12.py
+++ b/version3/python/fp12.py
@@ -5,6 +5,7 @@
 #
 
 import copy
+from constants import *
 from XXX import curve
 from XXX.fp4 import *
 
@@ -105,6 +106,8 @@
 
 # regular squaring
     def sqr(self):  # mutable
+        if self.isone() :
+            return
         A = self.a.copy()
         A.sqr()
         B = self.b * self.c
@@ -165,6 +168,62 @@
             R *= other
         return R
 
+# multiply line functions
+    def smul(self,other) :
+        if curve.SexticTwist == D_TYPE :
+            w1=self.a.a*other.a.a
+            w2=self.a.b*other.a.b
+            w3=self.b.a*other.b.a
+
+            ta=self.a.a+self.a.b
+            tb=other.a.a+other.a.b
+            tc=ta*tb
+            tc-=(w1+w2)
+
+            ta=self.a.a+self.b.a
+            tb=other.a.a+other.b.a
+            td=ta*tb
+            td-=(w1+w3)
+
+            ta=self.a.b+self.b.a
+            tb=other.a.b+other.b.a
+            te=ta*tb
+            te-=(w2+w3)
+
+            w1+=w2.mulQNR()
+            self.a=Fp4(w1,tc)
+            self.b=Fp4(td,te)
+            self.c=Fp4(w3)
+        else :
+            w1=self.a.a*other.a.a
+            w2=self.a.b*other.a.b
+            w3=self.c.b*other.c.b
+
+            ta=self.a.a+self.a.b
+            tb=other.a.a+other.a.b
+            tc=ta*tb
+            tc-=(w1+w2)
+			
+            ta=self.a.a+self.c.b
+            tb=other.a.a+other.c.b
+            td=ta*tb
+            td-=(w1+w3)
+
+            ta=self.a.b+self.c.b
+            tb=other.a.b+other.c.b
+            te=ta*tb
+            te-=(w2+w3)
+
+            w1+=w2.mulQNR()
+            self.a=Fp4(w1,tc)
+
+            self.b=Fp4(w3.mulQNR())
+            self.b.times_i()
+
+            self.c=Fp4(te.mulQNR(),td)
+
+        return self
+
     def muls(self, other):  # multiple Fp12 by Fp4
         R = Fp12(self.a * other, self.b * other, self.c * other)
         return R
diff --git a/version3/python/mpin.py b/version3/python/mpin.py
index 4fbec4c..a6c7783 100644
--- a/version3/python/mpin.py
+++ b/version3/python/mpin.py
@@ -133,7 +133,7 @@
     TU.add(P)
     # TU.affine()
 
-    r = pair.double_miller(Q, TV, sQ, TU)
+    r = pair.double_ate(Q, TV, sQ, TU)
     r = pair.fexp(r)
 
     if r.isone():
diff --git a/version3/python/pair.py b/version3/python/pair.py
index e8a5323..6ab1190 100644
--- a/version3/python/pair.py
+++ b/version3/python/pair.py
@@ -16,7 +16,6 @@
 
 # line function
 
-
 def g(A, B, Qx, Qy):
     if A == B:
         XX, YY, ZZ = A.getxyz()
@@ -63,13 +62,7 @@
 
 # full pairing - miller loop followed by final exponentiation
 
-
-def e(P, Q):
-    r = miller(P, Q)
-    return fexp(r)
-
-
-def miller(P1, Q1):
+def lbits() :
     x = curve.x
     if curve.PairingFriendly == BN:
         n = 6 * x
@@ -80,7 +73,66 @@
     else:
         n = x
     n3 = 3 * n
+    return n3.bit_length(),n3,n
 
+def initmp() :
+    nb,n3,n=lbits()
+    r=[]
+    for i in range (nb-1,-1,-1) :
+        r.append(Fp12.one())
+    return r
+
+def miller(r) :
+    nb,n3,n=lbits()
+    res=Fp12.one()
+    for i in range (nb-1,0,-1) :
+        res.sqr()
+        res *= r[i]
+    if curve.SignOfX == NEGATIVEX:
+        res.conj()
+    res *= r[0]
+    return res
+	
+def another(r,P1,Q1) :
+    nb,n3,n=lbits()
+    P = P1.copy()
+    Q = Q1.copy()
+
+    P.affine()
+    Q.affine()
+    A = P.copy()
+    Qx, Qy = Q.getxy()	
+    for i in range(nb - 2, 0, -1):
+        lv=g(A, A, Qx, Qy)
+
+        if big.bit(n3, i) == 1 and big.bit(n, i) == 0:
+            lv2 =  g(A, P, Qx, Qy)
+            lv.smul(lv2)
+        if big.bit(n3, i) == 0 and big.bit(n, i) == 1:
+            lv2 = g(A, -P, Qx, Qy)
+            lv.smul(lv2)
+        r[i] *= lv
+    
+    if curve.PairingFriendly == BN:
+        KA = P.copy()
+        KA.frobenius()
+        if curve.SignOfX == NEGATIVEX:
+            A = -A
+        lv = g(A, KA, Qx, Qy)
+        KA.frobenius()
+        KA = -KA
+        lv2 = g(A, KA, Qx, Qy)
+        lv.smul(lv2)
+        r[0] *= lv
+
+def e(P, Q):
+    r = miller(P, Q)
+    return fexp(r)
+
+
+def ate(P1, Q1):
+    nb,n3,n=lbits()
+    
     P = P1.copy()
     Q = Q1.copy()
 
@@ -88,17 +140,19 @@
     Q.affine()
     A = P.copy()
     Qx, Qy = Q.getxy()
-    nb = n3.bit_length()
     r = Fp12.one()
 # miller loop
     for i in range(nb - 2, 0, -1):
         r.sqr()
-        r *= g(A, A, Qx, Qy)
+        lv=g(A, A, Qx, Qy)
 
         if big.bit(n3, i) == 1 and big.bit(n, i) == 0:
-            r *= g(A, P, Qx, Qy)
+            lv2 =  g(A, P, Qx, Qy)
+            lv.smul(lv2)
         if big.bit(n3, i) == 0 and big.bit(n, i) == 1:
-            r *= g(A, -P, Qx, Qy)
+            lv2 = g(A, -P, Qx, Qy)
+            lv.smul(lv2)
+        r *= lv
 
 # adjustment
     if curve.SignOfX == NEGATIVEX:
@@ -109,27 +163,19 @@
         KA.frobenius()
         if curve.SignOfX == NEGATIVEX:
             A = -A
-        r *= g(A, KA, Qx, Qy)
+        lv = g(A, KA, Qx, Qy)
         KA.frobenius()
         KA = -KA
-        r *= g(A, KA, Qx, Qy)
+        lv2 = g(A, KA, Qx, Qy)
+        lv.smul(lv2)
+        r *= lv
 
     return r
 
 
-def double_miller(P1, Q1, U1, V1):
-    x = curve.x
+def double_ate(P1, Q1, U1, V1):
 
-    if curve.PairingFriendly == BN:
-        n = 6 * x
-        if curve.SignOfX == POSITIVEX:
-            n += 2
-        else:
-            n -= 2
-    else:
-        n = x
-
-    n3 = 3 * n
+    nb,n3,n=lbits()
 
     P = P1.copy()
     Q = Q1.copy()
@@ -144,19 +190,26 @@
     Qx, Qy = Q.getxy()
     B = U.copy()
     Wx, Wy = V.getxy()
-    nb = n3.bit_length()
     r = Fp12.one()
 # miller loop
     for i in range(nb - 2, 0, -1):
         r.sqr()
-        r *= g(A, A, Qx, Qy)
-        r *= g(B, B, Wx, Wy)
+        lv = g(A, A, Qx, Qy)
+        lv2 = g(B, B, Wx, Wy)
+        lv.smul(lv2)
+        r *= lv
+        #r *= g(A, A, Qx, Qy)
+        #r *= g(B, B, Wx, Wy)
         if big.bit(n3, i) == 1 and big.bit(n, i) == 0:
-            r *= g(A, P, Qx, Qy)
-            r *= g(B, U, Wx, Wy)
+            lv = g(A, P, Qx, Qy)
+            lv2 = g(B, U, Wx, Wy)
+            lv.smul(lv2)
+            r *= lv
         if big.bit(n3, i) == 0 and big.bit(n, i) == 1:
-            r *= g(A, -P, Qx, Qy)
-            r *= g(B, -U, Wx, Wy)
+            lv = g(A, -P, Qx, Qy)
+            lv2 = g(B, -U, Wx, Wy)
+            lv.smul(lv2)
+            r *= lv
 # adjustment
     if curve.SignOfX == NEGATIVEX:
         r.conj()
@@ -167,18 +220,22 @@
         if curve.SignOfX == NEGATIVEX:
             A = -A
             B = -B
-        r *= g(A, KA, Qx, Qy)
+        lv = g(A, KA, Qx, Qy)
         KA.frobenius()
         KA = -KA
-        r *= g(A, KA, Qx, Qy)
+        lv2 = g(A, KA, Qx, Qy)
+        lv.smul(lv2)
+        r *= lv
 
         KB = U.copy()
         KB.frobenius()
 
-        r *= g(B, KB, Wx, Wy)
+        lv = g(B, KB, Wx, Wy)
         KB.frobenius()
         KB = -KB
-        r *= g(B, KB, Wx, Wy)
+        lv2 = g(B, KB, Wx, Wy)
+        lv.smul(lv2)
+        r *= lv
 
     return r
 
diff --git a/version3/readme.txt b/version3/readme.txt
index 0726b16..e648fc1 100644
--- a/version3/readme.txt
+++ b/version3/readme.txt
@@ -1,61 +1,17 @@
-The Apache Milagro Cryptographic Library
+**New** August 2018. A Python version of the library is now available.
 
-Note that the AMCL currently comes in two versions, version 2.2 
-and version 3.2
+Several helper programs are provided to assist with the addition of
+new elliptic curves. Note that these programs will not be needed if using
+one of the supported curves. These programs must be build using the MIRACL
+library. See source code for compilation instructions
 
----------------------------------------
+bigtobig.cpp - converts to BIG number format
 
-AMCL v2.2 is presented in what might be called a pre-library state.
+check.cpp - checks for optimal choice of number base
 
-In the various supported languages the source code is made available,
-but it is not organised into rigid packages/crates/jars/whatever
-It is expected that the consumer will themselves take this final step,
-depending on the exact requirements of their project.
+bestpair.cpp - finds best BN, BLS12 and BLS24 pairing-friendly curves
+(Note - the library does not currently support BLS24 curves)
 
-Note that version 2.2 is no longer supported.
+romgen.cpp - rough-and-ready program used to help generate ROM files for
+all of the different languages.
 
------------------------------------
-
-AMCL v3.2 incorporates many minor improvements
-
-Python version
-Web Assembly support
-Improved side channel resistance
-Faster Swift code
-Better Rust build system
-Improved modular inversion algorithm
-General speed optimizations
-Improved Javascript testbed
-More curves supported
-New BLS signature API
-Post quantum New Hope Implementation
-
------------------------------------
-
-AMCL v3.1 uses a standard Python 3 script to build libraries in all
-supported languages. New users should use this version.
-
-The main improvement is that AMCL v3 can optionally simultaneously support 
-multiple elliptic curves and RSA key sizes within a single appliction.
-
-Note that AMCL is largely configured at compile time. In version 3 this
-configuration is handled by the Python script.
-
-AMCL is available in 32-bit and 64-bit versions in most languages. Limited 
-support for 16-bit processors is provided by the C version.
-
-Now languages like to remain "standard" irrespective of the underlying 
-hardware. However when it comes to optimal performance, it is impossible 
-to remain architecture-agnostic. If a processor supports 64-bit 
-instructions that operate on 64-bit registers, it will be a waste not to
-use them. Therefore the 64-bit language versions should always be used
-on 64-bit processors.
-
-Version 3.1 is a major "under the hood" upgrade. Field arithmetic is 
-performed using ideas from http://eprint.iacr.org/2017/437 to ensure 
-that critical calculations are performed in constant time. This strongly 
-mitigates against side-channel attacks. Exception-free formulae are 
-now used for Weierstrass elliptic curves. A new standardised script 
-builds for the same set of curves across all languages.
-
----------------------------------------------
diff --git a/version3/rust/BenchtestALL.rs b/version3/rust/BenchtestALL.rs
index fff4e0d..55961be 100644
--- a/version3/rust/BenchtestALL.rs
+++ b/version3/rust/BenchtestALL.rs
@@ -715,7 +715,7 @@
 		println!("BN Pairing-Friendly Curve");
 	}
 	if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BLS {
-		println!("BLS24 Pairing-Friendly Curve");
+		println!("BLS48 Pairing-Friendly Curve");
 	}
 
 	println!("Modulus size {:} bits", fp::MODBITS);
diff --git a/version3/rust/src/bls.rs b/version3/rust/src/bls.rs
index 24381b4..7e7fd7a 100644
--- a/version3/rust/src/bls.rs
+++ b/version3/rust/src/bls.rs
@@ -78,7 +78,16 @@
     let g = ECP2::generator();
     let pk = ECP2::frombytes(&w);
     d.neg();
-    let mut v = pair::ate2(&g, &d, &pk, &hm);
+
+// 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;
diff --git a/version3/rust/src/bls192.rs b/version3/rust/src/bls192.rs
index 20dd579..20ee92e 100644
--- a/version3/rust/src/bls192.rs
+++ b/version3/rust/src/bls192.rs
@@ -78,7 +78,16 @@
     let g = ECP4::generator();
     let pk = ECP4::frombytes(&w);
     d.neg();
-    let mut v = pair192::ate2(&g, &d, &pk, &hm);
+
+// 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;
diff --git a/version3/rust/src/bls256.rs b/version3/rust/src/bls256.rs
index e9a00fb..cdb553d 100644
--- a/version3/rust/src/bls256.rs
+++ b/version3/rust/src/bls256.rs
@@ -78,7 +78,16 @@
     let g = ECP8::generator();
     let pk = ECP8::frombytes(&w);
     d.neg();
-    let mut v = pair256::ate2(&g, &d, &pk, &hm);
+
+// 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;
diff --git a/version3/rust/src/ecp4.rs b/version3/rust/src/ecp4.rs
index 79f3ee9..d34b0fd 100644
--- a/version3/rust/src/ecp4.rs
+++ b/version3/rust/src/ecp4.rs
@@ -24,7 +24,7 @@
 use super::fp4::FP4;
 use super::big::BIG;
 use types::{SexticTwist, SignOfX};
-use std::str::SplitWhitespace;
+//use std::str::SplitWhitespace;
 
 pub struct ECP4 {
     x: FP4,
diff --git a/version3/rust/src/ff.rs b/version3/rust/src/ff.rs
index 9a6611b..4737ee8 100644
--- a/version3/rust/src/ff.rs
+++ b/version3/rust/src/ff.rs
@@ -29,7 +29,7 @@
 /* RSA/DH modulus length as multiple of BIGBITS */
 
 pub use super::rom::FFLEN;
-use std::str::SplitWhitespace;
+//use std::str::SplitWhitespace;
 
 pub const FF_BITS: usize = (big::BIGBITS * FFLEN); /* Finite Field Size in bits - must be 256.2^n */
 pub const HFLEN: usize = (FFLEN / 2); /* Useful for half-size RSA private key operations */
diff --git a/version3/rust/src/fp12.rs b/version3/rust/src/fp12.rs
index 98e6d31..4e5bfe5 100644
--- a/version3/rust/src/fp12.rs
+++ b/version3/rust/src/fp12.rs
@@ -24,12 +24,20 @@
 use super::rom;
 use types::SexticTwist;
 use std::str::SplitWhitespace;
+use super::ecp;
+
+pub const ZERO: usize=0;
+pub const ONE: usize=1;
+pub const SPARSER: usize=2;
+pub const SPARSE: usize=3;
+pub const DENSE: usize=4;
 
 #[derive(Copy, Clone)]
 pub struct FP12 {
     a: FP4,
     b: FP4,
     c: FP4,
+    stype: usize,
 }
 
 impl PartialEq for FP12 {
@@ -44,14 +52,28 @@
             a: FP4::new(),
             b: FP4::new(),
             c: FP4::new(),
+	    stype: ZERO,
         }
     }
 
+    pub fn settype(&mut self,t: usize)  {
+	self.stype = t;
+    }
+
+    pub fn gettype(&self) -> usize {
+        return self.stype;
+    }
+
     pub fn new_int(a: isize) -> FP12 {
         let mut f = FP12::new();
         f.a.copy(&FP4::new_int(a));
         f.b.zero();
         f.c.zero();
+	if a == 1 {
+	    f.stype=ONE;
+	} else {
+	    f.stype=SPARSER;
+	}
         return f;
     }
 
@@ -60,6 +82,7 @@
         f.a.copy(&x.a);
         f.b.copy(&x.b);
         f.c.copy(&x.c);
+	f.stype=x.stype;
         return f;
     }
 
@@ -68,6 +91,7 @@
         g.a.copy(d);
         g.b.copy(e);
         g.c.copy(f);
+	g.stype=DENSE;
         return g;
     }
 
@@ -76,6 +100,7 @@
         g.a.copy(d);
         g.b.zero();
         g.c.zero();
+	g.stype=SPARSER;
         return g;
     }
 
@@ -104,6 +129,9 @@
         self.a.cmove(&g.a, d);
         self.b.cmove(&g.b, d);
         self.c.cmove(&g.c, d);
+	let mut u=d as usize;
+	u=!(u-1);
+	self.stype^=(self.stype^g.stype)&u;
     }
 
     /* return 1 if b==c, no branching */
@@ -146,18 +174,21 @@
     }
 
     pub fn geta(&mut self) -> FP4 {
-        let f = FP4::new_copy(&self.a);
-        return f;
+        return self.a;
+//        let f = FP4::new_copy(&self.a);
+//        return f;
     }
 
     pub fn getb(&mut self) -> FP4 {
-        let f = FP4::new_copy(&self.b);
-        return f;
+        return self.b;
+//        let f = FP4::new_copy(&self.b);
+//        return f;
     }
 
     pub fn getc(&mut self) -> FP4 {
-        let f = FP4::new_copy(&self.c);
-        return f;
+        return self.c;
+//        let f = FP4::new_copy(&self.c);
+//        return f;
     }
 
     /* copy self=x */
@@ -165,6 +196,7 @@
         self.a.copy(&x.a);
         self.b.copy(&x.b);
         self.c.copy(&x.c);
+	self.stype=x.stype;
     }
 
     /* set self=1 */
@@ -172,6 +204,7 @@
         self.a.one();
         self.b.zero();
         self.c.zero();
+	self.stype=ONE;
     }
 
     /* this=conj(this) */
@@ -219,11 +252,16 @@
         self.c.dbl();
         self.b.add(&b);
         self.c.add(&c);
+        self.stype=DENSE;
         self.reduce();
     }
 
     /* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
     pub fn sqr(&mut self) {
+        if self.stype==ONE {
+            return;
+        }
+
         let mut a = FP4::new_copy(&self.a);
         let mut b = FP4::new_copy(&self.b);
         let mut c = FP4::new_copy(&self.c);
@@ -258,6 +296,11 @@
         self.b.copy(&c);
         self.b.add(&d);
         self.c.add(&a);
+        if self.stype==SPARSER {
+            self.stype=SPARSE;
+        } else {
+            self.stype=DENSE;
+        }
         self.norm();
     }
 
@@ -328,114 +371,310 @@
         z3.times_i();
         self.a.copy(&z0);
         self.a.add(&z3);
+        self.stype=DENSE;
+        self.norm();
+    }
+
+
+/* FP12 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+    pub fn ssmul(&mut self, y: &FP12) {
+        if self.stype==ONE {
+            self.copy(&y);
+            return;
+        }
+        if y.stype==ONE {
+            return;
+        }
+        if y.stype>=SPARSE {
+            let mut z0=FP4::new_copy(&self.a);
+            let mut z1=FP4::new_int(0);
+            let mut z2=FP4::new_int(0);
+            let mut z3=FP4::new_int(0);
+            z0.mul(&y.a);
+
+            if ecp::SEXTIC_TWIST==SexticTwist::M_TYPE {
+                if y.stype==SPARSE || self.stype==SPARSE {
+
+                    let mut ga=FP2::new_int(0);
+		    let mut gb=FP2::new_int(0);
+
+                    gb.copy(&self.b.getb());
+                    gb.mul(&y.b.getb());
+                    ga.zero();
+                    if y.stype!=SPARSE {
+                        ga.copy(&self.b.getb());
+                        ga.mul(&y.b.geta());
+                    }
+                    if self.stype!=SPARSE {
+                        ga.copy(&self.b.geta());
+                        ga.mul(&y.b.getb());
+                    }
+		    z2.set_fp2s(&ga,&gb);
+                    z2.times_i();
+                } else {
+                    z2.copy(&self.b);
+                    z2.mul(&y.b);
+                }
+            } else { 
+               z2.copy(&self.b);
+               z2.mul(&y.b);
+            }
+            let mut t0=FP4::new_copy(&self.a);
+            let mut t1=FP4::new_copy(&y.a);
+            t0.add(&self.b); t0.norm();
+            t1.add(&y.b); t1.norm();
+
+            z1.copy(&t0); z1.mul(&t1);
+            t0.copy(&self.b); t0.add(&self.c); t0.norm();
+            t1.copy(&y.b); t1.add(&y.c); t1.norm();
+
+            z3.copy(&t0); z3.mul(&t1);
+
+            t0.copy(&z0); t0.neg();
+            t1.copy(&z2); t1.neg();
+ 
+            z1.add(&t0);
+            self.b.copy(&z1); self.b.add(&t1);
+
+            z3.add(&t1);
+            z2.add(&t0);
+
+            t0.copy(&self.a); t0.add(&self.c); t0.norm();
+            t1.copy(&y.a); t1.add(&y.c); t1.norm();
+	
+            t0.mul(&t1);
+            z2.add(&t0);
+
+            if ecp::SEXTIC_TWIST==SexticTwist::D_TYPE {
+                if y.stype==SPARSE || self.stype==SPARSE {
+
+                    let mut ga=FP2::new_int(0);
+		    let mut gb=FP2::new_int(0);
+
+                    ga.copy(&self.c.geta());
+                    ga.mul(&y.c.geta());
+                    gb.zero();
+                    if y.stype!=SPARSE {
+                        gb.copy(&self.c.geta());
+                        gb.mul(&y.c.getb());
+                    }
+                    if self.stype!=SPARSE {
+                        gb.copy(&self.c.getb());
+                        gb.mul(&y.c.geta());
+                    }
+		    t0.set_fp2s(&ga,&gb);
+                } else {
+                    t0.copy(&self.c);
+                    t0.mul(&y.c);
+                }
+            } else { 
+                t0.copy(&self.c);
+                t0.mul(&y.c);
+            }
+            t1.copy(&t0); t1.neg();
+
+            self.c.copy(&z2); self.c.add(&t1);
+            z3.add(&t1);
+            t0.times_i();
+            self.b.add(&t0);
+            z3.norm();
+            z3.times_i();
+            self.a.copy(&z0); self.a.add(&z3);
+        } else {
+            if self.stype==SPARSER {
+                self.smul(&y);
+                return;
+            }
+            if ecp::SEXTIC_TWIST==SexticTwist::D_TYPE { // dense by sparser - 13m 
+                let mut z0=FP4::new_copy(&self.a);
+                let mut z2=FP4::new_copy(&self.b);
+                let mut z3=FP4::new_copy(&self.b);
+                let mut t0=FP4::new_int(0);
+                let mut t1=FP4::new_copy(&y.a);
+
+                z0.mul(&y.a);
+                z2.pmul(&y.b.geta());
+                self.b.add(&self.a);
+                t1.padd(&y.b.geta());
+
+                t1.norm();
+                self.b.norm();
+                self.b.mul(&t1);
+                z3.add(&self.c);
+                z3.norm();
+                z3.pmul(&y.b.geta());
+
+                t0.copy(&z0); t0.neg();
+                t1.copy(&z2); t1.neg();
+
+                self.b.add(&t0);
+
+                self.b.add(&t1);
+                z3.add(&t1);
+                z2.add(&t0);
+
+                t0.copy(&self.a); t0.add(&self.c); t0.norm();
+                z3.norm();
+                t0.mul(&y.a);
+                self.c.copy(&z2); self.c.add(&t0);
+
+                z3.times_i();
+                self.a.copy(&z0); self.a.add(&z3);
+            }
+            if ecp::SEXTIC_TWIST==SexticTwist::M_TYPE {
+
+                let mut z0 = FP4::new_copy(&self.a);
+                let mut z1 = FP4::new();
+                let mut z2 = FP4::new();
+                let mut z3 = FP4::new();
+                let mut t0 = FP4::new_copy(&self.a);
+                let mut t1 = FP4::new();
+	
+                z0.mul(&y.a);
+                t0.add(&self.b); t0.norm();
+
+                z1.copy(&t0); z1.mul(&y.a);
+                t0.copy(&self.b); t0.add(&self.c);
+                t0.norm();
+
+                z3.copy(&t0);
+                z3.pmul(&y.c.getb());
+                z3.times_i();
+
+                t0.copy(&z0); t0.neg();
+                z1.add(&t0);
+                self.b.copy(&z1);
+                z2.copy(&t0);
+
+                t0.copy(&self.a); t0.add(&self.c); t0.norm();
+                t1.copy(&y.a); t1.add(&y.c); t1.norm();
+
+                t0.mul(&t1);
+                z2.add(&t0);
+                t0.copy(&self.c);
+			
+                t0.pmul(&y.c.getb());
+                t0.times_i();
+                t1.copy(&t0); t1.neg();
+
+                self.c.copy(&z2); self.c.add(&t1);
+                z3.add(&t1);
+                t0.times_i();
+                self.b.add(&t0);
+                z3.norm();
+                z3.times_i();
+                self.a.copy(&z0); self.a.add(&z3);
+           }	
+        }
+        self.stype=DENSE;
         self.norm();
     }
 
     /* Special case of multiplication arises from special form of ATE pairing line function */
-    pub fn smul(&mut self, y: &FP12, twist: usize) {
-        if twist == SexticTwist::D_TYPE.into() {
-            let mut z0 = FP4::new_copy(&self.a);
-            let mut z2 = FP4::new_copy(&self.b);
-            let mut z3 = FP4::new_copy(&self.b);
-            let mut t0 = FP4::new();
-            let mut t1 = FP4::new_copy(&y.a);
+    pub fn smul(&mut self, y: &FP12) {
+        if ecp::SEXTIC_TWIST==SexticTwist::D_TYPE {	
+            let mut w1=FP2::new_copy(&self.a.geta());
+            let mut w2=FP2::new_copy(&self.a.getb());
+            let mut w3=FP2::new_copy(&self.b.geta());
 
-            z0.mul(&y.a);
-            z2.pmul(&y.b.real());
-            self.b.add(&self.a);
-            t1.padd(&y.b.real());
+            w1.mul(&y.a.geta());
+            w2.mul(&y.a.getb());
+            w3.mul(&y.b.geta());
 
+            let mut ta=FP2::new_copy(&self.a.geta());
+            let mut tb=FP2::new_copy(&y.a.geta());
+            ta.add(&self.a.getb()); ta.norm();
+            tb.add(&y.a.getb()); tb.norm();
+            let mut tc=FP2::new_copy(&ta);
+            tc.mul(&tb);
+            let mut t=FP2::new_copy(&w1);
+            t.add(&w2);
+            t.neg();
+            tc.add(&t);
+
+            ta.copy(&self.a.geta()); ta.add(&self.b.geta()); ta.norm();
+            tb.copy(&y.a.geta()); tb.add(&y.b.geta()); tb.norm();
+            let mut td=FP2::new_copy(&ta);
+            td.mul(&tb);
+            t.copy(&w1);
+            t.add(&w3);
+            t.neg();
+            td.add(&t);
+
+            ta.copy(&self.a.getb()); ta.add(&self.b.geta()); ta.norm();
+            tb.copy(&y.a.getb()); tb.add(&y.b.geta()); tb.norm();
+            let mut te=FP2::new_copy(&ta);
+            te.mul(&tb);
+            t.copy(&w2);
+            t.add(&w3);
+            t.neg();
+            te.add(&t);
+
+            w2.mul_ip();
+            w1.add(&w2);
+
+	    self.a.set_fp2s(&w1,&tc);
+	    self.b.set_fp2s(&td,&te);
+	    self.c.set_fp2(&w3);
+           
+            self.a.norm();
             self.b.norm();
-            t1.norm();
+        } else {
+            let mut w1=FP2::new_copy(&self.a.geta());
+            let mut w2=FP2::new_copy(&self.a.getb());
+            let mut w3=FP2::new_copy(&self.c.getb());
 
-            self.b.mul(&t1);
-            z3.add(&self.c);
-            z3.norm();
-            z3.pmul(&y.b.real());
+            w1.mul(&y.a.geta());
+            w2.mul(&y.a.getb());
+            w3.mul(&y.c.getb());
 
-            t0.copy(&z0);
-            t0.neg();
-            t1.copy(&z2);
-            t1.neg();
+            let mut ta=FP2::new_copy(&self.a.geta());
+            let mut tb=FP2::new_copy(&y.a.geta());
+            ta.add(&self.a.getb()); ta.norm();
+            tb.add(&y.a.getb()); tb.norm();
+            let mut tc=FP2::new_copy(&ta);
+            tc.mul(&tb);
+            let mut t=FP2::new_copy(&w1);
+            t.add(&w2);
+            t.neg();
+            tc.add(&t);
 
-            self.b.add(&t0);
+            ta.copy(&self.a.geta()); ta.add(&self.c.getb()); ta.norm();
+            tb.copy(&y.a.geta()); tb.add(&y.c.getb()); tb.norm();
+            let mut td=FP2::new_copy(&ta);
+            td.mul(&tb);
+            t.copy(&w1);
+            t.add(&w3);
+            t.neg();
+            td.add(&t);
 
-            self.b.add(&t1);
-            z3.add(&t1);
-            z2.add(&t0);
+            ta.copy(&self.a.getb()); ta.add(&self.c.getb()); ta.norm();
+            tb.copy(&y.a.getb()); tb.add(&y.c.getb()); tb.norm();
+            let mut te=FP2::new_copy(&ta);
+            te.mul(&tb);
+            t.copy(&w2);
+            t.add(&w3);
+            t.neg();
+            te.add(&t);
 
-            t0.copy(&self.a);
-            t0.add(&self.c);
-            t0.norm();
-            z3.norm();
+            w2.mul_ip();
+            w1.add(&w2);
+	    self.a.set_fp2s(&w1,&tc);
 
-            t0.mul(&y.a);
-            self.c.copy(&z2);
-            self.c.add(&t0);
+            w3.mul_ip();
+            w3.norm();
+	    self.b.set_fp2h(&w3);
 
-            z3.times_i();
-            self.a.copy(&z0);
-            self.a.add(&z3);
-        }
-        if twist == SexticTwist::M_TYPE.into() {
-            let mut z0 = FP4::new_copy(&self.a);
-            let mut z1 = FP4::new();
-            let mut z2 = FP4::new();
-            let mut z3 = FP4::new();
-            let mut t0 = FP4::new_copy(&self.a);
-            let mut t1 = FP4::new();
+            te.norm();
+            te.mul_ip();
+	    self.c.set_fp2s(&te,&td);
 
-            z0.mul(&y.a);
-            t0.add(&self.b);
-            t0.norm();
-
-            z1.copy(&t0);
-            z1.mul(&y.a);
-            t0.copy(&self.b);
-            t0.add(&self.c);
-            t0.norm();
-
-            z3.copy(&t0); //z3.mul(y.c);
-            z3.pmul(&y.c.getb());
-            z3.times_i();
-
-            t0.copy(&z0);
-            t0.neg();
-
-            z1.add(&t0);
-            self.b.copy(&z1);
-            z2.copy(&t0);
-
-            t0.copy(&self.a);
-            t0.add(&self.c);
-            t1.copy(&y.a);
-            t1.add(&y.c);
-
-            t0.norm();
-            t1.norm();
-
-            t0.mul(&t1);
-            z2.add(&t0);
-
-            t0.copy(&self.c);
-
-            t0.pmul(&y.c.getb());
-            t0.times_i();
-
-            t1.copy(&t0);
-            t1.neg();
-
-            self.c.copy(&z2);
-            self.c.add(&t1);
-            z3.add(&t1);
-            t0.times_i();
-            self.b.add(&t0);
-            z3.norm();
-            z3.times_i();
-            self.a.copy(&z0);
-            self.a.add(&z3);
-        }
-        self.norm();
+            self.a.norm();
+            self.c.norm();
+	}
+	self.stype=SPARSE;
     }
 
     /* self=1/self */
@@ -483,6 +722,7 @@
         self.b.mul(&f3);
         self.c.copy(&f2);
         self.c.mul(&f3);
+        self.stype=DENSE;
     }
 
     /* self=self^p using Frobenius */
@@ -499,6 +739,7 @@
 
         self.b.pmul(f);
         self.c.pmul(&f2);
+        self.stype=DENSE;
     }
 
     /* trace function */
@@ -660,7 +901,8 @@
         FP12 {
             a: FP4::from_hex_iter(iter),
             b: FP4::from_hex_iter(iter),
-            c: FP4::from_hex_iter(iter)
+            c: FP4::from_hex_iter(iter),
+	    stype: DENSE
         }
     }
 
diff --git a/version3/rust/src/fp16.rs b/version3/rust/src/fp16.rs
index 0cbc348..c579db4 100644
--- a/version3/rust/src/fp16.rs
+++ b/version3/rust/src/fp16.rs
@@ -20,7 +20,7 @@
 use super::fp2::FP2;
 use super::fp8::FP8;
 use super::big::BIG;
-use std::str::SplitWhitespace;
+//use std::str::SplitWhitespace;
 
 #[derive(Copy, Clone)]
 pub struct FP16 {
@@ -64,6 +64,21 @@
         return f;
     }
 
+    pub fn set_fp8s(&mut self,c: &FP8, d: &FP8) {
+        self.a.copy(&c);
+	self.b.copy(&d);
+    }
+
+    pub fn set_fp8(&mut self,c: &FP8) {
+        self.a.copy(&c);
+	self.b.zero();
+    }
+
+    pub fn set_fp8h(&mut self,c: &FP8) {
+        self.b.copy(&c);
+	self.a.zero();
+    }
+
     /* reduce components mod Modulus */
     pub fn reduce(&mut self) {
         self.a.reduce();
@@ -103,13 +118,15 @@
     }
 
     pub fn geta(&self) -> FP8 {
-        let f = FP8::new_copy(&self.a);
-        return f;
+        return self.a;
+//        let f = FP8::new_copy(&self.a);
+//        return f;
     }
     /* extract imaginary part b */
     pub fn getb(&self) -> FP8 {
-        let f = FP8::new_copy(&self.b);
-        return f;
+        return self.b;
+//        let f = FP8::new_copy(&self.b);
+//        return f;
     }
 
     /* test self=x */
diff --git a/version3/rust/src/fp24.rs b/version3/rust/src/fp24.rs
index a7067bf..20149c8 100644
--- a/version3/rust/src/fp24.rs
+++ b/version3/rust/src/fp24.rs
@@ -25,13 +25,20 @@
 use super::big::BIG;
 use super::rom;
 use types::{SexticTwist};
-use std::str::SplitWhitespace;
+//use std::str::SplitWhitespace;
+
+pub const ZERO: usize=0;
+pub const ONE: usize=1;
+pub const SPARSER: usize=2;
+pub const SPARSE: usize=3;
+pub const DENSE: usize=4;
 
 #[derive(Copy, Clone)]
 pub struct FP24 {
     a: FP8,
     b: FP8,
     c: FP8,
+    stype: usize,
 }
 
 impl FP24 {
@@ -40,14 +47,28 @@
             a: FP8::new(),
             b: FP8::new(),
             c: FP8::new(),
+            stype: ZERO,
         }
     }
 
+    pub fn settype(&mut self,t: usize)  {
+	self.stype = t;
+    }
+
+    pub fn gettype(&self) -> usize {
+        return self.stype;
+    }
+
     pub fn new_int(a: isize) -> FP24 {
         let mut f = FP24::new();
         f.a.copy(&FP8::new_int(a));
         f.b.zero();
         f.c.zero();
+	if a == 1 {
+	    f.stype=ONE;
+	} else {
+	    f.stype=SPARSER;
+	}
         return f;
     }
 
@@ -56,6 +77,7 @@
         f.a.copy(&x.a);
         f.b.copy(&x.b);
         f.c.copy(&x.c);
+	f.stype=x.stype;
         return f;
     }
 
@@ -64,6 +86,7 @@
         g.a.copy(d);
         g.b.copy(e);
         g.c.copy(f);
+	g.stype=DENSE;
         return g;
     }
 
@@ -72,6 +95,7 @@
         g.a.copy(d);
         g.b.zero();
         g.c.zero();
+	g.stype=SPARSER;
         return g;
     }
 
@@ -99,6 +123,9 @@
         self.a.cmove(&g.a, d);
         self.b.cmove(&g.b, d);
         self.c.cmove(&g.c, d);
+	let mut u=d as usize;
+	u=!(u-1);
+	self.stype^=(self.stype^g.stype)&u;
     }
 
     /* return 1 if b==c, no branching */
@@ -141,18 +168,21 @@
     }
 
     pub fn geta(&mut self) -> FP8 {
-        let f = FP8::new_copy(&self.a);
-        return f;
+        return self.a;
+//        let f = FP8::new_copy(&self.a);
+//        return f;
     }
 
     pub fn getb(&mut self) -> FP8 {
-        let f = FP8::new_copy(&self.b);
-        return f;
+        return self.b;
+//	let f = FP8::new_copy(&self.b);
+//        return f;
     }
 
     pub fn getc(&mut self) -> FP8 {
-        let f = FP8::new_copy(&self.c);
-        return f;
+        return self.c;
+//        let f = FP8::new_copy(&self.c);
+//        return f;
     }
 
     /* copy self=x */
@@ -160,6 +190,7 @@
         self.a.copy(&x.a);
         self.b.copy(&x.b);
         self.c.copy(&x.c);
+	self.stype=x.stype;
     }
 
     /* set self=1 */
@@ -167,6 +198,7 @@
         self.a.one();
         self.b.zero();
         self.c.zero();
+	self.stype=ONE;
     }
 
     /* this=conj(this) */
@@ -214,11 +246,15 @@
         self.c.dbl();
         self.b.add(&b);
         self.c.add(&c);
+        self.stype=DENSE;
         self.reduce();
     }
 
     /* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
     pub fn sqr(&mut self) {
+        if self.stype==ONE {
+            return;
+        }
         let mut a = FP8::new_copy(&self.a);
         let mut b = FP8::new_copy(&self.b);
         let mut c = FP8::new_copy(&self.c);
@@ -253,6 +289,11 @@
         self.b.copy(&c);
         self.b.add(&d);
         self.c.add(&a);
+        if self.stype==SPARSER {
+            self.stype=SPARSE;
+        } else {
+            self.stype=DENSE;
+        }
         self.norm();
     }
 
@@ -324,116 +365,312 @@
         z3.times_i();
         self.a.copy(&z0);
         self.a.add(&z3);
+        self.stype=DENSE;
         self.norm();
     }
 
-    /* Special case of multiplication arises from special form of ATE pairing line function */
-    pub fn smul(&mut self, y: &FP24, twist: usize) {
-        if twist == SexticTwist::D_TYPE.into() {
-            let mut z0 = FP8::new_copy(&self.a);
-            let mut z2 = FP8::new_copy(&self.b);
-            let mut z3 = FP8::new_copy(&self.b);
-            let mut t0 = FP8::new();
-            let mut t1 = FP8::new_copy(&y.a);
-
+/* FP24 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+    pub fn ssmul(&mut self, y: &FP24) {
+        if self.stype==ONE {
+            self.copy(&y);
+            return;
+        }
+        if y.stype==ONE {
+            return;
+        }
+        if y.stype>=SPARSE {
+            let mut z0=FP8::new_copy(&self.a);
+            let mut z1=FP8::new_int(0);
+            let mut z2=FP8::new_int(0);
+            let mut z3=FP8::new_int(0);
             z0.mul(&y.a);
-            z2.pmul(&y.b.real());
-            self.b.add(&self.a);
-            t1.padd(&y.b.real());
 
-            self.b.norm();
-            t1.norm();
+            if ecp::SEXTIC_TWIST==SexticTwist::M_TYPE {
+                if y.stype==SPARSE || self.stype==SPARSE {
 
-            self.b.mul(&t1);
-            z3.add(&self.c);
-            z3.norm();
-            z3.pmul(&y.b.real());
+                    let mut ga=FP4::new_int(0);
+		    let mut gb=FP4::new_int(0);
 
-            t0.copy(&z0);
-            t0.neg();
-            t1.copy(&z2);
-            t1.neg();
+                    gb.copy(&self.b.getb());
+                    gb.mul(&y.b.getb());
+                    ga.zero();
+                    if y.stype!=SPARSE {
+                        ga.copy(&self.b.getb());
+                        ga.mul(&y.b.geta());
+                    }
+                    if self.stype!=SPARSE {
+                        ga.copy(&self.b.geta());
+                        ga.mul(&y.b.getb());
+                    }
+		    z2.set_fp4s(&ga,&gb);
+                    z2.times_i();
+                } else {
+                    z2.copy(&self.b);
+                    z2.mul(&y.b);
+                }
+            } else { 
+               z2.copy(&self.b);
+               z2.mul(&y.b);
+            }
+            let mut t0=FP8::new_copy(&self.a);
+            let mut t1=FP8::new_copy(&y.a);
+            t0.add(&self.b); t0.norm();
+            t1.add(&y.b); t1.norm();
 
-            self.b.add(&t0);
+            z1.copy(&t0); z1.mul(&t1);
+            t0.copy(&self.b); t0.add(&self.c); t0.norm();
+            t1.copy(&y.b); t1.add(&y.c); t1.norm();
 
-            self.b.add(&t1);
+            z3.copy(&t0); z3.mul(&t1);
+
+            t0.copy(&z0); t0.neg();
+            t1.copy(&z2); t1.neg();
+ 
+            z1.add(&t0);
+            self.b.copy(&z1); self.b.add(&t1);
+
             z3.add(&t1);
             z2.add(&t0);
 
-            t0.copy(&self.a);
-            t0.add(&self.c);
-            t0.norm();
-            z3.norm();
-
-            t0.mul(&y.a);
-            self.c.copy(&z2);
-            self.c.add(&t0);
-
-            z3.times_i();
-            self.a.copy(&z0);
-            self.a.add(&z3);
-        }
-        if twist == SexticTwist::M_TYPE.into() {
-            let mut z0 = FP8::new_copy(&self.a);
-            let mut z1 = FP8::new();
-            let mut z2 = FP8::new();
-            let mut z3 = FP8::new();
-            let mut t0 = FP8::new_copy(&self.a);
-            let mut t1 = FP8::new();
-
-            z0.mul(&y.a);
-            t0.add(&self.b);
-            t0.norm();
-
-            z1.copy(&t0);
-            z1.mul(&y.a);
-            t0.copy(&self.b);
-            t0.add(&self.c);
-            t0.norm();
-
-            z3.copy(&t0);
-            z3.pmul(&y.c.getb());
-            z3.times_i();
-
-            t0.copy(&z0);
-            t0.neg();
-
-            z1.add(&t0);
-            self.b.copy(&z1);
-            z2.copy(&t0);
-
-            t0.copy(&self.a);
-            t0.add(&self.c);
-            t1.copy(&y.a);
-            t1.add(&y.c);
-
-            t0.norm();
-            t1.norm();
-
+            t0.copy(&self.a); t0.add(&self.c); t0.norm();
+            t1.copy(&y.a); t1.add(&y.c); t1.norm();
+	
             t0.mul(&t1);
             z2.add(&t0);
 
-            t0.copy(&self.c);
+            if ecp::SEXTIC_TWIST==SexticTwist::D_TYPE {
+                if y.stype==SPARSE || self.stype==SPARSE {
 
-            t0.pmul(&y.c.getb());
-            t0.times_i();
+                    let mut ga=FP4::new_int(0);
+		    let mut gb=FP4::new_int(0);
 
-            t1.copy(&t0);
-            t1.neg();
+                    ga.copy(&self.c.geta());
+                    ga.mul(&y.c.geta());
+                    gb.zero();
+                    if y.stype!=SPARSE {
+                        gb.copy(&self.c.geta());
+                        gb.mul(&y.c.getb());
+                    }
+                    if self.stype!=SPARSE {
+                        gb.copy(&self.c.getb());
+                        gb.mul(&y.c.geta());
+                    }
+		    t0.set_fp4s(&ga,&gb);
+                } else {
+                    t0.copy(&self.c);
+                    t0.mul(&y.c);
+                }
+            } else { 
+                t0.copy(&self.c);
+                t0.mul(&y.c);
+            }
+            t1.copy(&t0); t1.neg();
 
-            self.c.copy(&z2);
-            self.c.add(&t1);
+            self.c.copy(&z2); self.c.add(&t1);
             z3.add(&t1);
             t0.times_i();
             self.b.add(&t0);
             z3.norm();
             z3.times_i();
-            self.a.copy(&z0);
-            self.a.add(&z3);
+            self.a.copy(&z0); self.a.add(&z3);
+        } else {
+            if self.stype==SPARSER {
+                self.smul(&y);
+                return;
+            }
+            if ecp::SEXTIC_TWIST==SexticTwist::D_TYPE { // dense by sparser - 13m 
+                let mut z0=FP8::new_copy(&self.a);
+                let mut z2=FP8::new_copy(&self.b);
+                let mut z3=FP8::new_copy(&self.b);
+                let mut t0=FP8::new_int(0);
+                let mut t1=FP8::new_copy(&y.a);
+
+                z0.mul(&y.a);
+                z2.pmul(&y.b.geta());
+                self.b.add(&self.a);
+                t1.padd(&y.b.geta());
+
+                t1.norm();
+                self.b.norm();
+                self.b.mul(&t1);
+                z3.add(&self.c);
+                z3.norm();
+                z3.pmul(&y.b.geta());
+
+                t0.copy(&z0); t0.neg();
+                t1.copy(&z2); t1.neg();
+
+                self.b.add(&t0);
+
+                self.b.add(&t1);
+                z3.add(&t1);
+                z2.add(&t0);
+
+                t0.copy(&self.a); t0.add(&self.c); t0.norm();
+                z3.norm();
+                t0.mul(&y.a);
+                self.c.copy(&z2); self.c.add(&t0);
+
+                z3.times_i();
+                self.a.copy(&z0); self.a.add(&z3);
+            }
+            if ecp::SEXTIC_TWIST==SexticTwist::M_TYPE {
+
+                let mut z0 = FP8::new_copy(&self.a);
+                let mut z1 = FP8::new();
+                let mut z2 = FP8::new();
+                let mut z3 = FP8::new();
+                let mut t0 = FP8::new_copy(&self.a);
+                let mut t1 = FP8::new();
+	
+                z0.mul(&y.a);
+                t0.add(&self.b); t0.norm();
+
+                z1.copy(&t0); z1.mul(&y.a);
+                t0.copy(&self.b); t0.add(&self.c);
+                t0.norm();
+
+                z3.copy(&t0);
+                z3.pmul(&y.c.getb());
+                z3.times_i();
+
+                t0.copy(&z0); t0.neg();
+                z1.add(&t0);
+                self.b.copy(&z1);
+                z2.copy(&t0);
+
+                t0.copy(&self.a); t0.add(&self.c); t0.norm();
+                t1.copy(&y.a); t1.add(&y.c); t1.norm();
+
+                t0.mul(&t1);
+                z2.add(&t0);
+                t0.copy(&self.c);
+			
+                t0.pmul(&y.c.getb());
+                t0.times_i();
+                t1.copy(&t0); t1.neg();
+
+                self.c.copy(&z2); self.c.add(&t1);
+                z3.add(&t1);
+                t0.times_i();
+                self.b.add(&t0);
+                z3.norm();
+                z3.times_i();
+                self.a.copy(&z0); self.a.add(&z3);
+           }	
         }
+        self.stype=DENSE;
         self.norm();
     }
 
+
+    /* Special case of multiplication arises from special form of ATE pairing line function */
+    pub fn smul(&mut self, y: &FP24) {
+        if ecp::SEXTIC_TWIST==SexticTwist::D_TYPE {	
+            let mut w1=FP4::new_copy(&self.a.geta());
+            let mut w2=FP4::new_copy(&self.a.getb());
+            let mut w3=FP4::new_copy(&self.b.geta());
+
+            w1.mul(&y.a.geta());
+            w2.mul(&y.a.getb());
+            w3.mul(&y.b.geta());
+
+            let mut ta=FP4::new_copy(&self.a.geta());
+            let mut tb=FP4::new_copy(&y.a.geta());
+            ta.add(&self.a.getb()); ta.norm();
+            tb.add(&y.a.getb()); tb.norm();
+            let mut tc=FP4::new_copy(&ta);
+            tc.mul(&tb);
+            let mut t=FP4::new_copy(&w1);
+            t.add(&w2);
+            t.neg();
+            tc.add(&t);
+
+            ta.copy(&self.a.geta()); ta.add(&self.b.geta()); ta.norm();
+            tb.copy(&y.a.geta()); tb.add(&y.b.geta()); tb.norm();
+            let mut td=FP4::new_copy(&ta);
+            td.mul(&tb);
+            t.copy(&w1);
+            t.add(&w3);
+            t.neg();
+            td.add(&t);
+
+            ta.copy(&self.a.getb()); ta.add(&self.b.geta()); ta.norm();
+            tb.copy(&y.a.getb()); tb.add(&y.b.geta()); tb.norm();
+            let mut te=FP4::new_copy(&ta);
+            te.mul(&tb);
+            t.copy(&w2);
+            t.add(&w3);
+            t.neg();
+            te.add(&t);
+
+            w2.times_i();
+            w1.add(&w2);
+
+	    self.a.set_fp4s(&w1,&tc);
+	    self.b.set_fp4s(&td,&te);
+	    self.c.set_fp4(&w3);
+
+            self.a.norm();
+            self.b.norm();
+        } else {
+            let mut w1=FP4::new_copy(&self.a.geta());
+            let mut w2=FP4::new_copy(&self.a.getb());
+            let mut w3=FP4::new_copy(&self.c.getb());
+
+            w1.mul(&y.a.geta());
+            w2.mul(&y.a.getb());
+            w3.mul(&y.c.getb());
+
+            let mut ta=FP4::new_copy(&self.a.geta());
+            let mut tb=FP4::new_copy(&y.a.geta());
+            ta.add(&self.a.getb()); ta.norm();
+            tb.add(&y.a.getb()); tb.norm();
+            let mut tc=FP4::new_copy(&ta);
+            tc.mul(&tb);
+            let mut t=FP4::new_copy(&w1);
+            t.add(&w2);
+            t.neg();
+            tc.add(&t);
+
+            ta.copy(&self.a.geta()); ta.add(&self.c.getb()); ta.norm();
+            tb.copy(&y.a.geta()); tb.add(&y.c.getb()); tb.norm();
+            let mut td=FP4::new_copy(&ta);
+            td.mul(&tb);
+            t.copy(&w1);
+            t.add(&w3);
+            t.neg();
+            td.add(&t);
+
+            ta.copy(&self.a.getb()); ta.add(&self.c.getb()); ta.norm();
+            tb.copy(&y.a.getb()); tb.add(&y.c.getb()); tb.norm();
+            let mut te=FP4::new_copy(&ta);
+            te.mul(&tb);
+            t.copy(&w2);
+            t.add(&w3);
+            t.neg();
+            te.add(&t);
+
+            w2.times_i();
+            w1.add(&w2);
+	    self.a.set_fp4s(&w1,&tc);
+
+            w3.times_i();
+            w3.norm();
+	    self.b.set_fp4h(&w3);
+
+            te.norm();
+            te.times_i();
+	    self.c.set_fp4s(&te,&td);
+
+            self.a.norm();
+            self.c.norm();
+	}
+	self.stype=SPARSE;
+    }
+
     /* self=1/self */
     pub fn inverse(&mut self) {
         let mut f0 = FP8::new_copy(&self.a);
@@ -479,6 +716,7 @@
         self.b.mul(&f3);
         self.c.copy(&f2);
         self.c.mul(&f3);
+        self.stype=DENSE;
     }
 
     /* self=self^p using Frobenius */
@@ -503,6 +741,7 @@
             self.c.times_i2();
             self.c.times_i2();
         }
+        self.stype=DENSE;
     }
 
     /* trace function */
diff --git a/version3/rust/src/fp4.rs b/version3/rust/src/fp4.rs
index 97193ba..1db4b73 100644
--- a/version3/rust/src/fp4.rs
+++ b/version3/rust/src/fp4.rs
@@ -69,6 +69,21 @@
         f.b.zero();
         return f;
     }
+	
+    pub fn set_fp2s(&mut self,c: &FP2, d: &FP2) {
+        self.a.copy(&c);
+	self.b.copy(&d);
+    }
+
+    pub fn set_fp2(&mut self,c: &FP2) {
+        self.a.copy(&c);
+	self.b.zero();
+    }
+
+    pub fn set_fp2h(&mut self,c: &FP2) {
+        self.b.copy(&c);
+	self.a.zero();
+    }
 
     /* reduce components mod Modulus */
     pub fn reduce(&mut self) {
@@ -109,13 +124,15 @@
     }
 
     pub fn geta(&self) -> FP2 {
-        let f = FP2::new_copy(&self.a);
-        return f;
+        return self.a;
+//        let f = FP2::new_copy(&self.a);
+//        return f;
     }
     /* extract imaginary part b */
     pub fn getb(&self) -> FP2 {
-        let f = FP2::new_copy(&self.b);
-        return f;
+        return self.b;
+//        let f = FP2::new_copy(&self.b);
+//        return f;
     }
 
     /* test self=x */
diff --git a/version3/rust/src/fp48.rs b/version3/rust/src/fp48.rs
index 7b57e2b..bda42e0 100644
--- a/version3/rust/src/fp48.rs
+++ b/version3/rust/src/fp48.rs
@@ -26,13 +26,20 @@
 use super::big::BIG;
 use super::rom;
 use types::SexticTwist;
-use std::str::SplitWhitespace;
+//use std::str::SplitWhitespace;
+
+pub const ZERO: usize=0;
+pub const ONE: usize=1;
+pub const SPARSER: usize=2;
+pub const SPARSE: usize=3;
+pub const DENSE: usize=4;
 
 #[derive(Copy, Clone)]
 pub struct FP48 {
     a: FP16,
     b: FP16,
     c: FP16,
+    stype: usize,
 }
 
 impl FP48 {
@@ -41,14 +48,28 @@
             a: FP16::new(),
             b: FP16::new(),
             c: FP16::new(),
+	    stype: ZERO,
         }
     }
 
+    pub fn settype(&mut self,t: usize)  {
+	self.stype = t;
+    }
+
+    pub fn gettype(&self) -> usize {
+        return self.stype;
+    }
+
     pub fn new_int(a: isize) -> FP48 {
         let mut f = FP48::new();
         f.a.copy(&FP16::new_int(a));
         f.b.zero();
         f.c.zero();
+	if a == 1 {
+	    f.stype=ONE;
+	} else {
+	    f.stype=SPARSER;
+	}
         return f;
     }
 
@@ -57,6 +78,7 @@
         f.a.copy(&x.a);
         f.b.copy(&x.b);
         f.c.copy(&x.c);
+	f.stype=x.stype;
         return f;
     }
 
@@ -65,6 +87,7 @@
         g.a.copy(d);
         g.b.copy(e);
         g.c.copy(f);
+	g.stype=DENSE;
         return g;
     }
 
@@ -73,6 +96,7 @@
         g.a.copy(d);
         g.b.zero();
         g.c.zero();
+	g.stype=SPARSER;
         return g;
     }
 
@@ -100,6 +124,9 @@
         self.a.cmove(&g.a, d);
         self.b.cmove(&g.b, d);
         self.c.cmove(&g.c, d);
+	let mut u=d as usize;
+	u=!(u-1);
+	self.stype^=(self.stype^g.stype)&u;
     }
 
     /* return 1 if b==c, no branching */
@@ -142,18 +169,21 @@
     }
 
     pub fn geta(&mut self) -> FP16 {
-        let f = FP16::new_copy(&self.a);
-        return f;
+        return self.a;
+//        let f = FP16::new_copy(&self.a);
+//        return f;
     }
 
     pub fn getb(&mut self) -> FP16 {
-        let f = FP16::new_copy(&self.b);
-        return f;
+        return self.b;
+//        let f = FP16::new_copy(&self.b);
+//        return f;
     }
 
     pub fn getc(&mut self) -> FP16 {
-        let f = FP16::new_copy(&self.c);
-        return f;
+        return self.c;
+//        let f = FP16::new_copy(&self.c);
+//        return f;
     }
 
     /* copy self=x */
@@ -161,6 +191,7 @@
         self.a.copy(&x.a);
         self.b.copy(&x.b);
         self.c.copy(&x.c);
+	self.stype=x.stype;
     }
 
     /* set self=1 */
@@ -168,6 +199,7 @@
         self.a.one();
         self.b.zero();
         self.c.zero();
+	self.stype=ONE;
     }
 
     /* this=conj(this) */
@@ -215,11 +247,15 @@
         self.c.dbl();
         self.b.add(&b);
         self.c.add(&c);
+        self.stype=DENSE;
         self.reduce();
     }
 
     /* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
     pub fn sqr(&mut self) {
+        if self.stype==ONE {
+            return;
+        }
         let mut a = FP16::new_copy(&self.a);
         let mut b = FP16::new_copy(&self.b);
         let mut c = FP16::new_copy(&self.c);
@@ -254,6 +290,11 @@
         self.b.copy(&c);
         self.b.add(&d);
         self.c.add(&a);
+        if self.stype==SPARSER {
+            self.stype=SPARSE;
+        } else {
+            self.stype=DENSE;
+        }
         self.norm();
     }
 
@@ -324,115 +365,312 @@
         z3.times_i();
         self.a.copy(&z0);
         self.a.add(&z3);
+        self.stype=DENSE;
         self.norm();
     }
 
-    /* Special case of multiplication arises from special form of ATE pairing line function */
-    pub fn smul(&mut self, y: &FP48, twist: usize) {
-        if twist == SexticTwist::D_TYPE.into() {
-            let mut z0 = FP16::new_copy(&self.a);
-            let mut z2 = FP16::new_copy(&self.b);
-            let mut z3 = FP16::new_copy(&self.b);
-            let mut t0 = FP16::new();
-            let mut t1 = FP16::new_copy(&y.a);
-
+/* FP48 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+    pub fn ssmul(&mut self, y: &FP48) {
+        if self.stype==ONE {
+            self.copy(&y);
+            return;
+        }
+        if y.stype==ONE {
+            return;
+        }
+        if y.stype>=SPARSE {
+            let mut z0=FP16::new_copy(&self.a);
+            let mut z1=FP16::new_int(0);
+            let mut z2=FP16::new_int(0);
+            let mut z3=FP16::new_int(0);
             z0.mul(&y.a);
-            z2.pmul(&y.b.real());
-            self.b.add(&self.a);
-            t1.padd(&y.b.real());
 
-            self.b.norm();
-            t1.norm();
+            if ecp::SEXTIC_TWIST==SexticTwist::M_TYPE {
+                if y.stype==SPARSE || self.stype==SPARSE {
 
-            self.b.mul(&t1);
-            z3.add(&self.c);
-            z3.norm();
-            z3.pmul(&y.b.real());
+                    let mut ga=FP8::new_int(0);
+		    let mut gb=FP8::new_int(0);
 
-            t0.copy(&z0);
-            t0.neg();
-            t1.copy(&z2);
-            t1.neg();
+                    gb.copy(&self.b.getb());
+                    gb.mul(&y.b.getb());
+                    ga.zero();
+                    if y.stype!=SPARSE {
+                        ga.copy(&self.b.getb());
+                        ga.mul(&y.b.geta());
+                    }
+                    if self.stype!=SPARSE {
+                        ga.copy(&self.b.geta());
+                        ga.mul(&y.b.getb());
+                    }
+		    z2.set_fp8s(&ga,&gb);
+                    z2.times_i();
+                } else {
+                    z2.copy(&self.b);
+                    z2.mul(&y.b);
+                }
+            } else { 
+               z2.copy(&self.b);
+               z2.mul(&y.b);
+            }
+            let mut t0=FP16::new_copy(&self.a);
+            let mut t1=FP16::new_copy(&y.a);
+            t0.add(&self.b); t0.norm();
+            t1.add(&y.b); t1.norm();
 
-            self.b.add(&t0);
-            self.b.add(&t1);
+            z1.copy(&t0); z1.mul(&t1);
+            t0.copy(&self.b); t0.add(&self.c); t0.norm();
+            t1.copy(&y.b); t1.add(&y.c); t1.norm();
+
+            z3.copy(&t0); z3.mul(&t1);
+
+            t0.copy(&z0); t0.neg();
+            t1.copy(&z2); t1.neg();
+ 
+            z1.add(&t0);
+            self.b.copy(&z1); self.b.add(&t1);
+
             z3.add(&t1);
             z2.add(&t0);
 
-            t0.copy(&self.a);
-            t0.add(&self.c);
-            t0.norm();
-            z3.norm();
-
-            t0.mul(&y.a);
-            self.c.copy(&z2);
-            self.c.add(&t0);
-
-            z3.times_i();
-            self.a.copy(&z0);
-            self.a.add(&z3);
-        }
-        if twist == SexticTwist::M_TYPE.into() {
-            let mut z0 = FP16::new_copy(&self.a);
-            let mut z1 = FP16::new();
-            let mut z2 = FP16::new();
-            let mut z3 = FP16::new();
-            let mut t0 = FP16::new_copy(&self.a);
-            let mut t1 = FP16::new();
-
-            z0.mul(&y.a);
-            t0.add(&self.b);
-            t0.norm();
-
-            z1.copy(&t0);
-            z1.mul(&y.a);
-            t0.copy(&self.b);
-            t0.add(&self.c);
-            t0.norm();
-
-            z3.copy(&t0);
-            z3.pmul(&y.c.getb());
-            z3.times_i();
-
-            t0.copy(&z0);
-            t0.neg();
-
-            z1.add(&t0);
-            self.b.copy(&z1);
-            z2.copy(&t0);
-
-            t0.copy(&self.a);
-            t0.add(&self.c);
-            t1.copy(&y.a);
-            t1.add(&y.c);
-
-            t0.norm();
-            t1.norm();
-
+            t0.copy(&self.a); t0.add(&self.c); t0.norm();
+            t1.copy(&y.a); t1.add(&y.c); t1.norm();
+	
             t0.mul(&t1);
             z2.add(&t0);
 
-            t0.copy(&self.c);
+            if ecp::SEXTIC_TWIST==SexticTwist::D_TYPE {
+                if y.stype==SPARSE || self.stype==SPARSE {
 
-            t0.pmul(&y.c.getb());
-            t0.times_i();
+                    let mut ga=FP8::new_int(0);
+		    let mut gb=FP8::new_int(0);
 
-            t1.copy(&t0);
-            t1.neg();
+                    ga.copy(&self.c.geta());
+                    ga.mul(&y.c.geta());
+                    gb.zero();
+                    if y.stype!=SPARSE {
+                        gb.copy(&self.c.geta());
+                        gb.mul(&y.c.getb());
+                    }
+                    if self.stype!=SPARSE {
+                        gb.copy(&self.c.getb());
+                        gb.mul(&y.c.geta());
+                    }
+		    t0.set_fp8s(&ga,&gb);
+                } else {
+                    t0.copy(&self.c);
+                    t0.mul(&y.c);
+                }
+            } else { 
+                t0.copy(&self.c);
+                t0.mul(&y.c);
+            }
+            t1.copy(&t0); t1.neg();
 
-            self.c.copy(&z2);
-            self.c.add(&t1);
+            self.c.copy(&z2); self.c.add(&t1);
             z3.add(&t1);
             t0.times_i();
             self.b.add(&t0);
             z3.norm();
             z3.times_i();
-            self.a.copy(&z0);
-            self.a.add(&z3);
+            self.a.copy(&z0); self.a.add(&z3);
+        } else {
+            if self.stype==SPARSER {
+                self.smul(&y);
+                return;
+            }
+            if ecp::SEXTIC_TWIST==SexticTwist::D_TYPE { // dense by sparser - 13m 
+                let mut z0=FP16::new_copy(&self.a);
+                let mut z2=FP16::new_copy(&self.b);
+                let mut z3=FP16::new_copy(&self.b);
+                let mut t0=FP16::new_int(0);
+                let mut t1=FP16::new_copy(&y.a);
+
+                z0.mul(&y.a);
+                z2.pmul(&y.b.geta());
+                self.b.add(&self.a);
+                t1.padd(&y.b.geta());
+
+                t1.norm();
+                self.b.norm();
+                self.b.mul(&t1);
+                z3.add(&self.c);
+                z3.norm();
+                z3.pmul(&y.b.geta());
+
+                t0.copy(&z0); t0.neg();
+                t1.copy(&z2); t1.neg();
+
+                self.b.add(&t0);
+
+                self.b.add(&t1);
+                z3.add(&t1);
+                z2.add(&t0);
+
+                t0.copy(&self.a); t0.add(&self.c); t0.norm();
+                z3.norm();
+                t0.mul(&y.a);
+                self.c.copy(&z2); self.c.add(&t0);
+
+                z3.times_i();
+                self.a.copy(&z0); self.a.add(&z3);
+            }
+            if ecp::SEXTIC_TWIST==SexticTwist::M_TYPE {
+
+                let mut z0 = FP16::new_copy(&self.a);
+                let mut z1 = FP16::new();
+                let mut z2 = FP16::new();
+                let mut z3 = FP16::new();
+                let mut t0 = FP16::new_copy(&self.a);
+                let mut t1 = FP16::new();
+	
+                z0.mul(&y.a);
+                t0.add(&self.b); t0.norm();
+
+                z1.copy(&t0); z1.mul(&y.a);
+                t0.copy(&self.b); t0.add(&self.c);
+                t0.norm();
+
+                z3.copy(&t0);
+                z3.pmul(&y.c.getb());
+                z3.times_i();
+
+                t0.copy(&z0); t0.neg();
+                z1.add(&t0);
+                self.b.copy(&z1);
+                z2.copy(&t0);
+
+                t0.copy(&self.a); t0.add(&self.c); t0.norm();
+                t1.copy(&y.a); t1.add(&y.c); t1.norm();
+
+                t0.mul(&t1);
+                z2.add(&t0);
+                t0.copy(&self.c);
+			
+                t0.pmul(&y.c.getb());
+                t0.times_i();
+                t1.copy(&t0); t1.neg();
+
+                self.c.copy(&z2); self.c.add(&t1);
+                z3.add(&t1);
+                t0.times_i();
+                self.b.add(&t0);
+                z3.norm();
+                z3.times_i();
+                self.a.copy(&z0); self.a.add(&z3);
+           }	
         }
+        self.stype=DENSE;
         self.norm();
     }
 
+
+    /* Special case of multiplication arises from special form of ATE pairing line function */
+    pub fn smul(&mut self, y: &FP48) {
+        if ecp::SEXTIC_TWIST==SexticTwist::D_TYPE {	
+            let mut w1=FP8::new_copy(&self.a.geta());
+            let mut w2=FP8::new_copy(&self.a.getb());
+            let mut w3=FP8::new_copy(&self.b.geta());
+
+            w1.mul(&y.a.geta());
+            w2.mul(&y.a.getb());
+            w3.mul(&y.b.geta());
+
+            let mut ta=FP8::new_copy(&self.a.geta());
+            let mut tb=FP8::new_copy(&y.a.geta());
+            ta.add(&self.a.getb()); ta.norm();
+            tb.add(&y.a.getb()); tb.norm();
+            let mut tc=FP8::new_copy(&ta);
+            tc.mul(&tb);
+            let mut t=FP8::new_copy(&w1);
+            t.add(&w2);
+            t.neg();
+            tc.add(&t);
+
+            ta.copy(&self.a.geta()); ta.add(&self.b.geta()); ta.norm();
+            tb.copy(&y.a.geta()); tb.add(&y.b.geta()); tb.norm();
+            let mut td=FP8::new_copy(&ta);
+            td.mul(&tb);
+            t.copy(&w1);
+            t.add(&w3);
+            t.neg();
+            td.add(&t);
+
+            ta.copy(&self.a.getb()); ta.add(&self.b.geta()); ta.norm();
+            tb.copy(&y.a.getb()); tb.add(&y.b.geta()); tb.norm();
+            let mut te=FP8::new_copy(&ta);
+            te.mul(&tb);
+            t.copy(&w2);
+            t.add(&w3);
+            t.neg();
+            te.add(&t);
+
+            w2.times_i();
+            w1.add(&w2);
+
+	    self.a.set_fp8s(&w1,&tc);
+	    self.b.set_fp8s(&td,&te);
+	    self.c.set_fp8(&w3);
+
+            self.a.norm();
+            self.b.norm();
+        } else {
+            let mut w1=FP8::new_copy(&self.a.geta());
+            let mut w2=FP8::new_copy(&self.a.getb());
+            let mut w3=FP8::new_copy(&self.c.getb());
+
+            w1.mul(&y.a.geta());
+            w2.mul(&y.a.getb());
+            w3.mul(&y.c.getb());
+
+            let mut ta=FP8::new_copy(&self.a.geta());
+            let mut tb=FP8::new_copy(&y.a.geta());
+            ta.add(&self.a.getb()); ta.norm();
+            tb.add(&y.a.getb()); tb.norm();
+            let mut tc=FP8::new_copy(&ta);
+            tc.mul(&tb);
+            let mut t=FP8::new_copy(&w1);
+            t.add(&w2);
+            t.neg();
+            tc.add(&t);
+
+            ta.copy(&self.a.geta()); ta.add(&self.c.getb()); ta.norm();
+            tb.copy(&y.a.geta()); tb.add(&y.c.getb()); tb.norm();
+            let mut td=FP8::new_copy(&ta);
+            td.mul(&tb);
+            t.copy(&w1);
+            t.add(&w3);
+            t.neg();
+            td.add(&t);
+
+            ta.copy(&self.a.getb()); ta.add(&self.c.getb()); ta.norm();
+            tb.copy(&y.a.getb()); tb.add(&y.c.getb()); tb.norm();
+            let mut te=FP8::new_copy(&ta);
+            te.mul(&tb);
+            t.copy(&w2);
+            t.add(&w3);
+            t.neg();
+            te.add(&t);
+
+            w2.times_i();
+            w1.add(&w2);
+	    self.a.set_fp8s(&w1,&tc);
+
+            w3.times_i();
+            w3.norm();
+	    self.b.set_fp8h(&w3);
+
+            te.norm();
+            te.times_i();
+	    self.c.set_fp8s(&te,&td);
+
+            self.a.norm();
+            self.c.norm();
+	}
+	self.stype=SPARSE;
+    }
+
     /* self=1/self */
     pub fn inverse(&mut self) {
         let mut f0 = FP16::new_copy(&self.a);
@@ -478,6 +716,7 @@
         self.b.mul(&f3);
         self.c.copy(&f2);
         self.c.mul(&f3);
+        self.stype=DENSE;
     }
 
     /* self=self^p using Frobenius */
@@ -506,6 +745,7 @@
             self.c.times_i4();
             self.c.times_i4();
         }
+        self.stype=DENSE;
     }
 
     /* trace function */
diff --git a/version3/rust/src/fp8.rs b/version3/rust/src/fp8.rs
index 5ceac43..dfc84d1 100644
--- a/version3/rust/src/fp8.rs
+++ b/version3/rust/src/fp8.rs
@@ -21,7 +21,7 @@
 use super::fp2::FP2;
 use super::fp4::FP4;
 use super::big::BIG;
-use std::str::SplitWhitespace;
+//use std::str::SplitWhitespace;
 
 #[derive(Copy, Clone)]
 pub struct FP8 {
@@ -65,6 +65,22 @@
         return f;
     }
 
+    pub fn set_fp4s(&mut self,c: &FP4, d: &FP4) {
+        self.a.copy(&c);
+	self.b.copy(&d);
+    }
+
+    pub fn set_fp4(&mut self,c: &FP4) {
+        self.a.copy(&c);
+	self.b.zero();
+    }
+
+    pub fn set_fp4h(&mut self,c: &FP4) {
+        self.b.copy(&c);
+	self.a.zero();
+    }
+
+
     /* reduce components mod Modulus */
     pub fn reduce(&mut self) {
         self.a.reduce();
@@ -104,13 +120,15 @@
     }
 
     pub fn geta(&self) -> FP4 {
-        let f = FP4::new_copy(&self.a);
-        return f;
+        return self.a;
+//        let f = FP4::new_copy(&self.a);
+//        return f;
     }
     /* extract imaginary part b */
     pub fn getb(&self) -> FP4 {
-        let f = FP4::new_copy(&self.b);
-        return f;
+        return self.b;
+//        let f = FP4::new_copy(&self.b);
+//        return f;
     }
 
     /* test self=x */
diff --git a/version3/rust/src/pair.rs b/version3/rust/src/pair.rs
index 7aeb493..876c385 100644
--- a/version3/rust/src/pair.rs
+++ b/version3/rust/src/pair.rs
@@ -23,6 +23,7 @@
 use super::fp2::FP2;
 use super::ecp2::ECP2;
 use super::fp4::FP4;
+use super::fp12;
 use super::fp12::FP12;
 use super::big::BIG;
 use super::dbig::DBIG;
@@ -80,7 +81,9 @@
         c.times_i();
     }
     A.dbl();
-    return FP12::new_fp4s(&a, &b, &c);
+    let mut res= FP12::new_fp4s(&a, &b, &c);
+    res.settype(fp12::SPARSER);
+    return res;
 }
 
 #[allow(non_snake_case)]
@@ -129,15 +132,123 @@
     }
 
     A.add(B);
-    return FP12::new_fp4s(&a, &b, &c);
+    let mut res= FP12::new_fp4s(&a, &b, &c);
+    res.settype(fp12::SPARSER);
+    return res;
+}
+
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+#[allow(non_snake_case)]
+fn lbits(n3: &mut BIG,n: &mut BIG) -> usize {
+    n.copy(&BIG::new_ints(&rom::CURVE_BNX));
+    if ecp::CURVE_PAIRING_TYPE==CurvePairingType::BN {
+        n.pmul(6);
+        if ecp::SIGN_OF_X==SignOfX::POSITIVEX {
+            n.inc(2);
+        } else {
+            n.dec(2);
+        }
+    }
+    n.norm();
+    n3.copy(&n);
+    n3.pmul(3);
+    n3.norm();
+    return n3.nbits();
+}
+
+/* prepare for multi-pairing */
+pub fn initmp() -> [FP12; rom::ATE_BITS] {
+    let r: [FP12; rom::ATE_BITS] = [FP12::new_int(1); rom::ATE_BITS];
+    return r
+}
+
+/* basic Miller loop */
+pub fn miller(r:&[FP12]) -> FP12 {
+    let mut res=FP12::new_int(1);
+    for i in (1..rom::ATE_BITS).rev() {
+        res.sqr();
+        res.ssmul(&r[i]);
+    }
+
+    if ecp::SIGN_OF_X==SignOfX::NEGATIVEX {
+        res.conj();
+    }
+    res.ssmul(&r[0]);
+    return res;
+}
+
+/* Accumulate another set of line functions for n-pairing */
+#[allow(non_snake_case)]
+pub fn another(r:&mut [FP12],P1: &ECP2,Q1: &ECP) {
+    let mut f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
+    let mut n = BIG::new();
+    let mut n3 = BIG::new();
+    let mut K = ECP2::new();
+
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+    let mut P = ECP2::new();
+    P.copy(P1);
+    P.affine();
+    let mut Q = ECP::new();
+    Q.copy(Q1);
+    Q.affine();
+
+    if ecp::CURVE_PAIRING_TYPE==CurvePairingType::BN {
+        if ecp::SEXTIC_TWIST==SexticTwist::M_TYPE {
+            f.inverse();
+            f.norm();
+        }
+    }
+
+    let qx = FP::new_copy(&Q.getpx());
+    let qy = FP::new_copy(&Q.getpy());
+    let mut A = ECP2::new();
+
+    A.copy(&P);
+    let mut NP = ECP2::new();
+    NP.copy(&P);
+    NP.neg();
+
+    let nb=lbits(&mut n3,&mut n);
+
+    for i in (1..nb-1).rev() {
+        let mut lv=linedbl(&mut A,&qx,&qy);
+
+	let bt=n3.bit(i)-n.bit(i);
+        if bt==1 {
+            let lv2=lineadd(&mut A,&P,&qx,&qy);
+            lv.smul(&lv2);
+        }
+        if bt==-1 {
+            let lv2=lineadd(&mut A,&NP,&qx,&qy);
+            lv.smul(&lv2);
+        }
+        r[i].ssmul(&lv);
+    }
+
+/* R-ate fixup required for BN curves */
+    if ecp::CURVE_PAIRING_TYPE==CurvePairingType::BN {
+        if ecp::SIGN_OF_X==SignOfX::NEGATIVEX {
+            A.neg();
+        }
+        K.copy(&P);
+        K.frob(&f);
+        let mut lv=lineadd(&mut A,&K,&qx,&qy);
+        K.frob(&f);
+        K.neg();
+        let lv2=lineadd(&mut A,&K,&qx,&qy);
+        lv.smul(&lv2);
+	r[0].ssmul(&lv);
+    } 
 }
 
 #[allow(non_snake_case)]
 /* Optimal R-ate pairing */
 pub fn ate(P1: &ECP2, Q1: &ECP) -> FP12 {
     let mut f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
-    let x = BIG::new_ints(&rom::CURVE_BNX);
-    let mut n = BIG::new_copy(&x);
+    let mut n = BIG::new();
+    let mut n3 = BIG::new();
     let mut K = ECP2::new();
 
     if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BN {
@@ -145,21 +256,7 @@
             f.inverse();
             f.norm();
         }
-        n.pmul(6);
-        if ecp::SIGN_OF_X == SignOfX::POSITIVEX {
-            n.inc(2);
-        } else {
-            n.dec(2);
-        }
-    } else {
-        n.copy(&x)
-    }
-
-    n.norm();
-    let mut n3 = BIG::new_copy(&n);
-    n3.pmul(3);
-    n3.norm();
-
+    } 
     let mut P = ECP2::new();
     P.copy(P1);
     P.affine();
@@ -178,22 +275,21 @@
     NP.copy(&P);
     NP.neg();
 
-    let nb = n3.nbits();
+    let nb=lbits(&mut n3,&mut n);
 
     for i in (1..nb - 1).rev() {
         r.sqr();
-
         let mut lv = linedbl(&mut A, &qx, &qy);
-        r.smul(&lv, ecp::SEXTIC_TWIST.into());
         let bt = n3.bit(i) - n.bit(i);
         if bt == 1 {
-            lv = lineadd(&mut A, &P, &qx, &qy);
-            r.smul(&lv, ecp::SEXTIC_TWIST.into());
+            let lv2 = lineadd(&mut A, &P, &qx, &qy);
+            lv.smul(&lv2);
         }
         if bt == -1 {
-            lv = lineadd(&mut A, &NP, &qx, &qy);
-            r.smul(&lv, ecp::SEXTIC_TWIST.into());
+            let lv2 = lineadd(&mut A, &NP, &qx, &qy);
+            lv.smul(&lv2);
         }
+        r.ssmul(&lv);
     }
 
     if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
@@ -211,11 +307,11 @@
         K.frob(&f);
 
         let mut lv = lineadd(&mut A, &K, &qx, &qy);
-        r.smul(&lv, ecp::SEXTIC_TWIST.into());
         K.frob(&f);
         K.neg();
-        lv = lineadd(&mut A, &K, &qx, &qy);
-        r.smul(&lv, ecp::SEXTIC_TWIST.into());
+        let lv2 = lineadd(&mut A, &K, &qx, &qy);
+	lv.smul(&lv2);
+        r.ssmul(&lv);
     }
 
     return r;
@@ -225,8 +321,8 @@
 /* Optimal R-ate double pairing e(P,Q).e(R,S) */
 pub fn ate2(P1: &ECP2, Q1: &ECP, R1: &ECP2, S1: &ECP) -> FP12 {
     let mut f = FP2::new_bigs(&BIG::new_ints(&rom::FRA), &BIG::new_ints(&rom::FRB));
-    let x = BIG::new_ints(&rom::CURVE_BNX);
-    let mut n = BIG::new_copy(&x);
+    let mut n = BIG::new();
+    let mut n3 = BIG::new();
     let mut K = ECP2::new();
 
     if ecp::CURVE_PAIRING_TYPE == CurvePairingType::BN {
@@ -234,20 +330,7 @@
             f.inverse();
             f.norm();
         }
-        n.pmul(6);
-        if ecp::SIGN_OF_X == SignOfX::POSITIVEX {
-            n.inc(2);
-        } else {
-            n.dec(2);
-        }
-    } else {
-        n.copy(&x)
-    }
-
-    n.norm();
-    let mut n3 = BIG::new_copy(&n);
-    n3.pmul(3);
-    n3.norm();
+    } 
 
     let mut P = ECP2::new();
     P.copy(P1);
@@ -282,26 +365,26 @@
     NR.copy(&R);
     NR.neg();
 
-    let nb = n3.nbits();
+    let nb=lbits(&mut n3,&mut n);
 
     for i in (1..nb - 1).rev() {
         r.sqr();
         let mut lv = linedbl(&mut A, &qx, &qy);
-        r.smul(&lv, ecp::SEXTIC_TWIST.into());
-        lv = linedbl(&mut B, &sx, &sy);
-        r.smul(&lv, ecp::SEXTIC_TWIST.into());
+        let lv2 = linedbl(&mut B, &sx, &sy);
+	lv.smul(&lv2);
+        r.ssmul(&lv);
         let bt = n3.bit(i) - n.bit(i);
         if bt == 1 {
             lv = lineadd(&mut A, &P, &qx, &qy);
-            r.smul(&lv, ecp::SEXTIC_TWIST.into());
-            lv = lineadd(&mut B, &R, &sx, &sy);
-            r.smul(&lv, ecp::SEXTIC_TWIST.into());
+            let lv2 = lineadd(&mut B, &R, &sx, &sy);
+	    lv.smul(&lv2);
+            r.ssmul(&lv);
         }
         if bt == -1 {
             lv = lineadd(&mut A, &NP, &qx, &qy);
-            r.smul(&lv, ecp::SEXTIC_TWIST.into());
-            lv = lineadd(&mut B, &NR, &sx, &sy);
-            r.smul(&lv, ecp::SEXTIC_TWIST.into());
+            let lv2 = lineadd(&mut B, &NR, &sx, &sy);
+	    lv.smul(&lv2);
+            r.ssmul(&lv);
         }
     }
 
@@ -319,21 +402,22 @@
         K.frob(&f);
 
         let mut lv = lineadd(&mut A, &K, &qx, &qy);
-        r.smul(&lv, ecp::SEXTIC_TWIST.into());
         K.frob(&f);
         K.neg();
-        lv = lineadd(&mut A, &K, &qx, &qy);
-        r.smul(&lv, ecp::SEXTIC_TWIST.into());
+        let mut lv2 = lineadd(&mut A, &K, &qx, &qy);
+	lv.smul(&lv2);
+        r.ssmul(&lv);
 
         K.copy(&R);
         K.frob(&f);
 
         lv = lineadd(&mut B, &K, &sx, &sy);
-        r.smul(&lv, ecp::SEXTIC_TWIST.into());
         K.frob(&f);
         K.neg();
-        lv = lineadd(&mut B, &K, &sx, &sy);
-        r.smul(&lv, ecp::SEXTIC_TWIST.into());
+        lv2 = lineadd(&mut B, &K, &sx, &sy);
+	lv.smul(&lv2);
+        r.ssmul(&lv);
+
     }
 
     return r;
diff --git a/version3/rust/src/pair192.rs b/version3/rust/src/pair192.rs
index f47e294..4d2e769 100644
--- a/version3/rust/src/pair192.rs
+++ b/version3/rust/src/pair192.rs
@@ -24,6 +24,7 @@
 use super::ecp4::ECP4;
 use super::fp4::FP4;
 use super::fp8::FP8;
+use super::fp24;
 use super::fp24::FP24;
 use super::big::BIG;
 use super::ecp;
@@ -79,7 +80,9 @@
         c.times_i();
     }
     A.dbl();
-    return FP24::new_fp8s(&a, &b, &c);
+    let mut res= FP24::new_fp8s(&a, &b, &c);
+    res.settype(fp24::SPARSER);
+    return res;
 }
 
 #[allow(non_snake_case)]
@@ -127,18 +130,88 @@
     }
 
     A.add(B);
-    return FP24::new_fp8s(&a, &b, &c);
+    let mut res= FP24::new_fp8s(&a, &b, &c);
+    res.settype(fp24::SPARSER);
+    return res;
+}
+
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+#[allow(non_snake_case)]
+fn lbits(n3: &mut BIG,n: &mut BIG) -> usize {
+    n.copy(&BIG::new_ints(&rom::CURVE_BNX));
+    n3.copy(&n);
+    n3.pmul(3);
+    n3.norm();
+    return n3.nbits();
+}
+
+/* prepare for multi-pairing */
+pub fn initmp() -> [FP24; rom::ATE_BITS] {
+    let r: [FP24; rom::ATE_BITS] = [FP24::new_int(1); rom::ATE_BITS];
+    return r
+}
+
+/* basic Miller loop */
+pub fn miller(r:&[FP24]) -> FP24 {
+    let mut res=FP24::new_int(1);
+    for i in (1..rom::ATE_BITS).rev() {
+        res.sqr();
+        res.ssmul(&r[i]);
+    }
+
+    if ecp::SIGN_OF_X==SignOfX::NEGATIVEX {
+        res.conj();
+    }
+    res.ssmul(&r[0]);
+    return res;
+}
+
+/* Accumulate another set of line functions for n-pairing */
+#[allow(non_snake_case)]
+pub fn another(r:&mut [FP24],P1: &ECP4,Q1: &ECP) {
+    let mut n = BIG::new();
+    let mut n3 = BIG::new();
+    
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+    let mut P = ECP4::new();
+    P.copy(P1);
+    P.affine();
+    let mut Q = ECP::new();
+    Q.copy(Q1);
+    Q.affine();
+
+    let qx = FP::new_copy(&Q.getpx());
+    let qy = FP::new_copy(&Q.getpy());
+    let mut A = ECP4::new();
+
+    A.copy(&P);
+    let mut NP = ECP4::new();
+    NP.copy(&P);
+    NP.neg();
+
+    let nb=lbits(&mut n3,&mut n);
+
+    for i in (1..nb-1).rev() {
+        let mut lv=linedbl(&mut A,&qx,&qy);
+
+	let bt=n3.bit(i)-n.bit(i);
+        if bt==1 {
+            let lv2=lineadd(&mut A,&P,&qx,&qy);
+            lv.smul(&lv2);
+        }
+        if bt==-1 {
+            let lv2=lineadd(&mut A,&NP,&qx,&qy);
+            lv.smul(&lv2);
+        }
+        r[i].ssmul(&lv);
+    }
 }
 
 #[allow(non_snake_case)]
 /* Optimal R-ate pairing */
 pub fn ate(P1: &ECP4, Q1: &ECP) -> FP24 {
-    let x = BIG::new_ints(&rom::CURVE_BNX);
-    let n = BIG::new_copy(&x);
-
-    let mut n3 = BIG::new_copy(&n);
-    n3.pmul(3);
-    n3.norm();
+    let mut n = BIG::new();
+    let mut n3 = BIG::new();
 
     let mut P = ECP4::new();
     P.copy(P1);
@@ -158,21 +231,23 @@
     NP.copy(&P);
     NP.neg();
 
-    let nb = n3.nbits();
+    let nb=lbits(&mut n3,&mut n);
 
     for i in (1..nb - 1).rev() {
         r.sqr();
+
         let mut lv = linedbl(&mut A, &qx, &qy);
-        r.smul(&lv, ecp::SEXTIC_TWIST.into());
+
         let bt = n3.bit(i) - n.bit(i);
         if bt == 1 {
-            lv = lineadd(&mut A, &P, &qx, &qy);
-            r.smul(&lv, ecp::SEXTIC_TWIST.into());
+            let lv2 = lineadd(&mut A, &P, &qx, &qy);
+            lv.smul(&lv2);
         }
         if bt == -1 {
-            lv = lineadd(&mut A, &NP, &qx, &qy);
-            r.smul(&lv, ecp::SEXTIC_TWIST.into());
+            let lv2 = lineadd(&mut A, &NP, &qx, &qy);
+            lv.smul(&lv2);
         }
+        r.ssmul(&lv);
     }
 
     if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
@@ -185,12 +260,8 @@
 #[allow(non_snake_case)]
 /* Optimal R-ate double pairing e(P,Q).e(R,S) */
 pub fn ate2(P1: &ECP4, Q1: &ECP, R1: &ECP4, S1: &ECP) -> FP24 {
-    let x = BIG::new_ints(&rom::CURVE_BNX);
-    let n = BIG::new_copy(&x);
-
-    let mut n3 = BIG::new_copy(&n);
-    n3.pmul(3);
-    n3.norm();
+    let mut n = BIG::new();
+    let mut n3 = BIG::new();
 
     let mut P = ECP4::new();
     P.copy(P1);
@@ -225,26 +296,26 @@
     NR.copy(&R);
     NR.neg();
 
-    let nb = n3.nbits();
+    let nb=lbits(&mut n3,&mut n);
 
     for i in (1..nb - 1).rev() {
         r.sqr();
         let mut lv = linedbl(&mut A, &qx, &qy);
-        r.smul(&lv, ecp::SEXTIC_TWIST.into());
-        lv = linedbl(&mut B, &sx, &sy);
-        r.smul(&lv, ecp::SEXTIC_TWIST.into());
+        let lv2 = linedbl(&mut B, &sx, &sy);
+	lv.smul(&lv2);
+        r.ssmul(&lv);
         let bt = n3.bit(i) - n.bit(i);
         if bt == 1 {
             lv = lineadd(&mut A, &P, &qx, &qy);
-            r.smul(&lv, ecp::SEXTIC_TWIST.into());
-            lv = lineadd(&mut B, &R, &sx, &sy);
-            r.smul(&lv, ecp::SEXTIC_TWIST.into());
+            let lv2 = lineadd(&mut B, &R, &sx, &sy);
+	    lv.smul(&lv2);
+            r.ssmul(&lv);
         }
         if bt == -1 {
             lv = lineadd(&mut A, &NP, &qx, &qy);
-            r.smul(&lv, ecp::SEXTIC_TWIST.into());
-            lv = lineadd(&mut B, &NR, &sx, &sy);
-            r.smul(&lv, ecp::SEXTIC_TWIST.into());
+            let lv2 = lineadd(&mut B, &NR, &sx, &sy);
+	    lv.smul(&lv2);
+            r.ssmul(&lv);
         }
     }
 
diff --git a/version3/rust/src/pair256.rs b/version3/rust/src/pair256.rs
index 92b4099..bdf2e5b 100644
--- a/version3/rust/src/pair256.rs
+++ b/version3/rust/src/pair256.rs
@@ -24,6 +24,7 @@
 use super::ecp8::ECP8;
 use super::fp8::FP8;
 use super::fp16::FP16;
+use super::fp48;
 use super::fp48::FP48;
 use super::big::BIG;
 use super::ecp;
@@ -79,7 +80,9 @@
         c.times_i();
     }
     A.dbl();
-    return FP48::new_fp16s(&a, &b, &c);
+    let mut res= FP48::new_fp16s(&a, &b, &c);
+    res.settype(fp48::SPARSER);
+    return res;
 }
 
 #[allow(non_snake_case)]
@@ -127,18 +130,88 @@
     }
 
     A.add(B);
-    return FP48::new_fp16s(&a, &b, &c);
+    let mut res= FP48::new_fp16s(&a, &b, &c);
+    res.settype(fp48::SPARSER);
+    return res;
+}
+
+/* prepare ate parameter, n=6u+2 (BN) or n=u (BLS), n3=3*n */
+#[allow(non_snake_case)]
+fn lbits(n3: &mut BIG,n: &mut BIG) -> usize {
+    n.copy(&BIG::new_ints(&rom::CURVE_BNX));
+    n3.copy(&n);
+    n3.pmul(3);
+    n3.norm();
+    return n3.nbits();
+}
+
+/* prepare for multi-pairing */
+pub fn initmp() -> [FP48; rom::ATE_BITS] {
+    let r: [FP48; rom::ATE_BITS] = [FP48::new_int(1); rom::ATE_BITS];
+    return r
+}
+
+/* basic Miller loop */
+pub fn miller(r:&[FP48]) -> FP48 {
+    let mut res=FP48::new_int(1);
+    for i in (1..rom::ATE_BITS).rev() {
+        res.sqr();
+        res.ssmul(&r[i]);
+    }
+
+    if ecp::SIGN_OF_X==SignOfX::NEGATIVEX {
+        res.conj();
+    }
+    res.ssmul(&r[0]);
+    return res;
+}
+
+/* Accumulate another set of line functions for n-pairing */
+#[allow(non_snake_case)]
+pub fn another(r:&mut [FP48],P1: &ECP8,Q1: &ECP) {
+    let mut n = BIG::new();
+    let mut n3 = BIG::new();
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+    let mut P = ECP8::new();
+    P.copy(P1);
+    P.affine();
+    let mut Q = ECP::new();
+    Q.copy(Q1);
+    Q.affine();
+
+    let qx = FP::new_copy(&Q.getpx());
+    let qy = FP::new_copy(&Q.getpy());
+    let mut A = ECP8::new();
+
+    A.copy(&P);
+    let mut NP = ECP8::new();
+    NP.copy(&P);
+    NP.neg();
+
+    let nb=lbits(&mut n3,&mut n);
+
+    for i in (1..nb-1).rev() {
+        let mut lv=linedbl(&mut A,&qx,&qy);
+
+	let bt=n3.bit(i)-n.bit(i);
+        if bt==1 {
+            let lv2=lineadd(&mut A,&P,&qx,&qy);
+            lv.smul(&lv2);
+        }
+        if bt==-1 {
+            let lv2=lineadd(&mut A,&NP,&qx,&qy);
+            lv.smul(&lv2);
+        }
+        r[i].ssmul(&lv);
+    }
 }
 
 #[allow(non_snake_case)]
 /* Optimal R-ate pairing */
 pub fn ate(P1: &ECP8, Q1: &ECP) -> FP48 {
-    let x = BIG::new_ints(&rom::CURVE_BNX);
-    let n = BIG::new_copy(&x);
-
-    let mut n3 = BIG::new_copy(&n);
-    n3.pmul(3);
-    n3.norm();
+    let mut n = BIG::new();
+    let mut n3 = BIG::new();
 
     let mut P = ECP8::new();
     P.copy(P1);
@@ -158,21 +231,21 @@
     NP.copy(&P);
     NP.neg();
 
-    let nb = n3.nbits();
+    let nb=lbits(&mut n3,&mut n);
 
     for i in (1..nb - 1).rev() {
         r.sqr();
         let mut lv = linedbl(&mut A, &qx, &qy);
-        r.smul(&lv, ecp::SEXTIC_TWIST.into());
         let bt = n3.bit(i) - n.bit(i);
         if bt == 1 {
-            lv = lineadd(&mut A, &P, &qx, &qy);
-            r.smul(&lv, ecp::SEXTIC_TWIST.into());
+            let lv2 = lineadd(&mut A, &P, &qx, &qy);
+            lv.smul(&lv2);
         }
         if bt == -1 {
-            lv = lineadd(&mut A, &NP, &qx, &qy);
-            r.smul(&lv, ecp::SEXTIC_TWIST.into());
+            let lv2 = lineadd(&mut A, &NP, &qx, &qy);
+            lv.smul(&lv2);
         }
+        r.ssmul(&lv);
     }
 
     if ecp::SIGN_OF_X == SignOfX::NEGATIVEX {
@@ -185,12 +258,8 @@
 #[allow(non_snake_case)]
 /* Optimal R-ate double pairing e(P,Q).e(R,S) */
 pub fn ate2(P1: &ECP8, Q1: &ECP, R1: &ECP8, S1: &ECP) -> FP48 {
-    let x = BIG::new_ints(&rom::CURVE_BNX);
-    let n = BIG::new_copy(&x);
-
-    let mut n3 = BIG::new_copy(&n);
-    n3.pmul(3);
-    n3.norm();
+    let mut n = BIG::new();
+    let mut n3 = BIG::new();
 
     let mut P = ECP8::new();
     P.copy(P1);
@@ -225,26 +294,26 @@
     NR.copy(&R);
     NR.neg();
 
-    let nb = n3.nbits();
+     let nb=lbits(&mut n3,&mut n);
 
     for i in (1..nb - 1).rev() {
         r.sqr();
         let mut lv = linedbl(&mut A, &qx, &qy);
-        r.smul(&lv, ecp::SEXTIC_TWIST.into());
-        lv = linedbl(&mut B, &sx, &sy);
-        r.smul(&lv, ecp::SEXTIC_TWIST.into());
+        let lv2 = linedbl(&mut B, &sx, &sy);
+	lv.smul(&lv2);
+        r.ssmul(&lv);
         let bt = n3.bit(i) - n.bit(i);
         if bt == 1 {
             lv = lineadd(&mut A, &P, &qx, &qy);
-            r.smul(&lv, ecp::SEXTIC_TWIST.into());
-            lv = lineadd(&mut B, &R, &sx, &sy);
-            r.smul(&lv, ecp::SEXTIC_TWIST.into());
+            let lv2 = lineadd(&mut B, &R, &sx, &sy);
+	    lv.smul(&lv2);
+            r.ssmul(&lv);
         }
         if bt == -1 {
             lv = lineadd(&mut A, &NP, &qx, &qy);
-            r.smul(&lv, ecp::SEXTIC_TWIST.into());
-            lv = lineadd(&mut B, &NR, &sx, &sy);
-            r.smul(&lv, ecp::SEXTIC_TWIST.into());
+            let lv2 = lineadd(&mut B, &NR, &sx, &sy);
+	    lv.smul(&lv2);
+            r.ssmul(&lv);
         }
     }
 
diff --git a/version3/rust/src/roms/rom_anssi_32.rs b/version3/rust/src/roms/rom_anssi_32.rs
index c041396..403fb04 100644
--- a/version3/rust/src/roms/rom_anssi_32.rs
+++ b/version3/rust/src/roms/rom_anssi_32.rs
@@ -67,6 +67,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_anssi_64.rs b/version3/rust/src/roms/rom_anssi_64.rs
index c65e7f7..b0add0f 100644
--- a/version3/rust/src/roms/rom_anssi_64.rs
+++ b/version3/rust/src/roms/rom_anssi_64.rs
@@ -85,6 +85,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_bls24_32.rs b/version3/rust/src/roms/rom_bls24_32.rs
index 2f4c424..72afe58 100644
--- a/version3/rust/src/roms/rom_bls24_32.rs
+++ b/version3/rust/src/roms/rom_bls24_32.rs
@@ -234,6 +234,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 49;
 pub const SIGN_OF_X: SignOfX = SignOfX::POSITIVEX;
 pub const HASH_TYPE: usize = 48;
 pub const AESKEY: usize = 24;
diff --git a/version3/rust/src/roms/rom_bls24_64.rs b/version3/rust/src/roms/rom_bls24_64.rs
index 8378e28..0c500da 100644
--- a/version3/rust/src/roms/rom_bls24_64.rs
+++ b/version3/rust/src/roms/rom_bls24_64.rs
@@ -282,6 +282,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 49;
 pub const SIGN_OF_X: SignOfX = SignOfX::POSITIVEX;
 pub const HASH_TYPE: usize = 48;
 pub const AESKEY: usize = 24;
diff --git a/version3/rust/src/roms/rom_bls381_32.rs b/version3/rust/src/roms/rom_bls381_32.rs
index a6ca0fd..1ba0fe5 100644
--- a/version3/rust/src/roms/rom_bls381_32.rs
+++ b/version3/rust/src/roms/rom_bls381_32.rs
@@ -203,6 +203,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 65;
 pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_bls381_64.rs b/version3/rust/src/roms/rom_bls381_64.rs
index 3c97727..08df12c 100644
--- a/version3/rust/src/roms/rom_bls381_64.rs
+++ b/version3/rust/src/roms/rom_bls381_64.rs
@@ -205,6 +205,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 65;
 pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_bls383_32.rs b/version3/rust/src/roms/rom_bls383_32.rs
index 6c3453e..0ccb42d 100644
--- a/version3/rust/src/roms/rom_bls383_32.rs
+++ b/version3/rust/src/roms/rom_bls383_32.rs
@@ -201,6 +201,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 65;
 pub const SIGN_OF_X: SignOfX = SignOfX::POSITIVEX;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_bls383_64.rs b/version3/rust/src/roms/rom_bls383_64.rs
index 88efdb1..71bef66 100644
--- a/version3/rust/src/roms/rom_bls383_64.rs
+++ b/version3/rust/src/roms/rom_bls383_64.rs
@@ -212,6 +212,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 65;
 pub const SIGN_OF_X: SignOfX = SignOfX::POSITIVEX;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_bls461_32.rs b/version3/rust/src/roms/rom_bls461_32.rs
index 3490811..c452a9f 100644
--- a/version3/rust/src/roms/rom_bls461_32.rs
+++ b/version3/rust/src/roms/rom_bls461_32.rs
@@ -203,6 +203,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 78;
 pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_bls461_64.rs b/version3/rust/src/roms/rom_bls461_64.rs
index 042b8dc..500ef04 100644
--- a/version3/rust/src/roms/rom_bls461_64.rs
+++ b/version3/rust/src/roms/rom_bls461_64.rs
@@ -226,6 +226,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 78;
 pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_bls48_32.rs b/version3/rust/src/roms/rom_bls48_32.rs
index ddb836c..83517a8 100644
--- a/version3/rust/src/roms/rom_bls48_32.rs
+++ b/version3/rust/src/roms/rom_bls48_32.rs
@@ -303,6 +303,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 32;
 pub const SIGN_OF_X: SignOfX = SignOfX::POSITIVEX;
 pub const HASH_TYPE: usize = 64;
 pub const AESKEY: usize = 32;
diff --git a/version3/rust/src/roms/rom_bls48_64.rs b/version3/rust/src/roms/rom_bls48_64.rs
index d47d835..129c776 100644
--- a/version3/rust/src/roms/rom_bls48_64.rs
+++ b/version3/rust/src/roms/rom_bls48_64.rs
@@ -395,6 +395,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BLS;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 32;
 pub const SIGN_OF_X: SignOfX = SignOfX::POSITIVEX;
 pub const HASH_TYPE: usize = 64;
 pub const AESKEY: usize = 32;
diff --git a/version3/rust/src/roms/rom_bn254CX_32.rs b/version3/rust/src/roms/rom_bn254CX_32.rs
index bb99a41..e1cff0d 100644
--- a/version3/rust/src/roms/rom_bn254CX_32.rs
+++ b/version3/rust/src/roms/rom_bn254CX_32.rs
@@ -177,6 +177,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BN;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::D_TYPE;
+pub const ATE_BITS: usize = 66;
 pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_bn254CX_64.rs b/version3/rust/src/roms/rom_bn254CX_64.rs
index efc622c..7f6d274 100644
--- a/version3/rust/src/roms/rom_bn254CX_64.rs
+++ b/version3/rust/src/roms/rom_bn254CX_64.rs
@@ -236,6 +236,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BN;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::D_TYPE;
+pub const ATE_BITS: usize = 66;
 pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_bn254_32.rs b/version3/rust/src/roms/rom_bn254_32.rs
index 2488208..c9ae5ec 100644
--- a/version3/rust/src/roms/rom_bn254_32.rs
+++ b/version3/rust/src/roms/rom_bn254_32.rs
@@ -165,6 +165,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BN;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::D_TYPE;
+pub const ATE_BITS: usize = 66;
 pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_bn254_64.rs b/version3/rust/src/roms/rom_bn254_64.rs
index d33d631..f61b542 100644
--- a/version3/rust/src/roms/rom_bn254_64.rs
+++ b/version3/rust/src/roms/rom_bn254_64.rs
@@ -202,6 +202,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BN;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::D_TYPE;
+pub const ATE_BITS: usize = 66;
 pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
\ No newline at end of file
diff --git a/version3/rust/src/roms/rom_brainpool_32.rs b/version3/rust/src/roms/rom_brainpool_32.rs
index f4d33db..b788632 100644
--- a/version3/rust/src/roms/rom_brainpool_32.rs
+++ b/version3/rust/src/roms/rom_brainpool_32.rs
@@ -68,6 +68,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_brainpool_64.rs b/version3/rust/src/roms/rom_brainpool_64.rs
index de754c0..8191561 100644
--- a/version3/rust/src/roms/rom_brainpool_64.rs
+++ b/version3/rust/src/roms/rom_brainpool_64.rs
@@ -87,5 +87,6 @@
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
+pub const ATE_BITS: usize = 0;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_c25519_32.rs b/version3/rust/src/roms/rom_c25519_32.rs
index f975cb4..6da6a05 100644
--- a/version3/rust/src/roms/rom_c25519_32.rs
+++ b/version3/rust/src/roms/rom_c25519_32.rs
@@ -53,6 +53,7 @@
 pub const CURVETYPE: CurveType = CurveType::MONTGOMERY;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_c25519_64.rs b/version3/rust/src/roms/rom_c25519_64.rs
index 8954b0f..9a8c59f 100644
--- a/version3/rust/src/roms/rom_c25519_64.rs
+++ b/version3/rust/src/roms/rom_c25519_64.rs
@@ -55,6 +55,7 @@
 pub const CURVETYPE: CurveType = CurveType::MONTGOMERY;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_c41417_32.rs b/version3/rust/src/roms/rom_c41417_32.rs
index 11e61db..a8330cb 100644
--- a/version3/rust/src/roms/rom_c41417_32.rs
+++ b/version3/rust/src/roms/rom_c41417_32.rs
@@ -65,6 +65,7 @@
 pub const CURVETYPE: CurveType = CurveType::EDWARDS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 64;
 pub const AESKEY: usize = 32;
diff --git a/version3/rust/src/roms/rom_c41417_64.rs b/version3/rust/src/roms/rom_c41417_64.rs
index b61f883..52b51b1 100644
--- a/version3/rust/src/roms/rom_c41417_64.rs
+++ b/version3/rust/src/roms/rom_c41417_64.rs
@@ -72,6 +72,7 @@
 pub const CURVETYPE: CurveType = CurveType::EDWARDS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 64;
 pub const AESKEY: usize = 32;
diff --git a/version3/rust/src/roms/rom_ed25519_32.rs b/version3/rust/src/roms/rom_ed25519_32.rs
index ceff788..be1d156 100644
--- a/version3/rust/src/roms/rom_ed25519_32.rs
+++ b/version3/rust/src/roms/rom_ed25519_32.rs
@@ -62,6 +62,7 @@
 pub const CURVETYPE: CurveType = CurveType::EDWARDS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_ed25519_64.rs b/version3/rust/src/roms/rom_ed25519_64.rs
index b118e30..cf23672 100644
--- a/version3/rust/src/roms/rom_ed25519_64.rs
+++ b/version3/rust/src/roms/rom_ed25519_64.rs
@@ -73,6 +73,7 @@
 pub const CURVETYPE: CurveType = CurveType::EDWARDS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_fp256bn_32.rs b/version3/rust/src/roms/rom_fp256bn_32.rs
index 6743b1b..e3cabe1 100644
--- a/version3/rust/src/roms/rom_fp256bn_32.rs
+++ b/version3/rust/src/roms/rom_fp256bn_32.rs
@@ -174,6 +174,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BN;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 66;
 pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_fp256bn_64.rs b/version3/rust/src/roms/rom_fp256bn_64.rs
index c70465c..bde7639 100644
--- a/version3/rust/src/roms/rom_fp256bn_64.rs
+++ b/version3/rust/src/roms/rom_fp256bn_64.rs
@@ -227,6 +227,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BN;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 66;
 pub const SIGN_OF_X: SignOfX = SignOfX::NEGATIVEX;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_fp512bn_32.rs b/version3/rust/src/roms/rom_fp512bn_32.rs
index 15246ef..cdc44af 100644
--- a/version3/rust/src/roms/rom_fp512bn_32.rs
+++ b/version3/rust/src/roms/rom_fp512bn_32.rs
@@ -243,6 +243,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BN;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 130;
 pub const SIGN_OF_X: SignOfX = SignOfX::POSITIVEX;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_fp512bn_64.rs b/version3/rust/src/roms/rom_fp512bn_64.rs
index d291cd8..54d85c4 100644
--- a/version3/rust/src/roms/rom_fp512bn_64.rs
+++ b/version3/rust/src/roms/rom_fp512bn_64.rs
@@ -476,6 +476,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::BN;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::M_TYPE;
+pub const ATE_BITS: usize = 130;
 pub const SIGN_OF_X: SignOfX = SignOfX::POSITIVEX;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_goldilocks_32.rs b/version3/rust/src/roms/rom_goldilocks_32.rs
index 7a8a71e..5d5f8bb 100644
--- a/version3/rust/src/roms/rom_goldilocks_32.rs
+++ b/version3/rust/src/roms/rom_goldilocks_32.rs
@@ -67,6 +67,7 @@
 pub const CURVETYPE: CurveType = CurveType::EDWARDS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 64;
 pub const AESKEY: usize = 32;
diff --git a/version3/rust/src/roms/rom_goldilocks_64.rs b/version3/rust/src/roms/rom_goldilocks_64.rs
index 9ae100e..d6cadf1 100644
--- a/version3/rust/src/roms/rom_goldilocks_64.rs
+++ b/version3/rust/src/roms/rom_goldilocks_64.rs
@@ -93,6 +93,7 @@
 pub const CURVETYPE: CurveType = CurveType::EDWARDS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 64;
 pub const AESKEY: usize = 32;
diff --git a/version3/rust/src/roms/rom_hifive_32.rs b/version3/rust/src/roms/rom_hifive_32.rs
index da119e3..cfa9f59 100644
--- a/version3/rust/src/roms/rom_hifive_32.rs
+++ b/version3/rust/src/roms/rom_hifive_32.rs
@@ -62,6 +62,7 @@
 pub const CURVETYPE: CurveType = CurveType::EDWARDS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 48;
 pub const AESKEY: usize = 24;
diff --git a/version3/rust/src/roms/rom_hifive_64.rs b/version3/rust/src/roms/rom_hifive_64.rs
index 54b03bd..b2eebb9 100644
--- a/version3/rust/src/roms/rom_hifive_64.rs
+++ b/version3/rust/src/roms/rom_hifive_64.rs
@@ -71,6 +71,7 @@
 pub const CURVETYPE: CurveType = CurveType::EDWARDS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 48;
 pub const AESKEY: usize = 24;
diff --git a/version3/rust/src/roms/rom_nist256_32.rs b/version3/rust/src/roms/rom_nist256_32.rs
index 1900c3b..6859da5 100644
--- a/version3/rust/src/roms/rom_nist256_32.rs
+++ b/version3/rust/src/roms/rom_nist256_32.rs
@@ -68,6 +68,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_nist256_64.rs b/version3/rust/src/roms/rom_nist256_64.rs
index 259eefb..015bbb3 100644
--- a/version3/rust/src/roms/rom_nist256_64.rs
+++ b/version3/rust/src/roms/rom_nist256_64.rs
@@ -80,6 +80,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_nist384_32.rs b/version3/rust/src/roms/rom_nist384_32.rs
index 50a3976..48d7826 100644
--- a/version3/rust/src/roms/rom_nist384_32.rs
+++ b/version3/rust/src/roms/rom_nist384_32.rs
@@ -70,6 +70,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 48;
 pub const AESKEY: usize = 24;
diff --git a/version3/rust/src/roms/rom_nist384_64.rs b/version3/rust/src/roms/rom_nist384_64.rs
index b8fe79a..f0f5c69 100644
--- a/version3/rust/src/roms/rom_nist384_64.rs
+++ b/version3/rust/src/roms/rom_nist384_64.rs
@@ -98,6 +98,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 48;
 pub const AESKEY: usize = 24;
diff --git a/version3/rust/src/roms/rom_nist521_32.rs b/version3/rust/src/roms/rom_nist521_32.rs
index 1d37cbf..55cbf30 100644
--- a/version3/rust/src/roms/rom_nist521_32.rs
+++ b/version3/rust/src/roms/rom_nist521_32.rs
@@ -73,6 +73,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 64;
 pub const AESKEY: usize = 32;
diff --git a/version3/rust/src/roms/rom_nist521_64.rs b/version3/rust/src/roms/rom_nist521_64.rs
index f019d98..bf241b2 100644
--- a/version3/rust/src/roms/rom_nist521_64.rs
+++ b/version3/rust/src/roms/rom_nist521_64.rs
@@ -98,6 +98,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 64;
 pub const AESKEY: usize = 32;
diff --git a/version3/rust/src/roms/rom_nums256e_32.rs b/version3/rust/src/roms/rom_nums256e_32.rs
index fd994e5..45506bd 100644
--- a/version3/rust/src/roms/rom_nums256e_32.rs
+++ b/version3/rust/src/roms/rom_nums256e_32.rs
@@ -64,6 +64,7 @@
 pub const CURVETYPE: CurveType = CurveType::EDWARDS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_nums256e_64.rs b/version3/rust/src/roms/rom_nums256e_64.rs
index 4d4aa68..4382924 100644
--- a/version3/rust/src/roms/rom_nums256e_64.rs
+++ b/version3/rust/src/roms/rom_nums256e_64.rs
@@ -73,6 +73,7 @@
 pub const CURVETYPE: CurveType = CurveType::EDWARDS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_nums256w_32.rs b/version3/rust/src/roms/rom_nums256w_32.rs
index 9439232..b768380 100644
--- a/version3/rust/src/roms/rom_nums256w_32.rs
+++ b/version3/rust/src/roms/rom_nums256w_32.rs
@@ -60,6 +60,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_nums256w_64.rs b/version3/rust/src/roms/rom_nums256w_64.rs
index 78fa6c5..5b5d491 100644
--- a/version3/rust/src/roms/rom_nums256w_64.rs
+++ b/version3/rust/src/roms/rom_nums256w_64.rs
@@ -72,6 +72,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_nums384e_32.rs b/version3/rust/src/roms/rom_nums384e_32.rs
index 05fd563..eaad1c8 100644
--- a/version3/rust/src/roms/rom_nums384e_32.rs
+++ b/version3/rust/src/roms/rom_nums384e_32.rs
@@ -67,6 +67,7 @@
 pub const CURVETYPE: CurveType = CurveType::EDWARDS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 48;
 pub const AESKEY: usize = 24;
diff --git a/version3/rust/src/roms/rom_nums384e_64.rs b/version3/rust/src/roms/rom_nums384e_64.rs
index 535026d..08d83c2 100644
--- a/version3/rust/src/roms/rom_nums384e_64.rs
+++ b/version3/rust/src/roms/rom_nums384e_64.rs
@@ -88,6 +88,7 @@
 pub const CURVETYPE: CurveType = CurveType::EDWARDS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 48;
 pub const AESKEY: usize = 24;
diff --git a/version3/rust/src/roms/rom_nums384w_32.rs b/version3/rust/src/roms/rom_nums384w_32.rs
index c733516..92181a6 100644
--- a/version3/rust/src/roms/rom_nums384w_32.rs
+++ b/version3/rust/src/roms/rom_nums384w_32.rs
@@ -67,6 +67,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 48;
 pub const AESKEY: usize = 24;
diff --git a/version3/rust/src/roms/rom_nums384w_64.rs b/version3/rust/src/roms/rom_nums384w_64.rs
index 532cdc5..3f51d94 100644
--- a/version3/rust/src/roms/rom_nums384w_64.rs
+++ b/version3/rust/src/roms/rom_nums384w_64.rs
@@ -87,6 +87,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 48;
 pub const AESKEY: usize = 24;
diff --git a/version3/rust/src/roms/rom_nums512e_32.rs b/version3/rust/src/roms/rom_nums512e_32.rs
index 897b27b..8d53f9f 100644
--- a/version3/rust/src/roms/rom_nums512e_32.rs
+++ b/version3/rust/src/roms/rom_nums512e_32.rs
@@ -73,6 +73,7 @@
 pub const CURVETYPE: CurveType = CurveType::EDWARDS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 64;
 pub const AESKEY: usize = 32;
diff --git a/version3/rust/src/roms/rom_nums512e_64.rs b/version3/rust/src/roms/rom_nums512e_64.rs
index 4c11a9f..298f3cc 100644
--- a/version3/rust/src/roms/rom_nums512e_64.rs
+++ b/version3/rust/src/roms/rom_nums512e_64.rs
@@ -98,6 +98,7 @@
 pub const CURVETYPE: CurveType = CurveType::EDWARDS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 64;
 pub const AESKEY: usize = 32;
diff --git a/version3/rust/src/roms/rom_nums512w_32.rs b/version3/rust/src/roms/rom_nums512w_32.rs
index df06a06..785070c 100644
--- a/version3/rust/src/roms/rom_nums512w_32.rs
+++ b/version3/rust/src/roms/rom_nums512w_32.rs
@@ -71,6 +71,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 64;
 pub const AESKEY: usize = 32;
diff --git a/version3/rust/src/roms/rom_nums512w_64.rs b/version3/rust/src/roms/rom_nums512w_64.rs
index e666a88..6868c87 100644
--- a/version3/rust/src/roms/rom_nums512w_64.rs
+++ b/version3/rust/src/roms/rom_nums512w_64.rs
@@ -88,6 +88,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 64;
 pub const AESKEY: usize = 32;
diff --git a/version3/rust/src/roms/rom_secp256k1_32.rs b/version3/rust/src/roms/rom_secp256k1_32.rs
index 15184a9..94bbbaa 100644
--- a/version3/rust/src/roms/rom_secp256k1_32.rs
+++ b/version3/rust/src/roms/rom_secp256k1_32.rs
@@ -64,6 +64,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/roms/rom_secp256k1_64.rs b/version3/rust/src/roms/rom_secp256k1_64.rs
index d9746ec..b22d875 100644
--- a/version3/rust/src/roms/rom_secp256k1_64.rs
+++ b/version3/rust/src/roms/rom_secp256k1_64.rs
@@ -75,6 +75,7 @@
 pub const CURVETYPE: CurveType = CurveType::WEIERSTRASS;
 pub const CURVE_PAIRING_TYPE: CurvePairingType = CurvePairingType::NOT;
 pub const SEXTIC_TWIST: SexticTwist = SexticTwist::NOT;
+pub const ATE_BITS: usize = 0;
 pub const SIGN_OF_X: SignOfX = SignOfX::NOT;
 pub const HASH_TYPE: usize = 32;
 pub const AESKEY: usize = 16;
diff --git a/version3/rust/src/types.rs b/version3/rust/src/types.rs
index be66c92..ea310d7 100644
--- a/version3/rust/src/types.rs
+++ b/version3/rust/src/types.rs
@@ -41,4 +41,5 @@
     NOT,
     POSITIVEX,
     NEGATIVEX,
-}
\ No newline at end of file
+}
+
diff --git a/version3/swift/bls.swift b/version3/swift/bls.swift
index 6ebba98..cac62a6 100644
--- a/version3/swift/bls.swift
+++ b/version3/swift/bls.swift
@@ -82,7 +82,16 @@
         let G=ECP2.generator()
         let PK=ECP2.fromBytes(W)
         D.neg()
-        var v=PAIR.ate2(G,D,PK,HM)
+
+// Use new multi-pairing mechanism 
+        var r=PAIR.initmp()
+        PAIR.another(&r,G,D)
+        PAIR.another(&r,PK,HM)
+        var v=PAIR.miller(r)
+
+//.. or alternatively
+//        var v=PAIR.ate2(G,D,PK,HM)
+
         v=PAIR.fexp(v)
     
         if v.isunity() {
diff --git a/version3/swift/bls192.swift b/version3/swift/bls192.swift
index 950c8c0..54a31cd 100644
--- a/version3/swift/bls192.swift
+++ b/version3/swift/bls192.swift
@@ -82,7 +82,17 @@
         let G=ECP4.generator()
         let PK=ECP4.fromBytes(W)
         D.neg()
-        var v=PAIR192.ate2(G,D,PK,HM)
+
+
+// Use new multi-pairing mechanism 
+        var r=PAIR192.initmp()
+        PAIR192.another(&r,G,D)
+        PAIR192.another(&r,PK,HM)
+        var v=PAIR192.miller(r)
+
+//.. or alternatively
+//        var v=PAIR192.ate2(G,D,PK,HM)
+
         v=PAIR192.fexp(v)
     
         if v.isunity() {
diff --git a/version3/swift/bls256.swift b/version3/swift/bls256.swift
index f60470e..07fd817 100644
--- a/version3/swift/bls256.swift
+++ b/version3/swift/bls256.swift
@@ -82,7 +82,16 @@
         let G=ECP8.generator()
         let PK=ECP8.fromBytes(W)
         D.neg()
-        var v=PAIR256.ate2(G,D,PK,HM)
+
+// Use new multi-pairing mechanism 
+        var r=PAIR256.initmp()
+        PAIR256.another(&r,G,D)
+        PAIR256.another(&r,PK,HM)
+        var v=PAIR256.miller(r)
+
+//.. or alternatively
+//        var v=PAIR256.ate2(G,D,PK,HM)
+
         v=PAIR256.fexp(v)
     
         if v.isunity() {
diff --git a/version3/swift/config32.py b/version3/swift/config32.py
index 9eaa7cf..9b69e35 100644
--- a/version3/swift/config32.py
+++ b/version3/swift/config32.py
@@ -56,7 +56,7 @@
 	os.system("rmdir amcl"+slashtext+tb)
 
 
-def curveset(tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,cs) :
+def curveset(tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,ab,cs) :
 	global deltext,slashtext,copytext
 	global cptr,chosen
 
@@ -97,6 +97,7 @@
 
 	replace(fpath+"config_curve.swift","@ST@",stw)
 	replace(fpath+"config_curve.swift","@SX@",sx)
+	replace(fpath+"config_curve.swift","@AB@",ab)
 
 	if cs == "128" :
 		replace(fpath+"config_curve.swift","@HT@","32")
@@ -225,93 +226,93 @@
 
 
 	if x==1:
-		curveset("ed25519","32","29","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","128")
+		curveset("ed25519","32","29","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==2:
-		curveset("c25519","32","29","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","NOT","NOT","128")
+		curveset("c25519","32","29","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==3:
-		curveset("nist256","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("nist256","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==4:
-		curveset("brainpool","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("brainpool","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==5:
-		curveset("anssi","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("anssi","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 
 	if x==6:
-		curveset("hifive","42","29","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","192")
+		curveset("hifive","42","29","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==7:
-		curveset("goldilocks","56","29","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","NOT","NOT","256")
+		curveset("goldilocks","56","29","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 	if x==8:
-		curveset("nist384","48","29","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","192")
+		curveset("nist384","48","29","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==9:
-		curveset("c41417","52","29","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","256")
+		curveset("c41417","52","29","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 	if x==10:
-		curveset("nist521","66","28","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","256")
+		curveset("nist521","66","28","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 
 
 	if x==11:
-		curveset("nums256w","32","28","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("nums256w","32","28","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==12:
-		curveset("nums256e","32","29","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","128")
+		curveset("nums256e","32","29","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==13:
-		curveset("nums384w","48","29","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","192")
+		curveset("nums384w","48","29","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==14:
-		curveset("nums384e","48","29","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","192")
+		curveset("nums384e","48","29","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==15:
-		curveset("nums512w","64","29","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","256")
+		curveset("nums512w","64","29","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 	if x==16:
-		curveset("nums512e","64","29","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","256")
+		curveset("nums512e","64","29","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 
 	if x==17:
-		curveset("secp256k1","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("secp256k1","32","28","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 
 
 	if x==18:
-		curveset("bn254","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("bn254","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==19:
-		curveset("bn254CX","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("bn254CX","32","28","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==20:
-		curveset("bls383","48","29","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","128")
+		curveset("bls383","48","29","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","65","128")
 		pfcurve_selected=True
 
 	if x==21:
-		curveset("bls381","48","29","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128")
+		curveset("bls381","48","29","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","65","128")
 		pfcurve_selected=True
 
 	if x==22:
-		curveset("fp256bn","32","28","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","128")
+		curveset("fp256bn","32","28","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==23:
-		curveset("fp512bn","64","29","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","128")
+		curveset("fp512bn","64","29","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","130","128")
 		pfcurve_selected=True
 # https://eprint.iacr.org/2017/334.pdf
 	if x==24:
-		curveset("bls461","58","28","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128")
+		curveset("bls461","58","28","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","78","128")
 		pfcurve_selected=True
 
 	if x==25:
-		curveset("bls24","60","29","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","192")
+		curveset("bls24","60","29","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","49","192")
 		pfcurve_selected=True
 
 	if x==26:
-		curveset("bls48","70","29","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","256")
+		curveset("bls48","70","29","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","32","256")
 		pfcurve_selected=True
 
 
diff --git a/version3/swift/config64.py b/version3/swift/config64.py
index 133726f..9a17880 100644
--- a/version3/swift/config64.py
+++ b/version3/swift/config64.py
@@ -57,7 +57,7 @@
 	os.system("rmdir amcl"+slashtext+tb)
 
 
-def curveset(tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,cs) :
+def curveset(tc,nb,base,nbt,m8,mt,ct,pf,stw,sx,ab,cs) :
 	global deltext,slashtext,copytext
 	global cptr,chosen
 
@@ -97,6 +97,7 @@
 
 	replace(fpath+"config_curve.swift","@ST@",stw)
 	replace(fpath+"config_curve.swift","@SX@",sx)
+	replace(fpath+"config_curve.swift","@AB@",ab)	
 
 	if cs == "128" :
 		replace(fpath+"config_curve.swift","@HT@","32")
@@ -225,92 +226,92 @@
 
 
 	if x==1:
-		curveset("ed25519","32","56","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","128")
+		curveset("ed25519","32","56","255","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==2:
-		curveset("c25519","32","56","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","NOT","NOT","128")
+		curveset("c25519","32","56","255","5","PSEUDO_MERSENNE","MONTGOMERY","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==3:
-		curveset("nist256","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("nist256","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==4:
-		curveset("brainpool","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("brainpool","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==5:
-		curveset("anssi","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("anssi","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 
 	if x==6:
-		curveset("hifive","42","60","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","192")
+		curveset("hifive","42","60","336","5","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==7:
-		curveset("goldilocks","56","58","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","NOT","NOT","256")
+		curveset("goldilocks","56","58","448","7","GENERALISED_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 	if x==8:
-		curveset("nist384","48","56","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","192")
+		curveset("nist384","48","56","384","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==9:
-		curveset("c41417","52","60","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","256")
+		curveset("c41417","52","60","414","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 	if x==10:
-		curveset("nist521","66","60","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","256")
+		curveset("nist521","66","60","521","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 
 	if x==11:
-		curveset("nums256w","32","56","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("nums256w","32","56","256","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==12:
-		curveset("nums256e","32","56","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","128")
+		curveset("nums256e","32","56","256","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 	if x==13:
-		curveset("nums384w","48","58","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","192")
+		curveset("nums384w","48","58","384","3","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==14:
-		curveset("nums384e","48","56","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","192")
+		curveset("nums384e","48","56","384","3","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","192")
 		curve_selected=True
 	if x==15:
-		curveset("nums512w","64","60","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","256")
+		curveset("nums512w","64","60","512","7","PSEUDO_MERSENNE","WEIERSTRASS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 	if x==16:
-		curveset("nums512e","64","60","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","256")
+		curveset("nums512e","64","60","512","7","PSEUDO_MERSENNE","EDWARDS","NOT","NOT","NOT","NOT","256")
 		curve_selected=True
 
 	if x==17:
-		curveset("secp256k1","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","128")
+		curveset("secp256k1","32","56","256","7","NOT_SPECIAL","WEIERSTRASS","NOT","NOT","NOT","NOT","128")
 		curve_selected=True
 
 	if x==18:
-		curveset("bn254","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("bn254","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==19:
-		curveset("bn254CX","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","128")
+		curveset("bn254CX","32","56","254","3","NOT_SPECIAL","WEIERSTRASS","BN","D_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==20:
-		curveset("bls383","48","58","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","128")
+		curveset("bls383","48","58","383","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","65","128")
 		pfcurve_selected=True
 
 	if x==21:
-		curveset("bls381","48","58","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128")
+		curveset("bls381","48","58","381","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","65","128")
 		pfcurve_selected=True
 
 
 	if x==22:
-		curveset("fp256bn","32","56","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","128")
+		curveset("fp256bn","32","56","256","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","NEGATIVEX","66","128")
 		pfcurve_selected=True
 	if x==23:
-		curveset("fp512bn","64","60","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","128")
+		curveset("fp512bn","64","60","512","3","NOT_SPECIAL","WEIERSTRASS","BN","M_TYPE","POSITIVEX","130","128")
 		pfcurve_selected=True
 # https://eprint.iacr.org/2017/334.pdf
 	if x==24:
-		curveset("bls461","58","60","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","128")
+		curveset("bls461","58","60","461","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","NEGATIVEX","78","128")
 		pfcurve_selected=True
 
 	if x==25:
-		curveset("bls24","60","56","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","192")
+		curveset("bls24","60","56","479","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","49","192")
 		pfcurve_selected=True
 
 	if x==26:
-		curveset("bls48","70","58","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","256")
+		curveset("bls48","70","58","556","3","NOT_SPECIAL","WEIERSTRASS","BLS","M_TYPE","POSITIVEX","32","256")
 		pfcurve_selected=True
 
 # rsaset(rsaname,big_length_bytes,bits_in_base,multiplier)
diff --git a/version3/swift/config_curve.swift b/version3/swift/config_curve.swift
index f5da8e2..66c64da 100644
--- a/version3/swift/config_curve.swift
+++ b/version3/swift/config_curve.swift
@@ -14,6 +14,7 @@
     static public let CURVE_PAIRING_TYPE = @PF@
     static public let SEXTIC_TWIST = @ST@
     static public let SIGN_OF_X = @SX@
+    static public let ATE_BITS = @AB@    
 
     static public let HASH_TYPE = @HT@
     static public let AESKEY = @AK@
diff --git a/version3/swift/fp12.swift b/version3/swift/fp12.swift
index 5733402..88a24ed 100644
--- a/version3/swift/fp12.swift
+++ b/version3/swift/fp12.swift
@@ -29,9 +29,16 @@
 
 public struct FP12
 {
+    static public let ZERO:Int=0
+    static public let ONE:Int=1
+    static public let SPARSER:Int=2
+    static public let SPARSE:Int=3
+    static public let DENSE:Int=4
+
     private var a:FP4
     private var b:FP4
     private var c:FP4
+    private var stype:Int
     
     /* reduce all components of this mod Modulus */
     mutating func reduce()
@@ -43,16 +50,28 @@
     /* normalise all components of this */
     mutating func norm()
     {
-        a.norm();
-        b.norm();
-        c.norm();
+        a.norm()
+        b.norm()
+        c.norm()
     }
+
+    mutating func settype(_ t:Int)
+    {
+        stype=t
+    }
+
+    mutating func gettype() -> Int
+    {
+        return stype
+    }
+
     /* Constructors */
     init(_ d:FP4)
     {
         a=FP4(d)
         b=FP4(0)
         c=FP4(0)
+        stype=FP12.SPARSER
     }
     
     init(_ d:Int)
@@ -60,6 +79,8 @@
         a=FP4(d)
         b=FP4(0)
         c=FP4(0)
+        if (d==1) {stype=FP12.ONE}
+        else {stype=FP12.SPARSER}
     }
     
     init(_ d:FP4,_ e:FP4,_ f:FP4)
@@ -67,6 +88,7 @@
         a=FP4(d)
         b=FP4(e)
         c=FP4(f)
+        stype=FP12.DENSE
     }
     
     init(_ x:FP12)
@@ -74,6 +96,7 @@
         a=FP4(x.a)
         b=FP4(x.b)
         c=FP4(x.c)
+        stype=x.stype
     }
     /* test x==0 ? */
     func iszilch() -> Bool
@@ -86,6 +109,8 @@
         a.cmove(g.a,d)
         b.cmove(g.b,d)
         c.cmove(g.c,d)
+        let u = ~(d-1)
+        stype^=(stype^g.stype)&u   
     }
 
     /* return 1 if b==c, no branching */
@@ -150,6 +175,7 @@
         a.copy(x.a)
         b.copy(x.b)
         c.copy(x.c)
+        stype=x.stype
     }
     /* set self=1 */
     mutating func one()
@@ -157,6 +183,7 @@
         a.one()
         b.zero()
         c.zero()
+        stype=FP12.ONE
     }
     /* self=conj(self) */
     mutating func conj()
@@ -201,12 +228,14 @@
         c.add(c)
         b.add(B)
         c.add(C)
+        stype=FP12.DENSE
         reduce()
     
     }
     /* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
     mutating func sqr()
     {
+        if (stype==FP12.ONE) {return}
         var A=FP4(a)
         var B=FP4(b)
         var C=FP4(c)
@@ -239,7 +268,11 @@
 
         b.copy(C); b.add(D)
         c.add(A)
-    
+        if (stype==FP12.SPARSER) {
+            stype=FP12.SPARSE
+        } else {
+            stype=FP12.DENSE
+        }
         norm()
     }
     
@@ -298,88 +331,106 @@
         z3.norm()
         z3.times_i()
         a.copy(z0); a.add(z3)
-    
+        stype=FP12.DENSE
         norm()
     }
     
-    /* Special case of multiplication arises from special form of ATE pairing line function */
-    mutating func smul(_ y:FP12,_ twist:Int)
+/* FP12 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+    mutating func ssmul(_ y:FP12)
     {
-        if twist == CONFIG_CURVE.D_TYPE {
-            var z0=FP4(a)
-            var z2=FP4(b)
-            var z3=FP4(b)
-            var t0=FP4(0)
-            var t1=FP4(y.a)
-    
-            z0.mul(y.a)
-            z2.pmul(y.b.real())
-            b.add(a)
-            t1.adds(y.b.real())
-    
-            b.norm(); t1.norm()
-
-            b.mul(t1)
-            z3.add(c); z3.norm()
-            z3.pmul(y.b.real())
-    
-            t0.copy(z0); t0.neg()
-            t1.copy(z2); t1.neg()
-    
-            b.add(t0)
-    
-            b.add(t1)
-            z3.add(t1)
-            z2.add(t0)
-    
-            t0.copy(a); t0.add(c)
-            t0.norm(); z3.norm()
-            t0.mul(y.a)
-            c.copy(z2); c.add(t0)
-    
-            z3.times_i()
-            a.copy(z0); a.add(z3)
+        if stype==FP12.ONE {
+            copy(y)
+            return
         }
-        if twist == CONFIG_CURVE.M_TYPE {
+        if y.stype==FP12.ONE {
+            return
+        }
+        if y.stype>=FP12.SPARSE {
             var z0=FP4(a)
             var z1=FP4(0)
             var z2=FP4(0)
             var z3=FP4(0)
-            var t0=FP4(a)
-            var t1=FP4(0)
-        
             z0.mul(y.a)
-            t0.add(b)
-            t0.norm()
 
-            z1.copy(t0); z1.mul(y.a)
-            t0.copy(b); t0.add(c)
-            t0.norm()
+            if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.M_TYPE {  
+                if y.stype==FP12.SPARSE || stype==FP12.SPARSE {
+                    var ga=FP2(0)
+                    var gb=FP2(0)
 
-            z3.copy(t0)
-            z3.pmul(y.c.getb())
-            z3.times_i()
+                    gb.copy(b.getb())
+                    gb.mul(y.b.getb())
+                    ga.zero()
+                    if y.stype != FP12.SPARSE {
+                        ga.copy(b.getb())
+                        ga.mul(y.b.geta())
+                    }
+                    if stype != FP12.SPARSE {
+                        ga.copy(b.geta())
+                        ga.mul(y.b.getb())
+                    }
+                    z2.set_fp2s(ga,gb)
+                    z2.times_i()
+                } else {
+                    z2.copy(b)
+                    z2.mul(y.b)
+                }
+            } else {
+                z2.copy(b)
+                z2.mul(y.b)
+            }
+            var t0=FP4(a)
+            var t1=FP4(y.a)
+            t0.add(b); t0.norm()
+            t1.add(y.b); t1.norm()
+
+            z1.copy(t0); z1.mul(t1)
+            t0.copy(b); t0.add(c); t0.norm()
+            t1.copy(y.b); t1.add(y.c); t1.norm()
+
+            z3.copy(t0); z3.mul(t1)
 
             t0.copy(z0); t0.neg()
+            t1.copy(z2); t1.neg()
 
             z1.add(t0)
-            b.copy(z1)
-            z2.copy(t0)
+            b.copy(z1); b.add(t1)
 
-            t0.copy(a); t0.add(c)
-            t1.copy(y.a); t1.add(y.c)
+            z3.add(t1)
+            z2.add(t0)
 
-            t0.norm()
-            t1.norm()
+            t0.copy(a); t0.add(c); t0.norm()
+            t1.copy(y.a); t1.add(y.c); t1.norm()
     
             t0.mul(t1)
             z2.add(t0)
 
-            t0.copy(c)
-            
-            t0.pmul(y.c.getb())
-            t0.times_i()
+            if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.D_TYPE {  
+                if y.stype==FP12.SPARSE || stype==FP12.SPARSE {
+                    var ga=FP2(0)
+                    var gb=FP2(0)
 
+                    ga.copy(c.geta())
+                    ga.mul(y.c.geta())
+                    gb.zero()
+                    if y.stype != FP12.SPARSE {
+                        gb.copy(c.geta())
+                        gb.mul(y.c.getb())
+                    }
+                    if stype != FP12.SPARSE {
+                        gb.copy(c.getb())
+                        gb.mul(y.c.geta())
+                    }
+                    t0.set_fp2s(ga,gb)
+                } else {
+                    t0.copy(c)
+                    t0.mul(y.c)
+                }
+            } else {
+                t0.copy(c)
+                t0.mul(y.c)
+            }
             t1.copy(t0); t1.neg()
 
             c.copy(z2); c.add(t1)
@@ -388,9 +439,200 @@
             b.add(t0)
             z3.norm()
             z3.times_i()
-            a.copy(z0); a.add(z3)      
+            a.copy(z0); a.add(z3);
+        } else {
+            if stype==FP12.SPARSER {
+                smul(y)
+                return
+            }
+            if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.D_TYPE {  // dense by sparser - 13m 
+                var z0=FP4(a)
+                var z2=FP4(b)
+                var z3=FP4(b)
+                var t0=FP4(0)
+                var t1=FP4(y.a)
+                z0.mul(y.a)
+                z2.pmul(y.b.real())
+                b.add(a)
+                t1.adds(y.b.real())
+
+                t1.norm()
+                b.norm()
+                b.mul(t1)
+                z3.add(c)
+                z3.norm()
+                z3.pmul(y.b.real())
+
+                t0.copy(z0); t0.neg()
+                t1.copy(z2); t1.neg()
+
+                b.add(t0)
+
+                b.add(t1)
+                z3.add(t1)
+                z2.add(t0)
+
+                t0.copy(a); t0.add(c); t0.norm()
+                z3.norm()
+                t0.mul(y.a)
+                c.copy(z2); c.add(t0)
+
+                z3.times_i()
+                a.copy(z0); a.add(z3)
+            }
+            if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.M_TYPE {
+                var z0=FP4(a)
+                var z1=FP4(0)
+                var z2=FP4(0)
+                var z3=FP4(0)
+                var t0=FP4(a)
+                var t1=FP4(0)
+        
+                z0.mul(y.a)
+                t0.add(b); t0.norm()
+
+                z1.copy(t0); z1.mul(y.a)
+                t0.copy(b); t0.add(c)
+                t0.norm()
+
+                z3.copy(t0)
+                z3.pmul(y.c.getb())
+                z3.times_i()
+
+                t0.copy(z0); t0.neg()
+                z1.add(t0)
+                b.copy(z1)
+                z2.copy(t0)
+
+                t0.copy(a); t0.add(c); t0.norm()
+                t1.copy(y.a); t1.add(y.c); t1.norm()
+
+                t0.mul(t1)
+                z2.add(t0)
+                t0.copy(c)
+            
+                t0.pmul(y.c.getb())
+                t0.times_i()
+                t1.copy(t0); t1.neg()
+
+                c.copy(z2); c.add(t1)
+                z3.add(t1)
+                t0.times_i()
+                b.add(t0)
+                z3.norm()
+                z3.times_i()
+                a.copy(z0); a.add(z3)
+            }   
         }
-        norm()
+        stype=FP12.DENSE
+        norm()    
+    }
+
+    /* Special case of multiplication arises from special form of ATE pairing line function */
+    mutating func smul(_ y:FP12)
+    {
+        if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.D_TYPE {  
+            var w1=FP2(a.geta())
+            var w2=FP2(a.getb())
+            var w3=FP2(b.geta())
+
+            w1.mul(y.a.geta())
+            w2.mul(y.a.getb())
+            w3.mul(y.b.geta())
+
+            var ta=FP2(a.geta())
+            var tb=FP2(y.a.geta())
+            ta.add(a.getb()); ta.norm()
+            tb.add(y.a.getb()); tb.norm()
+            var tc=FP2(ta)
+            tc.mul(tb)
+            var t=FP2(w1)
+            t.add(w2)
+            t.neg()
+            tc.add(t)
+
+            ta.copy(a.geta()); ta.add(b.geta()); ta.norm()
+            tb.copy(y.a.geta()); tb.add(y.b.geta()); tb.norm()
+            var td=FP2(ta)
+            td.mul(tb)
+            t.copy(w1)
+            t.add(w3)
+            t.neg()
+            td.add(t)
+
+            ta.copy(a.getb()); ta.add(b.geta()); ta.norm()
+            tb.copy(y.a.getb()); tb.add(y.b.geta()); tb.norm()
+            var te=FP2(ta)
+            te.mul(tb)
+            t.copy(w2)
+            t.add(w3)
+            t.neg()
+            te.add(t)
+
+            w2.mul_ip()
+            w1.add(w2)
+
+            a.set_fp2s(w1,tc)
+            b.set_fp2s(td,te)
+            c.set_fp2(w3)
+
+            a.norm()
+            b.norm()
+    } else {
+            var w1=FP2(a.geta())
+            var w2=FP2(a.getb())
+            var w3=FP2(c.getb())
+
+            w1.mul(y.a.geta())
+            w2.mul(y.a.getb())
+            w3.mul(y.c.getb())
+
+            var ta=FP2(a.geta())
+            var tb=FP2(y.a.geta())
+            ta.add(a.getb()); ta.norm()
+            tb.add(y.a.getb()); tb.norm()
+            var tc=FP2(ta)
+            tc.mul(tb)
+            var t=FP2(w1)
+            t.add(w2)
+            t.neg()
+            tc.add(t)
+
+            ta.copy(a.geta()); ta.add(c.getb()); ta.norm()
+            tb.copy(y.a.geta()); tb.add(y.c.getb()); tb.norm()
+            var td=FP2(ta)
+            td.mul(tb)
+            t.copy(w1)
+            t.add(w3)
+            t.neg()
+            td.add(t)
+
+            ta.copy(a.getb()); ta.add(c.getb()); ta.norm()
+            tb.copy(y.a.getb()); tb.add(y.c.getb()); tb.norm()
+            var te=FP2(ta)
+            te.mul(tb)
+            t.copy(w2)
+            t.add(w3)
+            t.neg()
+            te.add(t)
+
+            w2.mul_ip()
+            w1.add(w2)
+            a.set_fp2s(w1,tc);
+
+            w3.mul_ip()
+            w3.norm()
+            b.set_fp2h(w3);
+
+            te.norm()
+            te.mul_ip()
+            c.set_fp2s(te,td)
+
+            a.norm()
+            c.norm()
+
+        }
+        stype=FP12.SPARSE
     }
     /* self=1/self */
     mutating func inverse()
@@ -427,6 +669,7 @@
         a.copy(f0); a.mul(f3)
         b.copy(f1); b.mul(f3)
         c.copy(f2); c.mul(f3)
+        stype=FP12.DENSE        
     }
     
     /* self=self^p using Frobenius */
@@ -444,6 +687,7 @@
     
         b.pmul(f)
         c.pmul(f2)
+        stype=FP12.DENSE        
     }
     
     /* trace function */
diff --git a/version3/swift/fp16.swift b/version3/swift/fp16.swift
index 766ee55..196f7c5 100644
--- a/version3/swift/fp16.swift
+++ b/version3/swift/fp16.swift
@@ -108,7 +108,21 @@
     {
         return b;
     }
-
+    mutating func set_fp8s(_ c: FP8,_ d: FP8)
+    {
+        a.copy(c)
+        b.copy(d)
+    }
+    mutating func set_fp8(_ c: FP8)
+    {
+        a.copy(c)
+        b.zero()
+    }    
+    mutating func set_fp8h(_ c: FP8)
+    {
+        b.copy(c)
+        a.zero()
+    }  
     /* test self=x? */
     func equals(_ x:FP16) -> Bool
     {
diff --git a/version3/swift/fp24.swift b/version3/swift/fp24.swift
index 735c508..f780cf2 100644
--- a/version3/swift/fp24.swift
+++ b/version3/swift/fp24.swift
@@ -29,9 +29,16 @@
 
 public struct FP24
 {
+    static public let ZERO:Int=0
+    static public let ONE:Int=1
+    static public let SPARSER:Int=2
+    static public let SPARSE:Int=3
+    static public let DENSE:Int=4
+
     private var a:FP8
     private var b:FP8
     private var c:FP8
+    private var stype:Int    
     
     /* reduce all components of this mod Modulus */
     mutating func reduce()
@@ -47,12 +54,24 @@
         b.norm();
         c.norm();
     }
+
+    mutating func settype(_ t:Int)
+    {
+        stype=t
+    }
+
+    mutating func gettype() -> Int
+    {
+        return stype
+    }
+
     /* Constructors */
     init(_ d:FP8)
     {
         a=FP8(d)
         b=FP8(0)
         c=FP8(0)
+        stype=FP24.SPARSER        
     }
     
     init(_ d:Int)
@@ -60,6 +79,8 @@
         a=FP8(d)
         b=FP8(0)
         c=FP8(0)
+        if (d==1) {stype=FP24.ONE}
+        else {stype=FP24.SPARSER}        
     }
 
     init(_ d:FP8,_ e:FP8,_ f:FP8)
@@ -67,6 +88,7 @@
         a=FP8(d)
         b=FP8(e)
         c=FP8(f)
+        stype=FP24.DENSE        
     }
     
     init(_ x:FP24)
@@ -74,6 +96,7 @@
         a=FP8(x.a)
         b=FP8(x.b)
         c=FP8(x.c)
+        stype=x.stype        
     }
     /* test x==0 ? */
     func iszilch() -> Bool
@@ -86,6 +109,8 @@
         a.cmove(g.a,d)
         b.cmove(g.b,d)
         c.cmove(g.c,d)
+        let u = ~(d-1)
+        stype^=(stype^g.stype)&u           
     }
 
     /* return 1 if b==c, no branching */
@@ -150,6 +175,7 @@
         a.copy(x.a)
         b.copy(x.b)
         c.copy(x.c)
+        stype=x.stype        
     }
     /* set self=1 */
     mutating func one()
@@ -157,6 +183,7 @@
         a.one()
         b.zero()
         c.zero()
+       stype=FP24.ONE        
     }
     /* self=conj(self) */
     mutating func conj()
@@ -202,6 +229,7 @@
         c.add(c)
         b.add(B)
         c.add(C)
+        stype=FP24.DENSE        
         reduce()
     
     }
@@ -209,6 +237,7 @@
     /* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
     mutating func sqr()
     {
+        if (stype==FP24.ONE) {return}        
         var A=FP8(a)
         var B=FP8(b)
         var C=FP8(c)
@@ -241,7 +270,11 @@
 
         b.copy(C); b.add(D)
         c.add(A)
-    
+        if (stype==FP24.SPARSER) {
+            stype=FP24.SPARSE
+        } else {
+            stype=FP24.DENSE
+        }    
         norm()
     }
 
@@ -300,99 +333,308 @@
         z3.norm()
         z3.times_i()
         a.copy(z0); a.add(z3)
-    
+        stype=FP24.DENSE    
         norm()
     }
 
-    /* Special case of multiplication arises from special form of ATE pairing line function */
-    mutating func smul(_ y:FP24,_ twist:Int)
+/* FP24 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+    mutating func ssmul(_ y:FP24)
     {
-        if twist == CONFIG_CURVE.D_TYPE {
-            var z0=FP8(a)
-            var z2=FP8(b)
-            var z3=FP8(b)
-            var t0=FP8(0)
-            var t1=FP8(y.a)
-    
-            z0.mul(y.a)
-            z2.pmul(y.b.real())
-            b.add(a)
-            t1.adds(y.b.real())
-    
-            b.norm(); t1.norm()
-
-            b.mul(t1)
-            z3.add(c); z3.norm()
-            z3.pmul(y.b.real())
-    
-            t0.copy(z0); t0.neg()
-            t1.copy(z2); t1.neg()
-    
-            b.add(t0)
-    
-            b.add(t1)
-            z3.add(t1)
-            z2.add(t0)
-    
-            t0.copy(a); t0.add(c)
-            t0.norm(); z3.norm()
-            t0.mul(y.a)
-            c.copy(z2); c.add(t0)
-    
-            z3.times_i()
-            a.copy(z0); a.add(z3)
+        if stype==FP24.ONE {
+            copy(y)
+            return
         }
-        if twist == CONFIG_CURVE.M_TYPE {
+        if y.stype==FP24.ONE {
+            return
+        }
+        if y.stype>=FP24.SPARSE {
             var z0=FP8(a)
             var z1=FP8(0)
             var z2=FP8(0)
             var z3=FP8(0)
-            var t0=FP8(a)
-            var t1=FP8(0)
-        
             z0.mul(y.a)
-            t0.add(b)
-            t0.norm()
 
-            z1.copy(t0); z1.mul(y.a)
-            t0.copy(b); t0.add(c)
-            t0.norm();
+            if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.M_TYPE {  
+                if y.stype==FP24.SPARSE || stype==FP24.SPARSE {
+                    var ga=FP4(0)
+                    var gb=FP4(0)
 
-            z3.copy(t0); //z3.mul(y.c);
-            z3.pmul(y.c.getb())
-            z3.times_i()
+                    gb.copy(b.getb())
+                    gb.mul(y.b.getb())
+                    ga.zero()
+                    if y.stype != FP24.SPARSE {
+                        ga.copy(b.getb())
+                        ga.mul(y.b.geta())
+                    }
+                    if stype != FP24.SPARSE {
+                        ga.copy(b.geta())
+                        ga.mul(y.b.getb())
+                    }
+                    z2.set_fp4s(ga,gb)
+                    z2.times_i()
+                } else {
+                    z2.copy(b)
+                    z2.mul(y.b)
+                }
+            } else {
+                z2.copy(b)
+                z2.mul(y.b)
+            }
+            var t0=FP8(a)
+            var t1=FP8(y.a)
+            t0.add(b); t0.norm()
+            t1.add(y.b); t1.norm()
+
+            z1.copy(t0); z1.mul(t1)
+            t0.copy(b); t0.add(c); t0.norm()
+            t1.copy(y.b); t1.add(y.c); t1.norm()
+
+            z3.copy(t0); z3.mul(t1)
 
             t0.copy(z0); t0.neg()
+            t1.copy(z2); t1.neg()
 
             z1.add(t0)
-            b.copy(z1);
-            z2.copy(t0)
+            b.copy(z1); b.add(t1)
 
-            t0.copy(a); t0.add(c)
-            t1.copy(y.a); t1.add(y.c)
+            z3.add(t1)
+            z2.add(t0)
 
-            t0.norm()
-            t1.norm()
+            t0.copy(a); t0.add(c); t0.norm()
+            t1.copy(y.a); t1.add(y.c); t1.norm()
     
             t0.mul(t1)
             z2.add(t0)
 
-            t0.copy(c)
-            
-            t0.pmul(y.c.getb())
-            t0.times_i()
+            if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.D_TYPE {  
+                if y.stype==FP24.SPARSE || stype==FP24.SPARSE {
+                    var ga=FP4(0)
+                    var gb=FP4(0)
 
+                    ga.copy(c.geta())
+                    ga.mul(y.c.geta())
+                    gb.zero()
+                    if y.stype != FP24.SPARSE {
+                        gb.copy(c.geta())
+                        gb.mul(y.c.getb())
+                    }
+                    if stype != FP24.SPARSE {
+                        gb.copy(c.getb())
+                        gb.mul(y.c.geta())
+                    }
+                    t0.set_fp4s(ga,gb)
+                } else {
+                    t0.copy(c)
+                    t0.mul(y.c)
+                }
+            } else {
+                t0.copy(c)
+                t0.mul(y.c)
+            }
             t1.copy(t0); t1.neg()
 
             c.copy(z2); c.add(t1)
             z3.add(t1)
             t0.times_i()
             b.add(t0)
-            z3.norm();
+            z3.norm()
             z3.times_i()
-            a.copy(z0); a.add(z3)      
+            a.copy(z0); a.add(z3);
+        } else {
+            if stype==FP24.SPARSER {
+                smul(y)
+                return
+            }
+            if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.D_TYPE {  // dense by sparser - 13m 
+                var z0=FP8(a)
+                var z2=FP8(b)
+                var z3=FP8(b)
+                var t0=FP8(0)
+                var t1=FP8(y.a)
+                z0.mul(y.a)
+                z2.pmul(y.b.real())
+                b.add(a)
+                t1.adds(y.b.real())
+
+                t1.norm()
+                b.norm()
+                b.mul(t1)
+                z3.add(c)
+                z3.norm()
+                z3.pmul(y.b.real())
+
+                t0.copy(z0); t0.neg()
+                t1.copy(z2); t1.neg()
+
+                b.add(t0)
+
+                b.add(t1)
+                z3.add(t1)
+                z2.add(t0)
+
+                t0.copy(a); t0.add(c); t0.norm()
+                z3.norm()
+                t0.mul(y.a)
+                c.copy(z2); c.add(t0)
+
+                z3.times_i()
+                a.copy(z0); a.add(z3)
+            }
+            if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.M_TYPE {
+                var z0=FP8(a)
+                var z1=FP8(0)
+                var z2=FP8(0)
+                var z3=FP8(0)
+                var t0=FP8(a)
+                var t1=FP8(0)
+        
+                z0.mul(y.a)
+                t0.add(b); t0.norm()
+
+                z1.copy(t0); z1.mul(y.a)
+                t0.copy(b); t0.add(c)
+                t0.norm()
+
+                z3.copy(t0)
+                z3.pmul(y.c.getb())
+                z3.times_i()
+
+                t0.copy(z0); t0.neg()
+                z1.add(t0)
+                b.copy(z1)
+                z2.copy(t0)
+
+                t0.copy(a); t0.add(c); t0.norm()
+                t1.copy(y.a); t1.add(y.c); t1.norm()
+
+                t0.mul(t1)
+                z2.add(t0)
+                t0.copy(c)
+            
+                t0.pmul(y.c.getb())
+                t0.times_i()
+                t1.copy(t0); t1.neg()
+
+                c.copy(z2); c.add(t1)
+                z3.add(t1)
+                t0.times_i()
+                b.add(t0)
+                z3.norm()
+                z3.times_i()
+                a.copy(z0); a.add(z3)
+            }   
         }
-        norm()
+        stype=FP24.DENSE
+        norm()    
+    }
+
+    /* Special case of multiplication arises from special form of ATE pairing line function */
+    mutating func smul(_ y:FP24)
+    {
+        if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.D_TYPE {  
+            var w1=FP4(a.geta())
+            var w2=FP4(a.getb())
+            var w3=FP4(b.geta())
+
+            w1.mul(y.a.geta())
+            w2.mul(y.a.getb())
+            w3.mul(y.b.geta())
+
+            var ta=FP4(a.geta())
+            var tb=FP4(y.a.geta())
+            ta.add(a.getb()); ta.norm()
+            tb.add(y.a.getb()); tb.norm()
+            var tc=FP4(ta)
+            tc.mul(tb)
+            var t=FP4(w1)
+            t.add(w2)
+            t.neg()
+            tc.add(t)
+
+            ta.copy(a.geta()); ta.add(b.geta()); ta.norm()
+            tb.copy(y.a.geta()); tb.add(y.b.geta()); tb.norm()
+            var td=FP4(ta)
+            td.mul(tb)
+            t.copy(w1)
+            t.add(w3)
+            t.neg()
+            td.add(t)
+
+            ta.copy(a.getb()); ta.add(b.geta()); ta.norm()
+            tb.copy(y.a.getb()); tb.add(y.b.geta()); tb.norm()
+            var te=FP4(ta)
+            te.mul(tb)
+            t.copy(w2)
+            t.add(w3)
+            t.neg()
+            te.add(t)
+
+            w2.times_i()
+            w1.add(w2)
+
+            a.set_fp4s(w1,tc)
+            b.set_fp4s(td,te)
+            c.set_fp4(w3)
+
+            a.norm()
+            b.norm()
+    } else {
+            var w1=FP4(a.geta())
+            var w2=FP4(a.getb())
+            var w3=FP4(c.getb())
+
+            w1.mul(y.a.geta())
+            w2.mul(y.a.getb())
+            w3.mul(y.c.getb())
+
+            var ta=FP4(a.geta())
+            var tb=FP4(y.a.geta())
+            ta.add(a.getb()); ta.norm()
+            tb.add(y.a.getb()); tb.norm()
+            var tc=FP4(ta)
+            tc.mul(tb)
+            var t=FP4(w1)
+            t.add(w2)
+            t.neg()
+            tc.add(t)
+
+            ta.copy(a.geta()); ta.add(c.getb()); ta.norm()
+            tb.copy(y.a.geta()); tb.add(y.c.getb()); tb.norm()
+            var td=FP4(ta)
+            td.mul(tb)
+            t.copy(w1)
+            t.add(w3)
+            t.neg()
+            td.add(t)
+
+            ta.copy(a.getb()); ta.add(c.getb()); ta.norm()
+            tb.copy(y.a.getb()); tb.add(y.c.getb()); tb.norm()
+            var te=FP4(ta)
+            te.mul(tb)
+            t.copy(w2)
+            t.add(w3)
+            t.neg()
+            te.add(t)
+
+            w2.times_i()
+            w1.add(w2)
+            a.set_fp4s(w1,tc);
+
+            w3.times_i()
+            w3.norm()
+            b.set_fp4h(w3);
+
+            te.norm()
+            te.times_i()
+            c.set_fp4s(te,td)
+
+            a.norm()
+            c.norm()
+
+        }
+        stype=FP24.SPARSE
     }
 
     /* self=1/self */
@@ -430,6 +672,7 @@
         a.copy(f0); a.mul(f3)
         b.copy(f1); b.mul(f3)
         c.copy(f2); c.mul(f3)
+        stype=FP24.DENSE        
     }
 
     /* self=self^p using Frobenius */
@@ -451,7 +694,7 @@
             b.qmul(f); b.times_i2()
             c.qmul(f2); c.times_i2(); c.times_i2()
         }        
-
+        stype=FP24.DENSE
     }
 
     /* trace function */
diff --git a/version3/swift/fp4.swift b/version3/swift/fp4.swift
index bbfd02b..90132a6 100644
--- a/version3/swift/fp4.swift
+++ b/version3/swift/fp4.swift
@@ -112,6 +112,21 @@
     {
         return a.equals(x.a) && b.equals(x.b)
     }
+    mutating func set_fp2s(_ c: FP2,_ d: FP2)
+    {
+        a.copy(c)
+        b.copy(d)
+    }
+    mutating func set_fp2(_ c: FP2)
+    {
+        a.copy(c)
+        b.zero()
+    }    
+    mutating func set_fp2h(_ c: FP2)
+    {
+        b.copy(c)
+        a.zero()
+    }      
     /* copy self=x */
     mutating func copy(_ x:FP4)
     {
diff --git a/version3/swift/fp48.swift b/version3/swift/fp48.swift
index 2620388..4ac17de 100644
--- a/version3/swift/fp48.swift
+++ b/version3/swift/fp48.swift
@@ -29,9 +29,16 @@
 
 public struct FP48
 {
+    static public let ZERO:Int=0
+    static public let ONE:Int=1
+    static public let SPARSER:Int=2
+    static public let SPARSE:Int=3
+    static public let DENSE:Int=4
+
     private var a:FP16
     private var b:FP16
     private var c:FP16
+    private var stype:Int    
     
     /* reduce all components of this mod Modulus */
     mutating func reduce()
@@ -47,12 +54,24 @@
         b.norm();
         c.norm();
     }
+
+    mutating func settype(_ t:Int)
+    {
+        stype=t
+    }
+
+    mutating func gettype() -> Int
+    {
+        return stype
+    }
+
     /* Constructors */
     init(_ d:FP16)
     {
         a=FP16(d)
         b=FP16(0)
         c=FP16(0)
+        stype=FP48.SPARSER        
     }
     
     init(_ d:Int)
@@ -60,6 +79,8 @@
         a=FP16(d)
         b=FP16(0)
         c=FP16(0)
+        if (d==1) {stype=FP48.ONE}
+        else {stype=FP48.SPARSER}        
     }
 
     init(_ d:FP16,_ e:FP16,_ f:FP16)
@@ -67,6 +88,7 @@
         a=FP16(d)
         b=FP16(e)
         c=FP16(f)
+        stype=FP48.DENSE        
     }
 
     init(_ x:FP48)
@@ -74,6 +96,7 @@
         a=FP16(x.a)
         b=FP16(x.b)
         c=FP16(x.c)
+        stype=x.stype        
     }
     /* test x==0 ? */
     func iszilch() -> Bool
@@ -86,6 +109,8 @@
         a.cmove(g.a,d)
         b.cmove(g.b,d)
         c.cmove(g.c,d)
+        let u = ~(d-1)
+        stype^=(stype^g.stype)&u           
     }
 
     /* return 1 if b==c, no branching */
@@ -151,6 +176,7 @@
         a.copy(x.a)
         b.copy(x.b)
         c.copy(x.c)
+        stype=x.stype        
     }
     /* set self=1 */
     mutating func one()
@@ -158,6 +184,7 @@
         a.one()
         b.zero()
         c.zero()
+        stype=FP48.ONE        
     }
     /* self=conj(self) */
     mutating func conj()
@@ -203,13 +230,14 @@
         c.add(c)
         b.add(B)
         c.add(C)
+        stype=FP48.DENSE        
         reduce()
-    
     }
 
     /* Chung-Hasan SQR2 method from http://cacr.uwaterloo.ca/techreports/2006/cacr2006-24.pdf */
     mutating func sqr()
     {
+        if (stype==FP48.ONE) {return}        
         var A=FP16(a)
         var B=FP16(b)
         var C=FP16(c)
@@ -242,7 +270,11 @@
 
         b.copy(C); b.add(D)
         c.add(A)
-    
+        if (stype==FP48.SPARSER) {
+            stype=FP48.SPARSE
+        } else {
+            stype=FP48.DENSE
+        }    
         norm()
     }
 
@@ -301,100 +333,311 @@
         z3.norm()
         z3.times_i()
         a.copy(z0); a.add(z3)
-    
+        stype=FP48.DENSE    
         norm()
     }
 
-    /* Special case of multiplication arises from special form of ATE pairing line function */
-    mutating func smul(_ y:FP48,_ twist:Int)
+/* FP48 full multiplication w=w*y */
+/* Supports sparse multiplicands */
+/* Usually w is denser than y */
+    mutating func ssmul(_ y:FP48)
     {
-        if twist == CONFIG_CURVE.D_TYPE {
-            var z0=FP16(a)
-            var z2=FP16(b)
-            var z3=FP16(b)
-            var t0=FP16(0)
-            var t1=FP16(y.a)
-    
-            z0.mul(y.a)
-            z2.pmul(y.b.real())
-            b.add(a)
-            t1.adds(y.b.real())
-    
-            b.norm(); t1.norm()
-
-            b.mul(t1)
-            z3.add(c); z3.norm()
-            z3.pmul(y.b.real())
-    
-            t0.copy(z0); t0.neg()
-            t1.copy(z2); t1.neg()
-    
-            b.add(t0)
-    
-            b.add(t1)
-            z3.add(t1)
-            z2.add(t0)
-    
-            t0.copy(a); t0.add(c)
-            t0.norm(); z3.norm()
-            t0.mul(y.a)
-            c.copy(z2); c.add(t0)
-    
-            z3.times_i()
-            a.copy(z0); a.add(z3)
+        if stype==FP48.ONE {
+            copy(y)
+            return
         }
-        if twist == CONFIG_CURVE.M_TYPE {
+        if y.stype==FP48.ONE {
+            return
+        }
+        if y.stype>=FP48.SPARSE {
             var z0=FP16(a)
             var z1=FP16(0)
             var z2=FP16(0)
             var z3=FP16(0)
-            var t0=FP16(a)
-            var t1=FP16(0)
-        
             z0.mul(y.a)
-            t0.add(b)
-            t0.norm()
 
-            z1.copy(t0); z1.mul(y.a)
-            t0.copy(b); t0.add(c)
-            t0.norm();
+            if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.M_TYPE {  
+                if y.stype==FP48.SPARSE || stype==FP48.SPARSE {
+                    var ga=FP8(0)
+                    var gb=FP8(0)
 
-            z3.copy(t0); 
-            z3.pmul(y.c.getb())
-            z3.times_i()
+                    gb.copy(b.getb())
+                    gb.mul(y.b.getb())
+                    ga.zero()
+                    if y.stype != FP48.SPARSE {
+                        ga.copy(b.getb())
+                        ga.mul(y.b.geta())
+                    }
+                    if stype != FP48.SPARSE {
+                        ga.copy(b.geta())
+                        ga.mul(y.b.getb())
+                    }
+                    z2.set_fp8s(ga,gb)
+                    z2.times_i()
+                } else {
+                    z2.copy(b)
+                    z2.mul(y.b)
+                }
+            } else {
+                z2.copy(b)
+                z2.mul(y.b)
+            }
+            var t0=FP16(a)
+            var t1=FP16(y.a)
+            t0.add(b); t0.norm()
+            t1.add(y.b); t1.norm()
+
+            z1.copy(t0); z1.mul(t1)
+            t0.copy(b); t0.add(c); t0.norm()
+            t1.copy(y.b); t1.add(y.c); t1.norm()
+
+            z3.copy(t0); z3.mul(t1)
 
             t0.copy(z0); t0.neg()
+            t1.copy(z2); t1.neg()
 
             z1.add(t0)
-            b.copy(z1);
-            z2.copy(t0)
+            b.copy(z1); b.add(t1)
 
-            t0.copy(a); t0.add(c)
-            t1.copy(y.a); t1.add(y.c)
+            z3.add(t1)
+            z2.add(t0)
 
-            t0.norm()
-            t1.norm()
+            t0.copy(a); t0.add(c); t0.norm()
+            t1.copy(y.a); t1.add(y.c); t1.norm()
     
             t0.mul(t1)
             z2.add(t0)
 
-            t0.copy(c)
-            
-            t0.pmul(y.c.getb())
-            t0.times_i()
+            if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.D_TYPE {  
+                if y.stype==FP48.SPARSE || stype==FP48.SPARSE {
+                    var ga=FP8(0)
+                    var gb=FP8(0)
 
+                    ga.copy(c.geta())
+                    ga.mul(y.c.geta())
+                    gb.zero()
+                    if y.stype != FP48.SPARSE {
+                        gb.copy(c.geta())
+                        gb.mul(y.c.getb())
+                    }
+                    if stype != FP48.SPARSE {
+                        gb.copy(c.getb())
+                        gb.mul(y.c.geta())
+                    }
+                    t0.set_fp8s(ga,gb)
+                } else {
+                    t0.copy(c)
+                    t0.mul(y.c)
+                }
+            } else {
+                t0.copy(c)
+                t0.mul(y.c)
+            }
             t1.copy(t0); t1.neg()
 
             c.copy(z2); c.add(t1)
             z3.add(t1)
             t0.times_i()
             b.add(t0)
-            z3.norm();
+            z3.norm()
             z3.times_i()
-            a.copy(z0); a.add(z3)      
+            a.copy(z0); a.add(z3);
+        } else {
+            if stype==FP48.SPARSER {
+                smul(y)
+                return
+            }
+            if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.D_TYPE {  // dense by sparser - 13m 
+                var z0=FP16(a)
+                var z2=FP16(b)
+                var z3=FP16(b)
+                var t0=FP16(0)
+                var t1=FP16(y.a)
+                z0.mul(y.a)
+                z2.pmul(y.b.real())
+                b.add(a)
+                t1.adds(y.b.real())
+
+                t1.norm()
+                b.norm()
+                b.mul(t1)
+                z3.add(c)
+                z3.norm()
+                z3.pmul(y.b.real())
+
+                t0.copy(z0); t0.neg()
+                t1.copy(z2); t1.neg()
+
+                b.add(t0)
+
+                b.add(t1)
+                z3.add(t1)
+                z2.add(t0)
+
+                t0.copy(a); t0.add(c); t0.norm()
+                z3.norm()
+                t0.mul(y.a)
+                c.copy(z2); c.add(t0)
+
+                z3.times_i()
+                a.copy(z0); a.add(z3)
+            }
+            if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.M_TYPE {
+                var z0=FP16(a)
+                var z1=FP16(0)
+                var z2=FP16(0)
+                var z3=FP16(0)
+                var t0=FP16(a)
+                var t1=FP16(0)
+        
+                z0.mul(y.a)
+                t0.add(b); t0.norm()
+
+                z1.copy(t0); z1.mul(y.a)
+                t0.copy(b); t0.add(c)
+                t0.norm()
+
+                z3.copy(t0)
+                z3.pmul(y.c.getb())
+                z3.times_i()
+
+                t0.copy(z0); t0.neg()
+                z1.add(t0)
+                b.copy(z1)
+                z2.copy(t0)
+
+                t0.copy(a); t0.add(c); t0.norm()
+                t1.copy(y.a); t1.add(y.c); t1.norm()
+
+                t0.mul(t1)
+                z2.add(t0)
+                t0.copy(c)
+            
+                t0.pmul(y.c.getb())
+                t0.times_i()
+                t1.copy(t0); t1.neg()
+
+                c.copy(z2); c.add(t1)
+                z3.add(t1)
+                t0.times_i()
+                b.add(t0)
+                z3.norm()
+                z3.times_i()
+                a.copy(z0); a.add(z3)
+            }   
         }
-        norm()
+        stype=FP48.DENSE
+        norm()    
     }
+
+
+    /* Special case of multiplication arises from special form of ATE pairing line function */
+    mutating func smul(_ y:FP48)
+    {
+        if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.D_TYPE {  
+            var w1=FP8(a.geta())
+            var w2=FP8(a.getb())
+            var w3=FP8(b.geta())
+
+            w1.mul(y.a.geta())
+            w2.mul(y.a.getb())
+            w3.mul(y.b.geta())
+
+            var ta=FP8(a.geta())
+            var tb=FP8(y.a.geta())
+            ta.add(a.getb()); ta.norm()
+            tb.add(y.a.getb()); tb.norm()
+            var tc=FP8(ta)
+            tc.mul(tb)
+            var t=FP8(w1)
+            t.add(w2)
+            t.neg()
+            tc.add(t)
+
+            ta.copy(a.geta()); ta.add(b.geta()); ta.norm()
+            tb.copy(y.a.geta()); tb.add(y.b.geta()); tb.norm()
+            var td=FP8(ta)
+            td.mul(tb)
+            t.copy(w1)
+            t.add(w3)
+            t.neg()
+            td.add(t)
+
+            ta.copy(a.getb()); ta.add(b.geta()); ta.norm()
+            tb.copy(y.a.getb()); tb.add(y.b.geta()); tb.norm()
+            var te=FP8(ta)
+            te.mul(tb)
+            t.copy(w2)
+            t.add(w3)
+            t.neg()
+            te.add(t)
+
+            w2.times_i()
+            w1.add(w2)
+
+            a.set_fp8s(w1,tc)
+            b.set_fp8s(td,te)
+            c.set_fp8(w3)
+
+            a.norm()
+            b.norm()
+    } else {
+            var w1=FP8(a.geta())
+            var w2=FP8(a.getb())
+            var w3=FP8(c.getb())
+
+            w1.mul(y.a.geta())
+            w2.mul(y.a.getb())
+            w3.mul(y.c.getb())
+
+            var ta=FP8(a.geta())
+            var tb=FP8(y.a.geta())
+            ta.add(a.getb()); ta.norm()
+            tb.add(y.a.getb()); tb.norm()
+            var tc=FP8(ta)
+            tc.mul(tb)
+            var t=FP8(w1)
+            t.add(w2)
+            t.neg()
+            tc.add(t)
+
+            ta.copy(a.geta()); ta.add(c.getb()); ta.norm()
+            tb.copy(y.a.geta()); tb.add(y.c.getb()); tb.norm()
+            var td=FP8(ta)
+            td.mul(tb)
+            t.copy(w1)
+            t.add(w3)
+            t.neg()
+            td.add(t)
+
+            ta.copy(a.getb()); ta.add(c.getb()); ta.norm()
+            tb.copy(y.a.getb()); tb.add(y.c.getb()); tb.norm()
+            var te=FP8(ta)
+            te.mul(tb)
+            t.copy(w2)
+            t.add(w3)
+            t.neg()
+            te.add(t)
+
+            w2.times_i()
+            w1.add(w2)
+            a.set_fp8s(w1,tc);
+
+            w3.times_i()
+            w3.norm()
+            b.set_fp8h(w3);
+
+            te.norm()
+            te.times_i()
+            c.set_fp8s(te,td)
+
+            a.norm()
+            c.norm()
+
+        }
+        stype=FP48.SPARSE
+    }
+
     /* self=1/self */
     mutating func inverse()
     {
@@ -430,6 +673,7 @@
         a.copy(f0); a.mul(f3)
         b.copy(f1); b.mul(f3)
         c.copy(f2); c.mul(f3)
+        stype=FP48.DENSE        
     }
 
     /* self=self^p using Frobenius */
@@ -453,7 +697,7 @@
             b.qmul(f); b.times_i4(); b.times_i2()
             c.qmul(f2); c.times_i4(); c.times_i4(); c.times_i4()
         }        
-
+        stype=FP48.DENSE
     }
 
     /* trace function */
diff --git a/version3/swift/fp8.swift b/version3/swift/fp8.swift
index a60dd41..7f64073 100644
--- a/version3/swift/fp8.swift
+++ b/version3/swift/fp8.swift
@@ -109,7 +109,21 @@
     {
     return b;
     }
-
+    mutating func set_fp4s(_ c: FP4,_ d: FP4)
+    {
+        a.copy(c)
+        b.copy(d)
+    }
+    mutating func set_fp4(_ c: FP4)
+    {
+        a.copy(c)
+        b.zero()
+    }    
+    mutating func set_fp4h(_ c: FP4)
+    {
+        b.copy(c)
+        a.zero()
+    }  
     /* test self=x? */
     func equals(_ x:FP8) -> Bool
     {
diff --git a/version3/swift/pair.swift b/version3/swift/pair.swift
index 94af78c..06044e7 100644
--- a/version3/swift/pair.swift
+++ b/version3/swift/pair.swift
@@ -78,7 +78,9 @@
         }        
         A.dbl()
 
-        return FP12(a,b,c)
+        var res=FP12(a,b,c)
+        res.settype(FP12.SPARSER)
+        return res
     }
 
 
@@ -122,7 +124,111 @@
             c=FP4(Y1); c.times_i()
          }  
         A.add(B)
-        return FP12(a,b,c)
+        var res=FP12(a,b,c)
+        res.settype(FP12.SPARSER)
+        return res
+    }
+
+    static private func lbits(_ n3:inout BIG,_ n:inout BIG) -> Int
+    {
+        n.copy(BIG(ROM.CURVE_Bnx))
+        if CONFIG_CURVE.CURVE_PAIRING_TYPE == CONFIG_CURVE.BN {
+            n.pmul(6)
+            if CONFIG_CURVE.SIGN_OF_X == CONFIG_CURVE.POSITIVEX {  
+                n.inc(2)
+            } else {
+                n.dec(2)
+            }
+        }
+        n.norm()
+        n3.copy(n)
+        n3.pmul(3)
+        n3.norm()
+        return n3.nbits()          
+    }
+
+    static public func initmp() -> [FP12]
+    {
+        var r=[FP12]();
+        for _ in (0...CONFIG_CURVE.ATE_BITS-1).reversed() {
+            r.append(FP12(1))
+        }
+        return r
+    }
+
+/* basic Miller loop */
+    static public func miller(_ r: [FP12]) -> FP12 {
+        var res=FP12(1)
+        for i in (1...CONFIG_CURVE.ATE_BITS-1).reversed() {
+            res.sqr()
+            res.ssmul(r[i])
+        }
+
+        if CONFIG_CURVE.SIGN_OF_X==CONFIG_CURVE.NEGATIVEX {
+            res.conj();
+        }
+        res.ssmul(r[0])
+        return res
+    }
+
+/* Accumulate another set of line functions for n-pairing */
+    static public func another(_ r: inout [FP12],_ P1: ECP2,_ Q1: ECP) {
+        var f=FP2(BIG(ROM.Fra),BIG(ROM.Frb))
+        var n = BIG();
+        var n3 = BIG();
+        var K = ECP2()
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+        var P=ECP2(); P.copy(P1); P.affine()
+        var Q=ECP(); Q.copy(Q1); Q.affine()
+
+        if CONFIG_CURVE.CURVE_PAIRING_TYPE == CONFIG_CURVE.BN {
+            if CONFIG_CURVE.SEXTIC_TWIST == CONFIG_CURVE.M_TYPE {  
+                f.inverse()
+                f.norm()
+            }
+        }
+
+        let Qx=FP(Q.getx())
+        let Qy=FP(Q.gety())
+    
+        var A=ECP2()
+        A.copy(P)
+        var NP=ECP2()
+        NP.copy(P)
+        NP.neg()
+
+        let nb=lbits(&n3,&n)
+
+        for i in (1...nb-2).reversed() {
+            var lv=linedbl(&A,Qx,Qy)
+
+            let bt=n3.bit(UInt(i))-n.bit(UInt(i))
+            if bt == 1 {
+                let lv2=lineadd(&A,P,Qx,Qy)
+                lv.smul(lv2)
+            }
+            if bt == -1 {
+                let lv2=lineadd(&A,NP,Qx,Qy)
+                lv.smul(lv2)
+            }
+            r[i].ssmul(lv)
+        }
+
+/* R-ate fixup required for BN curves */
+        if CONFIG_CURVE.CURVE_PAIRING_TYPE == CONFIG_CURVE.BN {
+            if CONFIG_CURVE.SIGN_OF_X==CONFIG_CURVE.NEGATIVEX {
+                A.neg()
+            }
+            K.copy(P)
+            K.frob(f)
+            var lv=lineadd(&A,K,Qx,Qy)
+            K.frob(f)
+            K.neg()
+            let lv2=lineadd(&A,K,Qx,Qy)
+            lv.smul(lv2)
+            r[0].ssmul(lv)
+        } 
     }
 
 
@@ -130,8 +236,8 @@
     static public func ate(_ P1:ECP2,_ Q1:ECP) -> FP12
     {
         var f=FP2(BIG(ROM.Fra),BIG(ROM.Frb))
-        let x=BIG(ROM.CURVE_Bnx)
-        var n=BIG(x)
+        var n = BIG();
+        var n3 = BIG();
         var K=ECP2()
         
         var lv:FP12
@@ -141,19 +247,7 @@
                 f.inverse()
                 f.norm()
             }
-            n.pmul(6);
-            if CONFIG_CURVE.SIGN_OF_X == CONFIG_CURVE.NEGATIVEX { 
-                n.dec(2)
-            } else {
-                n.inc(2)
-            }
-        } else {n.copy(x)}
-	
-        n.norm()
-
-        var n3=BIG(n)
-        n3.pmul(3)
-        n3.norm()
+        }
 
         var P=ECP2(); P.copy(P1); P.affine()
         var Q=ECP(); Q.copy(Q1); Q.affine()
@@ -170,23 +264,24 @@
         NP.neg()
 
         var r=FP12(1)
-        let nb=n3.nbits()
+        let nb=lbits(&n3,&n)
     
         for i in (1...nb-2).reversed()
         //for var i=nb-2;i>=1;i--
         {
             r.sqr()            
             lv=linedbl(&A,Qx,Qy)
-            r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
+
             let bt=n3.bit(UInt(i))-n.bit(UInt(i))
             if bt == 1 {
-		      lv=lineadd(&A,P,Qx,Qy)
-		      r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
+                let lv2=lineadd(&A,P,Qx,Qy)
+                lv.smul(lv2)
             }
             if bt == -1 {
-                lv=lineadd(&A,NP,Qx,Qy)
-                r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
+                let lv2=lineadd(&A,NP,Qx,Qy)
+                lv.smul(lv2)
             }
+            r.ssmul(lv)        
         }
     
         if CONFIG_CURVE.SIGN_OF_X == CONFIG_CURVE.NEGATIVEX {
@@ -203,11 +298,11 @@
             K.frob(f)
 
             lv=lineadd(&A,K,Qx,Qy)
-            r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
             K.frob(f)
             K.neg()
-            lv=lineadd(&A,K,Qx,Qy)
-            r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
+            let lv2=lineadd(&A,K,Qx,Qy)
+            lv.smul(lv2)
+            r.ssmul(lv)
         }
         return r
     }
@@ -215,8 +310,8 @@
     static public func ate2(_ P1:ECP2,_ Q1:ECP,_ R1:ECP2,_ S1:ECP) -> FP12
     {
         var f=FP2(BIG(ROM.Fra),BIG(ROM.Frb))
-        let x=BIG(ROM.CURVE_Bnx)
-        var n=BIG(x)
+        var n = BIG();
+        var n3 = BIG();
         var K=ECP2()
         var lv:FP12
 
@@ -225,24 +320,12 @@
                 f.inverse()
                 f.norm()
             }
-            n.pmul(6); 
-            if CONFIG_CURVE.SIGN_OF_X == CONFIG_CURVE.NEGATIVEX { 
-                n.dec(2)
-            } else {
-                n.inc(2)
-            }        
-        } else {n.copy(x)}
-	
-        n.norm()
-        var n3=BIG(n)
-        n3.pmul(3)
-        n3.norm()
+        }
     
-	   var P=ECP2(); P.copy(P1); P.affine()
-	   var Q=ECP(); Q.copy(Q1); Q.affine()
-	   var R=ECP2(); R.copy(R1); R.affine()
-	   var S=ECP(); S.copy(S1); S.affine()
-
+        var P=ECP2(); P.copy(P1); P.affine()
+        var Q=ECP(); Q.copy(Q1); Q.affine()
+        var R=ECP2(); R.copy(R1); R.affine()
+        var S=ECP(); S.copy(S1); S.affine()
 
         let Qx=FP(Q.getx())
         let Qy=FP(Q.gety())
@@ -262,30 +345,29 @@
         NR.copy(R)
         NR.neg()
 
-
-        let nb=n3.nbits()
+        let nb=lbits(&n3,&n)
     
         for i in (1...nb-2).reversed()
         {
             r.sqr()            
             lv=linedbl(&A,Qx,Qy)
-            r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
-            lv=linedbl(&B,Sx,Sy)
-            r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
+            var lv2=linedbl(&B,Sx,Sy)
+            lv.smul(lv2)
+            r.ssmul(lv)
             let bt=n3.bit(UInt(i))-n.bit(UInt(i))
 
             if bt == 1 {
                 lv=lineadd(&A,P,Qx,Qy)
-                r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
-                lv=lineadd(&B,R,Sx,Sy)
-                r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
+                lv2=lineadd(&B,R,Sx,Sy)
+                lv.smul(lv2)
+                r.ssmul(lv)
             }
 
             if bt == -1 {
                 lv=lineadd(&A,NP,Qx,Qy)
-                r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
-                lv=lineadd(&B,NR,Sx,Sy)
-                r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)              
+                lv2=lineadd(&B,NR,Sx,Sy)
+                lv.smul(lv2)
+                r.ssmul(lv)              
             }            
 
         }
@@ -305,21 +387,21 @@
             K.frob(f)
 
             lv=lineadd(&A,K,Qx,Qy)
-            r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
             K.frob(f)
             K.neg()
-            lv=lineadd(&A,K,Qx,Qy)
-            r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
+            var lv2=lineadd(&A,K,Qx,Qy)
+            lv.smul(lv2)
+            r.ssmul(lv)
     
             K.copy(R)
             K.frob(f)
 
             lv=lineadd(&B,K,Sx,Sy)
-            r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
             K.frob(f)
             K.neg()
-            lv=lineadd(&B,K,Sx,Sy)
-            r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
+            lv2=lineadd(&B,K,Sx,Sy)
+            lv.smul(lv2)
+            r.ssmul(lv)
         }
         return r
     }
diff --git a/version3/swift/pair192.swift b/version3/swift/pair192.swift
index 39488db..d066198 100644
--- a/version3/swift/pair192.swift
+++ b/version3/swift/pair192.swift
@@ -76,7 +76,9 @@
         }        
         A.dbl()
 
-        return FP24(a,b,c)
+        var res=FP24(a,b,c)
+        res.settype(FP24.SPARSER)
+        return res
     }
 
     static func lineadd(_ A: inout ECP4,_ B:ECP4,_ Qx:FP,_ Qy:FP) -> FP24
@@ -118,22 +120,89 @@
         }  
         A.add(B)
 
-        return FP24(a,b,c)
+        var res=FP24(a,b,c)
+        res.settype(FP24.SPARSER)
+        return res
+    }
+
+    static private func lbits(_ n3:inout BIG,_ n:inout BIG) -> Int
+    {
+        n.copy(BIG(ROM.CURVE_Bnx))
+        n3.copy(n)
+        n3.pmul(3)
+        n3.norm()
+        return n3.nbits()          
+    }
+
+    static public func initmp() -> [FP24]
+    {
+        var r=[FP24]();
+        for _ in (0...CONFIG_CURVE.ATE_BITS-1).reversed() {
+            r.append(FP24(1))
+        }
+        return r
+    }
+
+/* basic Miller loop */
+    static public func miller(_ r: [FP24]) -> FP24 {
+        var res=FP24(1)
+        for i in (1...CONFIG_CURVE.ATE_BITS-1).reversed() {
+            res.sqr()
+            res.ssmul(r[i])
+        }
+
+        if CONFIG_CURVE.SIGN_OF_X==CONFIG_CURVE.NEGATIVEX {
+            res.conj();
+        }
+        res.ssmul(r[0])
+        return res
+    }
+
+/* Accumulate another set of line functions for n-pairing */
+    static public func another(_ r: inout [FP24],_ P1: ECP4,_ Q1: ECP) {
+        var n = BIG();
+        var n3 = BIG();
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+        var P=ECP4(); P.copy(P1); P.affine()
+        var Q=ECP(); Q.copy(Q1); Q.affine()
+
+        let Qx=FP(Q.getx())
+        let Qy=FP(Q.gety())
+    
+        var A=ECP4()
+        A.copy(P)
+        var NP=ECP4()
+        NP.copy(P)
+        NP.neg()
+
+        let nb=lbits(&n3,&n)
+
+        for i in (1...nb-2).reversed() {
+            var lv=linedbl(&A,Qx,Qy)
+
+            let bt=n3.bit(UInt(i))-n.bit(UInt(i))
+            if bt == 1 {
+                let lv2=lineadd(&A,P,Qx,Qy)
+                lv.smul(lv2)
+            }
+            if bt == -1 {
+                let lv2=lineadd(&A,NP,Qx,Qy)
+                lv.smul(lv2)
+            }
+            r[i].ssmul(lv)
+        }
     }
 
 
     // Optimal R-ate pairing
     static public func ate(_ P1:ECP4,_ Q1:ECP) -> FP24
     {
-        let x=BIG(ROM.CURVE_Bnx)
-        let n=BIG(x)
+        var n = BIG();
+        var n3 = BIG();
         
         var lv:FP24
 
-        var n3=BIG(n)
-        n3.pmul(3)
-        n3.norm()
-
         var P=ECP4(); P.copy(P1); P.affine()
         var Q=ECP(); Q.copy(Q1); Q.affine()
 
@@ -149,22 +218,23 @@
         NP.copy(P)
         NP.neg()
 
-        let nb=n3.nbits()
+        let nb=lbits(&n3,&n)
     
         for i in (1...nb-2).reversed()
         {
             r.sqr()            
             lv=linedbl(&A,Qx,Qy)
-            r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
+
             let bt=n3.bit(UInt(i))-n.bit(UInt(i))
             if bt == 1 {
-              lv=lineadd(&A,P,Qx,Qy)
-              r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
+                let lv2=lineadd(&A,P,Qx,Qy)
+                lv.smul(lv2)
             }
             if bt == -1 {
-                lv=lineadd(&A,NP,Qx,Qy)
-                r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
+                let lv2=lineadd(&A,NP,Qx,Qy)
+                lv.smul(lv2)
             }
+            r.ssmul(lv)            
         }
     
         if CONFIG_CURVE.SIGN_OF_X == CONFIG_CURVE.NEGATIVEX {
@@ -177,13 +247,9 @@
     // Optimal R-ate double pairing e(P,Q).e(R,S)
     static public func ate2(_ P1:ECP4,_ Q1:ECP,_ R1:ECP4,_ S1:ECP) -> FP24
     {
-        let x=BIG(ROM.CURVE_Bnx)
-        let n=BIG(x)
+        var n = BIG();
+        var n3 = BIG();
         var lv:FP24
-
-        var n3=BIG(n)
-        n3.pmul(3)
-        n3.norm()
     
         var P=ECP4(); P.copy(P1); P.affine()
         var Q=ECP(); Q.copy(Q1); Q.affine()
@@ -209,31 +275,30 @@
         NR.copy(R)
         NR.neg()
 
-        let nb=n3.nbits()
+        let nb=lbits(&n3,&n)
     
         for i in (1...nb-2).reversed()
         {
             r.sqr()            
             lv=linedbl(&A,Qx,Qy)
-            r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
-            lv=linedbl(&B,Sx,Sy)
-            r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
+            var lv2=linedbl(&B,Sx,Sy)
+            lv.smul(lv2)
+            r.ssmul(lv)
             let bt=n3.bit(UInt(i))-n.bit(UInt(i))
 
             if bt == 1 {
                 lv=lineadd(&A,P,Qx,Qy)
-                r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
-                lv=lineadd(&B,R,Sx,Sy)
-                r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
+                lv2=lineadd(&B,R,Sx,Sy)
+                lv.smul(lv2)
+                r.ssmul(lv)
             }
 
             if bt == -1 {
                 lv=lineadd(&A,NP,Qx,Qy)
-                r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
-                lv=lineadd(&B,NR,Sx,Sy)
-                r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)              
+                lv2=lineadd(&B,NR,Sx,Sy)
+                lv.smul(lv2)
+                r.ssmul(lv)              
             }            
-
         }
     
         if CONFIG_CURVE.SIGN_OF_X == CONFIG_CURVE.NEGATIVEX {
diff --git a/version3/swift/pair256.swift b/version3/swift/pair256.swift
index e9f1517..1289e94 100644
--- a/version3/swift/pair256.swift
+++ b/version3/swift/pair256.swift
@@ -76,7 +76,9 @@
         }        
         A.dbl()
 
-        return FP48(a,b,c)
+        var res=FP48(a,b,c)
+        res.settype(FP48.SPARSER)
+        return res
     }
 
     static func lineadd(_ A: inout ECP8,_ B:ECP8,_ Qx:FP,_ Qy:FP) -> FP48
@@ -118,22 +120,88 @@
         }  
         A.add(B)
 
-        return FP48(a,b,c)
+        var res=FP48(a,b,c)
+        res.settype(FP48.SPARSER)
+        return res
     }
 
+    static private func lbits(_ n3:inout BIG,_ n:inout BIG) -> Int
+    {
+        n.copy(BIG(ROM.CURVE_Bnx))
+        n3.copy(n)
+        n3.pmul(3)
+        n3.norm()
+        return n3.nbits()          
+    }
+
+    static public func initmp() -> [FP48]
+    {
+        var r=[FP48]();
+        for _ in (0...CONFIG_CURVE.ATE_BITS-1).reversed() {
+            r.append(FP48(1))
+        }
+        return r
+    }
+
+/* basic Miller loop */
+    static public func miller(_ r: [FP48]) -> FP48 {
+        var res=FP48(1)
+        for i in (1...CONFIG_CURVE.ATE_BITS-1).reversed() {
+            res.sqr()
+            res.ssmul(r[i])
+        }
+
+        if CONFIG_CURVE.SIGN_OF_X==CONFIG_CURVE.NEGATIVEX {
+            res.conj();
+        }
+        res.ssmul(r[0])
+        return res
+    }
+
+/* Accumulate another set of line functions for n-pairing */
+    static public func another(_ r: inout [FP48],_ P1: ECP8,_ Q1: ECP) {
+        var n = BIG();
+        var n3 = BIG();
+
+// P is needed in affine form for line function, Q for (Qx,Qy) extraction
+        var P=ECP8(); P.copy(P1); P.affine()
+        var Q=ECP(); Q.copy(Q1); Q.affine()
+
+        let Qx=FP(Q.getx())
+        let Qy=FP(Q.gety())
+    
+        var A=ECP8()
+        A.copy(P)
+        var NP=ECP8()
+        NP.copy(P)
+        NP.neg()
+
+        let nb=lbits(&n3,&n)
+
+        for i in (1...nb-2).reversed() {
+            var lv=linedbl(&A,Qx,Qy)
+
+            let bt=n3.bit(UInt(i))-n.bit(UInt(i))
+            if bt == 1 {
+                let lv2=lineadd(&A,P,Qx,Qy)
+                lv.smul(lv2)
+            }
+            if bt == -1 {
+                let lv2=lineadd(&A,NP,Qx,Qy)
+                lv.smul(lv2)
+            }
+            r[i].ssmul(lv)
+        }
+    }
 
     // Optimal R-ate pairing
     static public func ate(_ P1:ECP8,_ Q1:ECP) -> FP48
     {
-        let x=BIG(ROM.CURVE_Bnx)
-        let n=BIG(x)
+        var n = BIG();
+        var n3 = BIG();
         
         var lv:FP48
 
-        var n3=BIG(n)
-        n3.pmul(3)
-        n3.norm()
-
         var P=ECP8(); P.copy(P1); P.affine()
         var Q=ECP(); Q.copy(Q1); Q.affine()
 
@@ -148,22 +216,23 @@
         NP.copy(P)
         NP.neg()
 
-        let nb=n3.nbits()
+        let nb=lbits(&n3,&n)
     
         for i in (1...nb-2).reversed()
         {
             r.sqr()            
             lv=linedbl(&A,Qx,Qy)
-            r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
+
             let bt=n3.bit(UInt(i))-n.bit(UInt(i))
             if bt == 1 {
-              lv=lineadd(&A,P,Qx,Qy)
-              r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
+                let lv2=lineadd(&A,P,Qx,Qy)
+                lv.smul(lv2)
             }
             if bt == -1 {
-                lv=lineadd(&A,NP,Qx,Qy)
-                r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
+                let lv2=lineadd(&A,NP,Qx,Qy)
+                lv.smul(lv2)
             }
+            r.ssmul(lv)        
         }
     
         if CONFIG_CURVE.SIGN_OF_X == CONFIG_CURVE.NEGATIVEX {
@@ -176,20 +245,15 @@
     // Optimal R-ate double pairing e(P,Q).e(R,S)
     static public func ate2(_ P1:ECP8,_ Q1:ECP,_ R1:ECP8,_ S1:ECP) -> FP48
     {
-        let x=BIG(ROM.CURVE_Bnx)
-        let n=BIG(x)
+        var n = BIG();
+        var n3 = BIG();
         var lv:FP48
-
-        var n3=BIG(n)
-        n3.pmul(3)
-        n3.norm()
     
         var P=ECP8(); P.copy(P1); P.affine()
         var Q=ECP(); Q.copy(Q1); Q.affine()
         var R=ECP8(); R.copy(R1); R.affine()
         var S=ECP(); S.copy(S1); S.affine()
 
-
         let Qx=FP(Q.getx())
         let Qy=FP(Q.gety())
         let Sx=FP(S.getx())
@@ -208,31 +272,30 @@
         NR.copy(R)
         NR.neg()
 
-        let nb=n3.nbits()
+        let nb=lbits(&n3,&n)
     
         for i in (1...nb-2).reversed()
         {
             r.sqr()            
             lv=linedbl(&A,Qx,Qy)
-            r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
-            lv=linedbl(&B,Sx,Sy)
-            r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
+            var lv2=linedbl(&B,Sx,Sy)
+            lv.smul(lv2)
+            r.ssmul(lv)
             let bt=n3.bit(UInt(i))-n.bit(UInt(i))
 
             if bt == 1 {
                 lv=lineadd(&A,P,Qx,Qy)
-                r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
-                lv=lineadd(&B,R,Sx,Sy)
-                r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
+                lv2=lineadd(&B,R,Sx,Sy)
+                lv.smul(lv2)
+                r.ssmul(lv)
             }
 
             if bt == -1 {
                 lv=lineadd(&A,NP,Qx,Qy)
-                r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)
-                lv=lineadd(&B,NR,Sx,Sy)
-                r.smul(lv,CONFIG_CURVE.SEXTIC_TWIST)           
+                lv2=lineadd(&B,NR,Sx,Sy)
+                lv.smul(lv2)
+                r.ssmul(lv)              
             }            
-
         }
     
         if CONFIG_CURVE.SIGN_OF_X == CONFIG_CURVE.NEGATIVEX {