summaryrefslogtreecommitdiffstats
path: root/bcprov/src/main/java/org/bouncycastle/pqc/math/ntru/polynomial/LongPolynomial2.java
blob: d71615a30a4fc61ae4c9f799bea3f185d9da4c78 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
package org.bouncycastle.pqc.math.ntru.polynomial;

import org.bouncycastle.util.Arrays;

/**
 * A polynomial class that combines two coefficients into one <code>long</code> value for
 * faster multiplication in 64 bit environments.<br>
 * Coefficients can be between 0 and 2047 and are stored in pairs in the bits 0..10 and 24..34 of a <code>long</code> number.
 */
public class LongPolynomial2
{
    private long[] coeffs;   // each representing two coefficients in the original IntegerPolynomial
    private int numCoeffs;

    /**
     * Constructs a <code>LongPolynomial2</code> from a <code>IntegerPolynomial</code>. The two polynomials are independent of each other.
     *
     * @param p the original polynomial. Coefficients must be between 0 and 2047.
     */
    public LongPolynomial2(IntegerPolynomial p)
    {
        numCoeffs = p.coeffs.length;
        coeffs = new long[(numCoeffs + 1) / 2];
        int idx = 0;
        for (int pIdx = 0; pIdx < numCoeffs; )
        {
            int c0 = p.coeffs[pIdx++];
            while (c0 < 0)
            {
                c0 += 2048;
            }
            long c1 = pIdx < numCoeffs ? p.coeffs[pIdx++] : 0;
            while (c1 < 0)
            {
                c1 += 2048;
            }
            coeffs[idx] = c0 + (c1 << 24);
            idx++;
        }
    }

    private LongPolynomial2(long[] coeffs)
    {
        this.coeffs = coeffs;
    }

    private LongPolynomial2(int N)
    {
        coeffs = new long[N];
    }

    /**
     * Multiplies the polynomial with another, taking the indices mod N and the values mod 2048.
     */
    public LongPolynomial2 mult(LongPolynomial2 poly2)
    {
        int N = coeffs.length;
        if (poly2.coeffs.length != N || numCoeffs != poly2.numCoeffs)
        {
            throw new IllegalArgumentException("Number of coefficients must be the same");
        }

        LongPolynomial2 c = multRecursive(poly2);

        if (c.coeffs.length > N)
        {
            if (numCoeffs % 2 == 0)
            {
                for (int k = N; k < c.coeffs.length; k++)
                {
                    c.coeffs[k - N] = (c.coeffs[k - N] + c.coeffs[k]) & 0x7FF0007FFL;
                }
                c.coeffs = Arrays.copyOf(c.coeffs, N);
            }
            else
            {
                for (int k = N; k < c.coeffs.length; k++)
                {
                    c.coeffs[k - N] = c.coeffs[k - N] + (c.coeffs[k - 1] >> 24);
                    c.coeffs[k - N] = c.coeffs[k - N] + ((c.coeffs[k] & 2047) << 24);
                    c.coeffs[k - N] &= 0x7FF0007FFL;
                }
                c.coeffs = Arrays.copyOf(c.coeffs, N);
                c.coeffs[c.coeffs.length - 1] &= 2047;
            }
        }

        c = new LongPolynomial2(c.coeffs);
        c.numCoeffs = numCoeffs;
        return c;
    }

    public IntegerPolynomial toIntegerPolynomial()
    {
        int[] intCoeffs = new int[numCoeffs];
        int uIdx = 0;
        for (int i = 0; i < coeffs.length; i++)
        {
            intCoeffs[uIdx++] = (int)(coeffs[i] & 2047);
            if (uIdx < numCoeffs)
            {
                intCoeffs[uIdx++] = (int)((coeffs[i] >> 24) & 2047);
            }
        }
        return new IntegerPolynomial(intCoeffs);
    }

