-
Notifications
You must be signed in to change notification settings - Fork 12
/
Binary.hs
223 lines (187 loc) · 6.73 KB
/
Binary.hs
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
module Data.Field.Galois.Binary
( Binary
, BinaryField
, fromB
, toB
, toB'
) where
import Protolude as P hiding (Semiring, natVal)
import Control.Monad.Random (Random(..))
import Data.Bit (Bit, F2Poly, gcdExt, toF2Poly, unF2Poly)
import Data.Euclidean as S (Euclidean(..), GcdDomain)
import Data.Field (Field)
import Data.Group (Group(..))
import Data.Semiring (Ring(..), Semiring(..))
import Data.Vector.Unboxed as V (fromList, length, toList)
import GHC.Exts (IsList(..))
import GHC.Natural (Natural)
import GHC.TypeNats (natVal)
import Test.QuickCheck (Arbitrary(..), choose)
import Text.PrettyPrint.Leijen.Text (Pretty(..))
import Data.Field.Galois.Base (GaloisField(..))
-------------------------------------------------------------------------------
-- Data types
-------------------------------------------------------------------------------
-- | Binary fields @GF(2^q)[X]/\<f(X)\>@ for @q@ positive and
-- @f(X)@ irreducible monic in @GF(2^q)[X]@ encoded as an integer.
class GaloisField k => BinaryField k where
{-# MINIMAL fromB #-}
-- | Convert from @GF(2^q)[X]/\<f(X)\>@ to @Z@.
fromB :: k -> Integer
-- | Binary field elements.
newtype Binary (p :: Nat) = B F2Poly
deriving (Eq, Generic, NFData, Ord, Show)
-- Binary fields are convertible.
instance KnownNat p => BinaryField (Binary p) where
fromB (B x) = toInteger x
{-# INLINABLE fromB #-}
-- Binary fields are Galois fields.
instance KnownNat p => GaloisField (Binary p) where
char = const 2
{-# INLINABLE char #-}
deg = pred . fromIntegral . V.length . unF2Poly . toPoly . natVal
{-# INLINABLE deg #-}
frob = join (*)
{-# INLINABLE frob #-}
{-# RULES "Binary.pow"
forall (k :: KnownNat p => Binary p) n . (^) k n = pow k n
#-}
-------------------------------------------------------------------------------
-- Group instances
-------------------------------------------------------------------------------
-- Binary fields are multiplicative groups.
instance KnownNat p => Group (Binary p) where
invert = recip
{-# INLINE invert #-}
pow x n
| n >= 0 = x ^ n
| otherwise = recip x ^ P.negate n
{-# INLINE pow #-}
-- Binary fields are multiplicative monoids.
instance KnownNat p => Monoid (Binary p) where
mempty = B 1
{-# INLINE mempty #-}
-- Binary fields are multiplicative semigroups.
instance KnownNat p => Semigroup (Binary p) where
(<>) = (*)
{-# INLINE (<>) #-}
stimes = flip pow
{-# INLINE stimes #-}
-------------------------------------------------------------------------------
-- Numeric instances
-------------------------------------------------------------------------------
-- Binary fields are fractional.
instance KnownNat p => Fractional (Binary p) where
recip (B x) = case gcdExt x $ toPoly $ natVal (witness :: Binary p) of
(1, y) -> B y
_ -> divZeroError
{-# INLINE recip #-}
fromRational rat = fromInteger (numerator rat) / fromInteger (denominator rat)
{-# INLINABLE fromRational #-}
-- Binary fields are numeric.
instance KnownNat p => Num (Binary p) where
B x + B y = B $ x + y
{-# INLINE (+) #-}
B x * B y = B $ P.rem (x * y) $ toPoly $ natVal (witness :: Binary p)
{-# INLINE (*) #-}
B x - B y = B $ x + y
{-# INLINE (-) #-}
negate = identity
{-# INLINE negate #-}
fromInteger = B . flip P.rem (toPoly $ natVal (witness :: Binary p)) . toPoly
{-# INLINABLE fromInteger #-}
abs = panic "Binary.abs: not implemented."
signum = panic "Binary.signum: not implemented."
-------------------------------------------------------------------------------
-- Semiring instances
-------------------------------------------------------------------------------
-- Binary fields are Euclidean domains.
instance KnownNat p => Euclidean (Binary p) where
degree = panic "Binary.degree: not implemented."
quotRem = (flip (,) 0 .) . (/)
{-# INLINE quotRem #-}
-- Binary fields are fields.
instance KnownNat p => Field (Binary p) where
-- Binary fields are GCD domains.
instance KnownNat p => GcdDomain (Binary p)
-- Binary fields are rings.
instance KnownNat p => Ring (Binary p) where
negate = P.negate
{-# INLINE negate #-}
-- Binary fields are semirings.
instance KnownNat p => Semiring (Binary p) where
fromNatural = fromIntegral
{-# INLINABLE fromNatural #-}
one = B 1
{-# INLINE one #-}
plus = (+)
{-# INLINE plus #-}
times = (*)
{-# INLINE times #-}
zero = B 0
{-# INLINE zero #-}
-------------------------------------------------------------------------------
-- Other instances
-------------------------------------------------------------------------------
-- Binary fields are arbitrary.
instance KnownNat p => Arbitrary (Binary p) where
arbitrary = toB' <$>
choose (0, toInteger $ order (witness :: Binary p) - 1)
{-# INLINABLE arbitrary #-}
-- Binary fields are lists.
instance KnownNat p => IsList (Binary p) where
type instance Item (Binary p) = Bit
fromList = B . toF2Poly . V.fromList
{-# INLINABLE fromList #-}
toList (B x) = V.toList $ unF2Poly x
{-# INLINABLE toList #-}
-- Binary fields are bounded.
instance KnownNat p => Bounded (Binary p) where
maxBound = B $ toPoly $ order (witness :: Binary p) - 1
{-# INLINE maxBound #-}
minBound = B 0
{-# INLINE minBound #-}
-- Binary fields are enumerable.
instance KnownNat p => Enum (Binary p) where
fromEnum = fromIntegral
{-# INLINABLE fromEnum #-}
toEnum = fromIntegral
{-# INLINABLE toEnum #-}
-- Binary fields are integral.
instance KnownNat p => Integral (Binary p) where
quotRem = S.quotRem
{-# INLINE quotRem #-}
toInteger = fromB
{-# INLINABLE toInteger #-}
-- Binary fields are pretty.
instance KnownNat p => Pretty (Binary p) where
pretty (B x) = pretty $ toInteger x
-- Binary fields are random.
instance KnownNat p => Random (Binary p) where
random = randomR (B 0, B $ toPoly $ order (witness :: Binary p) - 1)
{-# INLINABLE random #-}
randomR (a, b) = first toB' . randomR (fromB a, fromB b)
{-# INLINABLE randomR #-}
-- Binary fields are real.
instance KnownNat p => Real (Binary p) where
toRational = fromIntegral
{-# INLINABLE toRational #-}
-------------------------------------------------------------------------------
-- Auxiliary functions
-------------------------------------------------------------------------------
-- | Safe convert from @Z@ to @GF(2^q)[X]/\<f(X)\>@.
toB :: KnownNat p => Integer -> Binary p
toB = fromInteger
{-# INLINABLE toB #-}
-- | Unsafe convert from @Z@ to @GF(2^q)[X]/\<f(X)\>@.
toB' :: KnownNat p => Integer -> Binary p
toB' = B . toPoly
{-# INLINABLE toB' #-}
-- Specialisation convert from integer to polynomial.
toPoly :: Integral a => a -> F2Poly
toPoly = fromIntegral
{-# INLINABLE toPoly #-}
{-# SPECIALISE toPoly ::
Integer -> F2Poly,
Natural -> F2Poly
#-}