/*
 * logical comparisong, aka "where" and friends
 * all these functions work on C-indexed arrays
 */

#include "fits.h"

#define LOGICACTION >
#define ARRAYS
bool Fits::dpGT(Fits &a, Fits &b) {
#include "logic_helper.cpp"
    return _success;
}

#define LOGICACTION >=
#define ARRAYS
bool Fits::dpGE(Fits &a, Fits &b) {
#include "logic_helper.cpp"
    return _success;
}

#define LOGICACTION <
#define ARRAYS
bool Fits::dpLT(Fits &a, Fits &b) {
#include "logic_helper.cpp"
    return _success;
}

#define LOGICACTION <=
#define ARRAYS
bool Fits::dpLE(Fits &a, Fits &b) {
#include "logic_helper.cpp"
    return _success;
}

#define LOGICACTION !=
#define ARRAYS
bool Fits::dpNE(Fits &a, Fits &b) {
#include "logic_helper.cpp"
    return _success;
}

#define LOGICACTION ==
#define ARRAYS
bool Fits::dpEQ(Fits &a, Fits &b) {
#include "logic_helper.cpp"
    return _success;
}

#define LOGICACTION >
bool Fits::dpGT(Fits &a, double b) {
#include "logic_helper.cpp"
    return _success;
}

#define LOGICACTION >=
bool Fits::dpGE(Fits &a, double b) {
#include "logic_helper.cpp"
    return _success;
}

#define LOGICACTION <
bool Fits::dpLT(Fits &a, double b) {
#include "logic_helper.cpp"
    return _success;
}

#define LOGICACTION <=
bool Fits::dpLE(Fits &a, double b) {
#include "logic_helper.cpp"
    return _success;
}

#define LOGICACTION !=
bool Fits::dpNE(Fits &a, double b) {
#include "logic_helper.cpp"
    return _success;
}

#define LOGICACTION ==
bool Fits::dpEQ(Fits &a, double b) {
#include "logic_helper.cpp"
    return _success;
}

#define LOGICACTION &&
#define ARRAYS
bool Fits::dpAND(Fits &a, Fits &b) {
#include "logic_helper.cpp"
    return _success;
}

#define LOGICACTION ||
#define ARRAYS
bool Fits::dpOR(Fits &a, Fits &b) {
#include "logic_helper.cpp"
    return _success;
}

bool Fits::dpNOT() {
    if (membits != I1) return false;
    for (dpint64 i = 0; i < Nelements(); i++) {
        if (i1data[i] == 0) i1data[i] = 1;
        else i1data[i] = 0;
    }
    return true;
}

dpint64 Fits::where(Fits &a) {
    if (!create(a.Nelements(), 1, I4)) return 0;

    dpint64 count = 0;

    for (dpint64 i = 0; i < a.Nelements(); i++) {
        if (a[i]) {
            i4data[count] = i;
            count++;
        }
    }
    if (!resize(count)) return 0;

    return count;
}

// where function, returns the number of elements that matched

unsigned long Fits::where(Fits &source, const char *comparison, double argument) {
	unsigned long n, m = 0;
	unsigned char c = 10; // the comparison operator
	
	if (strlen(comparison) == 2) {
		switch (comparison[0]) {
			case '=': c = 0; break;
			case '!': c = 1; break;
			case '<': c = 4; break;
			case '>': c = 5; break;
			default: break;
		}
	} else {
		switch (comparison[0]) {
			case '<': c = 2; break;
			case '>': c = 3; break;
			default: break;
		}
	}
	if (c == 10) return 0;

	if (!create(source.Nelements(), 1, I4)) return 0;

	switch (c) {
		case 0: // equals; comparison == "=="
			for (n = 0; n < Nelements(); n++) {
				if (source[n] == argument) {
                    i4data[m] = n;
					m++;
				}
			}
			break;
		case 1: // not equals; comparison == "!="
			for (n = 0; n < Nelements(); n++) {
				if (source[n] != argument) {
                    i4data[m] = n;
					m++;
				}
			}
			break;
		case 2: // less than; comparison == "<"
			for (n = 0; n < Nelements(); n++) {
				if (source[n] < argument) {
                    i4data[m] = n;
					m++;
				}
			}
			break;
		case 3: // greater than; comparison == ">"
			for (n = 0; n < Nelements(); n++) {
				if (source[n] > argument) {
                    i4data[m] = n;
					m++;
				}
			}
			break;
		case 4: // less than or equal; comparison == "<="
			for (n = 0; n < Nelements(); n++) {
				if (source[n] <= argument) {
                    i4data[m] = n;
					m++;
				}
			}
			break;
		case 5: // greater than or equal; comparison == ">="
			for (n = 0; n < Nelements(); n++) {
				if (source[n] >= argument) {
                    i4data[m] = n;
					m++;
				}
			}
			break;
		default:
			break;
	}
	if (m > 0) recreate(m, 1);
	else recreate(1, 1);
	
	return m;
}

