packages feed

Flint2-0.1.0.0: src/Data/Number/Flint/QSieve/FFI.hsc

{-|
module      :  Data.Number.Flint.QSieve.FFI
copyright   :  (c) 2022 Hartmut Monien
license     :  GNU GPL, version 2 or above (see LICENSE)
maintainer  :  hmonien@uni-bonn.de
-}
module Data.Number.Flint.QSieve.FFI (
  -- * Quadratic sieve
    Qs (..)
  , CQs()
  , newQs
  , withQs
  -- *
  , qsieve_knuth_schroeppel
  , qsieve_primes_init
  , qsieve_primes_increment
  , qsieve_init_A
  , qsieve_next_A
  --, qsieve_compute_pre_data
  , qsieve_init_poly_first
  , qsieve_init_poly_next
  , qsieve_compute_C
  , qsieve_do_sieving
  , qsieve_do_sieving2
  , qsieve_evaluate_candidate
  , qsieve_evaluate_sieve
  , qsieve_collect_relations
  , qsieve_write_to_file
  , qsieve_get_table_entry
  , qsieve_add_to_hashtable
  , qsieve_parse_relation
  , qsieve_merge_relation
  , qsieve_compare_relation
  , qsieve_remove_duplicates
  --, qsieve_insert_relation2
  , qsieve_process_relation
  , qsieve_factor
) where

-- Quadratic sieve -------------------------------------------------------------

import Control.Monad

import Foreign.C.String
import Foreign.C.Types
import Foreign.ForeignPtr
import Foreign.Ptr ( Ptr, FunPtr, plusPtr )
import Foreign.Storable
import Foreign.Marshal ( free )

import Data.Number.Flint.Flint
import Data.Number.Flint.Fmpz
import Data.Number.Flint.Fmpz.Factor

#include <flint/flint.h>
#include <flint/fmpz.h>
#include <flint/qsieve.h>

-- qs_t ------------------------------------------------------------------------

