| /* |
| 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. |
| */ |
| |
| /* Large Finite Field arithmetic */ |
| /* AMCL mod p functions */ |
| |
| public sealed class FF |
| { |
| private readonly BIG[] v; |
| private readonly int length; |
| |
| private static readonly int P_MBITS = ROM.MODBYTES * 8; |
| private static readonly int P_MB = (P_MBITS % ROM.BASEBITS); |
| private static readonly long P_OMASK = ((long)(-1) << (P_MBITS % ROM.BASEBITS)); |
| private static readonly long P_FEXCESS = ((long)1 << (ROM.BASEBITS * ROM.NLEN - P_MBITS)); |
| private static readonly int P_TBITS = (P_MBITS % ROM.BASEBITS); |
| |
| public long P_EXCESS() |
| { |
| return ((v[length - 1].get(ROM.NLEN - 1) & P_OMASK) >> (P_MB)); |
| } |
| |
| /* Constructors */ |
| public FF(int n) |
| { |
| v = new BIG[n]; |
| for (int i = 0;i < n;i++) |
| { |
| v[i] = new BIG(0); |
| } |
| length = n; |
| } |
| |
| public FF(long[][] x, int n) |
| { |
| v = new BIG[n]; |
| for (int i = 0;i < n;i++) |
| { |
| v[i] = new BIG(x[i]); |
| } |
| length = n; |
| } |
| |
| public int getlen() |
| { |
| return length; |
| } |
| |
| /* set to integer */ |
| public void set(int m) |
| { |
| zero(); |
| v[0].set(0,(long)m); |
| } |
| |
| /* copy from FF b */ |
| public void copy(FF b) |
| { |
| for (int i = 0;i < length;i++) |
| { |
| v[i].copy(b.v[i]); |
| } |
| } |
| |
| /* x=y<<n */ |
| public void dsucopy(FF b) |
| { |
| for (int i = 0;i < b.length;i++) |
| { |
| v[b.length + i].copy(b.v[i]); |
| v[i].zero(); |
| } |
| } |
| |
| /* x=y */ |
| public void dscopy(FF b) |
| { |
| for (int i = 0;i < b.length;i++) |
| { |
| v[i].copy(b.v[i]); |
| v[b.length + i].zero(); |
| } |
| } |
| |
| /* x=y>>n */ |
| public void sducopy(FF b) |
| { |
| for (int i = 0;i < length;i++) |
| { |
| v[i].copy(b.v[length + i]); |
| } |
| } |
| |
| /* set to zero */ |
| public void zero() |
| { |
| for (int i = 0;i < length;i++) |
| { |
| v[i].zero(); |
| } |
| } |
| |
| public void one() |
| { |
| v[0].one(); |
| for (int i = 1;i < length;i++) |
| { |
| v[i].zero(); |
| } |
| } |
| |
| /* test equals 0 */ |
| public bool iszilch() |
| { |
| for (int i = 0;i < length;i++) |
| { |
| if (!v[i].iszilch()) |
| { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /* shift right by 256-bit words */ |
| public void shrw(int n) |
| { |
| for (int i = 0;i < n;i++) |
| { |
| v[i].copy(v[i + n]); |
| v[i + n].zero(); |
| } |
| } |
| |
| /* shift left by 256-bit words */ |
| public void shlw(int n) |
| { |
| for (int i = 0;i < n;i++) |
| { |
| v[n + i].copy(v[i]); |
| v[i].zero(); |
| } |
| } |
| |
| /* extract last bit */ |
| public int parity() |
| { |
| return v[0].parity(); |
| } |
| |
| public int lastbits(int m) |
| { |
| return v[0].lastbits(m); |
| } |
| |
| /* compare x and y - must be normalised, and of same length */ |
| public static int comp(FF a, FF b) |
| { |
| int i, j; |
| for (i = a.length - 1;i >= 0;i--) |
| { |
| j = BIG.comp(a.v[i],b.v[i]); |
| if (j != 0) |
| { |
| return j; |
| } |
| } |
| return 0; |
| } |
| |
| /* recursive add */ |
| public void radd(int vp, FF x, int xp, FF y, int yp, int n) |
| { |
| for (int i = 0;i < n;i++) |
| { |
| v[vp + i].copy(x.v[xp + i]); |
| v[vp + i].add(y.v[yp + i]); |
| } |
| } |
| |
| /* recursive inc */ |
| public void rinc(int vp, FF y, int yp, int n) |
| { |
| for (int i = 0;i < n;i++) |
| { |
| v[vp + i].add(y.v[yp + i]); |
| } |
| } |
| |
| /* recursive sub */ |
| public void rsub(int vp, FF x, int xp, FF y, int yp, int n) |
| { |
| for (int i = 0;i < n;i++) |
| { |
| v[vp + i].copy(x.v[xp + i]); |
| v[vp + i].sub(y.v[yp + i]); |
| } |
| } |
| |
| /* recursive dec */ |
| public void rdec(int vp, FF y, int yp, int n) |
| { |
| for (int i = 0;i < n;i++) |
| { |
| v[vp + i].sub(y.v[yp + i]); |
| } |
| } |
| |
| /* simple add */ |
| public void add(FF b) |
| { |
| for (int i = 0;i < length;i++) |
| { |
| v[i].add(b.v[i]); |
| } |
| } |
| |
| /* simple sub */ |
| public void sub(FF b) |
| { |
| for (int i = 0;i < length;i++) |
| { |
| v[i].sub(b.v[i]); |
| } |
| } |
| |
| /* reverse sub */ |
| public void revsub(FF b) |
| { |
| for (int i = 0;i < length;i++) |
| { |
| v[i].rsub(b.v[i]); |
| } |
| } |
| |
| /* increment/decrement by a small integer */ |
| public void inc(int m) |
| { |
| v[0].inc(m); |
| norm(); |
| } |
| |
| public void dec(int m) |
| { |
| v[0].dec(m); |
| norm(); |
| } |
| |
| /* normalise - but hold any overflow in top part unless n<0 */ |
| private void rnorm(int vp, int n) |
| { |
| bool trunc = false; |
| int i; |
| long carry; |
| if (n < 0) |
| { // -v n signals to do truncation |
| n = -n; |
| trunc = true; |
| } |
| for (i = 0;i < n - 1;i++) |
| { |
| carry = v[vp + i].norm(); |
| v[vp + i].xortop(carry << P_TBITS); |
| v[vp + i + 1].inc((int)carry); |
| } |
| carry = v[vp + n - 1].norm(); |
| if (trunc) |
| { |
| v[vp + n - 1].xortop(carry << P_TBITS); |
| } |
| |
| } |
| |
| public void norm() |
| { |
| rnorm(0,length); |
| } |
| |
| /* shift left by one bit */ |
| public void shl() |
| { |
| int i , delay_carry = 0; |
| long carry; |
| for (i = 0;i < length - 1;i++) |
| { |
| carry = v[i].fshl(1); |
| v[i].inc(delay_carry); |
| v[i].xortop(carry << P_TBITS); |
| delay_carry = (int)carry; |
| } |
| v[length - 1].fshl(1); |
| v[length - 1].inc(delay_carry); |
| } |
| |
| /* shift right by one bit */ |
| |
| public void shr() |
| { |
| int i; |
| long carry; |
| for (i = length - 1;i > 0;i--) |
| { |
| carry = v[i].fshr(1); |
| v[i - 1].ortop(carry << P_TBITS); |
| } |
| v[0].fshr(1); |
| } |
| |
| /* Convert to Hex String */ |
| public override string ToString() |
| { |
| norm(); |
| string s = ""; |
| for (int i = length - 1;i >= 0;i--) |
| { |
| s += v[i].ToString(); |
| } |
| return s; |
| } |
| |
| /* Convert FFs to/from byte arrays */ |
| public void toBytes(sbyte[] b) |
| { |
| for (int i = 0;i < length;i++) |
| { |
| v[i].tobytearray(b,(length - i - 1) * ROM.MODBYTES); |
| } |
| } |
| |
| public static void fromBytes(FF x, sbyte[] b) |
| { |
| for (int i = 0;i < x.length;i++) |
| { |
| x.v[i] = BIG.frombytearray(b,(x.length - i - 1) * ROM.MODBYTES); |
| } |
| } |
| |
| /* in-place swapping using xor - side channel resistant - lengths must be the same */ |
| private static void cswap(FF a, FF b, int d) |
| { |
| for (int i = 0;i < a.length;i++) |
| { |
| // BIG.cswap(a.v[i],b.v[i],d); |
| a.v[i].cswap(b.v[i],d); |
| } |
| } |
| |
| /* z=x*y, t is workspace */ |
| private void karmul(int vp, FF x, int xp, FF y, int yp, FF t, int tp, int n) |
| { |
| int nd2; |
| if (n == 1) |
| { |
| DBIG d = BIG.mul(x.v[xp],y.v[yp]); |
| v[vp + 1] = d.Split(8 * ROM.MODBYTES); |
| v[vp].copy(d); |
| return; |
| } |
| nd2 = n / 2; |
| radd(vp,x,xp,x,xp + nd2,nd2); |
| //rnorm(vp,nd2); |
| radd(vp + nd2,y,yp,y,yp + nd2,nd2); |
| //rnorm(vp+nd2,nd2); |
| t.karmul(tp,this,vp,this,vp + nd2,t,tp + n,nd2); |
| karmul(vp,x,xp,y,yp,t,tp + n,nd2); |
| karmul(vp + n,x,xp + nd2,y,yp + nd2,t,tp + n,nd2); |
| t.rdec(tp,this,vp,n); |
| t.rdec(tp,this,vp + n,n); |
| rinc(vp + nd2,t,tp,n); |
| rnorm(vp,2 * n); |
| } |
| |
| private void karsqr(int vp, FF x, int xp, FF t, int tp, int n) |
| { |
| int nd2; |
| if (n == 1) |
| { |
| DBIG d = BIG.sqr(x.v[xp]); |
| v[vp + 1].copy(d.Split(8 * ROM.MODBYTES)); |
| v[vp].copy(d); |
| return; |
| } |
| |
| nd2 = n / 2; |
| karsqr(vp,x,xp,t,tp + n,nd2); |
| karsqr(vp + n,x,xp + nd2,t,tp + n,nd2); |
| t.karmul(tp,x,xp,x,xp + nd2,t,tp + n,nd2); |
| rinc(vp + nd2,t,tp,n); |
| rinc(vp + nd2,t,tp,n); |
| rnorm(vp + nd2,n); |
| } |
| |
| |
| private void karmul_lower(int vp, FF x, int xp, FF y, int yp, FF t, int tp, int n) |
| { // Calculates Least Significant bottom half of x*y |
| int nd2; |
| if (n == 1) |
| { // only calculate bottom half of product |
| v[vp].copy(BIG.smul(x.v[xp],y.v[yp])); |
| return; |
| } |
| nd2 = n / 2; |
| |
| karmul(vp,x,xp,y,yp,t,tp + n,nd2); |
| t.karmul_lower(tp,x,xp + nd2,y,yp,t,tp + n,nd2); |
| rinc(vp + nd2,t,tp,nd2); |
| t.karmul_lower(tp,x,xp,y,yp + nd2,t,tp + n,nd2); |
| rinc(vp + nd2,t,tp,nd2); |
| rnorm(vp + nd2,-nd2); // truncate it |
| } |
| |
| private void karmul_upper(FF x, FF y, FF t, int n) |
| { // Calculates Most Significant upper half of x*y, given lower part |
| int nd2; |
| |
| nd2 = n / 2; |
| radd(n,x,0,x,nd2,nd2); |
| radd(n + nd2,y,0,y,nd2,nd2); |
| |
| t.karmul(0,this,n + nd2,this,n,t,n,nd2); // t = (a0+a1)(b0+b1) |
| karmul(n,x,nd2,y,nd2,t,n,nd2); // z[n]= a1*b1 |
| /* z[0-nd2]=l(a0b0) z[nd2-n]= h(a0b0)+l(t)-l(a0b0)-l(a1b1) */ |
| t.rdec(0,this,n,n); // t=t-a1b1 |
| rinc(nd2,this,0,nd2); // z[nd2-n]+=l(a0b0) = h(a0b0)+l(t)-l(a1b1) |
| rdec(nd2,t,0,nd2); // z[nd2-n]=h(a0b0)+l(t)-l(a1b1)-l(t-a1b1)=h(a0b0) |
| rnorm(0,-n); // a0b0 now in z - truncate it |
| t.rdec(0,this,0,n); // (a0+a1)(b0+b1) - a0b0 |
| rinc(nd2,t,0,n); |
| |
| rnorm(nd2,n); |
| } |
| |
| /* z=x*y. Assumes x and y are of same length. */ |
| public static FF mul(FF x, FF y) |
| { |
| int n = x.length; |
| FF z = new FF(2 * n); |
| FF t = new FF(2 * n); |
| z.karmul(0,x,0,y,0,t,0,n); |
| return z; |
| } |
| |
| /* return low part of product this*y */ |
| public void lmul(FF y) |
| { |
| int n = length; |
| FF t = new FF(2 * n); |
| FF x = new FF(n); |
| x.copy(this); |
| karmul_lower(0,x,0,y,0,t,0,n); |
| } |
| |
| /* Set b=b mod c */ |
| public void mod(FF c) |
| { |
| int k = 0; |
| |
| norm(); |
| if (comp(this,c) < 0) |
| { |
| return; |
| } |
| do |
| { |
| c.shl(); |
| k++; |
| } while (comp(this,c) >= 0); |
| |
| while (k > 0) |
| { |
| c.shr(); |
| if (comp(this,c) >= 0) |
| { |
| sub(c); |
| norm(); |
| } |
| k--; |
| } |
| } |
| |
| /* z=x^2 */ |
| public static FF sqr(FF x) |
| { |
| int n = x.length; |
| FF z = new FF(2 * n); |
| FF t = new FF(2 * n); |
| z.karsqr(0,x,0,t,0,n); |
| return z; |
| } |
| |
| /* return This mod modulus, N is modulus, ND is Montgomery Constant */ |
| public FF reduce(FF N, FF ND) |
| { // fast karatsuba Montgomery reduction |
| int n = N.length; |
| FF t = new FF(2 * n); |
| FF r = new FF(n); |
| FF m = new FF(n); |
| |
| r.sducopy(this); |
| m.karmul_lower(0,this,0,ND,0,t,0,n); |
| karmul_upper(N,m,t,n); |
| m.sducopy(this); |
| |
| r.add(N); |
| r.sub(m); |
| r.norm(); |
| |
| return r; |
| |
| } |
| |
| /* Set r=this mod b */ |
| /* this is of length - 2*n */ |
| /* r,b is of length - n */ |
| public FF dmod(FF b) |
| { |
| int k , n = b.length; |
| FF m = new FF(2 * n); |
| FF x = new FF(2 * n); |
| FF r = new FF(n); |
| |
| x.copy(this); |
| x.norm(); |
| m.dsucopy(b); |
| k = 256 * n; |
| |
| while (k > 0) |
| { |
| m.shr(); |
| |
| if (comp(x,m) >= 0) |
| { |
| x.sub(m); |
| x.norm(); |
| } |
| k--; |
| } |
| |
| r.copy(x); |
| r.mod(b); |
| return r; |
| } |
| |
| /* Set return=1/this mod p. Binary method - a<p on entry */ |
| |
| public void invmodp(FF p) |
| { |
| int n = p.length; |
| |
| FF u = new FF(n); |
| FF v = new FF(n); |
| FF x1 = new FF(n); |
| FF x2 = new FF(n); |
| FF t = new FF(n); |
| FF one = new FF(n); |
| |
| one.one(); |
| u.copy(this); |
| v.copy(p); |
| x1.copy(one); |
| x2.zero(); |
| |
| // reduce n in here as well! |
| while (comp(u,one) != 0 && comp(v,one) != 0) |
| { |
| while (u.parity() == 0) |
| { |
| u.shr(); |
| if (x1.parity() != 0) |
| { |
| x1.add(p); |
| x1.norm(); |
| } |
| x1.shr(); |
| } |
| while (v.parity() == 0) |
| { |
| v.shr(); |
| if (x2.parity() != 0) |
| { |
| x2.add(p); |
| x2.norm(); |
| } |
| x2.shr(); |
| } |
| if (comp(u,v) >= 0) |
| { |
| |
| u.sub(v); |
| u.norm(); |
| if (comp(x1,x2) >= 0) |
| { |
| x1.sub(x2); |
| } |
| else |
| { |
| t.copy(p); |
| t.sub(x2); |
| x1.add(t); |
| } |
| x1.norm(); |
| } |
| else |
| { |
| v.sub(u); |
| v.norm(); |
| if (comp(x2,x1) >= 0) |
| { |
| x2.sub(x1); |
| } |
| else |
| { |
| t.copy(p); |
| t.sub(x1); |
| x2.add(t); |
| } |
| x2.norm(); |
| } |
| } |
| if (comp(u,one) == 0) |
| { |
| copy(x1); |
| } |
| else |
| { |
| copy(x2); |
| } |
| } |
| |
| /* nresidue mod m */ |
| public void nres(FF m) |
| { |
| int n = m.length; |
| FF d = new FF(2 * n); |
| d.dsucopy(this); |
| copy(d.dmod(m)); |
| } |
| |
| public void redc(FF m, FF ND) |
| { |
| int n = m.length; |
| FF d = new FF(2 * n); |
| mod(m); |
| d.dscopy(this); |
| copy(d.reduce(m,ND)); |
| mod(m); |
| } |
| |
| private void mod2m(int m) |
| { |
| for (int i = m;i < length;i++) |
| { |
| v[i].zero(); |
| } |
| } |
| |
| /* U=1/a mod 2^m - Arazi & Qi */ |
| private FF invmod2m() |
| { |
| int i , n = length; |
| |
| FF b = new FF(n); |
| FF c = new FF(n); |
| FF U = new FF(n); |
| |
| FF t; |
| |
| U.zero(); |
| U.v[0].copy(v[0]); |
| U.v[0].invmod2m(); |
| |
| for (i = 1;i < n;i <<= 1) |
| { |
| b.copy(this); |
| b.mod2m(i); |
| t = mul(U,b); |
| t.shrw(i); |
| b.copy(t); |
| c.copy(this); |
| c.shrw(i); |
| c.mod2m(i); |
| c.lmul(U); |
| c.mod2m(i); |
| |
| b.add(c); |
| b.norm(); |
| b.lmul(U); |
| b.mod2m(i); |
| |
| c.one(); |
| c.shlw(i); |
| b.revsub(c); |
| b.norm(); |
| b.shlw(i); |
| U.add(b); |
| } |
| U.norm(); |
| return U; |
| } |
| |
| public void random(RAND rng) |
| { |
| int n = length; |
| for (int i = 0;i < n;i++) |
| { |
| v[i].copy(BIG.random(rng)); |
| } |
| /* make sure top bit is 1 */ |
| while (v[n - 1].nbits() < ROM.MODBYTES * 8) |
| { |
| v[n - 1].copy(BIG.random(rng)); |
| } |
| } |
| |
| /* generate random x */ |
| public void randomnum(FF p, RAND rng) |
| { |
| int n = length; |
| FF d = new FF(2 * n); |
| |
| for (int i = 0;i < 2 * n;i++) |
| { |
| d.v[i].copy(BIG.random(rng)); |
| } |
| copy(d.dmod(p)); |
| } |
| |
| /* this*=y mod p */ |
| public void modmul(FF y, FF p, FF nd) |
| { |
| //FF d=new FF(2*p.length); |
| long ex = P_EXCESS(); |
| long ey = y.P_EXCESS(); |
| if ((ex + 1) * (ey + 1) + 1 >= P_FEXCESS) |
| { |
| mod(p); |
| } |
| FF d = mul(this,y); |
| copy(d.reduce(p,nd)); |
| } |
| |
| /* this*=y mod p */ |
| public void modsqr(FF p, FF nd) |
| { |
| //FF d=new FF(2*p.length); |
| long ex = P_EXCESS(); |
| if ((ex + 1) * (ex + 1) + 1 >= P_FEXCESS) |
| { |
| mod(p); |
| } |
| FF d = sqr(this); |
| copy(d.reduce(p,nd)); |
| } |
| |
| /* this=this^e mod p using side-channel resistant Montgomery Ladder, for large e */ |
| public void skpow(FF e, FF p) |
| { |
| int i , b , n = p.length; |
| FF R0 = new FF(n); |
| FF R1 = new FF(n); |
| FF ND = p.invmod2m(); |
| |
| mod(p); |
| R0.one(); |
| R1.copy(this); |
| R0.nres(p); |
| R1.nres(p); |
| |
| for (i = 8 * ROM.MODBYTES * n - 1;i >= 0;i--) |
| { |
| b = e.v[i / 256].bit(i % 256); |
| copy(R0); |
| modmul(R1,p,ND); |
| |
| cswap(R0,R1,b); |
| R0.modsqr(p,ND); |
| |
| R1.copy(this); |
| cswap(R0,R1,b); |
| } |
| copy(R0); |
| redc(p,ND); |
| } |
| |
| /* this =this^e mod p using side-channel resistant Montgomery Ladder, for short e */ |
| public void skpow(BIG e, FF p) |
| { |
| int i , b , n = p.length; |
| FF R0 = new FF(n); |
| FF R1 = new FF(n); |
| FF ND = p.invmod2m(); |
| |
| mod(p); |
| R0.one(); |
| R1.copy(this); |
| R0.nres(p); |
| R1.nres(p); |
| |
| for (i = 8 * ROM.MODBYTES - 1;i >= 0;i--) |
| { |
| b = e.bit(i); |
| copy(R0); |
| modmul(R1,p,ND); |
| |
| cswap(R0,R1,b); |
| R0.modsqr(p,ND); |
| |
| R1.copy(this); |
| cswap(R0,R1,b); |
| } |
| copy(R0); |
| redc(p,ND); |
| } |
| |
| /* raise to an integer power - right-to-left method */ |
| public void power(int e, FF p) |
| { |
| int n = p.length; |
| FF w = new FF(n); |
| FF ND = p.invmod2m(); |
| bool f = true; |
| |
| w.copy(this); |
| w.nres(p); |
| |
| if (e == 2) |
| { |
| copy(w); |
| modsqr(p,ND); |
| } |
| else |
| { |
| for (; ;) |
| { |
| if (e % 2 == 1) |
| { |
| if (f) |
| { |
| copy(w); |
| } |
| else |
| { |
| modmul(w,p,ND); |
| } |
| f = false; |
| } |
| e >>= 1; |
| if (e == 0) |
| { |
| break; |
| } |
| w.modsqr(p,ND); |
| } |
| } |
| redc(p,ND); |
| } |
| |
| /* this=this^e mod p, faster but not side channel resistant */ |
| public void pow(FF e, FF p) |
| { |
| int i , b , n = p.length; |
| FF w = new FF(n); |
| FF ND = p.invmod2m(); |
| |
| w.copy(this); |
| one(); |
| nres(p); |
| w.nres(p); |
| for (i = 8 * ROM.MODBYTES * n - 1;i >= 0;i--) |
| { |
| modsqr(p,ND); |
| b = e.v[i / 256].bit(i % 256); |
| if (b == 1) |
| { |
| modmul(w,p,ND); |
| } |
| } |
| redc(p,ND); |
| } |
| |
| /* double exponentiation r=x^e.y^f mod p */ |
| public void pow2(BIG e, FF y, BIG f, FF p) |
| { |
| int i , eb , fb , n = p.length; |
| FF xn = new FF(n); |
| FF yn = new FF(n); |
| FF xy = new FF(n); |
| FF ND = p.invmod2m(); |
| |
| xn.copy(this); |
| yn.copy(y); |
| xn.nres(p); |
| yn.nres(p); |
| xy.copy(xn); |
| xy.modmul(yn,p,ND); |
| one(); |
| nres(p); |
| |
| for (i = 8 * ROM.MODBYTES - 1;i >= 0;i--) |
| { |
| eb = e.bit(i); |
| fb = f.bit(i); |
| modsqr(p,ND); |
| if (eb == 1) |
| { |
| if (fb == 1) |
| { |
| modmul(xy,p,ND); |
| } |
| else |
| { |
| modmul(xn,p,ND); |
| } |
| } |
| else |
| { |
| if (fb == 1) |
| { |
| modmul(yn,p,ND); |
| } |
| } |
| } |
| redc(p,ND); |
| } |
| |
| private static int igcd(int x, int y) |
| { // integer GCD, returns GCD of x and y |
| int r; |
| if (y == 0) |
| { |
| return x; |
| } |
| while ((r = x % y) != 0) |
| { |
| x = y; |
| y = r; |
| } |
| return y; |
| } |
| |
| /* quick and dirty check for common factor with n */ |
| public bool cfactor(int s) |
| { |
| int r , n = length; |
| int g; |
| |
| FF x = new FF(n); |
| FF y = new FF(n); |
| |
| y.set(s); |
| x.copy(this); |
| x.norm(); |
| |
| do |
| { |
| x.sub(y); |
| x.norm(); |
| while (!x.iszilch() && x.parity() == 0) |
| { |
| x.shr(); |
| } |
| } while (comp(x,y) > 0); |
| |
| g = (int)x.v[0].get(0); |
| r = igcd(s,g); |
| if (r > 1) |
| { |
| return true; |
| } |
| return false; |
| } |
| |
| /* Miller-Rabin test for primality. Slow. */ |
| public static bool prime(FF p, RAND rng) |
| { |
| int i , j , s = 0, n = p.length; |
| bool loop; |
| FF d = new FF(n); |
| FF x = new FF(n); |
| FF unity = new FF(n); |
| FF nm1 = new FF(n); |
| |
| int sf = 4849845; // 3*5*.. *19 |
| p.norm(); |
| |
| if (p.cfactor(sf)) |
| { |
| return false; |
| } |
| unity.one(); |
| nm1.copy(p); |
| nm1.sub(unity); |
| nm1.norm(); |
| d.copy(nm1); |
| |
| while (d.parity() == 0) |
| { |
| d.shr(); |
| s++; |
| } |
| if (s == 0) |
| { |
| return false; |
| } |
| for (i = 0;i < 10;i++) |
| { |
| x.randomnum(p,rng); |
| x.pow(d,p); |
| if (comp(x,unity) == 0 || comp(x,nm1) == 0) |
| { |
| continue; |
| } |
| loop = false; |
| for (j = 1;j < s;j++) |
| { |
| x.power(2,p); |
| if (comp(x,unity) == 0) |
| { |
| return false; |
| } |
| if (comp(x,nm1) == 0) |
| { |
| loop = true; |
| break; |
| } |
| } |
| if (loop) |
| { |
| continue; |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| /* |
| public static final long[][] P ={{0xAD19A781670957L,0x76A79C00965796L,0xDEFCC5FC9A9717L,0xF02F2940E20E9L,0xBF59E34FL},{0x6894F31844C908L,0x8DADA70E82C79FL,0xFD29F3836046F6L,0x8C1D874D314DD0L,0x46D077BL},{0x3C515217813331L,0x56680FD1CE935BL,0xE55C53EEA8838EL,0x92C2F7E14A4A95L,0xD945E5B1L},{0xACF673E919F5EFL,0x6723E7E7DAB446L,0x6B6FA69B36EB1BL,0xF7D13920ECA300L,0xB5FC2165L}}; |
| |
| public static void main(String[] args) { |
| byte[] raw=new byte[100]; |
| RAND rng=new RAND(); |
| |
| rng.clean(); |
| for (int i=0;i<100;i++) raw[i]=(byte)i; |
| |
| rng.seed(100,raw); |
| |
| int n=4; |
| |
| FF x=new FF(n); |
| x.set(3); |
| |
| FF p=new FF(P,n); |
| |
| if (prime(p,rng)) System.out.println("p is a prime"); |
| |
| FF e=new FF(n); |
| e.copy(p); |
| e.dec(1); e.norm(); |
| |
| System.out.println("e= "+e.toString()); |
| |
| x.skpow(e,p); |
| System.out.println("x= "+x.toString()); |
| |
| } */ |
| |
| } |