GNFS implementation + GF2 gaussian elimination fix

This commit is contained in:
sam.hadow committed 2026-10-12 20:48:39 +02:00
1 parent da4662ac1c
commit 529d887486
14 files changed
+980 -87

No files matched your search

+114
View File
@@ -0,0 +1,114 @@
module GNFS.FactorBase
( Relation(..)
, FactorBase(..)
, buildFactorBase
, relationRow
) where
import Data.Bits (setBit)
import Data.List (foldl')
import qualified Data.Map.Strict as M
import GNFS.FiniteField (rootsModP)
import GNFS.PolySelect (NFSPoly(..))
import GNFS.Polynomial
import GNFS.Util (primesFrom, modInv)
import LinearAlgebra.GF2 (BitVec)
import ModularArithmeticUtils (legendre)
import Primes.ErathosteneSieve (erathosteneSieve)
-- | A relation: the pair (a, b) with a - b*m smooth over the rational base and
-- a - b*alpha smooth over the algebraic base, with the full exponent data.
data Relation = Relation
{ relA :: !Integer
, relB :: !Integer
, relRat :: [(Integer, Int)] -- prime -> exponent of |a - b m|
, relAlg :: [((Integer, Integer), Int)] -- (p, r) -> valuation of a - b alpha
} deriving Show
data FactorBase = FactorBase
{ fbPoly :: NFSPoly
, fbRat :: [Integer] -- rational base: primes up to the bound
, fbAlg :: [(Integer, Integer)] -- algebraic base: (p, r), f(r) = 0 mod p, simple root
, fbAlgPrimes :: [Integer] -- distinct primes occurring in fbAlg
, fbAlgLifts :: [(Integer, [(Integer, Integer)])]
-- per prime p: for each root r, the Hensel lifts
-- (p^k, r_k) for k = 1, 2, ... (lazy, infinite)
, fbReal :: [Double] -- real roots of f (sign columns)
, fbQC :: [(Integer, Integer)] -- quadratic-character primes (q, s)
, fbColRat :: M.Map Integer Int
, fbColAlg :: M.Map (Integer, Integer) Int
, fbNumCols :: Int
}
-- | Build the three factor bases (rational, algebraic, quadratic characters).
buildFactorBase :: NFSPoly -> Integer -> Integer -> Int -> FactorBase
buildFactorBase np ratBound algBound nQC = fb
where
f = npCoeffs np
f' = derivP f
rat = erathosteneSieve ratBound
-- primes whose roots are all simple; primes with a repeated root are dropped
algPrimeRoots =
[ (p, rs)
| p <- erathosteneSieve algBound
, let rs = rootsModP p f
, not (null rs)
, all (\r -> evalP f' r `mod` p /= 0) rs
]
alg = [ (p, r) | (p, rs) <- algPrimeRoots, r <- rs ]
qc = take nQC
[ (q, s)
| q <- primesFrom (max algBound ratBound + 1)
, let rs = rootsModP q f
, s <- rs
, evalP f' s `mod` q /= 0
]
reals = realRoots f
-- Hensel lifting of a simple root r of f mod p to roots mod p^k
lifts p r0 = go p r0
where
go q rk =
let q' = q * p
inv = case modInv (evalP f' rk) q' of
Just i -> i
Nothing -> error "Hensel lift: derivative not invertible"
rk' = (rk - evalP f rk * inv) `mod` q'
in (q, rk) : go q' rk'
nRat = length rat
nAlg = length alg
fb = FactorBase
{ fbPoly = np
, fbRat = rat
, fbAlg = alg
, fbAlgPrimes = map fst algPrimeRoots
, fbAlgLifts = [ (p, lifts p r) | (p, r) <- alg ]
, fbReal = reals
, fbQC = qc
, fbColRat = M.fromList (zip rat [0 ..])
, fbColAlg = M.fromList (zip alg [0 ..])
, fbNumCols = 1 + nRat + nAlg + length reals + length qc
}
-- | Parity vector of a relation. Column layout:
-- [0] sign of a - b m | rational primes | algebraic ideals | real-place signs | quadratic characters
relationRow :: FactorBase -> Relation -> BitVec
relationRow fb rel = foldl' setBit 0 cols
where
a = relA rel
b = relB rel
m = npM (fbPoly fb)
nRat = length (fbRat fb)
nAlg = length (fbAlg fb)
offAlg = 1 + nRat
offReal = offAlg + nAlg
offQC = offReal + length (fbReal fb)
signCol = [ 0 | a - b * m < 0 ]
ratCols = [ 1 + fbColRat fb M.! p | (p, e) <- relRat rel, odd e ]
algCols = [ offAlg + fbColAlg fb M.! pr | (pr, e) <- relAlg rel, odd e ]
realCols = [ offReal + j | (j, th) <- zip [0 ..] (fbReal fb)
, fromInteger a < fromInteger b * th ]
qcCols = [ offQC + j | (j, (q, s)) <- zip [0 ..] (fbQC fb)
, let v = (a - b * s) `mod` q
, legendre v q == q - 1 ]
cols = signCol ++ ratCols ++ algCols ++ realCols ++ qcCols