content: /*
   * Copyright (C) 2014 - Martin Berger
   * Copyright (C) 2017 - Christopher Syben
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
  */
  package edu.stanford.rsl.conrad.data.generic.complex;
  
  import ij.ImageJ;
  import edu.stanford.rsl.conrad.data.generic.datatypes.Complex;
  import edu.stanford.rsl.conrad.data.numeric.Grid2D;
  import edu.stanford.rsl.tutorial.phantoms.SheppLogan;
  
  
  
  public class ComplexGrid2D extends ComplexGrid {
  
  	/** Linear float array stores the complex numbers in an alternating manner [Re1, Imag1, Re2, Imag2, ..., ReN,ImagN]. */
  	private float[] buffer;
  	
  	private int offset;
  
  	private ComplexGrid1D[] subGrids = null;
  	
  	
  	public ComplexGrid2D(int width, int height) {
  		this(new float[width*height*2],0,width,height);
  	}
  	
  	public ComplexGrid2D(float[] buffer, int offset, int width, int height){
  		this.buffer = buffer;
  		this.offset = offset;
  		this.spacing = new double[] {1,1};
  		this.origin = new double[2];
  		this.size = new int[]{width,height}; 
  		initialize(width, height);
  		notifyAfterWrite();
  	}
  	
  	public ComplexGrid2D(ComplexGrid2D input){
  		this.offset = 0;
  		this.size = new int[] {input.getSize()[0],input.getSize()[1]}; 
  		this.spacing = new double[] {input.spacing[0],input.spacing[1]};
  		this.origin = new double[] {input.origin[0],input.origin[1]};
  		buffer = new float[input.getNumberOfElements()*2];
  		System.arraycopy(input.getBuffer(), input.getOffset()*2, this.buffer, 0, this.buffer.length);
  		initialize(input.getSize()[0],input.getSize()[1]);
  		if(input.openCLactive)
  			activateCL();
  	}
  	
  	public ComplexGrid2D(Grid2D input){
  		this.offset = 0;
  		this.size = new int[] {input.getSize()[0],input.getSize()[1]}; 
  		this.spacing = new double[] {input.getSpacing()[0],input.getSpacing()[1]};
  		this.origin = new double[] {input.getOrigin()[0],input.getOrigin()[1]};
  		buffer = new float[input.getNumberOfElements()*2];
  		for (int i = 0; i < input.getBuffer().length; i++) {
  			this.buffer[i*2]=input.getBuffer()[i];
  		}
  		initialize(input.getSize()[0],input.getSize()[1]);
  	}
  	
  	protected void initialize(int width, int height)
  	{
  		for (int i = 0; i < 2; ++i) {
  			assert size[i] > 0 : "Size values have to be greater than zero!";
  		}
  		subGrids = new ComplexGrid1D[size[1]];
  		for (int i = 0; i < height; ++i) {
  			subGrids[i] = new ComplexGrid1D(this.buffer, i*size[0]+offset, size[0]);
  		}
  	}
  	
  	public void fftshift()
  	{	
  		ComplexGrid1D[] unshifted = subGrids.clone();
  		for(int i = 0; i < subGrids.length;i++ ){
  			unshifted[i].fftshift();
  		}
  		int height = this.getSize()[1];
  		int pH = (int)Math.ceil(height/2.0);
  		
  		for(int j = pH; j < this.getSize()[1];j++)
  		{
  		 	this.setSubGrid(j-pH,unshifted[j]);
  		}
  		for(int j = 0; j < pH;j++)
  		{
  		 	this.setSubGrid(j+height-pH,unshifted[j]);
  		}
  	}
  	
  	public float[] getBuffer(){
  		notifyBeforeRead();
  		return buffer;
  	}
  	
  	
  	public void multiplyAtIndex(int i, int j, float val) {
  		multiplyAtIndex(i, j, val, 0);
  	}
  	
  	public void multiplyAtIndex(int i, int j, float real, float imag) {
  		Complex in = new Complex(real,imag);
  		multiplyAtIndex(i,j,in);
  	}
  	
  	public void multiplyAtIndex(int i, int j, Complex in) {
  		setAtIndex(i, j, getAtIndex(i,j).mul(in));
  	}
  	
  	
  	public void divideAtIndex(int i, int j, float val) {
  		divideAtIndex(i, j, val, 0);
  	}
  	
  	public void divideAtIndex(int i, int j, float real, float imag) {
  		Complex in = new Complex(real,imag);
  		divideAtIndex(i, j, in);
  	}
  	
  	public void divideAtIndex(int i, int j, Complex in) {
  		setAtIndex(i, j, getAtIndex(i, j).div(in));
  	}
  	
  	
  	public void addAtIndex(int i, int j, float val) {
  		addAtIndex(i, j, val, 0);
  	}
  	
  	public void addAtIndex(int i, int j, float real, float imag) {
  		Complex in = new Complex(real,imag);
  		addAtIndex(i, j, in);
  	}
  	
  	public void addAtIndex(int i, int j, Complex in) {
  		setAtIndex(i, j, getAtIndex(i, j).add(in));
  	}
  	
  	
  	public void subtractAtIndex(int i, int j, float val) {
  		subtractAtIndex(i, j, val, 0);
  	}
  	
  	public void subtractAtIndex(int i, int j, float real, float imag) {
  		Complex in = new Complex(real,imag);
  		subtractAtIndex(i, j,in);
  	}
  	
  	public void subtractAtIndex(int i, int j, Complex in) {
  		setAtIndex(i, j, getAtIndex(i, j).sub(in));
  	}
  	
  	
  	
  	public void setAtIndex(int i, int j, float val){
  		setAtIndex(i, j,val,0);
  	}
  	
  	public void setAtIndex(int i, int j, float real, float imag){
  		Complex in = new Complex(real,imag);
  		setAtIndex(i, j, in);
  	}
  	
  	public void setAtIndex(int i, int j, Complex val){
  		getSubGrid(j).setAtIndex(i,val);
  		notifyAfterWrite();
  	}
  	
  	public Complex getAtIndex(int i, int j){
  		notifyBeforeRead();
  		return getSubGrid(j).getAtIndex(i);
  	}
  	
  	public ComplexGrid1D getSubGrid(int j){
  		notifyBeforeRead();
  		return this.subGrids[j];
  	}
  	
  	public void setSubGrid(int j, ComplexGrid1D collumn){
  		subGrids[j]=collumn;
  		notifyAfterWrite();
  	}
  	
  	@Override
  	public Complex getValue(int... idx) {
  		return getAtIndex(idx[0],idx[1]);
  	}
  
  
  	@Override
  	public ComplexGrid clone() {
  		notifyBeforeRead();
  		return new ComplexGrid2D(this);
  	}
  
  	@Override
  	public String toString() {
  		String result = new String();
  		result += "[";
  		for (int j = 0; j < size[1]; ++j) {
  			if (j != 0) result += "; ";
  			result += getSubGrid(j).toString();
  		}
  		result += "]";
  		return result;
  	}
  
  	@Override
  	public Grid2D getRealGrid() {
  		return getRealSubGrid(0, 0, size[0], size[1]);
  	}
  
  	@Override
  	public Grid2D getImagGrid() {
  		return getImagSubGrid(0, 0, size[0], size[1]);
  	}
  
  	@Override
  	public Grid2D getMagGrid() {
  		return getMagSubGrid(0, 0, size[0], size[1]);
  	}
  
  	@Override
  	public Grid2D getPhaseGrid() {
  		return getPhaseSubGrid(0, 0, size[0], size[1]);
  	}
  	
  	public Grid2D getRealSubGrid(final int startX, final int startY, final int lengthX, final int lengthY){
  		Grid2D subgrid = new Grid2D(lengthX,lengthY);
  		for (int i=0; i < lengthX; ++i){
  			for (int j=0; j < lengthY; ++j){
  			subgrid.setAtIndex(i,j, (float)this.getAtIndex(startX+i,startY+j).getReal());
  			}
  		}
  		return subgrid;
  	}
  	
  	public Grid2D getImagSubGrid(final int startX, final int startY, final int lengthX, final int lengthY){
  		Grid2D subgrid = new Grid2D(lengthX,lengthY);
  		for (int i=0; i < lengthX; ++i){
  			for (int j=0; j < lengthY; ++j){
  			subgrid.setAtIndex(i,j, (float)this.getAtIndex(startX+i,startY+j).getImag());
  			}
  		}
  		return subgrid;
  	}
  	
  	public Grid2D getMagSubGrid(final int startX, final int startY, final int lengthX, final int lengthY){
  		Grid2D subgrid = new Grid2D(lengthX,lengthY);
  		for (int i=0; i < lengthX; ++i){
  			for (int j=0; j < lengthY; ++j){
  			subgrid.setAtIndex(i,j, (float)this.getAtIndex(startX+i,startY+j).getMagn());
  			}
  		}
  		return subgrid;
  	}
  	
  	public Grid2D getPhaseSubGrid(final int startX, final int startY, final int lengthX, final int lengthY){
  		Grid2D subgrid = new Grid2D(lengthX,lengthY);
  		for (int i=0; i < lengthX; ++i){
  			for (int j=0; j < lengthY; ++j){
  			subgrid.setAtIndex(i,j, (float)this.getAtIndex(startX+i,startY+j).getAngle());
  			}
  		}
  		return subgrid;
  	}
  	
  	@Override
  	public void setValue(Complex val, int... idx) {
  		setAtIndex(idx[0], idx[1], val);
  	}
  	
  	@Override
  	public float[] getAslinearMemory() {
  		notifyBeforeRead();
  		return buffer;
  	}
  
  	@Override
  	public void setAslinearMemory(float[] buffer) {
  		this.buffer = buffer;
  		notifyAfterWrite();
  	}
  
  	@Override
  	public float getRealAtIndex(int... idx) {
  		return getSubGrid(idx[1]).getRealAtIndex(idx[0]);
  	}
  
  	@Override
  	public void setRealAtIndex(float val, int... idx) {
  		getSubGrid(idx[1]).setRealAtIndex(val,idx[0]);
  		notifyAfterWrite();
  	}
  
  	@Override
  	public float getImagAtIndex(int... idx) {
  		return getSubGrid(idx[1]).getImagAtIndex(idx[0]);
  	}
  
  	@Override
  	public void setImagAtIndex(float val, int... idx) {
  		getSubGrid(idx[1]).setImagAtIndex(val,idx[0]);
  		notifyAfterWrite();
  	}
  	
  	public int getOffset() {
  		return offset;
  	}
  	
  	public static void main(String[] args) {
  		new ImageJ();
  		ComplexGrid2D bla = new ComplexGrid2D(new SheppLogan(64));
  		ComplexGrid2D gc = new ComplexGrid2D(bla);
  		ComplexGrid2D gc1 = new ComplexGrid2D(gc);
  		bla.show();
  		Fourier ft = new Fourier();
  		ft.fft(gc);
  		ft.ifft(gc);
  		ft.fft2(gc1);
  		ft.ifft2(gc1);
  		
  		ComplexGridOperator gop = new ComplexGridOperator();
  		ComplexGrid diff = bla.clone();
  		gop.subtractBy(diff, gc);
  		
  		ComplexGrid diff1 = bla.clone();
  		gop.subtractBy(diff1, gc1);
  		
  		diff.show();
  		diff1.show();
  		gc.show();
  		gc1.show();
  
  	}
  }
  