WIPTensor
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © RicardoSantos

//@version=5

// @description A Tensor or 3 dimensional array structure and interface.
// ---
// Note: im just highjacking the name to use it as a 3d array on a project..
// there is no optimization attempts or tensor specific functionality within.
library("WIPTensor")
// reference: 
// https://en.wikipedia.org/wiki/Tensor



//#region	-> Types:

// @type Helper type for 3d structure.
// @field v Vector of the 3rd dimension.
export type Vector
	array<float> v

// @type A Tensor is a three dimensional array were the 3rd dimension accounts for time.
// @field m Matrix that holds the vectors.
export type Tensor
	matrix<Vector> m

//#endregion



//#region 	-> Translation:

// to_string () {

// @function Convert `Tensor` to a string format.
// @param this Tensor data.
// @returns string.
export method to_string (Tensor this) =>
	string _str = ''
	for _x = 0 to this.m.rows() - 1
		for _y = 0 to this.m.columns() - 1
			_str += str.format('\n({0}, {1}): {2}', _x, _y, str.tostring(this.m.get(_x, _y).v))
	_str

// string inTensor = input.text_area('1,2;3,4|5,6;7,8')
// if barstate.islast
// 	_T = from(inTensor)
// 	label.new(bar_index, 0.0, _T.to_string())

// }
// to_vector () {

// @function Convert `Tensor` to a one dimension array.
// @param this Tensor data.
// @returns New array with flattened `Tensor` data.
export method to_vector (Tensor this) =>
	array<float> _v = array.new<float>()
	for _x = 0 to this.m.rows() - 1
		for _y = 0 to this.m.columns() - 1
			_v.concat(this.m.get(_x, _y).v)
	_v

// }

//#endregion



//#region	-> Constructor:

// new () {

// @function Create a new `Tensor` with provided shape.
// @param x             Dimension `X` size.
// @param y             Dimension `Y` size.
// @param z             Dimension `Z` size.
// @param initial_value Value to fill the `Tensor`.
// @returns New `Tensor`.
export new (int x, int y, int z, float initial_value=na) =>
	Tensor _T = Tensor.new(matrix.new<Vector>(x, y))
	for _i = 0 to x-1
		for _j = 0 to y-1
			_T.m.set(_i, _j, Vector.new(array.new<float>(z, initial_value)))
	_T

// @function Create a new `Tensor` with provided shape.
// @param shape         Shape of dimensions size.
// @param initial_value Value to fill the `Tensor`.
// @returns New `Tensor`.
export new (array<int> shape, float initial_value=na) =>
	new(array.get(shape, 0), array.get(shape, 1), array.get(shape, 2), initial_value)

// if barstate.islast
// 	_T = new(2, 2, 2, 1.1)
// 	label.new(bar_index, 0.0, str.tostring(_T.m.get(0,0).v.get(0)))

// }
// from () {

// @function Create a `Tensor` from provided string expression.
// @param expression String expression with `Tensor` data.
// @param sepx       Default='|', Separator of the `X` dimension.
// @param sepy       Default=';', Separator of the `Y` dimension.
// @param sepz       Default=',', Separator of the `Z` dimension.
// @returns New `Tensor`.
export from (string expression, string sepx = '|', string sepy=';', string sepz=',') =>
	array<string> _D1 = str.split(str.replace_all(expression, ' ', ''), sepx)
	int _x = _D1.size()
	if _x > 0
		array<string> _D2 = str.split(_D1.get(0), sepy)
		int _y = _D2.size()
		if _y > 0
			array<string> _D3 = str.split(_D2.get(0), sepz)
			int _z = _D3.size()
			if _z > 0
				_T = new(_x, _y, _z)
				for _xi = 0 to _x - 1
					_D2 := str.split(_D1.get(_xi), sepy)
					for _yi = 0 to _y - 1
						_D3 := str.split(_D2.get(_yi), sepz)
						_current_z = _T.m.get(_xi, _yi)
						for _zi = 0 to _z - 1
							_current_z.v.set(_zi, str.tonumber(_D3.get(_zi)))
				_T

// string inTensor = input.text_area('1,2;3,4|5,6;7,8')
// int ix = input.int(0)
// int iy = input.int(0)
// int iz = input.int(0)
// if barstate.islast
// 	_T = from(inTensor)
// 	label.new(bar_index, 0.0, str.tostring(_T.m.get(ix,iy).v.get(iz)))

