/* Copyright (c) 1997-2010
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Darmstadt, Germany)
   http://www.polymake.de

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   This program 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.
--------------------------------------------------------------------------------
   $Project: polymake $$Id: inlines_int.h 9991 2010-12-12 23:36:28Z gawrilow $
*/

#ifndef POLYMAKE_INTERNAL_INLINES_INT_H
#define POLYMAKE_INTERNAL_INLINES_INT_H

#include <cstdlib>

namespace pm {

inline long gcd(long a, long b)
{
   if (a<0) a=-a;
   if (b<0) b=-b;
   if (a==0) return b;
   if (b==0) return a;
   if (a==1 || b==1) return 1;

   int k=0;
   while (!((a|b)&1)) {
      a>>=1;  b>>=1;  k++;
   }

   if (a&1)
      while (!(b&1)) b>>=1;
   else
      while (!(a&1)) a>>=1;

   long d=a-b;
   while (d) {
      while (!(d&1)) d>>=1;
      if (d>0)
         a=d;
      else
         b=-d;
      d=a-b;
   }

   return a<<k;
}

inline long lcm(long a, long b)
{
   return (a/gcd(a,b))*b;
}

/// g=gcd(a,b) && g==p*a+q*b
inline void gcd_ext(long a, long b, long& g, long& p, long& q)
{
   long A11= a<0 ? (a=-a, -1L) : 1L,  A12=0,
      A22= b<0 ? (b=-b, -1L) : 1L,  A21=0;
   long k;

   if (a<b) goto STEP2;
   for (;;) {
      k=a/b;
      A11-=k*A12;  A21-=k*A22;
      if (!(a-=k*b)) {
         g=b;  p=A12;  q=A22;
         break;
      }
   STEP2:
      k=b/a;
      A12-=k*A11;  A22-=k*A21;
      if (!(b-=k*a)) {
         g=a;  p=A11;  q=A21;
         break;
      }
   }
}

inline long div_exact(long x, long y) { return x/y; }

inline int log2_round(unsigned long x, int round)
{
   if (x<=1UL) return 0;

   int step=(sizeof(unsigned long)*8)/2, log2=step;
   unsigned long pow2=1UL<<step;

   while (x != pow2) {
      if (x < pow2) {
         if (!(step >>= 1)) return log2-1+round;
         pow2 >>= step;
         log2 -= step;
      } else {
         if (!(step >>= 1)) return log2+round;
         pow2 <<= step;
         log2 += step;
      }
   }
   return log2;
}

inline int log2_floor(unsigned long x) { return log2_round(x,0); }
inline int log2_ceil(unsigned long x) { return log2_round(x,1); }

inline bool abs_equal(long x, long y) { return x==y || -x==y; }
inline bool abs_equal(long x, int y) { return x==y || -x==y; }
inline bool abs_equal(int x, long y) { return x==y || -x==y; }
inline bool abs_equal(double x, double y) { return x==y || x==-y; }
inline bool abs_equal(double x, long y) { return x==y || x==-y; }
inline bool abs_equal(long x, double y) { return x==y || -x==y; }
inline bool abs_equal(double x, int y) { return x==y || x==-y; }
inline bool abs_equal(int x, double y) { return x==y || -x==y; }

template <typename T> T& inv_sign(T&);
inline int& inv_sign(int& x) { x=-x; return x; }
inline long& inv_sign(long& x) { x=-x; return x; }
inline float& inv_sign(float& x) { x=-x; return x; }
inline double& inv_sign(double& x) { x=-x; return x; }

}

namespace polymake {
   using pm::gcd;
   using pm::gcd_ext;
   using pm::lcm;
   using pm::div_exact;
   using pm::log2_floor;
   using pm::log2_ceil;
   using pm::abs_equal;
   using pm::inv_sign;
}

#endif // POLYMAKE_INTERNAL_INLINES_INT_H

// Local Variables:
// mode:C++
// c-basic-offset:3
// indent-tabs-mode:nil
// End:
