# http://mathling.com/core/vector  library module

http://mathling.com/core/vector

Vectors of numbers

May 2021
Status: Incomplete

### Imports

http://mathling.com/core/utilities
```import module namespace util="http://mathling.com/core/utilities"
at "../core/utilities.xqy"```
http://mathling.com/core/config
```import module namespace config="http://mathling.com/core/config"
at "../core/config.xqy"```
http://mathling.com/core/errors
```import module namespace errors="http://mathling.com/core/errors"
at "../core/errors.xqy"```

### Functions

#### `Function: as-dimensiondeclare function as-dimension(\$vector as xs:double*, \$dim as xs:integer) as xs:double*`

as-dimension()
Cast the vector to a vector with the given number of dimensions.
If we are reducing dimensions, just remove excess coordinates.
If we are expanding dimensions, fill in with 0.

##### Params
• vector as xs:double*: source vector
• dim as xs:integer: new dimension of vector
##### Returns
• xs:double*: new vector
```declare function this:as-dimension(\$vector as xs:double*, \$dim as xs:integer) as xs:double*
{
(\$vector, for \$i in 1 to \$dim return 0E0)[position() <= \$dim]
}```

#### `Function: unquotedeclare function unquote(\$string as xs:string) as xs:double*`

unquote()
Parse a string representation of a vector and produce a vector.
The string consists of the coordinates in with commas and
optional spaces in between.

##### Params
• string as xs:string: the input string to parse
##### Returns
• xs:double*: vector
```declare function this:unquote(\$string as xs:string) as xs:double*
{
tokenize(\$string, "[ ]*,[ ]*")!xs:double(.)
}```

#### `Function: dimensiondeclare function dimension(\$vector as xs:double*) as xs:integer`

dimension()
Return the dimension of the vector.

##### Params
• vector as xs:double*: the vector
##### Returns
• xs:integer: length of vector
```declare function this:dimension(\$vector as xs:double*) as xs:integer
{
count(\$vector)
}```

#### ```Function: mapdeclare function map(\$f as function(xs:double) as xs:double, \$vector as xs:double*) as xs:double*```

map()
Map a function over the coordinates of a vector, returning the new vector.

##### Params
• f as function(xs:double)asxs:double: function from coordinate to coordinate
• vector as xs:double*: input vector
##### Returns
• xs:double*: application of function to each coordinate
```declare function this:map(
\$f as function(xs:double) as xs:double,
\$vector as xs:double*
) as xs:double*
{
for-each(\$vector, \$f)
}```

#### ```Function: mapdeclare function map(\$f as function(xs:double) as xs:double, \$vector as xs:double*, \$d as xs:integer) as xs:double*```

map()
Map a function over the coordinates of a vector, returning the new vector.
Treat the vector as having a particular dimension.

##### Params
• f as function(xs:double)asxs:double: function from coordinate to coordinate
• vector as xs:double*: input vector
• d as xs:integer: the effective dimension of the vector
##### Returns
• xs:double*: application of function to coordinates
```declare function this:map(
\$f as function(xs:double) as xs:double,
\$vector as xs:double*,
\$d as xs:integer
) as xs:double*
{
for-each(this:as-dimension(\$vector, \$d), \$f)
}```

#### ```Function: map2declare function map2(\$f as function(xs:double, xs:double) as xs:double, \$p1 as xs:double*, \$p2 as xs:double*) as xs:double*```

map2()
Map a function over the coordinates of two vectors, pair by pair, returning
the new vector. That is, apply function to the x coordinates, then the
y coordinates, and so on.

##### Params
• f as function(xs:double,xs:double)asxs:double: function from coordinate pairs to coordinate
• p1 as xs:double*: one input vector
• p2 as xs:double*: another input vector
##### Returns
• xs:double*: application of function to each pair
```declare function this:map2(
\$f as function(xs:double, xs:double) as xs:double,
\$p1 as xs:double*,
\$p2 as xs:double*
) as xs:double*
{
util:zip(\$f, \$p1, \$p2)
}```

#### ```Function: map2declare function map2(\$f as function(xs:double, xs:double) as xs:double, \$p1 as xs:double*, \$p2 as xs:double*, \$d as xs:integer) as xs:double*```

map2()
Map a function over the coordinates of two vectors, pair by pair, returning
the new vector. That is, apply function to the x coordinates, then the
y coordinates, and so on. Treat the vector as having a particular dimension.

##### Params
• f as function(xs:double,xs:double)asxs:double: function from coordinate pairs to coordinate
• p1 as xs:double*: one input vector
• p2 as xs:double*: another input vector
• d as xs:integer: the effective dimension of the vectors
##### Returns
• xs:double*: application of function to each pair
```declare function this:map2(
\$f as function(xs:double, xs:double) as xs:double,
\$p1 as xs:double*,
\$p2 as xs:double*,
\$d as xs:integer
) as xs:double*
{
util:zip(\$f, this:as-dimension(\$p1, \$d), this:as-dimension(\$p2, \$d))
}```

#### ```Function: map3declare function map3(\$f as function(xs:double, xs:double, xs:double) as xs:double, \$p1 as xs:double*, \$p2 as xs:double*, \$p3 as xs:double*) as xs:double*```

map3()
Map a function over the coordinates of three vectors, triple by triple,
returning the new vector. That is, apply function to the x coordinates,
then the y coordinates, and so on.

##### Params
• f as function(xs:double,xs:double,xs:double)asxs:double: function from coordinate pairs to coordinate
• p1 as xs:double*: one input vector
• p2 as xs:double*: another input vector
• p3 as xs:double*: third input vector
##### Returns
• xs:double*: application of function to each triple
```declare function this:map3(
\$f as function(xs:double, xs:double, xs:double) as xs:double,
\$p1 as xs:double*,
\$p2 as xs:double*,
\$p3 as xs:double*
) as xs:double*
{
util:zip(
function (\$a as xs:double, \$fc as function(xs:double) as xs:double) as xs:double {\$fc(\$a)},
\$p3,
util:zip(
function (\$a as xs:double, \$b as xs:double) as function(*) { \$f(\$a, \$b, ?) },
\$p1, \$p2
)
)
}```

#### ```Function: map3declare function map3(\$f as function(xs:double, xs:double, xs:double) as xs:double, \$p1 as xs:double*, \$p2 as xs:double*, \$p3 as xs:double*, \$d as xs:integer) as xs:double*```

map3()
Map a function over the coordinates of three vectors, triple by triple,
returning the new vector. That is, apply function to the x coordinates,
then the y coordinates, and so on. Treat the vector as having a
particular dimension.

##### Params
• f as function(xs:double,xs:double,xs:double)asxs:double: function from coordinate pairs to coordinate
• p1 as xs:double*: one input vector
• p2 as xs:double*: another input vector
• p3 as xs:double*: third input vector
• d as xs:integer: the effective dimension of the vectors
##### Returns
• xs:double*: application of function to each triple
```declare function this:map3(
\$f as function(xs:double, xs:double, xs:double) as xs:double,
\$p1 as xs:double*,
\$p2 as xs:double*,
\$p3 as xs:double*,
\$d as xs:integer
) as xs:double*
{
util:zip(
function (\$a as xs:double, \$fc as function(xs:double) as xs:double) as xs:double {\$fc(\$a)},
this:as-dimension(\$p3, \$d),
util:zip(
function (\$a as xs:double, \$b as xs:double) as function(*) { \$f(\$a, \$b, ?) },
this:as-dimension(\$p1, \$d), this:as-dimension(\$p2, \$d)
)
)
}```

#### ```Function: map4declare function map4(\$f as function(xs:double, xs:double, xs:double, xs:double) as xs:double, \$p1 as xs:double*, \$p2 as xs:double*, \$p3 as xs:double*, \$p4 as xs:double*) as xs:double*```

map4()
Map a function over the coordinates of four vectors, coordinate by
coordinate, returning the new vector. That is, apply function to the x
coordinates, then the y coordinates, and so on.

##### Params
• f as function(xs:double,xs:double,xs:double,xs:double)asxs:double: function from coordinate pairs to coordinate
• p1 as xs:double*: one input vector
• p2 as xs:double*: another input vector
• p3 as xs:double*: third input vector
• p4 as xs:double*: fourth input vector
##### Returns
• xs:double*: application of function to each quad
```declare function this:map4(
\$f as function(xs:double, xs:double, xs:double, xs:double) as xs:double,
\$p1 as xs:double*,
\$p2 as xs:double*,
\$p3 as xs:double*,
\$p4 as xs:double*
) as xs:double*
{
util:zip(
function (\$d as xs:double, \$fd as function(xs:double) as xs:double) as xs:double {\$fd(\$d)},
\$p4,
util:zip(
function (\$c as xs:double, \$fc as function(xs:double, xs:double) as xs:double) as function(*) {\$fc(\$c, ?)},
\$p3,
util:zip(
function (\$a as xs:double, \$b as xs:double) as function(*) { \$f(\$a, \$b, ?, ?) },
\$p1, \$p2
)
)
)
}```