    /**
     * Karazuba multiplication
     */
    private LongPolynomial2 multRecursive(LongPolynomial2 poly2)
    {
        long[] a = coeffs;
        long[] b = poly2.coeffs;

        int n = poly2.coeffs.length;
        if (n <= 32)
        {
            int cn = 2 * n;
            LongPolynomial2 c = new LongPolynomial2(new long[cn]);
            for (int k = 0; k < cn; k++)
            {
                for (int i = Math.max(0, k - n + 1); i <= Math.min(k, n - 1); i++)
                {
                    long c0 = a[k - i] * b[i];
                    long cu = c0 & 0x7FF000000L + (c0 & 2047);
                    long co = (c0 >>> 48) & 2047;

                    c.coeffs[k] = (c.coeffs[k] + cu) & 0x7FF0007FFL;
                    c.coeffs[k + 1] = (c.coeffs[k + 1] + co) & 0x7FF0007FFL;
                }
            }
            return c;
        }
        else
        {
            int n1 = n / 2;

            LongPolynomial2 a1 = new LongPolynomial2(Arrays.copyOf(a, n1));
            LongPolynomial2 a2 = new LongPolynomial2(Arrays.copyOfRange(a, n1, n));
            LongPolynomial2 b1 = new LongPolynomial2(Arrays.copyOf(b, n1));
            LongPolynomial2 b2 = new LongPolynomial2(Arrays.copyOfRange(b, n1, n));

            LongPolynomial2 A = (LongPolynomial2)a1.clone();
            A.add(a2);
            LongPolynomial2 B = (LongPolynomial2)b1.clone();
            B.add(b2);

            LongPolynomial2 c1 = a1.multRecursive(b1);
            LongPolynomial2 c2 = a2.multRecursive(b2);
            LongPolynomial2 c3 = A.multRecursive(B);
            c3.sub(c1);
            c3.sub(c2);

            LongPolynomial2 c = new LongPolynomial2(2 * n);
            for (int i = 0; i < c1.coeffs.length; i++)
            {
                c.coeffs[i] = c1.coeffs[i] & 0x7FF0007FFL;
            }
            for (int i = 0; i < c3.coeffs.length; i++)
            {
                c.coeffs[n1 + i] = (c.coeffs[n1 + i] + c3.coeffs[i]) & 0x7FF0007FFL;
            }
            for (int i = 0; i < c2.coeffs.length; i++)
            {
                c.coeffs[2 * n1 + i] = (c.coeffs[2 * n1 + i] + c2.coeffs[i]) & 0x7FF0007FFL;
            }
            return c;
        }
    }

    /**
     * Adds another polynomial which can have a different number of coefficients.
     *
     * @param b another polynomial
     */
    private void add(LongPolynomial2 b)
    {
        if (b.coeffs.length > coeffs.length)
        {
            coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
        }
        for (int i = 0; i < b.coeffs.length; i++)
        {
            coeffs[i] = (coeffs[i] + b.coeffs[i]) & 0x7FF0007FFL;
        }
    }

    /**
     * Subtracts another polynomial which can have a different number of coefficients.
     *
     * @param b another polynomial
     */
    private void sub(LongPolynomial2 b)
    {
        if (b.coeffs.length > coeffs.length)
        {
            coeffs = Arrays.copyOf(coeffs, b.coeffs.length);
        }
        for (int i = 0; i < b.coeffs.length; i++)
        {
            coeffs[i] = (0x0800000800000L + coeffs[i] - b.coeffs[i]) & 0x7FF0007FFL;
        }
    }

    /**
     * Subtracts another polynomial which must have the same number of coefficients,
     * and applies an AND mask to the upper and lower halves of each coefficients.
     *
     * @param b    another polynomial
     * @param mask a bit mask less than 2048 to apply to each 11-bit coefficient
     */
    public void subAnd(LongPolynomial2 b, int mask)
    {
        long longMask = (((long)mask) << 24) + mask;
        for (int i = 0; i < b.coeffs.length; i++)
        {
            coeffs[i] = (0x0800000800000L + coeffs[i] - b.coeffs[i]) & longMask;
        }
    }

    /**
     * Multiplies this polynomial by 2 and applies an AND mask to the upper and
     * lower halves of each coefficients.
     *
     * @param mask a bit mask less than 2048 to apply to each 11-bit coefficient
     */
    public void mult2And(int mask)
    {
        long longMask = (((long)mask) << 24) + mask;
        for (int i = 0; i < coeffs.length; i++)
        {
            coeffs[i] = (coeffs[i] << 1) & longMask;
        }
    }

    public Object clone()
    {
        LongPolynomial2 p = new LongPolynomial2(coeffs.clone());
        p.numCoeffs = numCoeffs;
        return p;
    }

    public boolean equals(Object obj)
    {
        if (obj instanceof LongPolynomial2)
        {
            return Arrays.areEqual(coeffs, ((LongPolynomial2)obj).coeffs);
        }
        else
        {
            return false;
        }
    }
}