Vector2
// 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 Representation of two dimensional vectors or points.
//      This structure is used to represent positions in two dimensional space or vectors,
// for example in spacial coordinates in 2D space.
// ~~~
//  references:
//      https://docs.unity3d.com/ScriptReference/Vector2.html
//      https://gist.github.com/winduptoy/a1aa09c3499e09edbd33
//      https://github.com/dogancoruh/Javascript-Vector2/blob/master/js/vector2.js
//      https://gist.github.com/Dalimil/3daf2a0c531d7d030deb37a7bfeff454
//      https://gist.github.com/jeantimex/7fa22744e0c45e5df770b532f696af2d
//      https://gist.github.com/volkansalma/2972237
// ~~~
library(title='Vector2')




//#region ~~~ Imports and Helper Methods:

import RicardoSantos/CommonTypesMath/1 as TMath

// }

//#endregion



//#region ~~~ Constructor:

// new                    () {

// @function Create a new Vector2 object.
// @param x float . The x value of the vector, default=0.
// @param y float . The y value of the vector, default=0.
// @returns Vector2. Vector2 object.
// -> usage:
//      `unitx = Vector2.new(1.0) , plot(unitx.x)`
export new (float x = 0.0, float y=0.0) =>
    TMath.Vector2.new(x, y)

// }
// from                   () {

// @function Assigns value to a new vector `x,y` elements.
// @param value float, x and y value of the vector.
// @returns Vector2. Vector2 object.
// -> usage:
//      `one = Vector2.from(1.0), plot(one.x)`
export from (float value) =>
	new(value, value)

// @function Assigns value to a new vector `x,y` elements.
// @param value       string . The `x` and `y` value of the vector in a `x,y` or `(x,y)` format, spaces and parentesis will be removed automatically.
// @param element_sep string . Element separator character, default=`,`.
// @param open_par    string . Open parentesis character, default=`(`.
// @param close_par   string . Close parentesis character, default=`)`.
// @returns Vector2. Vector2 object.
// -> usage:
//      `one = Vector2.from("1.0,2"), plot(one.x)`
export from (string value, string element_sep=',', string open_par='(', string close_par=')') =>
    _s = str.split(str.replace_all(str.replace_all(str.replace_all(value, ' ', ''), open_par, ''), close_par, ''), element_sep)
    if _s.size() < 2
        runtime.error('Malformed String expression.')
    new(float(str.tonumber(_s.get(0))), float(str.tonumber(_s.get(1))))

// }
// copy                   () {

// @function Creates a deep copy of a vector.
// @param this Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = Vector2.new(1.0) , b = a.copy() , plot(b.x)`
export method copy (TMath.Vector2 this) =>
    TMath.Vector2.copy(this)

// }


//#region ~~~ ~~~ Static Properties:

// down                   () {

// @function Vector in the form `(0, -1)`.
// @returns Vector2. Vector2 object.
export down () =>
    new(0.0, -1.0)

// }
// left                   () {

// @function Vector in the form `(-1, 0)`.
// @returns Vector2. Vector2 object.
export left () =>
    new(-1.0, 0.0)

// }
// right                  () {

// @function Vector in the form `(1, 0)`.
// @returns Vector2. Vector2 object.
export right () =>
    new(1.0, 0.0)

// }
// up                     () {

// @function Vector in the form `(0, 1)`.
// @returns Vector2. Vector2 object.
export up () =>
    new(0.0, 1.0)

// }
// one                    () {

// @function Vector in the form `(1, 1)`.
// @returns Vector2. Vector2 object.
export one () =>
    new(1.0, 1.0)

// }
// zero                   () {

// @function Vector in the form `(0, 0)`.
// @returns Vector2. Vector2 object.
export zero () =>
    new(0.0, 0.0)

// }
// minus_one              () {

// @function Vector in the form `(-1, -1)`.
// @returns Vector2. Vector2 object.
export minus_one () =>
    new(-1.0, -1.0)

// }
// unit_x                 () {

// @function Vector in the form `(1, 0)`.
// @returns Vector2. Vector2 object.
export unit_x () =>
    new(1.0, 0.0)

// }
// unit_y                 () {

// @function Vector in the form `(0, 1)`.
// @returns Vector2. Vector2 object.
export unit_y () =>
    new(0.0, 1.0)

// }
// nan                    () {

// @function Vector in the form `(float(na), float(na))`.
// @returns Vector2. Vector2 object.
export nan () =>
    new(float(na), float(na))

// }

//#endregion

//#endregion



//#region ~~~ Properties:

// xy                     () {

// @function Return the values of `x` and `y` as a tuple.
// @param this Vector2 . Vector2 object.
// @returns [float, float].
// -> usage:
//      `a = Vector2.new(1.0, 1.0) , [ax, ay] = a.xy() , plot(ax)`
export method xy (TMath.Vector2 this) =>
    [this.x, this.y]

// }
// length_squared         () {

// @function Length of vector `a` in the form. `a.x^2 + a.y^2`, for comparing vectors this is computationaly lighter.
// @param this Vector2 . Vector2 object.
// @returns float. Squared length of vector.
// -> usage:
//      `a = Vector2.new(1.0, 1.0) , plot(a.length_squared())`
export method length_squared (TMath.Vector2 this) =>
    this.x * this.x + this.y * this.y

// }
// length                 () {

// @function Magnitude of vector `a` in the form. `sqrt(a.x^2 + a.y^2)`
// @param this Vector2 . Vector2 object.
// @returns float. Length of vector.
// -> usage:
//      `a = Vector2.new(1.0, 1.0) , plot(a.length())`
export method length (TMath.Vector2 this) =>
    math.sqrt(this.x * this.x + this.y * this.y)

// }
// normalize              () {

// @function Vector normalized with a magnitude of 1, in the form. `a / length(a)`.
// @param a Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = normalize(Vector2.new(3.0, 2.0)) , plot(a.y)`
export method normalize (TMath.Vector2 a) =>
    float _length = math.sqrt(a.x * a.x + a.y * a.y)
    new(a.x / _length, a.y / _length)

// }
// isNA () {

// @function Checks if any of the components is `na`.
// @param this Vector2 . Vector2 object.
// @returns bool.
// usage:
//		p = Vector2.new(1.0, na) , plot(isNA(p)?1:0)
export method isNA (TMath.Vector2 this) => na(this.x) or na(this.y)

// }

//#endregion



//#region ~~~ Operator Methods:

// add                    () {

// @function Adds vector `b` to `a`, in the form `(a.x + b.x, a.y + b.y)`.
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = one() , b = one() , c = add(a, b) , plot(c.x)`
export method add (TMath.Vector2 a, TMath.Vector2 b) =>
	new(a.x + b.x, a.y + b.y)

// @function Adds vector `b` to `a`, in the form `(a.x + b, a.y + b)`.
// @param a Vector2 . Vector2 object.
// @param b float   . Value.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = one() , b = 1.0 , c = add(a, b) , plot(c.x)`
export method add (TMath.Vector2 a, float b) =>
	new(a.x + b, a.y + b)

// @function Adds vector `b` to `a`, in the form `(a + b.x, a + b.y)`.
// @param a float   . Value.
// @param b Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = 1.0 , b = one() , c = add(a, b) , plot(c.x)`
export add (float a, TMath.Vector2 b) =>
	new(a + b.x, a + b.y)

// }
// subtract               () {

// @function Subtract vector `b` from `a`, in the form `(a.x - b.x, a.y - b.y)`.
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = one() , b = one() , c = subtract(a, b) , plot(c.x)`
export method subtract (TMath.Vector2 a, TMath.Vector2 b) =>
	new(a.x - b.x, a.y - b.y)

// @function Subtract vector `b` from `a`, in the form `(a.x - b, a.y - b)`.
// @param a Vector2 . vector2 object.
// @param b float   . Value.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = one() , b = 1.0 , c = subtract(a, b) , plot(c.x)`
export method subtract (TMath.Vector2 a, float b) =>
	new(a.x - b, a.y - b)

// @function Subtract vector `b` from `a`, in the form `(a - b.x, a - b.y)`.
// @param a float   . value.
// @param b Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = 1.0 , b = one() , c = subtract(a, b) , plot(c.x)`
export subtract (float a, TMath.Vector2 b) =>
	new(a - b.x, a - b.y)

// }
// multiply               () {

// @function Multiply vector `a` with `b`, in the form `(a.x * b.x, a.y * b.y)`.
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = one() , b = one() , c = multiply(a, b) , plot(c.x)`
export method multiply (TMath.Vector2 a, TMath.Vector2 b) =>
	new(a.x * b.x, a.y * b.y)

// @function Multiply vector `a` with `b`, in the form `(a.x * b, a.y * b)`.
// @param a Vector2 . Vector2 object.
// @param b float   . Value.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = one() , b = 1.0 , c = multiply(a, b) , plot(c.x)`
export method multiply (TMath.Vector2 a, float b) =>
	new(a.x * b, a.y * b)

// @function Multiply vector `a` with `b`, in the form `(a * b.x, a * b.y)`.
// @param a float   . Value.
// @param b Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = 1.0 , b = one() , c = multiply(a, b) , plot(c.x)`
export multiply (float a, TMath.Vector2 b) =>
	new(a * b.x, a * b.y)

// }
// divide                 () {

// @function Divide vector `a` with `b`, in the form `(a.x / b.x, a.y / b.y)`.
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = from(3.0) , b = from(2.0) , c = divide(a, b) , plot(c.x)`
export method divide (TMath.Vector2 a, TMath.Vector2 b) =>
	new(a.x / b.x, a.y / b.y)

// @function Divide vector `a` with value `b`, in the form `(a.x / b, a.y / b)`.
// @param a Vector2 . Vector2 object.
// @param b float   . Value.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = from(3.0) , b = 2.0 , c = divide(a, b) , plot(c.x)`
export method divide (TMath.Vector2 a, float b) =>
	new(a.x / b, a.y / b)

// @function Divide value `a` with vector `b`, in the form `(a / b.x, a / b.y)`.
// @param a float   . Value.
// @param b Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = 3.0 , b = from(2.0) , c = divide(a, b) , plot(c.x)`
export divide (float a, TMath.Vector2 b) =>
	new(a / b.x, a / b.y)

// }
// negate                 () {

// @function Negative of vector `a`, in the form `(-a.x, -a.y)`.
// @param a Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = from(3.0) , b = a.negate , plot(b.x)`
export method negate (TMath.Vector2 a) =>
    new(-a.x, -a.y)

// }
// pow                    () {

// @function Raise vector `a` with exponent vector `b`, in the form `(a.x ^ b.x, a.y ^ b.y)`.
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = from(3.0) , b = from(2.0) , c = pow(a, b) , plot(c.x)`
export method pow (TMath.Vector2 a, TMath.Vector2 b) =>
	new(math.pow(a.x, b.x), math.pow(a.y, b.y))

// @function Raise vector `a` with value `b`, in the form `(a.x ^ b, a.y ^ b)`.
// @param a Vector2 . Vector2 object.
// @param b float   . Value.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = from(3.0) , b = 2.0 , c = pow(a, b) , plot(c.x)`
export method pow (TMath.Vector2 a, float b) =>
	new(math.pow(a.x, b), math.pow(a.y, b))

// @function Raise value `a` with vector `b`, in the form `(a ^ b.x, a ^ b.y)`.
// @param a float   . Value.
// @param b Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = 3.0 , b = from(2.0) , c = pow(a, b) , plot(c.x)`
export pow (float a, TMath.Vector2 b) =>
	new(math.pow(a, b.x), math.pow(a, b.y))

// }
// sqrt                   () {

// @function Square root of the elements in a vector.
// @param a Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = from(3.0) , b = sqrt(a) , plot(b.x)`
export method sqrt (TMath.Vector2 a) =>
	new(math.sqrt(a.x), math.sqrt(a.y))

// }
// abs                    () {

// @function Absolute properties of the vector.
// @param a Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = from(-3.0) , b = abs(a) , plot(b.x)`
export method abs (TMath.Vector2 a) =>
	new(math.abs(a.x), math.abs(a.y))

// }
// min                    () {

// @function Lowest element of a vector.
// @param a Vector2 . Vector2 object.
// @returns float.
// -> usage:
//      `a = new(3.0, 1.5) , b = min(a) , plot(b)`
export method min (TMath.Vector2 a) =>
	math.min(a.x, a.y)

// }
// max                    () {

// @function Highest element of a vector.
// @param a Vector2 . Vector2 object.
// @returns float.
// -> usage:
//      `a = new(3.0, 1.5) , b = max(a) , plot(b)`
export method max (TMath.Vector2 a) =>
	math.max(a.x, a.y)

// }
// vmax                   () {

// @function Highest elements of two vectors.
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 2.0) , b = new(2.0, 3.0) , c = vmax(a, b) , plot(c.x)`
export vmax (TMath.Vector2 a, TMath.Vector2 b) =>
	new(math.max(a.x, b.x), math.max(a.y, b.y))

// @function Highest elements of three vectors.
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @param c Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 2.0) , b = new(2.0, 3.0) , c = new(1.5, 4.5) , d = vmax(a, b, c) , plot(d.x)`
export vmax (TMath.Vector2 a, TMath.Vector2 b, TMath.Vector2 c) =>
	new(math.max(a.x, b.x, c.x), math.max(a.y, b.y, c.y))

// }
// vmin                   () {

// @function Lowest elements of two vectors.
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 2.0) , b = new(2.0, 3.0) , c = vmin(a, b) , plot(c.x)`
export vmin (TMath.Vector2 a, TMath.Vector2 b) =>
	new(math.min(a.x, b.x), math.min(a.y, b.y))

// @function Lowest elements of three vectors.
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @param c Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 2.0) , b = new(2.0, 3.0) , c = new(1.5, 4.5) , d = vmin(a, b, c) , plot(d.x)`
export vmin (TMath.Vector2 a, TMath.Vector2 b, TMath.Vector2 c) =>
	new(math.min(a.x, b.x, c.x), math.min(a.y, b.y, c.y))

// }
// perp                   () {

// @function Perpendicular Vector of `a`, in the form `(a.y, -a.x)`.
// @param a Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 1.5) , b = perp(a) , plot(b.x)`
export method perp (TMath.Vector2 a) =>
	new(a.y, -a.x)

// }
// floor                  () {

// @function Compute the floor of vector `a`.
// @param a Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 1.5) , b = floor(a) , plot(b.x)`
export method floor (TMath.Vector2 a) =>
	new(math.floor(a.x), math.floor(a.y))

// }
// ceil                   () {

// @function Ceils vector `a`.
// @param a Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 1.5) , b = ceil(a) , plot(b.x)`
export method ceil (TMath.Vector2 a) =>
	new(math.ceil(a.x), math.ceil(a.y))

// @function Ceils vector `a`. 
// @param a      Vector2 . Vector2 object.
// @param digits int     . Digits to use as ceiling.
// @returns Vector2. Vector2 object.
export method ceil (TMath.Vector2 a, int digits) =>
    float _places = math.pow(10, digits)
	new(math.ceil(a.x  * _places) / _places, math.ceil(a.y * _places) / _places)

// }
// round                  () {

// @function Round of vector elements.
// @param a Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 1.5) , b = round(a) , plot(b.x)`
export method round (TMath.Vector2 a) =>
    new(math.round(a.x), math.round(a.y))

// @function Round of vector elements.
// @param a         Vector2 . Vector2 object.
// @param precision int     . Number of digits to round vector "a" elements.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(0.123456, 1.234567) , b = round(a, 2) , plot(b.x)`
export method round (TMath.Vector2 a, int precision) =>
    new(math.round(a.x, precision), math.round(a.y, precision))

// }
// fractional             () {

// @function Compute the fractional part of the elements from vector `a`.
// @param a Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.123456, 1.23456) , b = fractional(a) , plot(b.x)`
export method fractional (TMath.Vector2 a) =>
	new(a.x - math.floor(a.x), a.y - math.floor(a.y))

// }
// dot_product            () {

// @function dot_product product of 2 vectors, in the form `a.x * b.x + a.y * b.y.
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @returns float.
// -> usage:
//      `a = new(3.0, 1.5) , b = from(2.0) , c = dot_product(a, b) , plot(c)`
export method dot_product (TMath.Vector2 a, TMath.Vector2 b) =>
	(a.x * b.x) + (a.y * b.y)

// }
// cross_product          () {

// @function cross product of 2 vectors, in the form `a.x * b.y - a.y * b.x`.
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @returns float.
// -> usage:
//      `a = new(3.0, 1.5) , b = from(2.0) , c = cross_product(a, b) , plot(c)`
export method cross_product (TMath.Vector2 a, TMath.Vector2 b) =>
	(a.x * b.y) - (a.y * b.x)

// }
// equals                 () {

// @function Compares two vectors 
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @returns bool. Representing the equality.
// -> usage:
//      `a = new(3.0, 1.5) , b = from(2.0) , c = equals(a, b) ? 1 : 0 , plot(c)`
export method equals (TMath.Vector2 a, TMath.Vector2 b) =>
    a.x == b.x and a.y == b.y

// }

//#endregion



//#region ~~~ Trigonometry:

// sin                    () {

// @function Compute the sine of argument vector `a`.
// @param a Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 1.5) , b = sin(a) , plot(b.x)`
export method sin (TMath.Vector2 a) =>
	new(math.sin(a.x), math.sin(a.y))

// }
// cos                    () {

// @function Compute the cosine of argument vector `a`.
// @param a Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 1.5) , b = cos(a) , plot(b.x)`
export method cos (TMath.Vector2 a) =>
	new(math.cos(a.x), math.cos(a.y))

// }
// tan                    () {

// @function Compute the tangent of argument vector `a`.
// @param a Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 1.5) , b = tan(a) , plot(b.x)`
export method tan (TMath.Vector2 a) =>
	new(math.tan(a.x), math.tan(a.y))

// }
// atan2                  () {

// @function Approximation to atan2 calculation, arc tangent of `y/x` in the range (-pi,pi) radians.
// @param x float . The x value of the vector.
// @param y float . The y value of the vector.
// @returns float. Value with angle in radians. (negative if quadrante 3 or 4)
// -> usage:
//      `a = new(3.0, 1.5) , b = atan2(a.x, a.y) , plot(b)`
export atan2 (float x, float y) =>
    // float _one_qtr_pi = 0.25 * math.pi
    // float _three_qtr_pi = 0.75 * math.pi
    // float _abs_y = math.abs(y) + 1e-12 // kludge to void zero division
    // float _r = 0.0, float _angle = 0.0
	// if x < 0.0
	// 	_r := (x + _abs_y) / (_abs_y - x)
	// 	_angle := _three_qtr_pi
	// else
	// 	_r := (x - _abs_y) / (x + _abs_y)
	// 	_angle := _one_qtr_pi
	// _angle += (0.1963 * _r * _r - 0.9817) * _r
	// y < 0.0 ? -_angle : _angle
    // .
    // https://www.medcalc.org/manual/atan2-function.php
    switch
        x > 0.0 => math.atan(y / x)
        x < 0.0 and y >= 0.0 => math.atan(y / x) + math.pi
        x < 0.0 and y < 0.0  => math.atan(y / x) - math.pi
        x == 0.0 and y > 0.0 => math.pi / 2.0
        x == 0.0 and y < 0.0 => -math.pi / 2.0
        => 0.0

// @function Approximation to atan2 calculation, arc tangent of `y/x` in the range (-pi,pi) radians.
// @param a Vector2 . Vector2 object.
// @returns float, value with angle in radians. (negative if quadrante 3 or 4)
// -> usage:
//      `a = new(3.0, 1.5) , b = atan2(a) , plot(b)`
export method atan2 (TMath.Vector2 a) =>
    atan2(a.x, a.y)

// }

//#endregion


//#region ~~~ Methods:

// distance               () {

// @function Distance between vector `a` and `b`.
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @returns float.
// -> usage:
//      `a = new(3.0, 1.5) , b = from(2.0) , c = distance(a, b) , plot(c)`
export method distance (TMath.Vector2 a, TMath.Vector2 b) =>
	length(new(a.x - b.x, a.y - b.y))

// }
// rescale                () {

// @function Rescale a vector to a new magnitude.
// @param a      Vector2 . Vector2 object.
// @param length float   . Magnitude.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 1.5) , b = 2.0 , c = rescale(a, b) , plot(c.x)`
export method rescale (TMath.Vector2 a, float length) =>
    float _scalar = length / math.sqrt(a.x * a.x + a.y * a.y)
    new(a.x * _scalar, a.y * _scalar)

// }
// rotate                 () {

// @function Rotates vector by a angle.
// @param a       Vector2 . Vector2 object.
// @param radians float   . Angle value in radians.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 1.5) , b = 2.0 , c = rotate(a, b) , plot(c.x)`
export method rotate (TMath.Vector2 a, float radians) =>
    float _cos = math.cos(radians)
    float _sin = math.sin(radians)
	new(a.x * _cos - a.y * _sin, a.x * _sin + a.y * _cos)

// }
// rotate_degree          () {

// @function Rotates vector by a angle.
// @param a      Vector2 . Vector2 object.
// @param degree float   . Angle value in degrees.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 1.5) , b = 45.0 , c = rotate_degree(a, b) , plot(c.x)`
export method rotate_degree (TMath.Vector2 a, float degree) =>
	rotate(a, math.toradians(degree))

// }
// rotate_around          () {

// @function Rotates vector `target` around `origin` by angle value.
// @param target Vector2 . Vector2 object.
// @param center Vector2 . Vector2 object.
// @param angle  float   . Angle value in degrees.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 1.5) , b = from(2.0) , c = rotate_around(a, b, 45.0) , plot(c.x)`
export method rotate_around (TMath.Vector2 this, TMath.Vector2 center, float angle) =>
	if angle == 0
		this
	else
		_d = not isNA(center) ? subtract(this, center) : this
		_c = not isNA(center) ? center : zero()
		_a = math.toradians(angle)
		_sin = math.sin(_a)
		_cos = math.cos(_a)
		new(_c.x + (_d.x * _cos - _d.y * _sin), _c.y + (_d.x * _sin + _d.y * _cos))

// }
// perpendicular_distance () {

// @function Distance from point `a` to line between `b` and `c`.
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @param c Vector2 . Vector2 object.
// @returns float.
// -> usage:
//      `a = new(1.5, 2.6) , b = from(1.0) , c = from(3.0) , d = perpendicular_distance(a, b, c) , plot(d.x)`
export method perpendicular_distance (TMath.Vector2 a, TMath.Vector2 b, TMath.Vector2 c) =>
    TMath.Vector2 _d = new(c.x - b.x, c.y - b.y)
    math.abs(cross_product(a, _d) + cross_product(c, b)) / length(_d)

// }
// project                () {

// @function Project a vector onto another.
// @param a    Vector2 . Vector2 object.
// @param axis Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 1.5) , b = from(2.0) , c = project(a, b) , plot(c.x)`
export method project (TMath.Vector2 a, TMath.Vector2 axis) =>
    float _t = (a.x * axis.x + a.y * axis.y) / (axis.x * axis.x + axis.y * axis.y)
    new(_t * axis.x, _t * axis.y)

// }
// projectN               () {

// @function Project a vector onto a vector of unit length.
// @param a    Vector2 . Vector2 object.
// @param axis Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 1.5) , b = from(2.0) , c = projectN(a, b) , plot(c.x)`
export method projectN (TMath.Vector2 a, TMath.Vector2 axis) =>
    float _t  = (a.x * axis.x + a.y * axis.y)
    new(_t * axis.x, _t * axis.y)

// }
// reflect                () {

// @function Reflect a vector on another.
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 1.5) , b = from(2.0) , c = reflect(a, b) , plot(c.x)`
export method reflect (TMath.Vector2 a, TMath.Vector2 axis) =>
    TMath.Vector2 _p = project(a, axis)
    new(_p.x * 2.0 - a.x, _p.y * 2.0 - a.y)

// }
// reflectN               () {

// @function Reflect a vector to a arbitrary axis.
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 1.5) , b = from(2.0) , c = reflectN(a, b) , plot(c.x)`
export method reflectN (TMath.Vector2 a, TMath.Vector2 axis) =>
    TMath.Vector2 _p = projectN(a, axis)
    new(_p.x * 2.0 - a.x, _p.y * 2.0 - a.y)

// }
// angle                  () {

// @function Angle in radians of a vector.
// @param a Vector2 . Vector2 object.
// @returns float.
// -> usage:
//      `a = new(3.0, 1.5) , b = angle(a) , plot(b)`
export method angle (TMath.Vector2 a) =>
	-atan2(-a.y, a.x)

// }
// angle_unsigned         () {

// @function unsigned degree angle between 0 and +180 by given two vectors.
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @returns float.
// -> usage:
//      `a = new(3.0, 1.5) , b = from(2.0) , c = angle_unsigned(a, b) , plot(c)`
export method angle_unsigned (TMath.Vector2 a, TMath.Vector2 b) =>
	math.todegrees(math.acos(dot_product(normalize(a), normalize(b))))

// }
// angle_signed           () {

// @function Signed degree angle between -180 and +180 by given two vectors.
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @returns float.
// -> usage:
//      `a = new(3.0, 1.5) , b = from(2.0) , c = angle_signed(a, b) , plot(c)`
export method angle_signed (TMath.Vector2 a, TMath.Vector2 b) =>
    TMath.Vector2 _na = normalize(a)
    TMath.Vector2 _nb = normalize(b)
	math.todegrees(math.acos(dot_product(_na, _nb))) * (cross_product(_na, _nb) >= 0 ? 1.0 : -1.0)

// }
// angle_360              () {

// @function Degree angle between 0 and 360 by given two vectors
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @returns float.
// -> usage:
//      `a = new(3.0, 1.5) , b = from(2.0) , c = angle_360(a, b) , plot(c)`
export method angle_360 (TMath.Vector2 a, TMath.Vector2 b) =>
    TMath.Vector2 _na = normalize(a)
    TMath.Vector2 _nb = normalize(b)
    float _degree = math.todegrees(math.acos(dot_product(_na, _nb)))
	cross_product(_na, _nb) > 0.0 ? _degree : 360.0 - _degree

// }
// clamp                  () {

// @function Restricts a vector between a min and max value.
// @param a    Vector2 . Vector2 object.
// @param vmin Vector2 . Vector2 object.
// @param vmax Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 1.5) , b = from(2.0) , c = from(2.5) , d = clamp(a, b, c) , plot(d.x)`
export method clamp (TMath.Vector2 a, TMath.Vector2 min, TMath.Vector2 max) =>
    float _x = math.max(math.min(a.x , max.x), min.x)
    float _y = math.max(math.min(a.y, max.y), min.y)
    new(_x, _y)

// @function Restricts a vector between a min and max value.
// @param a   Vector2 . Vector2 object.
// @param min float   . Lower boundary value.
// @param max float   . Higher boundary value.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 1.5) , b = clamp(a, 2.0, 2.5) , plot(b.x)`
export method clamp (TMath.Vector2 a, float min, float max) =>
    float _x = math.max(math.min(a.x , max), min)
    float _y = math.max(math.min(a.y, max), min)
    new(_x, _y)

// }
// lerp                   () {

// @function Linearly interpolates between vectors a and b by rate.
// @param a    Vector2 . Vector2 object.
// @param b    Vector2 . Vector2 object.
// @param rate float   . Value between (a:-infinity -> b:1.0), negative values will move away from b.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 1.5) , b = from(2.0) , c = lerp(a, b, 0.5) , plot(c.x)`
export method lerp (TMath.Vector2 a, TMath.Vector2 b, float rate) =>
    new(a.x + (b.x - a.x) * rate, a.y + (b.y - a.y) * rate)

// }
// herp                   () {

// @function Hermite curve interpolation between vectors a and b by rate.
// @param a    Vector2 . Vector2 object.
// @param b    Vector2 . Vector2 object.
// @param rate Vector2 . Vector2 object. Value between (a:0 > 1:b).
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = new(3.0, 1.5) , b = from(2.0) , c = from(2.5) , d = herp(a, b, c) , plot(d.x)`
export method herp (TMath.Vector2 a, TMath.Vector2 b, TMath.Vector2 rate) =>
    TMath.Vector2 _p = new((rate.x - a.x) / (b.x - a.x), (rate.y - a.y) / (b.y - a.y))
    TMath.Vector2 _t = clamp(_p, 0.0, 1.0)
    new(_t.x * _t.x * (3.0 - 2.0 * _t.x), _t.y * _t.y * (3.0 - 2.0 * _t.y))

// }
// Transform              () {

// @function Transform a vector by the given matrix.
// @param position Vector2 . Source vector.
// @param mat      M32     . Transformation matrix
// @returns Vector2. Transformed vector.
export method transform (TMath.Vector2 position, TMath.M32 mat) =>
    new(
     position.x * mat.m11 + position.y * mat.m21 + mat.m31 ,
     position.x * mat.m12 + position.y * mat.m22 + mat.m32 )

// @function Transform a vector by the given matrix.
// @param position Vector2 . Source vector.
// @param mat      M44     . Transformation matrix
// @returns Vector2. Transformed vector.
export method transform (TMath.Vector2 position, TMath.M44 mat) =>
    new(
     position.x * mat.m11 + position.y * mat.m21 + mat.m41 ,
     position.x * mat.m12 + position.y * mat.m22 + mat.m42 )

// @function Transform a vector by the given matrix.
// @param position Vector2       . Source vector.
// @param mat      matrix<float> . Transformation matrix, requires a 3x2 or a 4x4 matrix.
// @returns Vector2. Transformed vector.
export method transform (TMath.Vector2 position, matrix<float> mat) =>
    if mat.rows() == 3 and mat.columns() == 2
        new(
         position.x * mat.get(0,0) + position.y * mat.get(1,0) + mat.get(2,0) ,
         position.x * mat.get(0,1) + position.y * mat.get(1,1) + mat.get(2,1) )
    else if mat.rows() == 4 and mat.columns() == 4
        new(
         position.x * mat.get(0,0) + position.y * mat.get(1,0) + mat.get(3,0) ,
         position.x * mat.get(0,1) + position.y * mat.get(1,1) + mat.get(3,1) )
    else
        runtime.error('transform() incorrect shape of matrix, must be 3x2 or 4x4')
        nan()

// @function Transform a vector by the given quaternion rotation value.
// @param this     Vector2    . Source vector.
// @param rotation Quaternion . Rotation to apply.
// @returns Vector2. Transformed vector.
export method transform (TMath.Vector2 this, TMath.Quaternion rotation) =>
    float _x2 = rotation.x + rotation.x
    float _y2 = rotation.y + rotation.y
    float _z2 = rotation.z + rotation.z

    float _wz2 = rotation.w * _z2
    float _xx2 = rotation.x * _x2
    float _xy2 = rotation.x * _y2
    float _yy2 = rotation.y * _y2
    float _zz2 = rotation.z * _z2

    new(
     this.x * (1.0 - _yy2 - _zz2) + this.y * (_xy2 - _wz2) , 
     this.x * (_xy2 + _wz2) + this.y * (1.0 - _xx2 - _zz2) )


// }
// area_triangle          () {

// @function Find the area in a triangle of vectors.
// @param a Vector2 . Vector2 object.
// @param b Vector2 . Vector2 object.
// @param c Vector2 . Vector2 object.
// @returns float.
// -> usage:
//      `a = new(1.0, 2.0) , b = from(2.0) , c = from(1.0) , d = area_triangle(a, b, c) , plot(d.x)`
export area_triangle (TMath.Vector2 a, TMath.Vector2 b, TMath.Vector2 c) =>
    math.abs(0.5 * ((a.x  - c.x) * (b.y - a.y) - (a.x  - b.x) * (c.y - a.y)))

// }
// random                 () {

// @function 2D random value.
// @param max Vector2 . Vector2 object. Vector upper boundary.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = from(2.0) , b = random(a) , plot(b.x)`
export method random (TMath.Vector2 max) =>
    new(math.random(0.0, max.x), math.random(0.0, max.y))