data Qs = Qs {-# UNPACK #-} !(ForeignPtr CQs)
type CQs = CFlint Qs

instance Storable CQs where
  {-# INLINE sizeOf #-}
  sizeOf _ = #{size qs_t}
  {-# INLINE alignment #-}
  alignment _ = #{alignment qs_t}
  peek = undefined
  poke = undefined

newQs n = do
  x <- mallocForeignPtr
  withForeignPtr x $ \x ->
    withFmpz n $ \n -> do
      qsieve_init x n
  addForeignPtrFinalizer p_qsieve_clear x
  return $ Qs x

{-# INLINE withQs #-}
withQs (Qs x) f = do
  withForeignPtr x $ \px -> f px >>= return . (Qs x,)

-- hash_t ----------------------------------------------------------------------

data Hash = Hash {-# UNPACK #-} !(ForeignPtr CHash)
data CHash = CHash CMpLimb CMpLimb CMpLimb

instance Storable CHash where
  {-# INLINE sizeOf #-}
  sizeOf _ = #{size hash_t}
  {-# INLINE alignment #-}
  alignment _ = #{alignment hash_t}
  peek ptr = CHash
    <$> #{peek hash_t, prime} ptr
    <*> #{peek hash_t, next } ptr
    <*> #{peek hash_t, count} ptr 
  poke ptr (CHash prime next count) = do
    #{poke hash_t, prime} ptr prime
    #{poke hash_t, next } ptr next
    #{poke hash_t, count} ptr count
    
-- relation_t ------------------------------------------------------------------

data Relation = Relation {-# UNPACK #-} !(ForeignPtr CRelation)
data CRelation = CRelation CMpLimb CLong CLong (Ptr CLong) (Ptr CFac) (Ptr CFmpz)

instance Storable CRelation where
  {-# INLINE sizeOf #-}
  sizeOf _ = #{size relation_t}
  {-# INLINE alignment #-}
  alignment _ = #{alignment relation_t}
  peek ptr = CRelation
    <$> #{peek relation_t, lp          } ptr
    <*> #{peek relation_t, num_factors } ptr
    <*> #{peek relation_t, small_primes} ptr
    <*> #{peek relation_t, small       } ptr
    <*> #{peek relation_t, factor      } ptr
    <*> #{peek relation_t, Y           } ptr
  poke ptr (CRelation lp num_factors small_primes small factor y) = do
    #{poke relation_t, lp          } ptr lp 
    #{poke relation_t, num_factors } ptr num_factors
    #{poke relation_t, small_primes} ptr small_primes
    #{poke relation_t, small       } ptr small
    #{poke relation_t, factor      } ptr factor
    #{poke relation_t, Y           } ptr y

-- fac_t -----------------------------------------------------------------------

data Fac = Fac {-# UNPACK #-} !(ForeignPtr CFac)
data CFac = CFac CLong CLong

instance Storable CFac where
  {-# INLINE sizeOf #-}
  sizeOf _ = #{size fac_t}
  {-# INLINE alignment #-}
  alignment _ = #{alignment fac_t}
  peek ptr = CFac
    <$> #{peek fac_t, ind} ptr
    <*> #{peek fac_t, exp} ptr
  poke ptr (CFac ind exp) = do
    #{poke fac_t, ind} ptr ind
    #{poke fac_t, ind} ptr exp

--------------------------------------------------------------------------------

foreign import ccall "flint/qsieve.h qsieve_init"
  qsieve_init :: Ptr CQs -> Ptr CFmpz -> IO ()
  
foreign import ccall "flint/qsieve.h qsieve_clear"
  qsieve_clear :: Ptr CQs -> IO ()

foreign import ccall "flint/qsieve.h &qsieve_clear"
  p_qsieve_clear :: FunPtr (Ptr CQs -> IO ())

--------------------------------------------------------------------------------

-- | /qsieve_knuth_schroeppel/ /qs_inf/ 
--
-- Return the Knuth-Schroeppel multiplier for the \(n\), integer to be
-- factored based upon the Knuth-Schroeppel function.
foreign import ccall "qsieve.h qsieve_knuth_schroeppel"
  qsieve_knuth_schroeppel :: Ptr CQs -> IO CMpLimb

-- | /qsieve_primes_init/ /qs_inf/ 
--
-- Compute the factor base prime along with there inverse for \(kn\), where
-- \(k\) is Knuth-Schroeppel multiplier and \(n\) is the integer to be
-- factored. It also computes the square root of \(kn\) modulo factor base
-- primes.
foreign import ccall "qsieve.h qsieve_primes_init"
  qsieve_primes_init :: Ptr CQs -> IO CMpLimb

-- | /qsieve_primes_increment/ /qs_inf/ /delta/ 
--
-- It increase the number of factor base primes by amount \'delta\' and
-- calculate inverse of those primes along with the square root of \(kn\)
-- modulo those primes.
foreign import ccall "qsieve.h qsieve_primes_increment"
  qsieve_primes_increment :: Ptr CQs -> CMpLimb -> IO CMpLimb

-- | /qsieve_init_A0/ /qs_inf/ 
--
-- First it chooses the possible range of factor of \(A _0\), based on the
-- number of bits in optimal value of \(A _0\). It tries to select range
-- such that we have plenty of primes to choose from as well as number of
-- factor in \(A _0\) are sufficient. For input of size less than 130 bit,
-- this selection method doesn\'t work therefore we randomly generate 2 or
-- 3-subset of all the factor base prime as the factor of \(A _0\).
-- Otherwise, if we have to select \(s\) factor for \(A _0\), we generate
-- \(s - 1\)-subset from odd indices of the possible range of factor and
-- then search last factor using binary search from the even indices of
-- possible range of factor such that value of \(A _0\) is close to it\'s
-- optimal value.
foreign import ccall "qsieve.h qsieve_init_A"
  qsieve_init_A :: Ptr CQs -> IO ()

-- | /qsieve_next_A0/ /qs_inf/ 
--
-- Find next candidate for \(A _0\) as follows: generate next lexicographic
-- \(s - 1\)-subset from the odd indices of possible range of factor base
-- and choose the last factor from even indices using binary search so that
-- value \(A _0\) is close to it\'s optimal value.
foreign import ccall "qsieve.h qsieve_next_A"
  qsieve_next_A :: Ptr CQs -> IO ()

-- -- | /qsieve_compute_pre_data/ /qs_inf/ 
-- --
-- -- Precompute all the data associated with factor\'s of \(A _0\), since
-- -- \(A _0\) is going to be fixed for several \(A\).
-- foreign import ccall "qsieve.h qsieve_compute_pre_data"
--   qsieve_compute_pre_data :: Ptr CQs -> IO ()

-- | /qsieve_init_poly_first/ /qs_inf/ 
--
-- Initializes the value of \(A = q _0 * A _0\), where \(q _0\) is
-- non-factor base prime. precompute the data necessary for generating
-- different \(B\) value using grey code formula. Combine the data
-- calculated for the factor of \(A _0\) along with the parameter \(q _0\)
-- to obtain data as for factor of \(A\). It also calculates the sieve
-- offset for all the factor base prime, for first polynomial.
foreign import ccall "qsieve.h qsieve_init_poly_first"
  qsieve_init_poly_first :: Ptr CQs -> IO ()

-- | /qsieve_init_poly_next/ /qs_inf/ 
--
-- Generate next polynomial or next \(B\) value for particular \(A\) and
-- also updates the sieve offsets for all the factor base prime, for this
-- \(B\) value.
foreign import ccall "qsieve.h qsieve_init_poly_next"
  qsieve_init_poly_next :: Ptr CQs -> IO ()

-- | /qsieve_compute_C/ /qs_inf/ 
--
-- Given \(A\) and \(B\), calculate \(C = (B ^2 - A) / N\).
foreign import ccall "qsieve.h qsieve_compute_C"
  qsieve_compute_C :: Ptr CQs -> IO ()

-- | /qsieve_do_sieving/ /qs_inf/ /sieve/ 
--
-- First initialize the sieve array to zero, then for each \(p \in\)
-- @factor base@, add \(\log_2(p)\) to the locations
-- \(\operatorname{soln1} _p + i * p\) and
-- \(\operatorname{soln2} _p + i * p\) for \(i = 0, 1, 2,\dots\), where
-- \(\operatorname{soln1} _p\) and \(\operatorname{soln2} _p\) are the
-- sieve offsets calculated for \(p\).
foreign import ccall "qsieve.h qsieve_do_sieving"
  qsieve_do_sieving :: Ptr CQs -> CString -> IO ()

-- | /qsieve_do_sieving2/ /qs_inf/ 
--
-- Perform the same task as above but instead of sieving over whole array
-- at once divide the array in blocks and then sieve over each block for
-- all the primes in factor base.
foreign import ccall "qsieve.h qsieve_do_sieving2"
  qsieve_do_sieving2 :: Ptr CQs -> IO ()

-- | /qsieve_evaluate_candidate/ /qs_inf/ /i/ /sieve/ 
--
-- For location \(i\) in sieve array value at which, is greater than sieve
-- threshold, check the value of \(Q(x)\) at position \(i\) for smoothness.
-- If value is found to be smooth then store it for later processing, else
-- check the residue for the partial if it is found to be partial then
-- store it for late processing.
foreign import ccall "qsieve.h qsieve_evaluate_candidate"
  qsieve_evaluate_candidate :: Ptr CQs -> CLong -> CString -> IO CLong

-- | /qsieve_evaluate_sieve/ /qs_inf/ /sieve/ 
--
-- Scan the sieve array for location at, which accumulated value is greater
-- than sieve threshold.
foreign import ccall "qsieve.h qsieve_evaluate_sieve"
  qsieve_evaluate_sieve :: Ptr CQs -> CString -> IO CLong

-- | /qsieve_collect_relations/ /qs_inf/ /sieve/ 
--
-- Call for initialization of polynomial, sieving, and scanning of sieve
-- for all the possible polynomials for particular hypercube i.e. \(A\).
foreign import ccall "qsieve.h qsieve_collect_relations"
  qsieve_collect_relations :: Ptr CQs -> CString -> IO CLong

-- | /qsieve_write_to_file/ /qs_inf/ /prime/ /Y/ 
--
-- Write a relation to the file. Format is as follows, first write large
-- prime, in case of full relation it is 1, then write exponent of small
-- primes, then write number of factor followed by offset of factor in
-- factor base and their exponent and at last value of \(Q(x)\) for
-- particular relation. each relation is written in new line.
foreign import ccall "qsieve.h qsieve_write_to_file"
  qsieve_write_to_file :: Ptr CQs -> CMpLimb -> Ptr CFmpz -> IO ()

-- | /qsieve_get_table_entry/ /qs_inf/ /prime/ 
--
-- Return the pointer to the location of \'prime\' is hash table if it
-- exist, else create and entry for it in hash table and return pointer to
-- that.
foreign import ccall "qsieve.h qsieve_get_table_entry"
  qsieve_get_table_entry :: Ptr CQs -> CMpLimb -> IO (Ptr (Ptr CHash))

-- | /qsieve_add_to_hashtable/ /qs_inf/ /prime/ 
--
-- Add \'prime\' to the hast table.
foreign import ccall "qsieve.h qsieve_add_to_hashtable"
  qsieve_add_to_hashtable :: Ptr CQs -> CMpLimb -> IO ()

-- | /qsieve_parse_relation/ /qs_inf/ /str/ 
--
-- Given a string representation of relation from the file, parse it to
-- obtain all the parameters of relation.
foreign import ccall "qsieve.h qsieve_parse_relation"
  qsieve_parse_relation :: Ptr CQs -> CString -> IO (Ptr CRelation)

-- | /qsieve_merge_relation/ /qs_inf/ /a/ /b/ 
--
-- Given two partial relation having same large prime, merge them to obtain
-- a full relation.
foreign import ccall "qsieve.h qsieve_merge_relation"
  qsieve_merge_relation :: Ptr CQs -> Ptr CRelation -> Ptr CRelation -> IO (Ptr CRelation)

-- | /qsieve_compare_relation/ /a/ /b/ 
--
-- Compare two relation based on, first large prime, then number of factor
-- and then offsets of factor in factor base.
foreign import ccall "qsieve.h qsieve_compare_relation"
  qsieve_compare_relation :: Ptr ()  -> Ptr ()  -> IO CInt

-- | /qsieve_remove_duplicates/ /rel_list/ /num_relations/ 
--
-- Remove duplicate from given list of relations by sorting relations in
-- the list.
foreign import ccall "qsieve.h qsieve_remove_duplicates"
  qsieve_remove_duplicates :: Ptr (Ptr CRelation) -> CLong -> IO CInt

-- -- | /qsieve_insert_relation2/ /qs_inf/ /rel_list/ /num_relations/ 
-- --
-- -- Given a list of relations, insert each relation from the list into the
-- -- matrix for further processing.
-- foreign import ccall "qsieve.h qsieve_insert_relation2"
--   qsieve_insert_relation2 :: Ptr CQs -> Ptr (Ptr CRelation) -> CLong -> IO ()

-- | /qsieve_process_relation/ /qs_inf/ 
--
-- After we have accumulated required number of relations, first process
-- the file by reading all the relations, removes singleton. Then merge all
-- the possible partial to obtain full relations.
foreign import ccall "qsieve.h qsieve_process_relation"
  qsieve_process_relation :: Ptr CQs -> IO ()

-- | /qsieve_factor/ /factors/ /n/ 
--
-- Factor \(n\) using the quadratic sieve method. It is required that \(n\)
-- is not a prime and not a perfect power. There is no guarantee that the
-- factors found will be prime, or distinct.
foreign import ccall "qsieve.h qsieve_factor"
  qsieve_factor :: Ptr CFmpzFactor -> Ptr CFmpz -> IO ()