-
Notifications
You must be signed in to change notification settings - Fork 49
/
Copy pathBinaryField.java
146 lines (115 loc) · 3.58 KB
/
BinaryField.java
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
/*
* Reed-Solomon error-correcting code decoder (Java)
*
* Copyright (c) 2019 Project Nayuki
* All rights reserved. Contact Nayuki for licensing.
* https://www.nayuki.io/page/reed-solomon-error-correcting-code-decoder
*/
import java.util.Objects;
/**
* A Galois field of the form GF(2<sup><var>n</var></sup>/<var>mod</var>). Each element of this kind of
* field is a polynomial of degree less than <var>n</var> where each monomial coefficient is either 0 or 1.
* Both the field and the elements are immutable and thread-safe.
*/
public final class BinaryField extends Field<Integer> {
/*---- Fields ----*/
/**
* The modulus of this field represented as a string of bits in natural order.
* For example, the modulus <var>x</var>^5 + <var>x</var>^1 + <var>x</var>^0
* is represented by the integer value 0b100011 (binary) or 35 (decimal).
*/
public final int modulus;
/**
* The number of (unique) elements in this field. It is a positive power of 2, e.g. 2, 4, 8, 16, etc.
* The size of the field is equal to 2 to the power of the degree of the modulus.
*/
public final int size;
/*---- Constructor ----*/
/**
* Constructs a binary field with the specified modulus. The modulus must have degree
* between 1 and 30, inclusive. Also the modulus must be irreducible (not factorable)
* in Z<sub>2</sub>, but this critical property is not checked by the constructor.
* @param mod the modulus polynomial
* @throws IllegalArgumentException if the modulus has degree less than 1
*/
public BinaryField(int mod) {
if (mod <= 1)
throw new IllegalArgumentException("Invalid modulus");
modulus = mod;
size = Integer.highestOneBit(mod);
}
/*---- Methods ----*/
public Integer zero() {
return 0;
}
public Integer one() {
return 1;
}
public boolean equals(Integer x, Integer y) {
return check(x) == check(y);
}
public Integer negate(Integer x) {
return check(x);
}
public Integer add(Integer x, Integer y) {
return check(x) ^ check(y);
}
public Integer subtract(Integer x, Integer y) {
return add(x, y);
}
public Integer multiply(Integer x, Integer y) {
return multiply(check(x), check(y));
}
private int multiply(int x, int y) {
int result = 0;
for (; y != 0; y >>>= 1) {
result ^= (y & 1) * x;
x <<= 1;
if ((x & size) != 0)
x ^= modulus;
}
return result;
}
public Integer reciprocal(Integer w) {
// Extended Euclidean GCD algorithm
int x = modulus;
int y = check(w);
if (y == 0)
throw new ArithmeticException("Division by zero");
int a = 0;
int b = 1;
while (y != 0) {
int[] quotrem = divideAndRemainder(x, y);
int c = a ^ multiply(quotrem[0], b);
x = y;
y = quotrem[1];
a = b;
b = c;
}
if (x == 1)
return a;
else // All non-zero values must have a reciprocal
throw new IllegalStateException("Field modulus is not irreducible");
}
// Returns a new array containing the pair of values (x div y, x mod y).
private static int[] divideAndRemainder(int x, int y) {
int quotient = 0;
int topY = Integer.highestOneBit(y);
for (int i = Integer.numberOfLeadingZeros(y) - Integer.numberOfLeadingZeros(x); i >= 0; i--) {
if (((topY << i) & x) != 0) {
x ^= y << i;
quotient |= 1 << i;
}
}
return new int[]{quotient, x};
}
// Checks if the given object is non-null and within the range
// of valid values, and returns the unboxed primitive value.
private int check(Integer x) {
Objects.requireNonNull(x);
int y = x.intValue();
if (y < 0 || y >= size)
throw new IllegalArgumentException("Not an element of this field: " + y);
return y;
}
}