// @function 2D random value.
// @param max float, Vector upper boundary.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = random(2.0) , plot(a.x)`
export random (float max=1.0) =>
    new(math.random(0.0, max), math.random(0.0, max))

// // @function 2D random value.
// // @param max  Vector2 . Vector2 object., vector upper bound
// // @param seed int     . Seed value.
// // @returns Vector2. Vector2 object.
// export random (Vector2 max, int seed) =>
//     Vector2.new(math.random(0.0, max.x, seed), math.random(0.0, max.y, seed))

// @function 2D random value.
// @param min Vector2 . Vector2 object. Vector lower boundary.
// @param max Vector2 . Vector2 object. Vector upper boundary.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = from(1.0) , b = from(2.0) , c = random(a, b) , plot(c.x)`
export random (TMath.Vector2 min, TMath.Vector2 max) =>
    new(math.random(min.x, max.x), math.random(min.y, max.y))

// @function 2D random value.
// @param min Vector2 . Vector2 object. Vector lower boundary.
// @param max Vector2 . Vector2 object. Vector upper boundary.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = random(1.0, 2.0) , plot(a.x)`
export random (float min, float max) =>
    new(math.random(min, max), math.random(min, max))

// }
// noise                  () {
// reference:
//     https://thebookofshaders.com/11/
//     https://www.shadertoy.com/view/4dS3Wd

// @function 2D Noise based on Morgan McGuire @morgan3d.
// @param a Vector2 . Vector2 object.
// @returns Vector2. Vector2 object.
// -> usage:
//      `a = from(2.0) , b = noise(a) , plot(b.x)`
export noise (TMath.Vector2 a) =>
    TMath.Vector2 _i = floor(a)
    TMath.Vector2 _f = fractional(a)
    // compute the 4 corners of a tile in 2D
    TMath.Vector2 _a = random(_i)
    TMath.Vector2 _b = random(add(_i, right()))
    TMath.Vector2 _c = random(add(_i, up()))
    TMath.Vector2 _d = random(add(_i, one()))

    // Smooth Interpolation
    TMath.Vector2 _u = herp(zero(), one(), _f)
    
    // Mix 4 coorners percentages
    TMath.Vector2 _mix0 = lerp(_a, _b, _u.x)
    TMath.Vector2 _mix1 = new((_c.x - _a.x) * _u.y * (1.0 - _u.x), (_c.y - _a.y) * _u.y * (1.0 - _u.x))
    TMath.Vector2 _mix2 = new((_d.x - _b.x) * _u.x, (_d.y - _b.y) * _u.y)
    new(_mix0.x + _mix1.x + _mix2.x, _mix0.y + _mix1.y + _mix2.y)

// }

//#region ~~~ ~~~ Translate:

// to_string              () {

// @function Converts vector `a` to a string format, in the form `"(x, y)"`.
// @param a Vector2 . Vector2 object.
// @returns string. In `"(x, y)"` format.
// -> usage:
//      `a = from(2.0) , l = barstate.islast ? label.new(bar_index, 0.0, to_string(a)) : label(na)`
export method to_string (TMath.Vector2 a) =>
    str.format('({0}, {1})', a.x, a.y)

// @function Converts vector `a` to a string format, in the form `"(x, y)"`.
// @param a      Vector2 . Vector2 object.
// @param format string  . Format to apply transformation.
// @returns string. In `"(x, y)"` format.
// -> usage:
//      `a = from(2.123456) , l = barstate.islast ? label.new(bar_index, 0.0, to_string(a, "#.##")) : label(na)`
export method to_string (TMath.Vector2 a, string format) =>
    str.format('({0,number,'+format+'}, {1,number,'+format+'})', a.x, a.y)

// }
// to_array               () {