#### ```Function: map4declare function map4(\$f as function(xs:double, xs:double, xs:double, xs:double) as xs:double, \$p1 as xs:double*, \$p2 as xs:double*, \$p3 as xs:double*, \$p4 as xs:double*, \$dim as xs:integer) as xs:double*```

map4()
Map a function over the coordinates of four vectors, coordinate by
coordinate, returning the new vector. That is, apply function to the x
coordinates, then the y coordinates, and so on. Treat the vector as having a
particular dimension.

##### Params
• f as function(xs:double,xs:double,xs:double,xs:double)asxs:double: function from coordinate pairs to coordinate
• p1 as xs:double*: one input vector
• p2 as xs:double*: another input vector
• p3 as xs:double*: third input vector
• p4 as xs:double*: fourth input vector
• dim as xs:integer
##### Returns
• xs:double*: application of function to each quad
```declare function this:map4(
\$f as function(xs:double, xs:double, xs:double, xs:double) as xs:double,
\$p1 as xs:double*,
\$p2 as xs:double*,
\$p3 as xs:double*,
\$p4 as xs:double*,
\$dim as xs:integer
) as xs:double*
{
util:zip(
function (\$d as xs:double, \$fd as function(xs:double) as xs:double) as xs:double {\$fd(\$d)},
this:as-dimension(\$p4, \$dim),
util:zip(
function (\$c as xs:double, \$fc as function(xs:double, xs:double) as xs:double) as function(*) {\$fc(\$c, ?)},
this:as-dimension(\$p3, \$dim),
util:zip(
function (\$a as xs:double, \$b as xs:double) as function(*) { \$f(\$a, \$b, ?, ?) },
this:as-dimension(\$p1, \$dim), this:as-dimension(\$p2, \$dim)
)
)
)
}```

#### `Function: pxdeclare function px(\$vector as xs:double*) as xs:double`

px()
Accessor for the raw precision x coordinate

##### Params
• vector as xs:double*: the vector
##### Returns
• xs:double: raw x coordinate
```declare function this:px(\$vector as xs:double*) as xs:double
{
}```

#### `Function: pydeclare function py(\$vector as xs:double*) as xs:double`

py()
Accessor for the raw precision y coordinate

##### Params
• vector as xs:double*: the vector
##### Returns
• xs:double: raw y coordinate
```declare function this:py(\$vector as xs:double*) as xs:double
{
}```

#### `Function: pzdeclare function pz(\$vector as xs:double*) as xs:double`

pz()
Accessor for the raw precision z coordinate; returns 0 for 2D vector

##### Params
• vector as xs:double*: the vector
##### Returns
• xs:double: raw z coordinate
```declare function this:pz(\$vector as xs:double*) as xs:double
{
}```

#### `Function: pwdeclare function pw(\$vector as xs:double*) as xs:double`

pw()
Accessor for the raw precision w coordinate; returns 0 for 2D or 3D vector

##### Params
• vector as xs:double*: the vector
##### Returns
• xs:double: raw w coordinate
```declare function this:pw(\$vector as xs:double*) as xs:double
{
}```

#### `Function: pxydeclare function pxy(\$vector as xs:double*) as xs:double*`

pxy()
Accessor for the raw precision xy vector.

##### Params
• vector as xs:double*: the vector
##### Returns
• xs:double*: new vector of just the raw x and y coordinates
```declare function this:pxy(\$vector as xs:double*) as xs:double*
{
}```

#### `Function: pxzdeclare function pxz(\$vector as xs:double*) as xs:double*`

pxz()
Accessor for the raw precision xz vector.

##### Params
• vector as xs:double*: the vector
##### Returns
• xs:double*: new vector of just the raw x and z coordinates
```declare function this:pxz(\$vector as xs:double*) as xs:double*
{
}```

#### `Function: xdeclare function x(\$vector as xs:double*) as xs:integer`

x()
Accessor for the snapped x coordinate

##### Params
• vector as xs:double*: the vector
##### Returns
• xs:integer: x coordinate, rounded
```declare function this:x(\$vector as xs:double*) as xs:integer
{
return (
if (\$x = xs:double("INF")) then \$util:UINT64_MAX
else if (\$x = xs:double("-INF")) then -\$util:UINT64_MAX
else round-half-to-even(\$x) cast as xs:integer
)
}```

#### `Function: ydeclare function y(\$vector as xs:double*) as xs:integer`

y()
Accessor for the snapped y coordinate

##### Params
• vector as xs:double*: the vector
##### Returns
• xs:integer: y coordinate, rounded
```declare function this:y(\$vector as xs:double*) as xs:integer
{
return (
if (\$y = xs:double("INF")) then \$util:UINT64_MAX
else if (\$y = xs:double("-INF")) then -\$util:UINT64_MAX
else round-half-to-even(\$y) cast as xs:integer
)
}```

#### `Function: zdeclare function z(\$vector as xs:double*) as xs:integer`

z()
Accessor for the snapped z coordinate

##### Params
• vector as xs:double*: the vector
##### Returns
• xs:integer: z coordinate, rounded
```declare function this:z(\$vector as xs:double*) as xs:integer
{
return (
if (\$z = xs:double("INF")) then \$util:UINT64_MAX
else if (\$z = xs:double("-INF")) then -\$util:UINT64_MAX
else round-half-to-even(\$z) cast as xs:integer
)
}```

#### `Function: wdeclare function w(\$vector as xs:double*) as xs:integer`

w()
Accessor for the snapped w coordinate

##### Params
• vector as xs:double*: the vector
##### Returns
• xs:integer: w coordinate, rounded
```declare function this:w(\$vector as xs:double*) as xs:integer
{
return (
if (\$w = xs:double("INF")) then \$util:UINT64_MAX
else if (\$w = xs:double("-INF")) then -\$util:UINT64_MAX
else round-half-to-even(\$w) cast as xs:integer
)
}```

#### `Function: magnitudedeclare function magnitude(\$v as xs:double*) as xs:double`

magnitude()
Magnitude of the vector from origin.

##### Params
• v as xs:double*: the vector
##### Returns
• xs:double: Euclidean length of vector
```declare function this:magnitude(\$v as xs:double*) as xs:double
{
math:sqrt(
fn:sum(for \$c in \$v return \$c*\$c)
)
}```

#### `Function: dotdeclare function dot(\$p1 as xs:double*, \$p2 as xs:double*) as xs:double`

dot()
Compute the dot product of two vectors

##### Params
• p1 as xs:double*: one vector
• p2 as xs:double*: another vector
##### Returns
• xs:double: p1·p2
```declare function this:dot(\$p1 as xs:double*, \$p2 as xs:double*) as xs:double
{
fn:sum(
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {\$a * \$b}, \$p1, \$p2)
)
}```

#### `Function: dot2declare function dot2(\$p1 as xs:double*) as xs:double`

dot2()
Compute the dot product of a vector with itself

##### Params
• p1 as xs:double*
##### Returns
• xs:double: p·p
```declare function this:dot2(\$p1 as xs:double*) as xs:double
{
fn:sum(
this:map(function (\$a as xs:double) as xs:double {\$a * \$a}, \$p1)
)
}```

#### `Function: ndotdeclare function ndot(\$p1 as xs:double*, \$p2 as xs:double*) as xs:double`

Negated dot: useful for some of the SDF functions.

##### Params
• p1 as xs:double*: one vector
• p2 as xs:double*: another vector
##### Returns
• xs:double: p1·-p2
```declare function this:ndot(\$p1 as xs:double*, \$p2 as xs:double*) as xs:double
{
fn:sum(
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {\$a * -\$b}, \$p1, \$p2)
)
}```

#### ```Function: mixdeclare function mix(\$u as xs:double*, \$v as xs:double*, \$f as xs:double) as xs:double*```

Linear combination of two vectors, coordinate by coordinate.
Generally \$f in in [0,1], but you can
get extrapolations with numbers outside that range.

##### Params
• u as xs:double*: one vector
• v as xs:double*: other vector
• f as xs:double: combination fraction
##### Returns
• xs:double*: vector of linear combination of each coordinate pair
```declare function this:mix(
\$u as xs:double*,
\$v as xs:double*,
\$f as xs:double
) as xs:double*
{
(: Logically \$a + (\$b - \$a) * \$f but better endvector behaviour :)
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {\$a * (1 - \$f) + \$b * \$f}, \$u, \$v)
}```

#### `Function: determinantdeclare function determinant(\$u as xs:double*, \$v as xs:double*) as xs:double`

determinant()
Compute the determinant of two 2D vectors.

##### Params
• u as xs:double*: one vector
• v as xs:double*: another vector
##### Returns
• xs:double
```declare function this:determinant(\$u as xs:double*, \$v as xs:double*) as xs:double
{
this:px(\$u)*this:py(\$v) - this:py(\$u)*this:px(\$v)
}```

#### `Function: crossdeclare function cross(\$u as xs:double*, \$v as xs:double*) as xs:double*`

cross()
Compute the cross product of two 3D vectors.

##### Params
• u as xs:double*: one vector
• v as xs:double*: another vector
##### Returns
• xs:double*: uXv
```declare function this:cross(\$u as xs:double*, \$v as xs:double*) as xs:double*
{
this:py(\$u)*this:pz(\$v) - this:pz(\$u)*this:py(\$v),
this:pz(\$u)*this:px(\$v) - this:px(\$u)*this:pz(\$v),
this:px(\$u)*this:py(\$v) - this:py(\$u)*this:px(\$v)
}```

#### `Function: rounddeclare function round(\$v as xs:double*) as xs:double*`

round()
Round the coordinates of the vector.

##### Params
• v as xs:double*: vector
##### Returns
• xs:double*: rounded vector
```declare function this:round(\$v as xs:double*) as xs:double*
{
this:map(function (\$c as xs:double) as xs:double {fn:round(\$c)}, \$v)
}```

#### `Function: timesdeclare function times(\$v as xs:double*, \$k as xs:double) as xs:double*`

times()
Scale every coordinate by a constant, returning the new vector.

##### Params
• v as xs:double*
• k as xs:double: constant multiplier
##### Returns
• xs:double*: scaled vector
```declare function this:times(\$v as xs:double*, \$k as xs:double) as xs:double*
{
this:map(function (\$c as xs:double) as xs:double {\$k * \$c}, \$v)
}```

#### `Function: multiplydeclare function multiply(\$u as xs:double*, \$v as xs:double*) as xs:double*`

multiply()
Coordinate-by-coordinate multiplication.

##### Params
• u as xs:double*: one vector
• v as xs:double*: another vector
##### Returns
• xs:double*: new vector
```declare function this:multiply(\$u as xs:double*, \$v as xs:double*) as xs:double*
{
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {\$a * \$b}, \$u, \$v)
}```

#### `Function: divdeclare function div(\$v as xs:double*, \$k as xs:double) as xs:double*`

div()
Divide every coordinate by a constant, returning the new vector.

##### Params
• v as xs:double*
• k as xs:double: constant multiplier
##### Returns
• xs:double*: scaled vector
```declare function this:div(\$v as xs:double*, \$k as xs:double) as xs:double*
{
this:map(function (\$c as xs:double) as xs:double {(\$c div \$k) cast as xs:double}, \$v)
}```

#### `Function: dividedeclare function divide(\$u as xs:double*, \$v as xs:double*) as xs:double*`

divide()
Coordinate-by-coordinate division

##### Params
• u as xs:double*: one vector
• v as xs:double*: another vector
##### Returns
• xs:double*: new vector
```declare function this:divide(\$u as xs:double*, \$v as xs:double*) as xs:double*
{
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {\$a div \$b}, \$u, \$v)
}```

#### `Function: moddeclare function mod(\$v as xs:double*, \$k as xs:double) as xs:double*`

mod()
Compute the mod of every coordinate by a constant, returning the new vector.

##### Params
• v as xs:double*
• k as xs:double: constant modulus
##### Returns
• xs:double*: new vector
```declare function this:mod(\$v as xs:double*, \$k as xs:double) as xs:double*
{
this:map(function (\$c as xs:double) as xs:double {\$c mod \$k}, \$v)
}```

#### `Function: modulodeclare function modulo(\$u as xs:double*, \$v as xs:double*) as xs:double*`

divide()
Coordinate-by-coordinate modulo.

##### Params
• u as xs:double*: one vector
• v as xs:double*: another vector
##### Returns
• xs:double*: new vector
```declare function this:modulo(\$u as xs:double*, \$v as xs:double*) as xs:double*
{
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {\$a mod \$b}, \$u, \$v)
}```

#### `Function: adddeclare function add(\$u as xs:double*, \$v as xs:double*) as xs:double*`

Add two vectors, returning the new vector.

##### Params
• u as xs:double*: one vector
• v as xs:double*: the other vector
##### Returns
• xs:double*: u+v
```declare function this:add(\$u as xs:double*, \$v as xs:double*) as xs:double*
{
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {\$a + \$b}, \$u, \$v)
}```

#### `Function: plusdeclare function plus(\$v as xs:double*, \$k as xs:double) as xs:double*`

plus()
Add a constant to every coordinate in a vector, returning the new vector.

##### Params
• v as xs:double*: the vector
• k as xs:double: constant to add
##### Returns
• xs:double*: new vector
```declare function this:plus(\$v as xs:double*, \$k as xs:double) as xs:double*
{
this:map(function (\$a as xs:double) as xs:double {\$a + \$k}, \$v)
}```

#### `Function: minusdeclare function minus(\$v as xs:double*) as xs:double*`

minus()
Negate the coordinates of the vector, returning the new vector.

##### Params
• v as xs:double*: the vector
##### Returns
• xs:double*: -v
```declare function this:minus(\$v as xs:double*) as xs:double*
{
this:map(function (\$a as xs:double) as xs:double {-\$a}, \$v)
}```

#### `Function: minusdeclare function minus(\$v as xs:double*, \$k as xs:double) as xs:double*`

minus()
Subtract a constant to every coordinate in a vector, returning the new vector.

##### Params
• v as xs:double*: the vector
• k as xs:double: constant to subtract
##### Returns
• xs:double*: new vector
```declare function this:minus(\$v as xs:double*, \$k as xs:double) as xs:double*
{
this:map(function (\$a as xs:double) as xs:double {\$a - \$k}, \$v)
}```

#### `Function: subdeclare function sub(\$u as xs:double*, \$v as xs:double*) as xs:double*`

sub()
Subtract one vector from another, returning the new vector.

##### Params
• u as xs:double*: one vector
• v as xs:double*: the other vector
##### Returns
• xs:double*: u-v
```declare function this:sub(\$u as xs:double*, \$v as xs:double*) as xs:double*
{
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {\$a - \$b}, \$u, \$v)
}```

#### `Function: absdeclare function abs(\$v as xs:double*) as xs:double*`

abs()
Coordinate-by-coordinate absolute value.

##### Params
• v as xs:double*: vector
##### Returns
• xs:double*: new vector
```declare function this:abs(\$v as xs:double*) as xs:double*
{
this:map(function (\$a as xs:double) as xs:double {fn:abs(\$a)}, \$v)
}```

#### `Function: signdeclare function sign(\$v as xs:double*) as xs:double*`

sign()
Coordinate-by-coordinate sign.

##### Params
• v as xs:double*: vector
##### Returns
• xs:double*: new vector
```declare function this:sign(\$v as xs:double*) as xs:double*
{
this:map(
function (\$a as xs:double) as xs:double {
if (\$a = 0) then 0E0
else if (\$a < 0) then -1E0
else 1E0
},
\$v
)
}```

#### `Function: mindeclare function min(\$u as xs:double*, \$v as xs:double*) as xs:double*`

min()
Coordinate-by-coordinate minimum: minimum of each coordinate pair.

##### Params
• u as xs:double*: one vector
• v as xs:double*: another vector
##### Returns
• xs:double*: new vector
```declare function this:min(\$u as xs:double*, \$v as xs:double*) as xs:double*
{
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {fn:min((\$a,\$b))}, \$u, \$v)
}```

#### `Function: maxdeclare function max(\$u as xs:double*, \$v as xs:double*) as xs:double*`

max()
Coordinate-by-coordinate maximum: maximum of each coordinate pair.

##### Params
• u as xs:double*: one vector
• v as xs:double*: another vector
##### Returns
• xs:double*: new vector
```declare function this:max(\$u as xs:double*, \$v as xs:double*) as xs:double*
{
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {fn:max((\$a,\$b))}, \$u, \$v)
}```

#### `Function: at-mindeclare function at-min(\$v as xs:double*, \$val as xs:double) as xs:double*`

at-min()
Coordinate-by-coordinate minimum: minimum of each coordinate with value.

##### Params
• v as xs:double*: vectoral: comparison value
• val as xs:double: comparison value
##### Returns
• xs:double*: new vector
```declare function this:at-min(\$v as xs:double*, \$val as xs:double) as xs:double*
{
this:map(function (\$a as xs:double) as xs:double {fn:min((\$a, \$val))}, \$v)
}```

#### `Function: at-maxdeclare function at-max(\$v as xs:double*, \$val as xs:double) as xs:double*`

at-max()
Coordinate-by-coordinate maximum: maximum of each coordinate with value.

##### Params
• v as xs:double*: vectoral: comparison value
• val as xs:double: comparison value
##### Returns
• xs:double*: new vector
```declare function this:at-max(\$v as xs:double*, \$val as xs:double) as xs:double*
{
this:map(function (\$a as xs:double) as xs:double {fn:max((\$a, \$val))}, \$v)
}```

#### `Function: normalizedeclare function normalize(\$v as xs:double*) as xs:double*`

normalize()
Normalize a vector represented as a vector (scale to unit vector), returning
the new vector.

##### Params
• v as xs:double*: the vector
##### Returns
• xs:double*: normalized vector
```declare function this:normalize(\$v as xs:double*) as xs:double*
{
let \$l := this:magnitude(\$v)
return
if (\$l = 0) then \$v
else this:map(function (\$c as xs:double) as xs:double {\$c div \$l}, \$v)
}```

#### `Function: clampdeclare function clamp(\$v as xs:double*, \$min as xs:double, \$max as xs:double) as xs:double*`

clamp()
Coordinate-by-coordinate clamping.

