//#####################################################################
// Copyright 2012.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYREGHT.txt.
//#####################################################################
// Class OP_COUNT
//#####################################################################
#ifndef __OP_COUNT__
#define __OP_COUNT__

#include <Core/Arrays/ARRAY.h>
#include <Core/Log/LOG.h>
#include <Core/Utilities/TYPE_UTILITIES.h>
#include <cmath>
#include <string>

namespace PhysBAM{
using std::abs;
int count_add=0;
int count_mul=0;
int count_div=0;
int count_cmp=0;
int count_slow=0;

inline void Print_Op_Counts()
{
    LOG::printf("add %i   mul %i   div %i   cmp %i   slow %i\n",
        count_add,count_mul,count_div,count_cmp,count_slow);
    count_add=count_mul=count_div=count_cmp=count_slow=0;
}

template<class T>
struct OP_COUNT
{
    T t;

    OP_COUNT(){t=0;};
    OP_COUNT(T x):t(x){}
};

template<class T> OP_COUNT<T> operator+ (OP_COUNT<T> a) {return a;}
template<class T> OP_COUNT<T> operator- (OP_COUNT<T> a) {count_add++;return {-a.t};}

template<class T> OP_COUNT<T> operator+ (OP_COUNT<T> a,OP_COUNT<T> b) {count_add++;return {a.t+b.t};}
template<class T> OP_COUNT<T> operator- (OP_COUNT<T> a,OP_COUNT<T> b) {count_add++;return {a.t-b.t};}
template<class T> OP_COUNT<T> operator* (OP_COUNT<T> a,OP_COUNT<T> b) {count_mul++;return {a.t*b.t};}
template<class T> OP_COUNT<T> operator/ (OP_COUNT<T> a,OP_COUNT<T> b) {count_div++;return {a.t/b.t};}

template<class T> OP_COUNT<T> operator+ (T a,OP_COUNT<T> b) {count_add++;return {a+b.t};}
template<class T> OP_COUNT<T> operator- (T a,OP_COUNT<T> b) {count_add++;return {a-b.t};}
template<class T> OP_COUNT<T> operator* (T a,OP_COUNT<T> b) {count_mul++;return {a*b.t};}
template<class T> OP_COUNT<T> operator/ (T a,OP_COUNT<T> b) {count_div++;return {a/b.t};}

template<class T> OP_COUNT<T> operator+ (OP_COUNT<T> a,T b) {count_add++;return {a.t+b};}
template<class T> OP_COUNT<T> operator- (OP_COUNT<T> a,T b) {count_add++;return {a.t-b};}
template<class T> OP_COUNT<T> operator* (OP_COUNT<T> a,T b) {count_mul++;return {a.t*b};}
template<class T> OP_COUNT<T> operator/ (OP_COUNT<T> a,T b) {count_div++;return {a.t/b};}

template<class T> OP_COUNT<T> operator+ (int a,OP_COUNT<T> b) {count_add++;return {a+b.t};}
template<class T> OP_COUNT<T> operator- (int a,OP_COUNT<T> b) {count_add++;return {a-b.t};}
template<class T> OP_COUNT<T> operator* (int a,OP_COUNT<T> b) {count_mul++;return {a*b.t};}
template<class T> OP_COUNT<T> operator/ (int a,OP_COUNT<T> b) {count_div++;return {a/b.t};}

template<class T> OP_COUNT<T> operator+ (OP_COUNT<T> a,int b) {count_add++;return {a.t+b};}
template<class T> OP_COUNT<T> operator- (OP_COUNT<T> a,int b) {count_add++;return {a.t-b};}
template<class T> OP_COUNT<T> operator* (OP_COUNT<T> a,int b) {count_mul++;return {a.t*b};}
template<class T> OP_COUNT<T> operator/ (OP_COUNT<T> a,int b) {count_div++;return {a.t/b};}

template<class T> OP_COUNT<T>& operator+= (OP_COUNT<T>& a,OP_COUNT<T> b) {count_add++;a.t+=b.t;return a;}
template<class T> OP_COUNT<T>& operator-= (OP_COUNT<T>& a,OP_COUNT<T> b) {count_add++;a.t-=b.t;return a;}
template<class T> OP_COUNT<T>& operator*= (OP_COUNT<T>& a,OP_COUNT<T> b) {count_mul++;a.t*=b.t;return a;}
template<class T> OP_COUNT<T>& operator/= (OP_COUNT<T>& a,OP_COUNT<T> b) {count_div++;a.t/=b.t;return a;}

template<class T> OP_COUNT<bool> operator< (OP_COUNT<T> a,OP_COUNT<T> b) {count_cmp++;return {a.t<b.t};}
template<class T> OP_COUNT<bool> operator> (OP_COUNT<T> a,OP_COUNT<T> b) {count_cmp++;return {a.t>b.t};}
template<class T> OP_COUNT<bool> operator<= (OP_COUNT<T> a,OP_COUNT<T> b) {count_cmp++;return {a.t<=b.t};}
template<class T> OP_COUNT<bool> operator>= (OP_COUNT<T> a,OP_COUNT<T> b) {count_cmp++;return {a.t>=b.t};}
template<class T> OP_COUNT<bool> operator== (OP_COUNT<T> a,OP_COUNT<T> b) {count_cmp++;return {a.t==b.t};}
template<class T> OP_COUNT<bool> operator!= (OP_COUNT<T> a,OP_COUNT<T> b) {count_cmp++;return {a.t!=b.t};}

template<class T> std::ostream&  operator<< (std::ostream& o, OP_COUNT<T> a) {return o<<a.t;}
template<class T> std::istream&  operator>> (std::ostream& i, OP_COUNT<T> a) {return i>>a.t;}


using std::abs;
using std::acos;
using std::asin;
using std::atan2;
using std::atan;
using std::ceil;
using std::cos;
using std::exp;
using std::floor;
using std::log;
using std::pow;
using std::sin;
using std::sqrt;
using std::tan;

template<class T> OP_COUNT<T> atan(OP_COUNT<T> a) {count_slow++;return {atan(a.t)};}
template<class T> OP_COUNT<T> acos(OP_COUNT<T> a) {count_slow++;return {acos(a.t)};}
template<class T> OP_COUNT<T> asin(OP_COUNT<T> a) {count_slow++;return {asin(a.t)};}
template<class T> OP_COUNT<T> tan(OP_COUNT<T> a) {count_slow++;return {tan(a.t)};}
template<class T> OP_COUNT<T> cos(OP_COUNT<T> a) {count_slow++;return {cos(a.t)};}
template<class T> OP_COUNT<T> sin(OP_COUNT<T> a) {count_slow++;return {sin(a.t)};}
template<class T> OP_COUNT<T> abs(OP_COUNT<T> a) {count_cmp++;return {abs(a.t)};}
template<class T> OP_COUNT<T> floor(OP_COUNT<T> a) {count_slow++;return {floor(a.t)};}
template<class T> OP_COUNT<T> ceil(OP_COUNT<T> a) {count_slow++;return {ceil(a.t)};}
template<class T> OP_COUNT<T> exp(OP_COUNT<T> a) {count_slow++;return {exp(a.t)};}
template<class T> OP_COUNT<T> log(OP_COUNT<T> a) {count_slow++;return {log(a.t)};}
template<class T> OP_COUNT<T> sqrt(OP_COUNT<T> a) {count_slow++;return {sqrt(a.t)};}
template<class T> OP_COUNT<T> pow(OP_COUNT<T> a) {count_slow++;return {pow(a.t)};}
template<class T> OP_COUNT<T> atan2(OP_COUNT<T> a,OP_COUNT<T> b) {count_slow++;return {atan2(a.t,b.t)};}

template<class T> OP_COUNT<T> Inverse(OP_COUNT<T> a){count_div++;return {Inverse(a.t)};}
template<class T> struct IS_SCALAR<OP_COUNT<T>>{static const bool value=true;};
template<class T> struct SCALAR_POLICY<OP_COUNT<T> > {typedef OP_COUNT<T> TYPE;};

}
#endif