// @function Converts vector to a array format.
// @param a Vector2 . Vector2 object. 
// @returns array<float>.
// -> usage:
//      `a = from(2.0) , b = to_array(a) , plot(array.get(b, 0))`
export method to_array (TMath.Vector2 a) =>
	array<float> _output = array.from(float(a.x), float(a.y))

// }
// to_barycentric () {
// reference:
//      https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/math/GeometryUtils.java
//
//      B
//      /\
//     /  \
//    / .p \
// A /______\C
// p = position on the triangle plane

// @function Captures the barycentric coordinate of a cartesian position in the triangle plane.
// @param this Vector2 . Source cartesian coordinate position.
// @param a    Vector2 . Triangle corner `a` vertice.
// @param b    Vector2 . Triangle corner `b` vertice.
// @param c    Vector2 . Triangle corner `c` vertice.
// @returns bool.
export method to_barycentric (TMath.Vector2 this, TMath.Vector2 a, TMath.Vector2 b, TMath.Vector2 c) =>
    _v0 = b.subtract(a)
    _v1 = c.subtract(a)
    _v2 = this.subtract(a)
    float _d00 = dot_product(_v0, _v0)
    float _d01 = dot_product(_v0, _v1)
    float _d11 = dot_product(_v1, _v1)
    float _d20 = dot_product(_v2, _v0)
    float _d21 = dot_product(_v2, _v1)
    float _denom = _d00 * _d11 - _d01 * _d01
    new((_d11 * _d20 - _d01 * _d21) / _denom , (_d00 * _d21 - _d01 * _d20) / _denom)

// TEST: OK 20230216 RS
// float px = input.float(50.0, step=10.0)
// float py = input.float(50.0, step=10.0)
// a = new(float(bar_index+10), 10.0)
// b = new(float(bar_index+100), 100.0)
// c = new(float(bar_index+200), 50.0)
// p = new(float(bar_index+px), py)
// if barstate.islast
//     line.new(int(a.x), a.y, int(b.x), b.y, xloc.bar_index, extend.none, #e3e3e3)
//     line.new(int(b.x), b.y, int(c.x), c.y, xloc.bar_index, extend.none, #e3e3e3)
//     line.new(int(c.x), c.y, int(a.x), a.y, xloc.bar_index, extend.none, #e3e3e3)
//     label.new(int(p.x), p.y, p.to_barycentric(a, b, c).to_string())

// }
// from_barycentric () {

// @function Captures the cartesian coordinate of a barycentric position in the triangle plane.
// @param this Vector2 . Source barycentric coordinate position.
// @param a    Vector2 . Triangle corner `a` vertice.
// @param b    Vector2 . Triangle corner `b` vertice.
// @param c    Vector2 . Triangle corner `c` vertice.
// @returns bool.
export method from_barycentric (TMath.Vector2 this, TMath.Vector2 a, TMath.Vector2 b, TMath.Vector2 c) =>
    float _u = 1.0 - this.x - this.y
    new(_u * a.x + this.x * b.x + this.y * c.x , _u * a.y + this.x * b.y + this.y * c.y)

// TEST: OK 20230216 RS
// float px = input.float(50.0, step=10.0)
// float py = input.float(50.0, step=10.0)
// a = new(float(bar_index+10), 10.0)
// b = new(float(bar_index+100), 100.0)
// c = new(float(bar_index+200), 50.0)
// p = new(float(bar_index+px), py)
// if barstate.islast
//     line.new(int(a.x), a.y, int(b.x), b.y, xloc.bar_index, extend.none, #e3e3e3)
//     line.new(int(b.x), b.y, int(c.x), c.y, xloc.bar_index, extend.none, #e3e3e3)
//     line.new(int(c.x), c.y, int(a.x), a.y, xloc.bar_index, extend.none, #e3e3e3)
//     _bary_coord = p.to_barycentric(a, b, c)
//     _cart_coord = _bary_coord.from_barycentric(a, b, c)
//     label.new(int(p.x), p.y, str.format('B: {0}\nC: {1}\n', _bary_coord.to_string(), _cart_coord.to_string()))

// }
// to_complex () {

// @function Translate a Vector2 structure to complex.
// @param this Vector2 . Source vector.
// @returns Complex.
export method to_complex (TMath.Vector2 this) =>
    TMath.complex.new(this.x, this.y)

// }
// to_polar () {
// reference:
//      https://brilliant.org/wiki/convert-cartesian-coordinates-to-polar/      bugged output!
//      https://keisan.casio.com/exec/system/1223526375

// @function  Translate a Vector2 cartesian coordinate into polar coordinates.
// @param this Vector2 . Source vector.
// @returns Pole. The returned angle is in radians.
export method to_polar (TMath.Vector2 this) =>
    float _radius = math.sqrt(this.x * this.x + this.y * this.y)
    float _angle = atan2(this)
    TMath.Pole.new(_radius, _angle)

// TEST: OK 20230217 RS
// float cx = input.float(3.0)
// float cy = input.float(4.0)
// if barstate.islast
//     TMath.Vector2 _cart = new(cx, cy)
//     TMath.Pole _p = _cart.to_polar()
//     label.new(bar_index, 0.0, str.format('{0} = {1}, {2}', _cart.to_string(), _p.radius, _p.angle))

// }

//#endregion

//#endregion