##### Params
• v as xs:double*: vector
• min as xs:double: minimum value
• max as xs:double: maximum value
##### Returns
• xs:double*: new vector
```declare function this:clamp(\$v as xs:double*, \$min as xs:double, \$max as xs:double) as xs:double*
{
this:map(function (\$a as xs:double) as xs:double {util:clamp(\$a, \$min, \$max)}, \$v)
}```

#### `Function: clampvdeclare function clampv(\$v as xs:double*, \$minv as xs:double*, \$maxv as xs:double*) as xs:double*`

clampv()
Coordinate-by-coordinate clamping: clamp with corresponding coordinates of
min/max vectors.

##### Params
• v as xs:double*: vector
• minv as xs:double*: minimum values
• maxv as xs:double*: maximum values
##### Returns
• xs:double*: new vector
```declare function this:clampv(\$v as xs:double*, \$minv as xs:double*, \$maxv as xs:double*) as xs:double*
{
this:map3(function (\$a as xs:double, \$min as xs:double?, \$max as xs:double?) as xs:double {util:clamp(\$a, \$min, \$max)}, \$v, \$minv, \$maxv)
}```

#### `Function: perpendiculardeclare function perpendicular(\$v as xs:double*) as xs:double*`

perpendicular()
Compute the perpendicular vector for a 2D vector represented as a vector.
(Rotated by 90 degrees.)

##### Params
• v as xs:double*: the vector
##### Returns
• xs:double*: perpendicular vector
```declare function this:perpendicular(\$v as xs:double*) as xs:double*
{
(this:py(\$v), -this:px(\$v))
}```

#### `Function: snapdeclare function snap(\$vector as xs:double*) as xs:integer*`

snap()
Snap the coordinates of the vector, returning the vector with snapped
(i.e. integer) coordinates. NaN snaps to UINT64_MAX.

##### Params
• vector as xs:double*: the vector
##### Returns
• xs:integer*
```declare function this:snap(\$vector as xs:double*) as xs:integer*
{
for-each(\$vector,
function (\$c as xs:double) as xs:integer {
if (\$c = xs:double("INF") or not(\$c = \$c)) then \$util:UINT64_MAX
else if (\$c = xs:double("-INF")) then -\$util:UINT64_MAX
else round-half-to-even(\$c) cast as xs:integer
}
)
}```

#### `Function: floordeclare function floor(\$vector as xs:double*) as xs:integer*`

floor()
Return vector where all the coordinates have been set to the floor of
the original vector's values. Differs from snap() for negative coordinates.

##### Params
• vector as xs:double*: the vector
##### Returns
• xs:integer*
```declare function this:floor(\$vector as xs:double*) as xs:integer*
{
for-each(\$vector, function (\$c as xs:double) as xs:integer {fn:floor(\$c) cast as xs:integer})
}```

#### `Function: decimaldeclare function decimal(\$vector as xs:double*, \$digits as xs:integer) as xs:double*`

decimal()
Perform decimal rounding on all the coordinates (see util:decimal).

##### Params
• vector as xs:double*: the vector to round
• digits as xs:integer: how many digits after the decimal vector to keep
##### Returns
• xs:double*
```declare function this:decimal(\$vector as xs:double*, \$digits as xs:integer) as xs:double*
{
for-each(\$vector, function (\$c as xs:double) as xs:double {util:decimal(\$c, \$digits)})
}```

#### `Function: quotedeclare function quote(\$vector as xs:double*) as xs:string`

quote()
Return a string value for the vector, suitable for debugging.

##### Params
• vector as xs:double*: the vector to quote
##### Returns
• xs:string
```declare function this:quote(\$vector as xs:double*) as xs:string
{
string-join(\$vector!string(.),",")
}```

#### `Function: validdeclare function valid(\$point as xs:double*) as xs:boolean`

valid()
False if a coordinate is INF or NaN

##### Params
• point as xs:double*
##### Returns
• xs:boolean
```declare function this:valid(\$point as xs:double*) as xs:boolean
{
every \$c in \$point
satisfies \$c=\$c and not(\$c=(xs:double("INF"),xs:double("-INF")))
}```

#### `Function: samedeclare function same(\$this as xs:double*, \$other as xs:double*) as xs:boolean`

same()
Equality comparison for vectors, ignoring annotation properties.
Return true() if they have equal coordinates.

##### Params
• this as xs:double*: one vector
• other as xs:double*: the vector to compare it to
##### Returns
• xs:boolean: this=other
```declare function this:same(\$this as xs:double*, \$other as xs:double*) as xs:boolean
{
let \$d := max((count(\$this), count(\$other)))
return (
deep-equal(
this:as-dimension(\$this, \$d),
this:as-dimension(\$other, \$d)
)
)
}```

#### `Function: distancedeclare function distance(\$v1 as xs:double*, \$v2 as xs:double*) as xs:double`

distance()
Euclidean distance between vectors

##### Params
• v1 as xs:double*: one vector
• v2 as xs:double*: another vector
##### Returns
• xs:double: distance(v1,v2)
```declare function this:distance(\$v1 as xs:double*, \$v2 as xs:double*) as xs:double
{
let \$d := fn:max((this:dimension(\$v1), this:dimension(\$v2)))
return (
math:sqrt(
fn:sum(
util:zip(
function (\$c1 as xs:double, \$c2 as xs:double) as xs:double {
(\$c1 - \$c2)*(\$c1 - \$c2)
},
this:as-dimension(\$v1, \$d),
this:as-dimension(\$v2, \$d)
)
)
)
)
}```

#### ```Function: polar-distancedeclare function polar-distance(\$p as xs:double*, \$q as xs:double*) as xs:double```

polar-distance()
Angular distance between points taken as vectors.

##### Params
• p as xs:double*
• q as xs:double*
##### Returns
• xs:double
```declare function this:polar-distance(
\$p as xs:double*,
\$q as xs:double*
) as xs:double
{
abs(this:angle((0,0), \$p) - this:angle((0,0), \$q))
}```

#### ```Function: taxi-distancedeclare function taxi-distance(\$p as xs:double*, \$q as xs:double*) as xs:double```

taxi-distance()
Taxi-cab distance between two points. That is, right angle distance, such as
a taxi going 2 blocks North plus 3 blocks East = distance 5 blocks.

##### Params
• p as xs:double*
• q as xs:double*
##### Returns
• xs:double
```declare function this:taxi-distance(
\$p as xs:double*,
\$q as xs:double*
) as xs:double
{
let \$d := max((count(\$p), count(\$q)))
return (
sum(
util:zip(
function (\$cp as xs:double, \$cq as xs:double) as xs:double {
abs(\$cp - \$cq)
},
this:as-dimension(\$p, \$d),
this:as-dimension(\$q, \$d)
)
)
)
}```

#### ```Function: avg-distancedeclare function avg-distance(\$p as xs:double*, \$q as xs:double*) as xs:double```

avg-distance()
Average of distances along each dimension.

##### Params
• p as xs:double*
• q as xs:double*
##### Returns
• xs:double
```declare function this:avg-distance(
\$p as xs:double*,
\$q as xs:double*
) as xs:double
{
let \$d := max((count(\$p), count(\$q)))
return (
avg(
util:zip(
function (\$cp as xs:double, \$cq as xs:double) as xs:double {
abs(\$cp - \$cq)
},
this:as-dimension(\$p, \$d),
this:as-dimension(\$q, \$d)
)
)
)
}```

#### ```Function: rail-distancedeclare function rail-distance(\$p as xs:double*, \$q as xs:double*) as xs:double```

rail-distance()
Railway distance, e.g distance to the central hub and back out.
The hub is the origin.

##### Params
• p as xs:double*
• q as xs:double*
##### Returns
• xs:double
```declare function this:rail-distance(
\$p as xs:double*,
\$q as xs:double*
) as xs:double
{
this:distance(\$p, (0,0)) + this:distance(\$q, (0,0))
}```

#### ```Function: rail-distancedeclare function rail-distance(\$p as xs:double*, \$q as xs:double*, \$origin as xs:double*) as xs:double```

rail-distance()
Railway distance, e.g distance to the central hub and back out where the hub
is specified.

##### Params
• p as xs:double*
• q as xs:double*
• origin as xs:double*
##### Returns
• xs:double
```declare function this:rail-distance(
\$p as xs:double*,
\$q as xs:double*,
\$origin as xs:double*
) as xs:double
{
this:distance(\$p, \$origin) + this:distance(\$q, \$origin)
}```

#### ```Function: chebyshev-distancedeclare function chebyshev-distance(\$p as xs:double*, \$q as xs:double*) as xs:double```

chebyshev-distance()
Chebyshev distance metric. The maximum of the dimensional distances.

##### Params
• p as xs:double*
• q as xs:double*
##### Returns
• xs:double
```declare function this:chebyshev-distance(
\$p as xs:double*,
\$q as xs:double*
) as xs:double
{
let \$d := max((count(\$p), count(\$q)))
return (
max(
util:zip(
function (\$cp as xs:double, \$cq as xs:double) as xs:double {
abs(\$cp - \$cq)
},
this:as-dimension(\$p, \$d),
this:as-dimension(\$q, \$d)
)
)
)
}```

