packages feed

sigma-ij-0.2: src/cbits/c_det.c

#include "c_det.h"

typedef __int128  int128_t;

// -----------------------------------------------------------------------------

// we assume a and b are already mod p
inline int64_t sub_modp( int64_t p , int64_t a , int64_t b )
{
  if (b <= a) 
    { return (a - b); }
  else
    { return (a + p - b); }
}

inline int64_t mul_modp( int64_t p0 , int64_t a0 , int64_t b0 )
{
  int128_t p = p0;
  int128_t a = a0;
  int128_t b = b0;
  int128_t c = a*b;
  c = c % p;
  return ((int64_t)c);
}

// -----------------------------------------------------------------------------

int64_t euclid( int64_t p , int64_t x1_ , int64_t x2_ , int64_t u_ , int64_t v_ )
{
  int64_t halfp1 = (p + 1) >> 1;

  int64_t x1 = x1_; 
  int64_t x2 = x2_;
  int64_t u  = u_;
  int64_t v  = v_;

  while( (u!=1) && (v!=1) )
  {
    while (!(u & 1))
    { // u even
      u = u >> 1;
      if (x1 & 1) { /* x1 odd */  x1 = (x1 >> 1) + halfp1; } else { x1 = x1 >> 1; }
    }
      
    while (!(v & 1))
    { // v even
      v = v >> 1;
      if (x2 & 1) { /* x2 odd */  x2 = (x2 >> 1) + halfp1; } else { x2 = x2 >> 1; }
    }

    if (u >= v)
    {
      u  = u - v;
      if ( x1 >= x2 ) { x1 = (x1 - x2); } else { x1 = (x1 + p - x2); } 
    }
    else 
    {
      v  = v - u;
      if ( x2 >= x1)  { x2 = (x2 - x1); } else { x2 = (x2 + p - x1); } 
    }

  }

  if (u==1) { return x1; }
  if (v==1) { return x2; }
  return 0;                       // shouldn't happen
}

// -----------------------------------------------------------------------------

inline int64_t div_modp( int64_t p , int64_t a , int64_t b )
{
  // return mul_modp( p , a , inv_modp( p , b ) );
  return euclid( p , a , 0 , b , p );}

// mod p inverse using the binary Euclidean algorithm 
int64_t inv_modp( int64_t p , int64_t a )
{
  return euclid( p , 1 , 0 , a , p );
}

// -----------------------------------------------------------------------------

// determinant mod p (64 bit), using Gauss elimination
int64_t det_modp(int64_t p, int n, int64_t *mat)
{
  // safety first
  for (int i=0;i<n*n;i++) { if ((mat[i] >= p) || (mat[i]<0)) { mat[i] = mat[i] % p; } }

  int negative = 0;

  for (int i=0;i<n-1;i++)
  {
    int64_t *row = mat + i*n;
   
    // find pivot element
    int j; 
    for (j=i;j<n;j++) { if (row[j] != 0) break; }
    if ( (j >= n) || (row[j] == 0) ) { return 0; }
     
    if (j > i)
    { // exchange columns
      int64_t *q = row;      
      for (int k=i;k<n;k++)
      { 
        int64_t x;
        x    = q[i];
        q[i] = q[j];
        q[j] = x;
        q += n;     
      }
      negative = negative ^ 1;     // track the sign changes
    }

    // zero out the i-th column
    int64_t *q = row + n;
    for (int k=i+1;k<n;k++)
    { 
      int64_t m = div_modp( p , q[i] , row[i] );
      q[i] = 0;  
      for (int l=i+1;l<n;l++) 
      { 
        q[l] = sub_modp( p , q[l] , mul_modp( p , m , row[l] ) );
      }
      q += n;
    }
  }

  int64_t det = mat[0];
  for (int i=1;i<n;i++) { det = mul_modp( p , det , mat[i*(n+1)] ); }

  if ((negative) && (det!=0)) { return (p-det); } else { return det; }
}