/**
 * \file graycode.h
 * \brief Gray code implementation.
 * 
 * The Gray code is a binary numeral system where two successive
 * values differ in only one digit.
 *
 * \author Gregory Bard <bard@fordham.edu>
 * \author Martin Albrecht <M.R.Albrecht@rhul.ac.uk>
 */

#ifndef M4RI_GRAYFLEX_H
#define M4RI_GRAYFLEX_H

#ifdef HAVE_CONFIG_H
 #include "config.h"
#endif

#ifdef _WIN32
  #if defined(DLL_EXPORT) && defined(M4RI_BUILDING_M4RI)
    #define M4RI_DLL_EXPORT __declspec(dllexport)
  #elif defined(M4RI_USE_DLL) && !defined(M4RI_BUILDING_M4RI)
    #define M4RI_DLL_EXPORT __declspec(dllimport)
  #else
    #define M4RI_DLL_EXPORT
  #endif
#else
  #define M4RI_DLL_EXPORT
#endif

/******************************************************************************
*
*                 M4RI: Linear Algebra over GF(2)
*
*    Copyright (C) 2007 Gregory Bard <gregory.bard@ieee.org> 
*    Copyright (C) 2007 Martin Albrecht <malb@informatik.uni-bremen.de> 
*
*  Distributed under the terms of the GNU General Public License (GPL)
*  version 2 or higher.
*
*    This code is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*    General Public License for more details.
*
*  The full text of the GPL is available at:
*
*                  http://www.gnu.org/licenses/
******************************************************************************/
 
/**
 * Maximum allowed value for k.
 */

#define __M4RI_MAXKAY 16

/**
 * \brief Gray codes.
 *
 * A codestruct represents one entry in the code book, i.e. it
 * represents a Gray code of a given length.
 *
 * For example the Gray code table of length \f$2^3\f$ is:
 *
 * \verbatim
-------------------
|  i  | ord | inc |
-------------------
|  0  |  0  |  0  | 
|  1  |  4  |  1  |
|  2  |  6  |  0  |
|  3  |  2  |  2  |
|  4  |  3  |  0  |
|  5  |  7  |  1  |
|  6  |  5  |  0  |
|  7  |  1  |  2  |
-------------------
 * \endverbatim
 */

typedef struct {
  /**
   * array of of Gray code entries
   */
  int *ord;
  /**
   * increment
   */
  int *inc;
} code;

/**
 * Global m4ri_codebook.
 *
 * \warning Not thread safe!
 */ 

M4RI_DLL_EXPORT extern code **m4ri_codebook;

/**
 * Returns the i-th gray code entry for a gray code of length \f$2^l\f$.
 * 
 * \param i The index in the Gray code table.
 * \param l Length of the Gray code.
 *
 * \return i-th Gray code entry.
 */

int m4ri_gray_code(int i, int l);

/**
 * Fills var ord and var inc with Gray code data for a Gray code of
 * length \f$2^l\f$.
 * 
 * \param ord Will hold gray code data, must be preallocated with correct size
 * \param inc Will hold some increment data, must be preallocated with correct size
 * \param l Logarithm of length of Gray code.
 *
 * \note Robert Miller had the idea for a non-recursive
 * implementation.
 */

void m4ri_build_code(int *ord, int *inc, int l);

/**
 * \brief Generates global code book. 
 *
 * This function is called automatically when the shared library is
 * loaded.
 *
 * \warning Not thread safe!
 */

void m4ri_build_all_codes(void);

/**
 * Frees memory from the global code book.
 *
 * This function is called automatically when the shared library is
 * unloaded.
 *
 * \warning Not thread safe!
 */

void m4ri_destroy_all_codes(void);

/**
 * floor(log_2(v))
 */

static inline int log2_floor(int v) {
  static unsigned const int b[] = { 0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000 };
  static unsigned const int S[] = { 1, 2, 4, 8, 16 };
  unsigned int r = 0;
  for (int i = 4; i >= 0; --i)
  {
    if ((v & b[i]))
    {
      v >>= S[i];
      r |= S[i];
    }
  }
  return r;
}


/**
 * \brief Return the optimal var k for the given parameters.
 *
 * If var c != 0 then var k for multiplication is returned, else
 * var k for inversion. The optimal var k here means \f$0.75 log_2(n)\f$
 * where \f$n\f$ is \f$min(a,b)\f$ for inversion and
 * \f$b\f$ for multiplication.
 *
 * \param a Number of rows of (first) matrix
 * \param b Number of columns of (first) matrix
 * \param c Number of columns of second matrix (may be 0)
 *
 * \return k
 */

int m4ri_opt_k(int a,int b,int c);

#endif // M4RI_GRAYFLEX_H