#### ```Function: minkowski-distancedeclare function minkowski-distance(\$p as xs:double*, \$q as xs:double*, \$order as xs:double) as xs:double```

minwokski-distance()
Minkowski distance metric of the given order. Certain orders give rise to
other metrics:
order=1=>taxi; order=2=>Euclidean; as order=>∞=>Chebyshev;
order<1 is not a metric unless undo the final pow. i.e. X is a metric but
X^(1/order) is not.

##### Params
• p as xs:double*
• q as xs:double*
• order as xs:double
##### Returns
• xs:double
```declare function this:minkowski-distance(
\$p as xs:double*,
\$q as xs:double*,
\$order as xs:double
) as xs:double
{
let \$d := max((count(\$p), count(\$q)))
return (
math:pow(
sum(
util:zip(
function (\$cp as xs:double, \$cq as xs:double) as xs:double {
math:pow(abs(\$cp - \$cq), \$order)
},
this:as-dimension(\$p, \$d),
this:as-dimension(\$q, \$d)
)
),
1 div \$order
)
)
}```

#### ```Function: cosine-similaritydeclare function cosine-similarity(\$p as xs:double*, \$q as xs:double*) as xs:double```

cosine-similarity()
Cosine similarity: (A·B)/(∥A∥∥B∥) = ΣAiBi/(√Ai²√Bi²)

##### Params
• p as xs:double*
• q as xs:double*
##### Returns
• xs:double
```declare function this:cosine-similarity(
\$p as xs:double*,
\$q as xs:double*
) as xs:double
{
this:dot(\$p, \$q) div (this:magnitude(\$p)*this:magnitude(\$q))
}```

#### ```Function: cosine-distancedeclare function cosine-distance(\$p as xs:double*, \$q as xs:double*) as xs:double```

cosine-distance()
Cosine similarity as a metric: acos(similarity)/π => [0,1]

##### Params
• p as xs:double*
• q as xs:double*
##### Returns
• xs:double
```declare function this:cosine-distance(
\$p as xs:double*,
\$q as xs:double*
) as xs:double
{
math:acos(this:cosine-similarity(\$p, \$q)) div math:pi()
}```

#### `Function: angledeclare function angle(\$last as xs:double*, \$curr as xs:double*) as xs:double`

angle()
Compute the angle (azimuth) from one point to the next, in degrees
Return 0 if points are the same

##### Params
• last as xs:double*: previous point; use (0,0) if no previous
• curr as xs:double*: current point
##### Returns
• xs:double
```declare function this:angle(\$last as xs:double*, \$curr as xs:double*) as xs:double
{
let \$this := \$curr=>this:as-dimension(2)
let \$prev := if (empty(\$last)) then (0,0) else this:as-dimension(\$last,2)
return
if (this:same(\$prev, \$this) or (this:distance(\$prev,\$this) < \$config:ε)) then 0
else if (this:px(\$this)=this:px(\$prev)) then (
if (this:py(\$prev) > this:py(\$this))
then 270 (: remapped -90 :)
else 90
) else (
util:remap-degrees(util:degrees(
(math:pi() div 2) - math:atan2(this:px(\$this) - this:px(\$prev), this:py(\$this) - this:py(\$prev))
))
)
}```

#### `Function: inclinationdeclare function inclination(\$last as xs:double*, \$curr as xs:double*) as xs:double`

inclination()
Compute the inclination angle from one point in the next, in degrees
Return 90 if the points are the same

##### Params
• last as xs:double*: previous point; use (0,0,0) if no previous
• curr as xs:double*: current point
##### Returns
• xs:double
```declare function this:inclination(\$last as xs:double*, \$curr as xs:double*) as xs:double
{
let \$curr := \$curr=>this:as-dimension(3)
let \$prev := if (empty(\$last)) then (0,0,0) else \$last=>this:as-dimension(3)
let \$d := this:distance(\$prev,\$curr)
return
if (this:same(\$prev,\$curr) or (\$d < \$config:ε)) then 90
else (
util:remap-degrees(util:degrees(
math:acos( (this:pz(\$curr) - this:pz(\$prev)) div \$d )
))
)
}```

#### ```Function: destinationdeclare function destination(\$point as xs:double*, \$degrees as xs:double, \$length as xs:double) as xs:double*```

destination()
Point at a particular distance and direction.

##### Params
• point as xs:double*: starting point
• degrees as xs:double: bearing from point
• length as xs:double: how far along bearing to travel
##### Returns
• xs:double*
```declare function this:destination(
\$point as xs:double*,
\$degrees as xs:double,
\$length as xs:double
) as xs:double*
{
return
(
this:px(\$point) + math:cos(\$angle)*\$length,
this:py(\$point) + math:sin(\$angle)*\$length
)
}```

#### ```Function: mutatedeclare function mutate(\$vectors as xs:double*, \$mutate as function(xs:double*) as xs:double*) as xs:double*```

mutate()
Mutate a sequence of 2D vectors using a mutation function.

##### Params
• vectors as xs:double*: the vectors
• mutate as function(xs:double*)asxs:double*: function that takes a vector as an argument and returns a new vector
##### Returns
• xs:double*
```declare function this:mutate(
\$vectors as xs:double*,
\$mutate as function(xs:double*) as xs:double*
) as xs:double*
{
for \$i in 1 to count(\$vectors) idiv 2 return (
\$mutate((\$vectors[2*\$i - 1], \$vectors[2*\$i]))
)
}```

### Original Source Code

