/* | |
Licensed to the Apache Software Foundation (ASF) under one | |
or more contributor license agreements. See the NOTICE file | |
distributed with this work for additional information | |
regarding copyright ownership. The ASF licenses this file | |
to you under the Apache License, Version 2.0 (the | |
"License"); you may not use this file except in compliance | |
with the License. You may obtain a copy of the License at | |
http://www.apache.org/licenses/LICENSE-2.0 | |
Unless required by applicable law or agreed to in writing, | |
software distributed under the License is distributed on an | |
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
KIND, either express or implied. See the License for the | |
specific language governing permissions and limitations | |
under the License. | |
*/ | |
// | |
// ecp.swift | |
// | |
// | |
// Created by Michael Scott on 30/06/2015. | |
// Copyright (c) 2015 Michael Scott. All rights reserved. | |
// | |
final class ECP { | |
private var x:FP | |
private var y:FP | |
private var z:FP | |
private var INF:Bool | |
/* Constructor - set to O */ | |
init() | |
{ | |
x=FP(0) | |
y=FP(0) | |
z=FP(1) | |
INF=true | |
} | |
/* test for O point-at-infinity */ | |
func is_infinity() -> Bool | |
{ | |
if (ROM.CURVETYPE==ROM.EDWARDS) | |
{ | |
x.reduce(); y.reduce(); z.reduce() | |
return x.iszilch() && y.equals(z) | |
} | |
else {return INF} | |
} | |
/* Conditional swap of P and Q dependant on d */ | |
private func cswap(Q: ECP,_ d:Int32) | |
{ | |
x.cswap(Q.x,d); | |
if ROM.CURVETYPE != ROM.MONTGOMERY {y.cswap(Q.y,d)} | |
z.cswap(Q.z,d); | |
if (ROM.CURVETYPE != ROM.EDWARDS) | |
{ | |
var bd:Bool | |
if d==0 {bd=false} | |
else {bd=true} | |
bd=bd && (INF != Q.INF) | |
INF = (INF != bd) | |
Q.INF = (Q.INF != bd) | |
} | |
} | |
/* Conditional move of Q to P dependant on d */ | |
private func cmove(Q: ECP,_ d:Int32) | |
{ | |
x.cmove(Q.x,d); | |
if ROM.CURVETYPE != ROM.MONTGOMERY {y.cmove(Q.y,d)} | |
z.cmove(Q.z,d); | |
if (ROM.CURVETYPE != ROM.EDWARDS) | |
{ | |
var bd:Bool | |
if d==0 {bd=false} | |
else {bd=true} | |
INF != (INF != Q.INF) && bd; | |
} | |
} | |
/* return 1 if b==c, no branching */ | |
private static func teq(b: Int32,_ c:Int32) -> Int32 | |
{ | |
var x=b^c | |
x-=1 // if x=0, x now -1 | |
return ((x>>31)&1) | |
} | |
/* self=P */ | |
func copy(P: ECP) | |
{ | |
x.copy(P.x) | |
if ROM.CURVETYPE != ROM.MONTGOMERY {y.copy(P.y)} | |
z.copy(P.z) | |
INF=P.INF | |
} | |
/* self=-self */ | |
func neg() { | |
if is_infinity() {return} | |
if (ROM.CURVETYPE==ROM.WEIERSTRASS) | |
{ | |
y.neg(); y.norm(); | |
} | |
if (ROM.CURVETYPE==ROM.EDWARDS) | |
{ | |
x.neg(); x.norm(); | |
} | |
return; | |
} | |
/* Constant time select from pre-computed table */ | |
private func select(W:[ECP],_ b:Int32) | |
{ | |
let MP=ECP() | |
let m=b>>31 | |
var babs=(b^m)-m | |
babs=(babs-1)/2 | |
cmove(W[0],ECP.teq(babs,0)); // conditional move | |
cmove(W[1],ECP.teq(babs,1)) | |
cmove(W[2],ECP.teq(babs,2)) | |
cmove(W[3],ECP.teq(babs,3)) | |
cmove(W[4],ECP.teq(babs,4)) | |
cmove(W[5],ECP.teq(babs,5)) | |
cmove(W[6],ECP.teq(babs,6)) | |
cmove(W[7],ECP.teq(babs,7)) | |
MP.copy(self) | |
MP.neg() | |
cmove(MP,(m&1)) | |
} | |
/* Test P == Q */ | |
func equals(Q: ECP) -> Bool | |
{ | |
if (is_infinity() && Q.is_infinity()) {return true} | |
if (is_infinity() || Q.is_infinity()) {return false} | |
if (ROM.CURVETYPE==ROM.WEIERSTRASS) | |
{ | |
let zs2=FP(z); zs2.sqr() | |
let zo2=FP(Q.z); zo2.sqr() | |
let zs3=FP(zs2); zs3.mul(z) | |
let zo3=FP(zo2); zo3.mul(Q.z) | |
zs2.mul(Q.x) | |
zo2.mul(x) | |
if !zs2.equals(zo2) {return false} | |
zs3.mul(Q.y) | |
zo3.mul(y) | |
if !zs3.equals(zo3) {return false} | |
} | |
else | |
{ | |
let a=FP(0) | |
let b=FP(0) | |
a.copy(x); a.mul(Q.z); a.reduce() | |
b.copy(Q.x); b.mul(z); b.reduce() | |
if !a.equals(b) {return false} | |
if ROM.CURVETYPE==ROM.EDWARDS | |
{ | |
a.copy(y); a.mul(Q.z); a.reduce() | |
b.copy(Q.y); b.mul(z); b.reduce() | |
if !a.equals(b) {return false} | |
} | |
} | |
return true | |
} | |
/* set self=O */ | |
func inf() | |
{ | |
INF=true; | |
x.zero() | |
y.one() | |
z.one() | |
} | |
/* Calculate RHS of curve equation */ | |
static func RHS(x: FP) -> FP | |
{ | |
x.norm(); | |
let r=FP(x); | |
r.sqr(); | |
if ROM.CURVETYPE==ROM.WEIERSTRASS | |
{ // x^3+Ax+B | |
let b=FP(BIG(ROM.CURVE_B)) | |
r.mul(x) | |
if (ROM.CURVE_A == -3) | |
{ | |
let cx=FP(x) | |
cx.imul(3) | |
cx.neg(); cx.norm() | |
r.add(cx) | |
} | |
r.add(b); | |
} | |
if (ROM.CURVETYPE==ROM.EDWARDS) | |
{ // (Ax^2-1)/(Bx^2-1) | |
let b=FP(BIG(ROM.CURVE_B)) | |
let one=FP(1); | |
b.mul(r); | |
b.sub(one); | |
if ROM.CURVE_A == -1 {r.neg()} | |
r.sub(one) | |
b.inverse() | |
r.mul(b); | |
} | |
if ROM.CURVETYPE==ROM.MONTGOMERY | |
{ // x^3+Ax^2+x | |
let x3=FP(0) | |
x3.copy(r); | |
x3.mul(x); | |
r.imul(ROM.CURVE_A); | |
r.add(x3); | |
r.add(x); | |
} | |
r.reduce(); | |
return r; | |
} | |
/* set (x,y) from two BIGs */ | |
init(_ ix: BIG,_ iy: BIG) | |
{ | |
x=FP(ix) | |
y=FP(iy) | |
z=FP(1) | |
INF=true | |
let rhs=ECP.RHS(x); | |
if ROM.CURVETYPE==ROM.MONTGOMERY | |
{ | |
if rhs.jacobi()==1 {INF=false} | |
else {inf()} | |
} | |
else | |
{ | |
let y2=FP(y) | |
y2.sqr() | |
if y2.equals(rhs) {INF=false} | |
else {inf()} | |
} | |
} | |
/* set (x,y) from BIG and a bit */ | |
init(_ ix: BIG,_ s:Int32) | |
{ | |
x=FP(ix) | |
let rhs=ECP.RHS(x) | |
y=FP(0) | |
z=FP(1) | |
INF=true | |
if rhs.jacobi()==1 | |
{ | |
let ny=rhs.sqrt() | |
if (ny.redc().parity() != s) {ny.neg()} | |
y.copy(ny) | |
INF=false; | |
} | |
else {inf()} | |
} | |
/* set from x - calculate y from curve equation */ | |
init(_ ix:BIG) | |
{ | |
x=FP(ix) | |
let rhs=ECP.RHS(x) | |
y=FP(0) | |
z=FP(1) | |
if rhs.jacobi()==1 | |
{ | |
if ROM.CURVETYPE != ROM.MONTGOMERY {y.copy(rhs.sqrt())} | |
INF=false; | |
} | |
else {INF=true} | |
} | |
/* set to affine - from (x,y,z) to (x,y) */ | |
func affine() | |
{ | |
if is_infinity() {return} | |
let one=FP(1) | |
if (z.equals(one)) {return} | |
z.inverse() | |
if ROM.CURVETYPE==ROM.WEIERSTRASS | |
{ | |
let z2=FP(z) | |
z2.sqr() | |
x.mul(z2); x.reduce() | |
y.mul(z2) | |
y.mul(z); y.reduce() | |
} | |
if ROM.CURVETYPE==ROM.EDWARDS | |
{ | |
x.mul(z); x.reduce() | |
y.mul(z); y.reduce() | |
} | |
if ROM.CURVETYPE==ROM.MONTGOMERY | |
{ | |
x.mul(z); x.reduce() | |
} | |
z.copy(one) | |
} | |
/* extract x as a BIG */ | |
func getX() -> BIG | |
{ | |
affine() | |
return x.redc() | |
} | |
/* extract y as a BIG */ | |
func getY() -> BIG | |
{ | |
affine(); | |
return y.redc(); | |
} | |
/* get sign of Y */ | |
func getS() -> Int32 | |
{ | |
affine() | |
let y=getY() | |
return y.parity() | |
} | |
/* extract x as an FP */ | |
func getx() -> FP | |
{ | |
return x; | |
} | |
/* extract y as an FP */ | |
func gety() -> FP | |
{ | |
return y; | |
} | |
/* extract z as an FP */ | |
func getz() -> FP | |
{ | |
return z; | |
} | |
/* convert to byte array */ | |
func toBytes(inout b:[UInt8]) | |
{ | |
let RM=Int(ROM.MODBYTES) | |
var t=[UInt8](count:RM,repeatedValue:0) | |
if ROM.CURVETYPE != ROM.MONTGOMERY {b[0]=0x04} | |
else {b[0]=0x02} | |
affine() | |
x.redc().toBytes(&t) | |
for var i=0;i<RM;i++ {b[i+1]=t[i]} | |
if ROM.CURVETYPE != ROM.MONTGOMERY | |
{ | |
y.redc().toBytes(&t); | |
for var i=0;i<RM;i++ {b[i+RM+1]=t[i]} | |
} | |
} | |
/* convert from byte array to point */ | |
static func fromBytes(b: [UInt8]) -> ECP | |
{ | |
let RM=Int(ROM.MODBYTES) | |
var t=[UInt8](count:RM,repeatedValue:0) | |
let p=BIG(ROM.Modulus); | |
for var i=0;i<RM;i++ {t[i]=b[i+1]} | |
let px=BIG.fromBytes(t) | |
if BIG.comp(px,p)>=0 {return ECP()} | |
if (b[0]==0x04) | |
{ | |
for var i=0;i<RM;i++ {t[i]=b[i+RM+1]} | |
let py=BIG.fromBytes(t) | |
if BIG.comp(py,p)>=0 {return ECP()} | |
return ECP(px,py) | |
} | |
else {return ECP(px)} | |
} | |
/* convert to hex string */ | |
func toString() -> String | |
{ | |
if is_infinity() {return "infinity"} | |
affine(); | |
if ROM.CURVETYPE==ROM.MONTGOMERY {return "("+x.redc().toString()+")"} | |
else {return "("+x.redc().toString()+","+y.redc().toString()+")"} | |
} | |
/* self*=2 */ | |
func dbl() | |
{ | |
if (ROM.CURVETYPE==ROM.WEIERSTRASS) | |
{ | |
if INF {return} | |
if y.iszilch() | |
{ | |
inf() | |
return | |
} | |
let w1=FP(x) | |
let w6=FP(z) | |
let w2=FP(0) | |
let w3=FP(x) | |
let w8=FP(x) | |
if (ROM.CURVE_A == -3) | |
{ | |
w6.sqr() | |
w1.copy(w6) | |
w1.neg() | |
w3.add(w1) | |
w8.add(w6) | |
w3.mul(w8) | |
w8.copy(w3) | |
w8.imul(3) | |
} | |
else | |
{ | |
w1.sqr() | |
w8.copy(w1) | |
w8.imul(3) | |
} | |
w2.copy(y); w2.sqr() | |
w3.copy(x); w3.mul(w2) | |
w3.imul(4) | |
w1.copy(w3); w1.neg() | |
w1.norm() | |
x.copy(w8); x.sqr() | |
x.add(w1) | |
x.add(w1) | |
x.norm() | |
z.mul(y) | |
z.add(z) | |
w2.add(w2) | |
w2.sqr() | |
w2.add(w2) | |
w3.sub(x) | |
y.copy(w8); y.mul(w3) | |
//w2.norm(); | |
y.sub(w2) | |
y.norm() | |
z.norm() | |
} | |
if ROM.CURVETYPE==ROM.EDWARDS | |
{ | |
let C=FP(x) | |
let D=FP(y) | |
let H=FP(z) | |
let J=FP(0) | |
x.mul(y); x.add(x) | |
C.sqr() | |
D.sqr() | |
if ROM.CURVE_A == -1 {C.neg()} | |
y.copy(C); y.add(D) | |
y.norm() | |
H.sqr(); H.add(H) | |
z.copy(y) | |
J.copy(y); J.sub(H) | |
x.mul(J) | |
C.sub(D) | |
y.mul(C) | |
z.mul(J) | |
x.norm(); | |
y.norm(); | |
z.norm(); | |
} | |
if ROM.CURVETYPE==ROM.MONTGOMERY | |
{ | |
let A=FP(x) | |
let B=FP(x); | |
let AA=FP(0); | |
let BB=FP(0); | |
let C=FP(0); | |
if INF {return} | |
A.add(z) | |
AA.copy(A); AA.sqr() | |
B.sub(z) | |
BB.copy(B); BB.sqr() | |
C.copy(AA); C.sub(BB) | |
//C.norm(); | |
x.copy(AA); x.mul(BB) | |
A.copy(C); A.imul((ROM.CURVE_A+2)/4) | |
BB.add(A) | |
z.copy(BB); z.mul(C) | |
x.norm() | |
z.norm() | |
} | |
return | |
} | |
/* self+=Q */ | |
func add(Q:ECP) | |
{ | |
if ROM.CURVETYPE==ROM.WEIERSTRASS | |
{ | |
if (INF) | |
{ | |
copy(Q) | |
return | |
} | |
if Q.INF {return} | |
var aff=false; | |
let one=FP(1); | |
if Q.z.equals(one) {aff=true} | |
var A:FP | |
var C:FP | |
let B=FP(z) | |
let D=FP(z) | |
if (!aff) | |
{ | |
A=FP(Q.z) | |
C=FP(Q.z) | |
A.sqr(); B.sqr() | |
C.mul(A); D.mul(B) | |
A.mul(x) | |
C.mul(y) | |
} | |
else | |
{ | |
A=FP(x) | |
C=FP(y) | |
B.sqr() | |
D.mul(B) | |
} | |
B.mul(Q.x); B.sub(A) | |
D.mul(Q.y); D.sub(C) | |
if B.iszilch() | |
{ | |
if (D.iszilch()) | |
{ | |
dbl() | |
return | |
} | |
else | |
{ | |
INF=true | |
return | |
} | |
} | |
if !aff {z.mul(Q.z)} | |
z.mul(B); | |
let e=FP(B); e.sqr() | |
B.mul(e) | |
A.mul(e) | |
e.copy(A) | |
e.add(A); e.add(B) | |
x.copy(D); x.sqr(); x.sub(e) | |
A.sub(x) | |
y.copy(A); y.mul(D) | |
C.mul(B); y.sub(C) | |
x.norm() | |
y.norm() | |
z.norm() | |
} | |
if ROM.CURVETYPE==ROM.EDWARDS | |
{ | |
let b=FP(BIG(ROM.CURVE_B)) | |
let A=FP(z) | |
let B=FP(0) | |
let C=FP(x) | |
let D=FP(y) | |
let E=FP(0) | |
let F=FP(0) | |
let G=FP(0) | |
A.mul(Q.z) | |
B.copy(A); B.sqr() | |
C.mul(Q.x) | |
D.mul(Q.y) | |
E.copy(C); E.mul(D); E.mul(b) | |
F.copy(B); F.sub(E) | |
G.copy(B); G.add(E) | |
C.add(D) | |
if ROM.CURVE_A==1 | |
{ | |
E.copy(D); D.sub(C) | |
} | |
B.copy(x); B.add(y) | |
D.copy(Q.x); D.add(Q.y) | |
B.mul(D) | |
B.sub(C) | |
B.mul(F) | |
x.copy(A); x.mul(B) | |
if ROM.CURVE_A==1 | |
{ | |
C.copy(E); C.mul(G) | |
} | |
if ROM.CURVE_A == -1 | |
{ | |
C.mul(G) | |
} | |
y.copy(A); y.mul(C) | |
z.copy(F); z.mul(G) | |
x.norm(); y.norm(); z.norm() | |
} | |
return; | |
} | |
/* Differential Add for Montgomery curves. self+=Q where W is self-Q and is affine. */ | |
func dadd(Q:ECP,_ W:ECP) | |
{ | |
let A=FP(x) | |
let B=FP(x) | |
let C=FP(Q.x) | |
let D=FP(Q.x) | |
let DA=FP(0) | |
let CB=FP(0) | |
A.add(z) | |
B.sub(z) | |
C.add(Q.z) | |
D.sub(Q.z) | |
DA.copy(D); DA.mul(A) | |
CB.copy(C); CB.mul(B) | |
A.copy(DA); A.add(CB); A.sqr() | |
B.copy(DA); B.sub(CB); B.sqr() | |
x.copy(A) | |
z.copy(W.x); z.mul(B) | |
if z.iszilch() {inf()} | |
else {INF=false} | |
x.norm() | |
} | |
/* this-=Q */ | |
func sub(Q:ECP) | |
{ | |
Q.neg() | |
add(Q) | |
Q.neg() | |
} | |
static func multiaffine(m: Int,_ P:[ECP]) | |
{ | |
let t1=FP(0) | |
let t2=FP(0) | |
var work=[FP]() | |
for var i=0;i<m;i++ | |
{work.append(FP(0))} | |
work[0].one() | |
work[1].copy(P[0].z) | |
for var i=2;i<m;i++ | |
{ | |
work[i].copy(work[i-1]) | |
work[i].mul(P[i-1].z) | |
} | |
t1.copy(work[m-1]); | |
t1.mul(P[m-1].z); | |
t1.inverse(); | |
t2.copy(P[m-1].z); | |
work[m-1].mul(t1); | |
for var i=m-2;;i-- | |
{ | |
if i==0 | |
{ | |
work[0].copy(t1) | |
work[0].mul(t2) | |
break | |
} | |
work[i].mul(t2); | |
work[i].mul(t1); | |
t2.mul(P[i].z); | |
} | |
/* now work[] contains inverses of all Z coordinates */ | |
for var i=0;i<m;i++ | |
{ | |
P[i].z.one(); | |
t1.copy(work[i]); | |
t1.sqr(); | |
P[i].x.mul(t1); | |
t1.mul(work[i]); | |
P[i].y.mul(t1); | |
} | |
} | |
/* constant time multiply by small integer of length bts - use ladder */ | |
func pinmul(e:Int32,_ bts:Int32) -> ECP | |
{ | |
if ROM.CURVETYPE==ROM.MONTGOMERY | |
{return self.mul(BIG(e))} | |
else | |
{ | |
let P=ECP() | |
let R0=ECP() | |
let R1=ECP(); R1.copy(self) | |
for var i=bts-1;i>=0;i-- | |
{ | |
let b=(e>>i)&1; | |
P.copy(R1); | |
P.add(R0); | |
R0.cswap(R1,b); | |
R1.copy(P); | |
R0.dbl(); | |
R0.cswap(R1,b); | |
} | |
P.copy(R0); | |
P.affine(); | |
return P; | |
} | |
} | |
/* return e.self */ | |
func mul(e:BIG) -> ECP | |
{ | |
if (e.iszilch() || is_infinity()) {return ECP()} | |
let P=ECP() | |
if ROM.CURVETYPE==ROM.MONTGOMERY | |
{ | |
/* use Ladder */ | |
let D=ECP() | |
let R0=ECP(); R0.copy(self) | |
let R1=ECP(); R1.copy(self) | |
R1.dbl(); | |
D.copy(self); D.affine(); | |
let nb=e.nbits(); | |
for var i=nb-2;i>=0;i-- | |
{ | |
let b=e.bit(i) | |
//print("\(b)") | |
P.copy(R1) | |
P.dadd(R0,D) | |
R0.cswap(R1,b) | |
R1.copy(P) | |
R0.dbl() | |
R0.cswap(R1,b) | |
} | |
P.copy(R0) | |
} | |
else | |
{ | |
// fixed size windows | |
let mt=BIG() | |
let t=BIG() | |
let Q=ECP() | |
let C=ECP() | |
var W=[ECP]() | |
let n=1+(ROM.NLEN*Int(ROM.BASEBITS)+3)/4 | |
var w=[Int8](count:n,repeatedValue:0) | |
affine(); | |
// precompute table | |
Q.copy(self) | |
Q.dbl() | |
W.append(ECP()) | |
W[0].copy(self) | |
for var i=1;i<8;i++ | |
{ | |
W.append(ECP()) | |
W[i].copy(W[i-1]) | |
W[i].add(Q) | |
} | |
// convert the table to affine | |
if ROM.CURVETYPE==ROM.WEIERSTRASS | |
{ECP.multiaffine(8,W)} | |
// make exponent odd - add 2P if even, P if odd | |
t.copy(e); | |
let s=t.parity(); | |
t.inc(1); t.norm(); let ns=t.parity(); | |
mt.copy(t); mt.inc(1); mt.norm(); | |
t.cmove(mt,s); | |
Q.cmove(self,ns); | |
C.copy(Q); | |
let nb=1+(t.nbits()+3)/4; | |
// convert exponent to signed 4-bit window | |
for var i=0;i<nb;i++ | |
{ | |
w[i]=Int8(t.lastbits(5)-16); | |
t.dec(Int32(w[i])); t.norm(); | |
t.fshr(4); | |
} | |
w[nb]=Int8(t.lastbits(5)) | |
P.copy(W[Int((w[nb])-1)/2]); | |
for var i=nb-1;i>=0;i-- | |
{ | |
Q.select(W,Int32(w[i])); | |
P.dbl(); | |
P.dbl(); | |
P.dbl(); | |
P.dbl(); | |
P.add(Q); | |
} | |
P.sub(C); /* apply correction */ | |
} | |
P.affine(); | |
return P; | |
} | |
/* Return e.this+f.Q */ | |
func mul2(e:BIG,_ Q:ECP,_ f:BIG) -> ECP | |
{ | |
let te=BIG() | |
let tf=BIG() | |
let mt=BIG() | |
let S=ECP() | |
let T=ECP() | |
let C=ECP() | |
var W=[ECP]() | |
let n=1+(ROM.NLEN*Int(ROM.BASEBITS)+1)/2 | |
var w=[Int8](count:n,repeatedValue:0); | |
affine(); | |
Q.affine(); | |
te.copy(e); | |
tf.copy(f); | |
// precompute table | |
for var i=0;i<8;i++ {W.append(ECP())} | |
W[1].copy(self); W[1].sub(Q) | |
W[2].copy(self); W[2].add(Q) | |
S.copy(Q); S.dbl(); | |
W[0].copy(W[1]); W[0].sub(S) | |
W[3].copy(W[2]); W[3].add(S) | |
T.copy(self); T.dbl() | |
W[5].copy(W[1]); W[5].add(T) | |
W[6].copy(W[2]); W[6].add(T) | |
W[4].copy(W[5]); W[4].sub(S) | |
W[7].copy(W[6]); W[7].add(S) | |
// convert the table to affine | |
if ROM.CURVETYPE==ROM.WEIERSTRASS | |
{ECP.multiaffine(8,W)} | |
// if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction | |
var s=te.parity() | |
te.inc(1); te.norm(); var ns=te.parity(); mt.copy(te); mt.inc(1); mt.norm() | |
te.cmove(mt,s) | |
T.cmove(self,ns) | |
C.copy(T) | |
s=tf.parity() | |
tf.inc(1); tf.norm(); ns=tf.parity(); mt.copy(tf); mt.inc(1); mt.norm() | |
tf.cmove(mt,s) | |
S.cmove(Q,ns) | |
C.add(S) | |
mt.copy(te); mt.add(tf); mt.norm() | |
let nb=1+(mt.nbits()+1)/2 | |
// convert exponent to signed 2-bit window | |
for var i=0;i<nb;i++ | |
{ | |
let a=(te.lastbits(3)-4); | |
te.dec(a); te.norm(); | |
te.fshr(2); | |
let b=(tf.lastbits(3)-4); | |
tf.dec(b); tf.norm(); | |
tf.fshr(2); | |
w[i]=Int8(4*a+b); | |
} | |
w[nb]=Int8(4*te.lastbits(3)+tf.lastbits(3)); | |
S.copy(W[(w[nb]-1)/2]); | |
for var i=nb-1;i>=0;i-- | |
{ | |
T.select(W,Int32(w[i])); | |
S.dbl(); | |
S.dbl(); | |
S.add(T); | |
} | |
S.sub(C); /* apply correction */ | |
S.affine(); | |
return S; | |
} | |
} |