// extract linear indices; the indices are supposed to be in I4 type
void Fits::extractLinearRange(Fits &source, Fits &indices) {
	if (!create(indices.Nelements(), 1, R8)) return;
	
	unsigned long n;
	for (n = 0; n < Nelements(); n++) {
		r8data[n] = source[indices.i4data[n]];
	}
}

unsigned long Fits::maxLinearIndex(double *value) {
	unsigned long j, rv;
	rv = 0;
	*value = ValueAt(0);
	
	for (j = 1; j < Nelements(); j++) {
		if (ValueAt(j) > (*value)) {
			rv = j;
			*value = ValueAt(j);
		}
	}
	return rv;
}

unsigned long Fits::minLinearIndex(double *value) {
	unsigned long j, rv;
	rv = 0;
	*value = ValueAt(0);
	
	for (j = 1; j < Nelements(); j++) {
		if (ValueAt(j) < (*value)) {
			rv = j;
			*value = ValueAt(j);
		}
	}
	return rv;
}

void Fits::extractLinearIndex(Fits &result, int x1, int x2) {
	unsigned long n;
	
	result.create(x2 - x1 + 1, 1, membits);
	switch (membits) {
		case I1:
			for (n = 0; n < result.Nelements(); n++)
				result.i1data[n] = i1data[x1 + n];
			break;
		case I2:
			for (n = 0; n < result.Nelements(); n++)
				result.i2data[n] = i2data[x1 + n];
			break;
		case I4:
			for (n = 0; n < result.Nelements(); n++)
				result.i4data[n] = i4data[x1 + n];
			break;
        case I8:
            for (n = 0; n < result.Nelements(); n++)
                result.i8data[n] = i8data[x1 + n];
            break;
        case R4:
			for (n = 0; n < result.Nelements(); n++)
				result.r4data[n] = r4data[x1 + n];
			break;
		case R8:
			for (n = 0; n < result.Nelements(); n++)
				result.r8data[n] = r8data[x1 + n];
			break;
		default:
			break;
	}
}

bool Fits::whereAND(const Fits &arg) {
	if ((membits != I4) || (arg.membits != I4)) return FALSE;
	
	unsigned long i, j, count = 0;
	Fits rv;
	
	rv.create(Nelements(), 1, I4);
	for (i = 0; i < Nelements(); i++) {
		for (j = 0; j < arg.Nelements(); j++) {
			if (i4data[i] == arg.i4data[j]) {
				rv.i4data[count] = i4data[i];
				count++;
			}
		}
	}
	rv.resize(count);
	copy(rv);
	
	return TRUE;
}

bool Fits::whereOR(const Fits &arg) {
	if ((membits != I4) || (arg.membits != I4)) return FALSE;
	
	unsigned long i, j, count = 0;
	bool alreadyThere;
	Fits rv, index;
	
	rv.create(Nelements() + arg.Nelements(), 1, I4);
	for (i = 0; i < Nelements(); i++) rv.i4data[i] = i4data[i];
	count = Nelements();
	
	for (j = 0; j < arg.Nelements(); j++) {
		alreadyThere = FALSE;
		for (i = 0; i < Nelements(); i++) {
			if (i4data[i] == arg.i4data[i]) alreadyThere = TRUE;
		}
		if (!alreadyThere) {
			rv.i4data[count] = arg.i4data[j];
			count++;
		}
	}
	rv.resize(count);
	index.createFitsIndex(rv);
	
	resize(count);
	for (i = 0; i < Nelements(); i++) {
		i4data[i] = rv.i4data[index.i4data[i] - 1];
	}
	
	return TRUE;
}

