Vector instance VECINST command¶
Commands in this namespace document operations accepted by an individual Rbc vector command.
VECINST is used below as a placeholder for the actual vector command:
::rbc::vector create x
x set {1 2 3 4 5}
puts [x length]
Vector indices¶
Vector indices are Tcl-sized integer values adjusted by the vector’s current offset.
The index end refers to the last value. Tcl index expressions based on end are supported, for example:
x index end
x index end-1
The special index ++end denotes the position immediately after the final value and may be used when setting a
value to append one element:
x index ++end 10.0
Colon-separated ranges may be used by operations that accept ranges:
x index 2:5
An omitted range endpoint denotes the beginning or end of the vector:
x index 2:
x index :5
x index :
For real vectors, the associated Tcl array additionally supports the special calculated indices min, max,
mean, sum, and prod. These calculated indices are not available for complex vectors.
Complex vector operations¶
Most vector instance operations support complex vectors. The following table summarizes the current status.
Operation |
Complex vectors |
|---|---|
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Not supported |
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Not supported |
|
Supported |
|
Supported |
|
Supported |
Operations that combine vector storage, such as append, merge, set, dup, and split, preserve vector
types rather than reinterpreting real storage as complex storage or vice versa.
normalize remains real-only because its definition depends on a minimum and maximum ordering. sort likewise
remains real-only because complex numbers have no natural ordering relation.
search supports exact-value searches on complex vectors. A two-bound range search is not defined for complex
values and is rejected.
The calculated Tcl array indices min, max, mean, sum, and prod remain real-only. Ordinary numeric
indices and ranges work with an explicitly mapped complex vector.
Vector expressions¶
Vector expressions perform arithmetic, comparison, logical, and mathematical operations on vectors.
Expressions can be evaluated with the top-level vector expr command:
vector expr {expression}
or with the expr operation of a vector instance:
VECINST expr {expression}
The top-level form returns the result. The vector-instance form stores the result in VECINST.
For example:
vector create x y
x set {1 2 3 4}
y set {10 20 30 40}
vector expr {x + y}
# -> 11.0 22.0 33.0 44.0
x expr {x * 2}
x range 0 end
# -> 2.0 4.0 6.0 8.0
Scalars and vectors¶
A numeric scalar is treated as a single value. When one operand is a scalar and the other is a vector, the scalar operation is applied to every component of the vector.
For example:
vector expr {x * 2}
vector expr {x + 10}
vector expr {100 - x}
When both operands are vectors, the operation is performed component by component. The vectors must have compatible lengths.
For example, given:
x set {1 2 3}
y set {10 20 30}
the expression:
vector expr {x * y}
returns:
10.0 40.0 90.0
Vector names, numeric values, Tcl variable substitutions, command substitutions, quoted or braced values, and parenthesized subexpressions may be used as expression operands.
For example:
set scale 10
vector expr {x * $scale}
vector expr {x + [y index 0]}
vector expr {(x + y) / 2}
Operators¶
The following operators are supported, listed from highest to lowest precedence.
Operators |
Description |
|---|---|
|
Unary minus and logical NOT |
|
Exponentiation |
|
Multiplication, division, and remainder |
|
Addition and subtraction |
|
Circular left and right vector shift |
|
Relational comparison |
|
Equality and inequality comparison |
|
Logical AND |
|
Logical OR |
Binary operators at the same precedence level are evaluated from left to right.
Arithmetic operators operate component by component.
For example:
x set {1 2 3}
vector expr {x + 10}
# -> 11.0 12.0 13.0
vector expr {x * 2}
# -> 2.0 4.0 6.0
Comparison operators return 1.0 when the comparison is true and 0.0 when it is false.
For example:
x set {1 5 10}
vector expr {x > 4}
# -> 0.0 1.0 1.0
Logical operators similarly produce 1.0 or 0.0.
The unary ! operator returns 1.0 for a zero-valued component and 0.0 for a non-zero component.
Circular shifts¶
The << and >> operators perform circular shifts of vector components. They are not integer bit-shift
operators.
The right-hand operand must be a scalar specifying the number of positions to shift.
For example:
x set {1 2 3 4}
vector expr {x << 1}
# -> 2.0 3.0 4.0 1.0
vector expr {x >> 1}
# -> 4.0 1.0 2.0 3.0
Values shifted past one end of the vector reappear at the opposite end.
Complex expression operators¶
Real and complex vectors may be mixed in an expression. Real operands are promoted to complex values with zero imaginary component when required.
Complex expressions support:
unary
-unary
!+,-,*, and/exponentiation with
^circular shifts
<<and>>equality and inequality with
==and!=logical
&&and||.
The remainder operator % and the ordered comparisons <, >, <=, and >= are not supported when either
operand is complex.
Complex exponentiation uses the principal complex value for non-integral exponents. A finite integral real exponent is handled directly and therefore does not depend on the logarithm branch.
Equality compares both the real and imaginary components. Logical operations treat a complex value as false
only when both components are zero. Equality and logical operations return real 0.0 or 1.0 values.
Circular shifts preserve complete complex values. The shift count must be real; a complex scalar with zero imaginary component is accepted as a real shift count.
Mathematical functions¶
Vector expressions provide mathematical functions in three general categories:
component functions, which operate independently on each vector component
scalar functions, which reduce a vector to one value
vector functions, which transform a vector into another vector.
Component functions¶
Component functions apply the selected operation independently to every vector component and return a vector of the same length.
Function |
Description |
|---|---|
|
Absolute value |
|
Arc cosine |
|
Phase angle of the component |
|
Arc sine |
|
Arc tangent |
|
Smallest integral value not less than the component |
|
Complex conjugate |
|
Cosine |
|
Hyperbolic cosine |
|
Exponential |
|
Largest integral value not greater than the component |
|
Imaginary component |
|
Natural logarithm |
|
Base-10 logarithm |
|
Generates a pseudo-random value in the range |
|
Real component |
|
Rounds to the nearest integral value |
|
Sine |
|
Hyperbolic sine |
|
Square root |
|
Tangent |
|
Hyperbolic tangent |
For example:
x set {1 4 9 16}
vector expr {sqrt(x)}
# -> 1.0 2.0 3.0 4.0
Functions may be combined with operators and other functions:
vector expr {sin(x) * 2}
vector expr {sqrt(abs(x))}
The random function ignores the values of its input components and generates one pseudo-random value for each
component:
x length 100
set values [vector expr {random(x)}]
The number of generated values is therefore determined by the length of the argument vector.
Scalar functions¶
Scalar functions reduce a vector to a single numeric value.
Function |
Description |
|---|---|
|
Average absolute deviation from the mean |
|
Fisher excess kurtosis |
|
Number of finite components |
|
Maximum finite component |
|
Arithmetic mean |
|
Median value |
|
Minimum finite component |
|
Number of non-zero components |
|
Product of the components |
|
First quartile |
|
Third quartile |
|
Sample standard deviation |
|
Skewness |
|
Sum of the components |
|
Sample variance |
For example:
x set {1 2 3 4 5}
vector expr {mean(x)}
# -> 3.0
vector expr {sum(x)}
# -> 15.0
vector expr {min(x)}
# -> 1.0
vector expr {max(x)}
# -> 5.0
Scalar functions can participate in larger expressions:
vector expr {x - mean(x)}
vector expr {x / max(x)}
Vector functions¶
Vector functions transform their argument and return another vector.
Function |
Description |
|---|---|
|
Normalizes the components to the range |
|
Returns the components sorted in ascending order |
For example:
x set {30 10 20}
vector expr {sort(x)}
# -> 10.0 20.0 30.0
Normalization maps the minimum component to 0.0 and the maximum component to 1.0:
x set {10 20 30}
vector expr {norm(x)}
# -> 0.0 0.5 1.0
Mathematical functions on complex vectors¶
Mathematical functions that have an unambiguous complex definition are supported directly.
The projection functions return real values:
abs(z)returns the magnitude|z|.arg(z)returns the phase angle.real(z)returns the real component.imag(z)returns the imaginary component.
conj, sqrt, exp, log, log10, sin, cos, tan, sinh, cosh, tanh, asin, acos, and atan
return complex values when their input is complex.
ceil, floor, and round operate independently on the real and imaginary components.
random applied to a complex vector produces a complex vector in which the real and imaginary components are
generated independently in the range [0.0, 1.0).
The following reductions support complex vectors:
Function |
Result |
Complex definition |
|---|---|---|
|
Real |
Mean of |
|
Real |
Number of finite complex values |
|
Complex |
Arithmetic mean |
|
Real |
Number of values other than |
|
Complex |
Product of the values |
|
Real |
Square root of the Hermitian sample variance |
|
Complex |
Sum of the values |
|
Real |
Hermitian sample variance |
kurtosis, max, median, min, q1, q3, and skew remain real-only. The vector functions norm and
sort are also real-only.
No implicit ordering of complex numbers is introduced for these operations.
Expression results¶
The result of vector expr is returned to the caller. Complex values are returned as {real imag} pairs:
vector expr {{1 2} + {3 -1}}
# -> {4.0 1.0}
The instance form stores the expression result directly in the vector:
x expr {x * 2}
A complex vector can store either a complex result or a real result; a real result is promoted to complex values with zero imaginary components.
A real vector cannot store a complex expression result:
vector create r
vector create c -type complex
c set {{1 2} {3 4}}
r expr {c + 1}
# error: can't store complex expression result in real vector
C language API¶
Rbc vectors can be created, inspected, modified, and destroyed directly from C code. This is useful for Tcl extensions that generate or acquire large numeric data sets and want to make those values available to Tcl scripts or graph elements without converting every value to and from a Tcl list.
Include the Rbc public header:
#include <rbc.h>
Rbc exports its public C interface through the Rbc stubs table. An extension using the stubs interface should initialize it before calling Rbc functions:
static int InitRbcApi(Tcl_Interp *interp, const char *version) {
if (Rbc_InitStubs(interp, version, 0) == NULL) {
return TCL_ERROR;
}
return TCL_OK;
}
version is the minimum Rbc package version required by the extension. Rbc_InitStubs also ensures that the
Rbc package is loaded in the interpreter.
Vector representation¶
Rbc_Vector is an opaque public type:
typedef struct Rbc_Vector_s Rbc_Vector;
Applications must not access the internal vector structure directly. Use the public accessor and mutation functions described below.
Every vector has one of two numeric types:
typedef enum {
RBC_VECTOR_REAL = 0,
RBC_VECTOR_COMPLEX
} Rbc_VectorType;
Complex values use the portable public representation:
typedef struct {
double real;
double imag;
} Rbc_Complex;
Rbc_Complex deliberately does not use the native C complex type, so the public ABI does not depend on
compiler-specific complex-number support.
Public vector functions¶
The following vector functions are exported through the Rbc stubs interface:
Function |
Description |
|---|---|
|
Creates a real vector using the traditional Tcl command and automatic array mapping. |
|
Looks up an existing real or complex vector. |
|
Changes the logical vector length while preserving its numeric type. |
|
Returns the vector name. The returned string belongs to Rbc. |
|
Replaces the storage of a real vector. |
|
Returns real-vector storage, or |
|
Returns the logical number of vector values. |
|
Returns the current vector capacity in values. |
|
Returns the vector update counter. |
|
Tests whether a named vector exists. |
|
Destroys the vector and releases its resources. |
|
Returns |
|
Returns the minimum and maximum of a real vector. Returns |
|
Creates a real or complex vector of the requested type. |
|
Replaces the storage of a complex vector. |
|
Returns complex-vector storage, or |
|
Reports that the existing vector storage was modified in place without changing its storage or ownership contract. |
|
Reports that the inclusive range |
Functions returning int as a Tcl status return TCL_OK on success and TCL_ERROR on failure. When an
interpreter is available, an explanatory error message is left in its result.
Vector lengths and capacities use Tcl_Size, allowing the C interface to represent Tcl 9-sized vectors.
Reading vector data¶
Rbc_GetVector obtains the opaque representation of an existing real or complex vector. Use
Rbc_VectorGetType before selecting the storage accessor.
static int InspectVector(Tcl_Interp *interp, const char *name) {
Rbc_Vector *vecPtr;
Tcl_Size nValues;
if (Rbc_GetVector(interp, name, &vecPtr) != TCL_OK) {
return TCL_ERROR;
}
nValues = Rbc_VectorLength(vecPtr);
switch (Rbc_VectorGetType(vecPtr)) {
case RBC_VECTOR_REAL: {
double *dataArr = Rbc_VectorData(vecPtr);
/* Use dataArr[0] ... dataArr[nValues - 1]. */
break;
}
case RBC_VECTOR_COMPLEX: {
Rbc_Complex *dataArr = Rbc_VectorComplexData(vecPtr);
/* Use dataArr[i].real and dataArr[i].imag. */
break;
}
}
return TCL_OK;
}
Storage returned by either accessor belongs to the vector and must not be freed by the caller. The pointer may become invalid after an operation that resizes, resets, or destroys the vector.
Updating vectors from C¶
Rbc_VectorData and Rbc_VectorComplexData return mutable storage. A caller may update that storage directly
as long as no operation that can resize, replace, or destroy the vector is performed while the pointer is being
used.
Direct writes do not themselves invalidate cached vector information or notify vector clients. After modifying existing storage in place, the caller must report the change with either:
Rbc_VectorChanged(vecPtr);
or:
Rbc_VectorChangedRange(vecPtr, first, last);
Rbc_VectorChangedRange should be preferred when the exact modified source interval is known.
first and last are zero-based inclusive source indices and must describe values already present in the
vector. The operation reports a content change only; it does not resize the vector or change its storage
ownership.
For example, a C extension updating part of a real waveform can write directly into the vector and then report exactly which samples changed:
static int UpdateSamples(Tcl_Interp *interp, const char *name, Tcl_Size first, Tcl_Size last, const double
*values) {
Rbc_Vector *vecPtr;
double *dataArr;
Tcl_Size nValues;
Tcl_Size i;
if (Rbc_GetVector(interp, name, &vecPtr) != TCL_OK) {
return TCL_ERROR;
}
if (Rbc_VectorGetType(vecPtr) != RBC_VECTOR_REAL) {
Tcl_SetObjResult(interp, Tcl_NewStringObj("vector must be real", -1));
return TCL_ERROR;
}
nValues = Rbc_VectorLength(vecPtr);
if ((first < 0) || (last < first) || (last >= nValues)) {
Tcl_SetObjResult(interp, Tcl_NewStringObj("changed range is outside the vector", -1));
return TCL_ERROR;
}
dataArr = Rbc_VectorData(vecPtr);
for (i = first; i <= last; i++) {
dataArr[i] = values[i - first];
}
Rbc_VectorChangedRange(vecPtr, first, last);
return TCL_OK;
}
Supplying the changed range allows Rbc clients to preserve caches that are independent of the modified samples. For real vectors, Rbc also attempts to maintain the cached global minimum and maximum from the changed interval. If the modification touches information needed to determine an old global extremum, Rbc automatically falls back to a complete range calculation when required.
Graph line elements can use ranged Y-vector notifications to update only the affected part of a persistent display-decimation cache instead of rebuilding source-domain summaries for the complete vector.
Use Rbc_VectorChanged when the modified source interval is unknown, when changes cover effectively the whole
vector, or when a caller does not need to provide more precise change information:
static int FillComplexVector(Tcl_Interp *interp, const char *name) {
Rbc_Vector *vecPtr;
Rbc_Complex *dataArr;
Tcl_Size nValues;
Tcl_Size i;
if (Rbc_GetVector(interp, name, &vecPtr) != TCL_OK) {
return TCL_ERROR;
}
if (Rbc_VectorGetType(vecPtr) != RBC_VECTOR_COMPLEX) {
Tcl_SetObjResult(interp, Tcl_NewStringObj("vector must be complex", -1));
return TCL_ERROR;
}
dataArr = Rbc_VectorComplexData(vecPtr);
nValues = Rbc_VectorLength(vecPtr);
for (i = 0; i < nValues; i++) {
dataArr[i].real = (double)i;
dataArr[i].imag = -(double)i;
}
Rbc_VectorChanged(vecPtr);
return TCL_OK;
}
Both notification functions preserve the current data pointer, vector length, capacity, numeric type, and storage ownership policy. They flush the associated Tcl-array cache when required and notify registered vector clients.
Multiple modifications may be reported as one inclusive changed range when convenient. The range may therefore conservatively include unchanged samples; correctness does not require every value inside the reported interval to have actually changed.
Do not use Rbc_VectorChanged or Rbc_VectorChangedRange to report a resize or storage replacement. Use
Rbc_ResizeVector, Rbc_ResetVector, or Rbc_ResetComplexVector for operations that change vector length,
storage, or ownership.
Replacing vector storage¶
Rbc_ResetVector replaces the storage of a real vector. Rbc_ResetComplexVector performs the corresponding
operation for a complex vector.
Calling the real reset function for a complex vector, or the complex reset function for a real vector, returns
TCL_ERROR.
Both functions accept nValues, arraySize, and freeProc with the same ownership rules.
|
Storage ownership |
|---|---|
|
Rbc copies the supplied data into its own storage. The caller retains ownership of |
|
Rbc uses |
|
Rbc takes ownership of |
custom |
Rbc takes ownership of |
The ownership policy supplied to a reset operation becomes the vector’s storage contract. This is true even if the supplied data pointer is identical to the vector’s current data pointer.
Therefore use a reset function when installing or replacing storage. When only the contents of already-installed
storage have been modified, use Rbc_VectorChangedRange if the modified interval is known, or
Rbc_VectorChanged for an unknown/full-vector modification.
nValues specifies the logical number of values currently stored in the vector. arraySize specifies the
capacity of the supplied array and must be greater than or equal to nValues.
Passing NULL for dataArr, or an arraySize of 0, resets the vector to an empty vector.
If arraySize is 0, a non-NULL dataArr is not adopted by the vector and ownership does not transfer to
Rbc. The caller remains responsible for that storage.
For example, dynamically allocated storage can be transferred directly to a vector:
static int SetGeneratedData(Tcl_Interp *interp, const char *name) {
Rbc_Vector *vecPtr;
double *dataArr;
Tcl_Size nValues;
Tcl_Size i;
nValues = 100;
if (Rbc_GetVector(interp, name, &vecPtr) != TCL_OK) {
return TCL_ERROR;
}
dataArr = (double *)Tcl_Alloc((size_t)nValues * sizeof(double));
for (i = 0; i < nValues; i++) {
dataArr[i] = (double)i * (double)i;
}
if (Rbc_ResetVector(vecPtr, dataArr, nValues, nValues, TCL_DYNAMIC) != TCL_OK) {
Tcl_Free((char *)dataArr);
return TCL_ERROR;
}
/*
* Rbc now owns dataArr.
* Do not free or reuse it here.
*/
return TCL_OK;
}
When TCL_DYNAMIC is used, ownership transfers to Rbc only after a successful Rbc_ResetVector call.
Complex reset example:
static int SetComplexData(Tcl_Interp *interp, const char *name) {
Rbc_Vector *vecPtr;
Rbc_Complex values[2];
if (Rbc_GetVector(interp, name, &vecPtr) != TCL_OK) {
return TCL_ERROR;
}
values[0].real = 1.0;
values[0].imag = 2.0;
values[1].real = 3.0;
values[1].imag = -4.0;
return Rbc_ResetComplexVector(
vecPtr, values, 2, 2, TCL_VOLATILE);
}
Creating vectors from C¶
Rbc_CreateVector is the compatibility interface for creating a real vector:
Rbc_Vector *realPtr;
if (Rbc_CreateVector(interp, "x", 100, &realPtr) != TCL_OK) {
return TCL_ERROR;
}
To select the numeric type explicitly, use Rbc_CreateVectorWithType:
Rbc_Vector *complexPtr;
if (Rbc_CreateVectorWithType(
interp, "z", 100, RBC_VECTOR_COMPLEX, &complexPtr) != TCL_OK) {
return TCL_ERROR;
}
Rbc_CreateVectorWithType follows the Tcl-level mapping convention: a newly created real vector receives its
traditional automatic Tcl array mapping, while a newly created complex vector does not.
For example:
static int CreateTimeVector(Tcl_Interp *interp) {
Rbc_Vector *vecPtr;
double *dataArr;
Tcl_Size nValues;
Tcl_Size i;
nValues = 1000;
if (Rbc_CreateVector(interp, "time", nValues, &vecPtr) != TCL_OK) {
return TCL_ERROR;
}
for (i = 0; i < nValues; i++) {
dataArr[i] = (double)i * 1.0e-6;
}
Rbc_VectorChanged(vecPtr);
return TCL_OK;
}
Directly modifying storage returned by Rbc_VectorData or Rbc_VectorComplexData does not itself invalidate
cached values or notify vector clients. Call Rbc_VectorChangedRange after a known ranged modification, or
Rbc_VectorChanged when the changed interval is unknown.
Use Rbc_ResetVector or Rbc_ResetComplexVector when replacing the vector’s storage or changing its ownership
contract, not merely to announce that existing storage was modified.
Resizing a vector¶
Rbc_ResizeVector changes a vector’s logical length and notifies clients of the change.
Rbc_Vector *vecPtr;
if (Rbc_GetVector(interp, "data", &vecPtr) != TCL_OK) {
return TCL_ERROR;
}
if (Rbc_ResizeVector(vecPtr, 10000) != TCL_OK) {
return TCL_ERROR;
}
Values already in the vector are preserved when possible. When a vector is enlarged, newly created components are initialized to zero.
Testing for a vector¶
Rbc_VectorExists2 can be used when C code accepts either an existing vector or needs to create one:
Rbc_Vector *vecPtr;
if (Rbc_VectorExists2(interp, "data")) {
if (Rbc_GetVector(interp, "data", &vecPtr) != TCL_OK) {
return TCL_ERROR;
}
} else {
if (Rbc_CreateVector(interp, "data", 0, &vecPtr) != TCL_OK) {
return TCL_ERROR;
}
}
Destroying a vector¶
Rbc_FreeVector destroys a vector obtained from Rbc_GetVector or Rbc_CreateVector:
Rbc_Vector *vecPtr;
if (Rbc_GetVector(interp, "temporary", &vecPtr) != TCL_OK) {
return TCL_ERROR;
}
Rbc_FreeVector(vecPtr);
/*
* vecPtr is invalid from this point onward.
*/
Destroying the vector also removes its associated Tcl command and array mapping and releases vector storage
according to the ownership policy established by Rbc_ResetVector.
Compatibility with older Rbc vector APIs¶
Older BLT and Rbc manuals describe additional C functions for vector client identifiers, change callbacks, deletion by name, and custom vector indices. Those routines are not part of the current public Rbc stubs interface and are intentionally not documented here.
Use the functions listed in Public vector functions when writing new extensions.
Copyright (c) George Yashin