/** \file
 * \brief Math Operations
 *
 * See Copyright Notice in im_lib.h
 * $Id: Exp $
 */

#ifndef __IM_MATH_OP_H
#define __IM_MATH_OP_H

#include "im_complex.h"

#define IM_NEARZERO 0.0000001f
#define IM_NEARINF   10000000

/// Crop value to Byte limit
template <class T>
inline T crop_byte(const T& v)
{
  return v <= 0? 0: v <= 255? v: 255;
}

/// Generic Addition with 2 template types
template <class T1, class T2>
inline T1 add_op(const T1& v1, const T2& v2)
{
  return v2 + v1;
}

/// Generic Subtraction with 2 template types
template <class T1, class T2>
inline T1 sub_op(const T1& v1, const T2& v2)
{
  return v2 - v1;
}

/// Generic Multiplication with 2 template types
template <class T1, class T2>
inline T1 mul_op(const T1& v1, const T2& v2)
{
  return v2 * v1;
}

/// Generic Division with 2 template types
template <class T1, class T2>
inline T1 div_op(const T1& v1, const T2& v2)
{
//  if (v2 <= (T2)IM_NEARZERO)
//    return (T1)IM_NEARINF;
  return v1 / v2;
}

/// Generic Difference with 2 template types
template <class T1, class T2>
inline T1 diff_op(const T1& v1, const T2& v2)
{
  if (v1 <= v2)
    return v2 - v1;
  return v1 - v2;
}

/// Generic Minimum with 2 template types
template <class T1, class T2>
inline T1 min_op(const T1& v1, const T2& v2)
{
  if (v1 <= v2)
    return v1;
  return v2;
}

/// Generic Maximum with 2 template types
template <class T1, class T2>
inline T1 max_op(const T1& v1, const T2& v2)
{
  if (v1 <= v2)
    return v2;
  return v1;
}

/// Generic Power with 2 template types
template <class T1, class T2>
inline T1 pow_op(const T1& v1, const T2& v2)
{
  return (T1)pow(v1, v2);
}

/// Generic Abssolute
template <class T>
inline T abs_op(const T& v)
{
  if (v <= 0)
    return -1*v;
  return v;
}

/// Generic Invert
template <class T>
inline T inv_op(const T& v)
{
//  if (v <= IM_NEARZERO)
//    return IM_NEARINF;
  return 1/v;
}

/// Generic Less
template <class T>
inline T less_op(const T& v)
{
  return -1*v;
}

/// Generic Square
template <class T>
inline T sqr_op(const T& v)
{
  return v*v;
}

inline int sqrt(const int& C)
{
  return (int)sqrt(float(C));
}

/// Generic Square Root
template <class T>
inline T sqrt_op(const T& v)
{
  return (T)sqrt(v);
}

inline int exp(const int& v)
{
  return (int)exp((float)v);
}

/// Generic Exponential
template <class T>
inline T exp_op(const T& v)
{
  return (T)exp(v);
}

/// Generic Logarithm
template <class T>
inline T log_op(const T& v)
{
  return (T)log(abs_op(v)+IM_NEARZERO);
}

// Dummy sin
inline imcfloat sin(const imcfloat& v)
{
  return (v);
}

inline int sin(const int& v)
{
  return (int)sin((float)v);
}

/// Generic Sine
template <class T>
inline T sin_op(const T& v)
{
  return (T)sin(v);
}

inline int cos(const int& v)
{
  return (int)cos((float)v);
}

// Dummy cos
inline imcfloat cos(const imcfloat& v)
{
  return (v);
}

/// Generic Cosine
template <class T>
inline T cos_op(const T& v)
{
  return (T)cos(v);
}

/// Sets a bit in an array
inline void imDataBitSet(imbyte* data, int index, int bit)
{
  if (bit)
    data[index / 8] |=  (0x01 << (7 - (index % 8)));
  else
    data[index / 8] &= ~(0x01 << (7 - (index % 8)));
}

/// Gets a bit from an array
inline int imDataBitGet(imbyte* data, int index)
{
  return (data[index / 8] >> (7 - (index % 8))) & 0x01;
}

#endif
