Skip to content

Polynomials over second-degree finite field extensions seem to loop forever on GCD operations #74

Description

@kozross

I'm currently writing some test code in a second-degree extension of GF(11). Specifically, elements of this extension are represented as (x + yu), where u^2 = 2. I am using the semirings interface for this, defining things as so:

-- Prelude is imported qualified as P, any clashing operations not qualified as from `semirings`

newtype GF11Elem2 = GF11E2 (Int, Int)
    deriving (Eq) via (Int, Int)

instance Semiring GF11Elem2 where
    plus (GF11E2 (r1, i1)) (GF11E2 (r2, i2)) = GF11E2 . reduce $ (r1 + r2, i1 + i2)
    times (GF11E2 (r1, i1)) (GF11E2 (r2, i2)) =
        let r1r2 = r1 * r2
            r1i2 = r1 * i2
            i1r2 = i1 * r2
            i1i2 = i1 * i2
         in GF11E2 . reduce $ (r1r2 + 2 * i1i2, r1i2 + i1r2)
    zero = GF11E2 (0, 0)
    one = GF11E2 (1, 0)
    fromNatural n = GF11E2 (P.fromIntegral $ n `mod` 11, 0)

instance Ring GF11Elem2 where
    negate (GF11E2 (r, i)) = GF11E2 . reduce $ (negate r, negate i)

instance GcdDomain GF11Elem2

instance Euclidean GF11Elem2 where
  quot (GF11E2 (u, v)) = \case
    GF11E2 (0, 0) -> P.error "Division by zero"
    GF11E2 (x, y) -> let recipExpr = (x * x) - (2 * y * y)
                         (_, recipr) = gcdExt recipExpr 11
                         ux = u * x
                         yv = y * v
                         xv = x * v
                         uy = u * y
                        in GF11E2 . reduce $ ((ux - (2 * yv)) * recipr, (xv - uy) * recipr)
  rem _ = \case
    GF11E2 (0, 0) -> P.error "Division by zero"
    _ -> zero
  quotRem x y = let !q = quot x y
    in (q, zero)
  degree = const zero

-- Helpers

reduce ::
    forall (f :: Type -> Type -> Type) (a :: Type).
    (Bifunctor f, Integral a) => f a a -> f a a
reduce = bimap (`mod` 11) (`mod` 11)

Part of my test code requires testing for squarefreeness of polynomials over GF11Elem2, as per this method. My 'initial' polynomials are always of the form toPoly [B, A, 0, 1], where A and B are constants. However, I find that even the initial gcd f (deriv f) loops forever, or at least runs so slowly it might as well loop forever. Furthermore, if I use the boxed polynomial representation, I rapidly exhaust all available RAM; switching to unboxed representation solves this (but still runs forever).

Am I missing something here, or is there some kind of bug or performance limitation in poly I need to be aware of?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions