#include <ImageStack.h>

using namespace ImageStack;

#define ITERS 20

class Test1 : public Operation {
    void help() {
	printf("An operation for testing the effects of various optimizations.\n");
    }

    void parse(vector<string> args) {
	Image im = stack(0);
	for (int c = 0; c < im.channels; c++) {
	    for (int x = 0; x < im.width; x++) {
		for (int y = 0; y < im.height; y++) {
		    for (int t = 0; t < im.frames; t++) {
			float val = im(t, x, y)[c];			
			for (int j = 0; j < ITERS; j++) {
			    val *= val;
			    val += val;
			}
			im(t, x, y)[c] = val;
		    }
		}
	    }
	}
    }
};

class Test2 : public Test1 {
    void parse(vector<string> args) {
        // Cache coherent version
	Image im = stack(0);
	for (int t = 0; t < im.frames; t++) {
	    for (int y = 0; y < im.height; y++) {
		for (int x = 0; x < im.width; x++) {
		    for (int c = 0; c < im.channels; c++) {
			float val = im(t, x, y)[c];			
			for (int j = 0; j < ITERS; j++) {
			    val *= val;
			    val += val;
			}
			im(t, x, y)[c] = val;
		    }
		}
	    }
	}
    }
};

class Test3 : public Test1 {
    void parse(vector<string> args) {
        // Pointer arithmetic version
	Image im = stack(0);
	for (int t = 0; t < im.frames; t++) {
	    for (int y = 0; y < im.height; y++) {
		float *ptr = im(t, 0, y);
		for (int x = 0; x < im.width; x++) {
		    for (int c = 0; c < im.channels; c++) {
			float val = *ptr;
			for (int j = 0; j < ITERS; j++) {
			    val *= val;
			    val += val;
			}
			*ptr++ = val;
		    }
		}
	    }
	}
    }
};

class Test4 : public Test1 {
    void parse(vector<string> args) {
        // Even more pointer arithmetic 
	Image im = stack(0);
	float *ptr = im(0, 0, 0);
	for (int t = 0; t < im.frames; t++) {
	    for (int y = 0; y < im.height; y++) {
		for (int x = 0; x < im.width; x++) {
		    for (int c = 0; c < im.channels; c++) {
			float val = *ptr;
			for (int j = 0; j < ITERS; j++) {
			    val *= val;
			    val += val;
			}
			*ptr++ = val;
		    }
		}
	    }
	}
    }
};

#include <xmmintrin.h>

class Test5 : public Test1 {
    // SSE version 

    void parse(vector<string> args) {
	Image im = stack(0);
	__m128 *ptr = (__m128 *)im(0, 0, 0);

	assert(( ((int)ptr) & 0x0000000f) == 0, 
	       "The image is not 16-bit aligned: %x\n", ptr);


	long long size = im.width*im.height*im.frames*im.channels;
	assert((size % 4) == 0,
	       "Image size must be a multiple of four\n");

	for (int i = 0; i < size/4; i++) {
	    __m128 val = *ptr;
	    for (int j = 0; j < ITERS; j++) {
		val = _mm_mul_ps(val, val);
		val = _mm_add_ps(val, val);
	    }
	    *ptr++ = val;
	}
    }
};


int main(int argc, char **argv) {
    start();

    if (argc == 1 || argv[1][0] != '-') {
	operationMap["-help"]->help();
    }

    operationMap["-test1"] = new Test1();
    operationMap["-test2"] = new Test2();
    operationMap["-test3"] = new Test3();
    operationMap["-test4"] = new Test4();
    operationMap["-test5"] = new Test5();

    vector<string> args;
    for (int i = 1; i < argc; i++) {
	args.push_back(argv[i]);
    }

    try {
        parseCommands(args);
    } catch(Exception &e) {
        printf(e.message);
    }

    fflush(stdout);
    fflush(stderr);

    end();    
}