```xquery version "3.1";
(:~
: Vectors of numbers
:
: @since May 2021
: @custom:Status Incomplete
:)
module namespace this="http://mathling.com/core/vector";

import module namespace config="http://mathling.com/core/config"
at "../core/config.xqy";
import module namespace errors="http://mathling.com/core/errors"
at "../core/errors.xqy";
import module namespace util="http://mathling.com/core/utilities"
at "../core/utilities.xqy";

declare namespace math="http://www.w3.org/2005/xpath-functions/math";

(:~
: as-dimension()
: Cast the vector to a vector with the given number of dimensions.
: If we are reducing dimensions, just remove excess coordinates.
: If we are expanding dimensions, fill in with 0.
:
: @param \$vector: source vector
: @param \$dim: new dimension of vector
: @return new vector
:)
declare function this:as-dimension(\$vector as xs:double*, \$dim as xs:integer) as xs:double*
{
(\$vector, for \$i in 1 to \$dim return 0E0)[position() <= \$dim]
};

(:~
: unquote()
: Parse a string representation of a vector and produce a vector.
: The string consists of the coordinates in with commas and
: optional spaces in between.
:
: @param \$string: the input string to parse
: @return vector
:)
declare function this:unquote(\$string as xs:string) as xs:double*
{
tokenize(\$string, "[ ]*,[ ]*")!xs:double(.)
};

(:~
: dimension()
: Return the dimension of the vector.
:
: @param \$vector: the vector
: @return length of vector
:)
declare function this:dimension(\$vector as xs:double*) as xs:integer
{
count(\$vector)
};

(:~
: map()
: Map a function over the coordinates of a vector, returning the new vector.
:
: @param \$f: function from coordinate to coordinate
: @param \$vector: input vector
: @return application of function to each coordinate
:)
declare function this:map(
\$f as function(xs:double) as xs:double,
\$vector as xs:double*
) as xs:double*
{
for-each(\$vector, \$f)
};

(:~
: map()
: Map a function over the coordinates of a vector, returning the new vector.
: Treat the vector as having a particular dimension.
:
: @param \$f: function from coordinate to coordinate
: @param \$vector: input vector
: @param \$d: the effective dimension of the vector
: @return application of function to coordinates
:)
declare function this:map(
\$f as function(xs:double) as xs:double,
\$vector as xs:double*,
\$d as xs:integer
) as xs:double*
{
for-each(this:as-dimension(\$vector, \$d), \$f)
};

(:~
: map2()
: Map a function over the coordinates of two vectors, pair by pair, returning
: the new vector. That is, apply function to the x coordinates, then the
: y coordinates, and so on.
:
: @param \$f: function from coordinate pairs to coordinate
: @param \$p1: one input vector
: @param \$p2: another input vector
: @return application of function to each pair
:)
declare function this:map2(
\$f as function(xs:double, xs:double) as xs:double,
\$p1 as xs:double*,
\$p2 as xs:double*
) as xs:double*
{
util:zip(\$f, \$p1, \$p2)
};

(:~
: map2()
: Map a function over the coordinates of two vectors, pair by pair, returning
: the new vector. That is, apply function to the x coordinates, then the
: y coordinates, and so on. Treat the vector as having a particular dimension.
:
: @param \$f: function from coordinate pairs to coordinate
: @param \$p1: one input vector
: @param \$p2: another input vector
: @param \$d: the effective dimension of the vectors
: @return application of function to each pair
:)
declare function this:map2(
\$f as function(xs:double, xs:double) as xs:double,
\$p1 as xs:double*,
\$p2 as xs:double*,
\$d as xs:integer
) as xs:double*
{
util:zip(\$f, this:as-dimension(\$p1, \$d), this:as-dimension(\$p2, \$d))
};

(:~
: map3()
: Map a function over the coordinates of three vectors, triple by triple,
: returning the new vector. That is, apply function to the x coordinates,
: then the y coordinates, and so on.
:
: @param \$f: function from coordinate pairs to coordinate
: @param \$p1: one input vector
: @param \$p2: another input vector
: @param \$p3: third input vector
: @return application of function to each triple
:)
declare function this:map3(
\$f as function(xs:double, xs:double, xs:double) as xs:double,
\$p1 as xs:double*,
\$p2 as xs:double*,
\$p3 as xs:double*
) as xs:double*
{
util:zip(
function (\$a as xs:double, \$fc as function(xs:double) as xs:double) as xs:double {\$fc(\$a)},
\$p3,
util:zip(
function (\$a as xs:double, \$b as xs:double) as function(*) { \$f(\$a, \$b, ?) },
\$p1, \$p2
)
)
};

(:~
: map3()
: Map a function over the coordinates of three vectors, triple by triple,
: returning the new vector. That is, apply function to the x coordinates,
: then the y coordinates, and so on. Treat the vector as having a
: particular dimension.
:
: @param \$f: function from coordinate pairs to coordinate
: @param \$p1: one input vector
: @param \$p2: another input vector
: @param \$p3: third input vector
: @param \$d: the effective dimension of the vectors
: @return application of function to each triple
:)
declare function this:map3(
\$f as function(xs:double, xs:double, xs:double) as xs:double,
\$p1 as xs:double*,
\$p2 as xs:double*,
\$p3 as xs:double*,
\$d as xs:integer
) as xs:double*
{
util:zip(
function (\$a as xs:double, \$fc as function(xs:double) as xs:double) as xs:double {\$fc(\$a)},
this:as-dimension(\$p3, \$d),
util:zip(
function (\$a as xs:double, \$b as xs:double) as function(*) { \$f(\$a, \$b, ?) },
this:as-dimension(\$p1, \$d), this:as-dimension(\$p2, \$d)
)
)
};

(:~
: map4()
: Map a function over the coordinates of four vectors, coordinate by
: coordinate, returning the new vector. That is, apply function to the x
: coordinates, then the y coordinates, and so on.
:
: @param \$f: function from coordinate pairs to coordinate
: @param \$p1: one input vector
: @param \$p2: another input vector
: @param \$p3: third input vector
: @param \$p4: fourth input vector
: @return application of function to each quad
:)
declare function this:map4(
\$f as function(xs:double, xs:double, xs:double, xs:double) as xs:double,
\$p1 as xs:double*,
\$p2 as xs:double*,
\$p3 as xs:double*,
\$p4 as xs:double*
) as xs:double*
{
util:zip(
function (\$d as xs:double, \$fd as function(xs:double) as xs:double) as xs:double {\$fd(\$d)},
\$p4,
util:zip(
function (\$c as xs:double, \$fc as function(xs:double, xs:double) as xs:double) as function(*) {\$fc(\$c, ?)},
\$p3,
util:zip(
function (\$a as xs:double, \$b as xs:double) as function(*) { \$f(\$a, \$b, ?, ?) },
\$p1, \$p2
)
)
)
};

(:~
: map4()
: Map a function over the coordinates of four vectors, coordinate by
: coordinate, returning the new vector. That is, apply function to the x
: coordinates, then the y coordinates, and so on. Treat the vector as having a
: particular dimension.
:
: @param \$f: function from coordinate pairs to coordinate
: @param \$p1: one input vector
: @param \$p2: another input vector
: @param \$p3: third input vector
: @param \$p4: fourth input vector
: @param \$d: the effective dimension of the vectors
: @return application of function to each quad
:)
declare function this:map4(
\$f as function(xs:double, xs:double, xs:double, xs:double) as xs:double,
\$p1 as xs:double*,
\$p2 as xs:double*,
\$p3 as xs:double*,
\$p4 as xs:double*,
\$dim as xs:integer
) as xs:double*
{
util:zip(
function (\$d as xs:double, \$fd as function(xs:double) as xs:double) as xs:double {\$fd(\$d)},
this:as-dimension(\$p4, \$dim),
util:zip(
function (\$c as xs:double, \$fc as function(xs:double, xs:double) as xs:double) as function(*) {\$fc(\$c, ?)},
this:as-dimension(\$p3, \$dim),
util:zip(
function (\$a as xs:double, \$b as xs:double) as function(*) { \$f(\$a, \$b, ?, ?) },
this:as-dimension(\$p1, \$dim), this:as-dimension(\$p2, \$dim)
)
)
)
};

(:~
: px()
: Accessor for the raw precision x coordinate
:
: @param \$vector: the vector
: @return raw x coordinate
:)
declare function this:px(\$vector as xs:double*) as xs:double
{
};

(:~
: py()
: Accessor for the raw precision y coordinate
:
: @param \$vector: the vector
: @return raw y coordinate
:)
declare function this:py(\$vector as xs:double*) as xs:double
{
};

(:~
: pz()
: Accessor for the raw precision z coordinate; returns 0 for 2D vector
:
: @param \$vector: the vector
: @return raw z coordinate
:)
declare function this:pz(\$vector as xs:double*) as xs:double
{
};

(:~
: pw()
: Accessor for the raw precision w coordinate; returns 0 for 2D or 3D vector
:
: @param \$vector: the vector
: @return raw w coordinate
:)
declare function this:pw(\$vector as xs:double*) as xs:double
{
};

(:~
: pxy()
: Accessor for the raw precision xy vector.
:
: @param \$vector: the vector
: @return new vector of just the raw x and y coordinates
:)
declare function this:pxy(\$vector as xs:double*) as xs:double*
{
};

(:~
: pxz()
: Accessor for the raw precision xz vector.
:
: @param \$vector: the vector
: @return new vector of just the raw x and z coordinates
:)
declare function this:pxz(\$vector as xs:double*) as xs:double*
{
};

(:~
: x()
: Accessor for the snapped x coordinate
:
: @param \$vector: the vector
: @return x coordinate, rounded
:)
declare function this:x(\$vector as xs:double*) as xs:integer
{
return (
if (\$x = xs:double("INF")) then \$util:UINT64_MAX
else if (\$x = xs:double("-INF")) then -\$util:UINT64_MAX
else round-half-to-even(\$x) cast as xs:integer
)
};

(:~
: y()
: Accessor for the snapped y coordinate
:
: @param \$vector: the vector
: @return y coordinate, rounded
:)
declare function this:y(\$vector as xs:double*) as xs:integer
{
return (
if (\$y = xs:double("INF")) then \$util:UINT64_MAX
else if (\$y = xs:double("-INF")) then -\$util:UINT64_MAX
else round-half-to-even(\$y) cast as xs:integer
)
};

(:~
: z()
: Accessor for the snapped z coordinate
:
: @param \$vector: the vector
: @return z coordinate, rounded
:)
declare function this:z(\$vector as xs:double*) as xs:integer
{
return (
if (\$z = xs:double("INF")) then \$util:UINT64_MAX
else if (\$z = xs:double("-INF")) then -\$util:UINT64_MAX
else round-half-to-even(\$z) cast as xs:integer
)
};

(:~
: w()
: Accessor for the snapped w coordinate
:
: @param \$vector: the vector
: @return w coordinate, rounded
:)
declare function this:w(\$vector as xs:double*) as xs:integer
{
return (
if (\$w = xs:double("INF")) then \$util:UINT64_MAX
else if (\$w = xs:double("-INF")) then -\$util:UINT64_MAX
else round-half-to-even(\$w) cast as xs:integer
)
};

(:~
: magnitude()
: Magnitude of the vector from origin.
:
: @param \$v: the vector
: @return Euclidean length of vector
:)
declare function this:magnitude(\$v as xs:double*) as xs:double
{
math:sqrt(
fn:sum(for \$c in \$v return \$c*\$c)
)
};

(:~
: dot()
: Compute the dot product of two vectors
:
: @param \$p1: one vector
: @param \$p2: another vector
: @return p1·p2
:)
declare function this:dot(\$p1 as xs:double*, \$p2 as xs:double*) as xs:double
{
fn:sum(
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {\$a * \$b}, \$p1, \$p2)
)
};

(:~
: dot2()
: Compute the dot product of a vector with itself
:
: @param \$p: the vector
: @return p·p
:)
declare function this:dot2(\$p1 as xs:double*) as xs:double
{
fn:sum(
this:map(function (\$a as xs:double) as xs:double {\$a * \$a}, \$p1)
)
};

(:~
: Negated dot: useful for some of the SDF functions.
:
: @param \$p1: one vector
: @param \$p2: another vector
: @return p1·-p2
:)
declare function this:ndot(\$p1 as xs:double*, \$p2 as xs:double*) as xs:double
{
fn:sum(
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {\$a * -\$b}, \$p1, \$p2)
)
};

(:~
: Linear combination of two vectors, coordinate by coordinate.
: Generally \$f in in [0,1], but you can
: get extrapolations with numbers outside that range.
:
: @param \$u: one vector
: @param \$v: other vector
: @param \$f: combination fraction
: @return vector of linear combination of each coordinate pair
:)
declare function this:mix(
\$u as xs:double*,
\$v as xs:double*,
\$f as xs:double
) as xs:double*
{
(: Logically \$a + (\$b - \$a) * \$f but better endvector behaviour :)
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {\$a * (1 - \$f) + \$b * \$f}, \$u, \$v)
};

(:~
: determinant()
: Compute the determinant of two 2D vectors.
:
: @param \$u: one vector
: @param \$v: another vector
: @param det(u,v)
:)
declare function this:determinant(\$u as xs:double*, \$v as xs:double*) as xs:double
{
this:px(\$u)*this:py(\$v) - this:py(\$u)*this:px(\$v)
};

(:~
: cross()
: Compute the cross product of two 3D vectors.
:
: @param \$u: one vector
: @param \$v: another vector
: @return uXv
:)
declare function this:cross(\$u as xs:double*, \$v as xs:double*) as xs:double*
{
this:py(\$u)*this:pz(\$v) - this:pz(\$u)*this:py(\$v),
this:pz(\$u)*this:px(\$v) - this:px(\$u)*this:pz(\$v),
this:px(\$u)*this:py(\$v) - this:py(\$u)*this:px(\$v)
};

(:~
: round()
: Round the coordinates of the vector.
:
: @param \$v: vector
: @return rounded vector
:)
declare function this:round(\$v as xs:double*) as xs:double*
{
this:map(function (\$c as xs:double) as xs:double {fn:round(\$c)}, \$v)
};

(:~
: times()
: Scale every coordinate by a constant, returning the new vector.
:
: @param \$u: vector to scale
: @param \$k: constant multiplier
: @return scaled vector
:)
declare function this:times(\$v as xs:double*, \$k as xs:double) as xs:double*
{
this:map(function (\$c as xs:double) as xs:double {\$k * \$c}, \$v)
};

(:~
: multiply()
: Coordinate-by-coordinate multiplication.
:
: @param \$u: one vector
: @param \$v: another vector
: @return new vector
:)
declare function this:multiply(\$u as xs:double*, \$v as xs:double*) as xs:double*
{
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {\$a * \$b}, \$u, \$v)
};

(:~
: div()
: Divide every coordinate by a constant, returning the new vector.
:
: @param \$u: vector to scale
: @param \$k: constant multiplier
: @return scaled vector
:)
declare function this:div(\$v as xs:double*, \$k as xs:double) as xs:double*
{
this:map(function (\$c as xs:double) as xs:double {(\$c div \$k) cast as xs:double}, \$v)
};

(:~
: divide()
: Coordinate-by-coordinate division
:
: @param \$u: one vector
: @param \$v: another vector
: @return new vector
:)
declare function this:divide(\$u as xs:double*, \$v as xs:double*) as xs:double*
{
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {\$a div \$b}, \$u, \$v)
};

(:~
: mod()
: Compute the mod of every coordinate by a constant, returning the new vector.
:
: @param \$u: vector
: @param \$k: constant modulus
: @return new vector
:)
declare function this:mod(\$v as xs:double*, \$k as xs:double) as xs:double*
{
this:map(function (\$c as xs:double) as xs:double {\$c mod \$k}, \$v)
};

(:~
: divide()
: Coordinate-by-coordinate modulo.
:
: @param \$u: one vector
: @param \$v: another vector
: @return new vector
:)
declare function this:modulo(\$u as xs:double*, \$v as xs:double*) as xs:double*
{
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {\$a mod \$b}, \$u, \$v)
};

(:~
: Add two vectors, returning the new vector.
:
: @param \$u: one vector
: @param \$v: the other vector
: @return u+v
:)
declare function this:add(\$u as xs:double*, \$v as xs:double*) as xs:double*
{
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {\$a + \$b}, \$u, \$v)
};

(:~
: plus()
: Add a constant to every coordinate in a vector, returning the new vector.
:
: @param \$v: the vector
: @param \$k: constant to add
: @return new vector
:)
declare function this:plus(\$v as xs:double*, \$k as xs:double) as xs:double*
{
this:map(function (\$a as xs:double) as xs:double {\$a + \$k}, \$v)
};

(:~
: minus()
: Negate the coordinates of the vector, returning the new vector.
:
: @param \$v: the vector
: @return -v
:)
declare function this:minus(\$v as xs:double*) as xs:double*
{
this:map(function (\$a as xs:double) as xs:double {-\$a}, \$v)
};

(:~
: minus()
: Subtract a constant to every coordinate in a vector, returning the new vector.
:
: @param \$v: the vector
: @param \$k: constant to subtract
: @return new vector
:)
declare function this:minus(\$v as xs:double*, \$k as xs:double) as xs:double*
{
this:map(function (\$a as xs:double) as xs:double {\$a - \$k}, \$v)
};

(:~
: sub()
: Subtract one vector from another, returning the new vector.
:
: @param \$u: one vector
: @param \$v: the other vector
: @return u-v
:)
declare function this:sub(\$u as xs:double*, \$v as xs:double*) as xs:double*
{
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {\$a - \$b}, \$u, \$v)
};

(:~
: abs()
: Coordinate-by-coordinate absolute value.
:
: @param \$v: vector
: @return new vector
:)
declare function this:abs(\$v as xs:double*) as xs:double*
{
this:map(function (\$a as xs:double) as xs:double {fn:abs(\$a)}, \$v)
};

(:~
: sign()
: Coordinate-by-coordinate sign.
:
: @param \$v: vector
: @return new vector
:)
declare function this:sign(\$v as xs:double*) as xs:double*
{
this:map(
function (\$a as xs:double) as xs:double {
if (\$a = 0) then 0E0
else if (\$a < 0) then -1E0
else 1E0
},
\$v
)
};

(:~
: min()
: Coordinate-by-coordinate minimum: minimum of each coordinate pair.
:
: @param \$u: one vector
: @param \$v: another vector
: @return new vector
:)
declare function this:min(\$u as xs:double*, \$v as xs:double*) as xs:double*
{
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {fn:min((\$a,\$b))}, \$u, \$v)
};

(:~
: max()
: Coordinate-by-coordinate maximum: maximum of each coordinate pair.
:
: @param \$u: one vector
: @param \$v: another vector
: @return new vector
:)
declare function this:max(\$u as xs:double*, \$v as xs:double*) as xs:double*
{
this:map2(function (\$a as xs:double, \$b as xs:double) as xs:double {fn:max((\$a,\$b))}, \$u, \$v)
};

(:~
: at-min()
: Coordinate-by-coordinate minimum: minimum of each coordinate with value.
:
: @param \$v: vector
: @param \$val: comparison value
: @return new vector
:)
declare function this:at-min(\$v as xs:double*, \$val as xs:double) as xs:double*
{
this:map(function (\$a as xs:double) as xs:double {fn:min((\$a, \$val))}, \$v)
};

(:~
: at-max()
: Coordinate-by-coordinate maximum: maximum of each coordinate with value.
:
: @param \$v: vector
: @param \$val: comparison value
: @return new vector
:)
declare function this:at-max(\$v as xs:double*, \$val as xs:double) as xs:double*
{
this:map(function (\$a as xs:double) as xs:double {fn:max((\$a, \$val))}, \$v)
};

(:~
: normalize()
: Normalize a vector represented as a vector (scale to unit vector), returning
: the new vector.
:
: @param \$v: the vector
: @return normalized vector
:)
declare function this:normalize(\$v as xs:double*) as xs:double*
{
let \$l := this:magnitude(\$v)
return
if (\$l = 0) then \$v
else this:map(function (\$c as xs:double) as xs:double {\$c div \$l}, \$v)
};

(:~
: clamp()
: Coordinate-by-coordinate clamping.
:
: @param \$v: vector
: @param \$min: minimum value
: @param \$max: maximum value
: @return new vector
:)
declare function this:clamp(\$v as xs:double*, \$min as xs:double, \$max as xs:double) as xs:double*
{
this:map(function (\$a as xs:double) as xs:double {util:clamp(\$a, \$min, \$max)}, \$v)
};

(:~
: clampv()
: Coordinate-by-coordinate clamping: clamp with corresponding coordinates of
: min/max vectors.
:
: @param \$v: vector
: @param \$minv: minimum values
: @param \$maxv: maximum values
: @return new vector
:)
declare function this:clampv(\$v as xs:double*, \$minv as xs:double*, \$maxv as xs:double*) as xs:double*
{
this:map3(function (\$a as xs:double, \$min as xs:double?, \$max as xs:double?) as xs:double {util:clamp(\$a, \$min, \$max)}, \$v, \$minv, \$maxv)
};

(:~
: perpendicular()
: Compute the perpendicular vector for a 2D vector represented as a vector.
: (Rotated by 90 degrees.)
:
: @param \$v: the vector
: @return perpendicular vector
:)
declare function this:perpendicular(\$v as xs:double*) as xs:double*
{
(this:py(\$v), -this:px(\$v))
};

(:~
: snap()
: Snap the coordinates of the vector, returning the vector with snapped
: (i.e. integer) coordinates. NaN snaps to UINT64_MAX.
:
: @param \$vector: the vector
:)
declare function this:snap(\$vector as xs:double*) as xs:integer*
{
for-each(\$vector,
function (\$c as xs:double) as xs:integer {
if (\$c = xs:double("INF") or not(\$c = \$c)) then \$util:UINT64_MAX
else if (\$c = xs:double("-INF")) then -\$util:UINT64_MAX
else round-half-to-even(\$c) cast as xs:integer
}
)
};

(:~
: floor()
: Return vector where all the coordinates have been set to the floor of
: the original vector's values. Differs from snap() for negative coordinates.
:
: @param \$vector: the vector
:)
declare function this:floor(\$vector as xs:double*) as xs:integer*
{
for-each(\$vector, function (\$c as xs:double) as xs:integer {fn:floor(\$c) cast as xs:integer})
};

(:~
: decimal()
: Perform decimal rounding on all the coordinates (see util:decimal).
:
: @param \$vector: the vector to round
: @param \$digits: how many digits after the decimal vector to keep
:)
declare function this:decimal(\$vector as xs:double*, \$digits as xs:integer) as xs:double*
{
for-each(\$vector, function (\$c as xs:double) as xs:double {util:decimal(\$c, \$digits)})
};

(:~
: quote()
: Return a string value for the vector, suitable for debugging.
:
: @param \$vector: the vector to quote
:)
declare function this:quote(\$vector as xs:double*) as xs:string
{
string-join(\$vector!string(.),",")
};

(:~
: valid()
: False if a coordinate is INF or NaN
:)
declare function this:valid(\$point as xs:double*) as xs:boolean
{
every \$c in \$point
satisfies \$c=\$c and not(\$c=(xs:double("INF"),xs:double("-INF")))
};

(:~
: same()
: Equality comparison for vectors, ignoring annotation properties.
: Return true() if they have equal coordinates.
:
: @param \$this: one vector
: @param \$other: the vector to compare it to
: @return this=other
:)
declare function this:same(\$this as xs:double*, \$other as xs:double*) as xs:boolean
{
let \$d := max((count(\$this), count(\$other)))
return (
deep-equal(
this:as-dimension(\$this, \$d),
this:as-dimension(\$other, \$d)
)
)
};

(:~
: distance()
: Euclidean distance between vectors
:
: @param \$v1: one vector
: @param \$v2: another vector
: @return distance(v1,v2)
:)
declare function this:distance(\$v1 as xs:double*, \$v2 as xs:double*) as xs:double
{
let \$d := fn:max((this:dimension(\$v1), this:dimension(\$v2)))
return (
math:sqrt(
fn:sum(
util:zip(
function (\$c1 as xs:double, \$c2 as xs:double) as xs:double {
(\$c1 - \$c2)*(\$c1 - \$c2)
},
this:as-dimension(\$v1, \$d),
this:as-dimension(\$v2, \$d)
)
)
)
)
};

(:~
: polar-distance()
: Angular distance between points taken as vectors.
:)
declare function this:polar-distance(
\$p as xs:double*,
\$q as xs:double*
) as xs:double
{
abs(this:angle((0,0), \$p) - this:angle((0,0), \$q))
};

(:~
: taxi-distance()
: Taxi-cab distance between two points. That is, right angle distance, such as
: a taxi going 2 blocks North plus 3 blocks East = distance 5 blocks.
:)
declare function this:taxi-distance(
\$p as xs:double*,
\$q as xs:double*
) as xs:double
{
let \$d := max((count(\$p), count(\$q)))
return (
sum(
util:zip(
function (\$cp as xs:double, \$cq as xs:double) as xs:double {
abs(\$cp - \$cq)
},
this:as-dimension(\$p, \$d),
this:as-dimension(\$q, \$d)
)
)
)
};

(:~
: avg-distance()
: Average of distances along each dimension.
:)
declare function this:avg-distance(
\$p as xs:double*,
\$q as xs:double*
) as xs:double
{
let \$d := max((count(\$p), count(\$q)))
return (
avg(
util:zip(
function (\$cp as xs:double, \$cq as xs:double) as xs:double {
abs(\$cp - \$cq)
},
this:as-dimension(\$p, \$d),
this:as-dimension(\$q, \$d)
)
)
)
};

(:~
: rail-distance()
: Railway distance, e.g distance to the central hub and back out.
: The hub is the origin.
:)
declare function this:rail-distance(
\$p as xs:double*,
\$q as xs:double*
) as xs:double
{
this:distance(\$p, (0,0)) + this:distance(\$q, (0,0))
};

(:~
: rail-distance()
: Railway distance, e.g distance to the central hub and back out where the hub
: is specified.
:)
declare function this:rail-distance(
\$p as xs:double*,
\$q as xs:double*,
\$origin as xs:double*
) as xs:double
{
this:distance(\$p, \$origin) + this:distance(\$q, \$origin)
};

(:~
: chebyshev-distance()
: Chebyshev distance metric. The maximum of the dimensional distances.
:)
declare function this:chebyshev-distance(
\$p as xs:double*,
\$q as xs:double*
) as xs:double
{
let \$d := max((count(\$p), count(\$q)))
return (
max(
util:zip(
function (\$cp as xs:double, \$cq as xs:double) as xs:double {
abs(\$cp - \$cq)
},
this:as-dimension(\$p, \$d),
this:as-dimension(\$q, \$d)
)
)
)
};

(:~
: minwokski-distance()
: Minkowski distance metric of the given order. Certain orders give rise to
: other metrics:
: order=1=>taxi; order=2=>Euclidean; as order=>∞=>Chebyshev;
: order<1 is not a metric unless undo the final pow. i.e. X is a metric but
:   X^(1/order) is not.
:)
declare function this:minkowski-distance(
\$p as xs:double*,
\$q as xs:double*,
\$order as xs:double
) as xs:double
{
let \$d := max((count(\$p), count(\$q)))
return (
math:pow(
sum(
util:zip(
function (\$cp as xs:double, \$cq as xs:double) as xs:double {
math:pow(abs(\$cp - \$cq), \$order)
},
this:as-dimension(\$p, \$d),
this:as-dimension(\$q, \$d)
)
),
1 div \$order
)
)
};

(:~
: cosine-similarity()
: Cosine similarity: (A·B)/(∥A∥∥B∥) = ΣAiBi/(√Ai²√Bi²)
:)
declare function this:cosine-similarity(
\$p as xs:double*,
\$q as xs:double*
) as xs:double
{
this:dot(\$p, \$q) div (this:magnitude(\$p)*this:magnitude(\$q))
};

(:~
: cosine-distance()
: Cosine similarity as a metric: acos(similarity)/π => [0,1]
:)
declare function this:cosine-distance(
\$p as xs:double*,
\$q as xs:double*
) as xs:double
{
math:acos(this:cosine-similarity(\$p, \$q)) div math:pi()
};

(:~
: angle()
: Compute the angle (azimuth) from one point to the next, in degrees
: Return 0 if points are the same
:
: @param \$last: previous point; use (0,0) if no previous
: @param \$curr: current point
:)
declare function this:angle(\$last as xs:double*, \$curr as xs:double*) as xs:double
{
let \$this := \$curr=>this:as-dimension(2)
let \$prev := if (empty(\$last)) then (0,0) else this:as-dimension(\$last,2)
return
if (this:same(\$prev, \$this) or (this:distance(\$prev,\$this) < \$config:ε)) then 0
else if (this:px(\$this)=this:px(\$prev)) then (
if (this:py(\$prev) > this:py(\$this))
then 270 (: remapped -90 :)
else 90
) else (
util:remap-degrees(util:degrees(
(math:pi() div 2) - math:atan2(this:px(\$this) - this:px(\$prev), this:py(\$this) - this:py(\$prev))
))
)
};

(:~
: inclination()
: Compute the inclination angle from one point in the next, in degrees
: Return 90 if the points are the same
:
: @param \$last: previous point; use (0,0,0) if no previous
: @param \$curr: current point
:)
declare function this:inclination(\$last as xs:double*, \$curr as xs:double*) as xs:double
{
let \$curr := \$curr=>this:as-dimension(3)
let \$prev := if (empty(\$last)) then (0,0,0) else \$last=>this:as-dimension(3)
let \$d := this:distance(\$prev,\$curr)
return
if (this:same(\$prev,\$curr) or (\$d < \$config:ε)) then 90
else (
util:remap-degrees(util:degrees(
math:acos( (this:pz(\$curr) - this:pz(\$prev)) div \$d )
))
)
};

(:~
: destination()
: Point at a particular distance and direction.
:
: @param \$point: starting point
: @param \$degrees: bearing from point
: @param \$length: how far along bearing to travel
:)
declare function this:destination(
\$point as xs:double*,
\$degrees as xs:double,
\$length as xs:double
) as xs:double*
{
return
(
this:px(\$point) + math:cos(\$angle)*\$length,
this:py(\$point) + math:sin(\$angle)*\$length
)
};

(:~
: mutate()
: Mutate a sequence of 2D vectors using a mutation function.
:
: @param \$vectors: the vectors
: @param \$mutate: function that takes a vector as an argument and returns a new vector
:)
declare function this:mutate(
\$vectors as xs:double*,
\$mutate as function(xs:double*) as xs:double*
) as xs:double*
{
for \$i in 1 to count(\$vectors) idiv 2 return (
\$mutate((\$vectors[2*\$i - 1], \$vectors[2*\$i]))
)
};```