#ifndef Foundation_matrix_type_INCLUDED
#define Foundation_matrix_type_INCLUDED

namespace Tn {

namespace math {

namespace matrix_type {

/*
  Distinguishing matrix types allows for more efficient multiplication
  and inversion because in practice most matrices tend to have many
  elements that are zero or one.

  The matrix types are based on shape more than semantic
  function. Originally the matrix forms were overly eager to
  distinguish between an element containing one and an element
  containing an arbitrary value. The distinction led many matrix
  multiply combinations to result in overly general matrices
  (i.e. e.g. NO_W, ARBITRARY) when often all that really happened is
  that one element went from being a one to being arbitrary. One
  solution is to add more matrix forms, but the number of multiply
  routines goes up with the square of the number of forms.
*/

enum Type {

    IDENTITY,
    SCALE,
    XLATE,
    SCALE_XLATE,
    FRUSTUM,
    ROT_X_90,
    ROT_Y_90,
    ROT_Z_90,
    ROT_X,
    ROT_Y,
    ROT_Z,
    ROT_ARB,
    NO_W,
    ARBITRARY,
};

int const count = ARBITRARY+1;

enum Value {
    ZERO, POS_ONE, NEG_ONE, ARB
};

extern Value const forms[count][4][4];
extern char const * const names[count];

template <typename T>
inline Value findValue(T v) {
    if (T(0)==v) return ZERO;
    if (T(1)==v) return POS_ONE;
    if (T(-1)==v) return NEG_ONE;
    return ARB;
}


// The lower the miss count, the more accurately the Type describes
// the provided matrix, unless the Type doesn't fit the matrix at all,
// in which case -1 is returned.

template <typename T>
inline int countMisses(Type t, T const m[4][4]) {

    int misses = 0;
    for (unsigned int j=0; j<4; ++j) {
        for (unsigned int i=0; i<4; ++i) {

            Value allowed_to_be = forms[t][j][i];
            Value is = findValue(m[j][i]);

            if (is != allowed_to_be) {
                if (allowed_to_be==ARB) {
                    ++misses;
                } else {
                    return -1;
                }
            }
        }
    }
    return misses;
}


template <typename T>
Type findType(T const m[4][4]) {

    Type type = ARBITRARY;
    int least_misses = 16;

    for (int i = 0; i<count; ++i) {
        int misses = countMisses(Type(i), m);
        if ((misses!=-1) && (misses<least_misses)) {
            least_misses = misses;
            type = Type(i);
        }
    }

    return type;
}

} // namespace matrix_type

} // namespace math

} // namespace Tn


#endif