// @function Create a `Tensor` from provided array and shape.
// @param vector Data with flattened dimensions.
// @param x Shape of `X` dimension.
// @param y Shape of `Y` dimension.
// @param z Shape of `Z` dimension.
// @returns New `Tensor`.
export from (array<float> vector, int x, int y, int z) =>
	Tensor _T = new(x, y, z)
	int _c = 0
	for _xi = 0 to x - 1
		for _yi = 0 to y - 1
			for _zi = 0 to z - 1
				_T.m.get(_xi, _yi).v.set(_zi, vector.get(_c))
				_c += 1
	_T

// if barstate.islast
// 	_T = from(array.from(1.0,2,3,4,5,6,7,8,9,10,11,12), 2, 3, 2)
// 	label.new(bar_index, 0.0, to_string(_T))

// @function Create a `Tensor` from provided array and shape.
// @param vector Data with flattened dimensions.
// @param shape Shape of the dimensions.
// @returns New `Tensor`.
export from (array<float> vector, array<int> shape) =>
	from(vector, array.get(shape, 0), array.get(shape, 1), array.get(shape, 2))

// }
//#endregion



//#region	-> Properties:

// get () {

// @function Get the value at position.
// @param this `Tensor` data.
// @param x Position in `X` dimension.
// @param y Position in `Y` dimension.
// @param z Position in `Z` dimension.
// @returns Value at position.
export method get (Tensor this, int x, int y, int z) =>
	this.m.get(x, y).v.get(z)

// @function Get the value at position.
// @param this `Tensor` data.
// @param position Coordinate position of value.
// @returns Value at position.
export method get (Tensor this, array<int> position) =>
	this.get(array.get(position, 0), array.get(position, 1), array.get(position, 2))

// }
// set () {

// @function Set the value at position.
// @param this `Tensor` data.
// @param x Position in `X` dimension.
// @param y Position in `Y` dimension.
// @param z Position in `Z` dimension.
// @param value New Value.
export method set (Tensor this, int x, int y, int z, float value) =>
	this.m.get(x, y).v.set(z, value)

// float new_value = input.float(1.123)
// if barstate.islast
// 	_T = from(array.from(1.0,2,3,4,5,6,7,8,9,10,11,12), 2, 3, 2)
// 	_T.set(1, 1, 1, new_value)
// 	label.new(bar_index, 0.0, to_string(_T))

// @function Set the value at position.
// @param this `Tensor` data.
// @param position Coordinate position of value.
// @param value New Value.
export method set (Tensor this, array<int> position, float value) =>
	this.set(array.get(position, 0), array.get(position, 1), array.get(position, 2), value)

// }
// get_vector_xy () {

// @function Get the vector at position.
// @param this `Tensor` data.
// @param x Position in `X` dimension.
// @param y Position in `Y` dimension.
// @returns Vector at position.
export method get_vector_xy (Tensor this, int x, int y) =>
	this.m.get(x, y).v

// @function Set the value at position.
// @param this `Tensor` data.
// @param position Coordinate position of value.
// @returns Vector at position.
export method get_vector_xy (Tensor this, array<int> position) =>
	this.get_vector_xy(array.get(position, 0), array.get(position, 1))

// }
// set_vector_xy () {

// @function Set the vector at position.
// @param this `Tensor` data.
// @param x Position in `X` dimension.
// @param y Position in `Y` dimension.
// @param values New values vector.
export method set_vector_xy (Tensor this, int x, int y, array<float> values) =>
	_oldv = this.m.get(x, y)
	_oldv.v := values

// if barstate.islast
// 	_T = from(array.from(1.0,2,3,4,5,6,7,8,9,10,11,12), 2, 3, 2)
// 	_T.set_vector_xy(1, 1, array.from(33.3, 33.3))
// 	label.new(bar_index, 0.0, to_string(_T))

// @function Set the vector at position.
// @param this `Tensor` data.
// @param position Coordinate position of value.
// @param values New values vector.
export method set_vector_xy (Tensor this, array<int> position, array<float> values) =>
	set_vector_xy(this, array.get(position, 0), array.get(position, 1), values)

// }

//#endregion



