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 |
|
Not supported |
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Not supported |
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Supported |
|
Not supported |
|
Supported |
|
Supported |
|
Supported |
|
Not 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.
Tcl callbacks with map¶
map(commandPrefix, expression) applies a Tcl command to each element of a vector expression and returns
a temporary vector of the same length. Its result can be used with the other expression functions and
operators, or assigned with VECINST expr. It does not create a named vector.
The command prefix is a nonempty Tcl list containing a command name and any fixed arguments. RBC appends
exactly one argument for the current element and invokes the command in the expression caller’s Tcl frame.
A real input element is passed as a scalar. A complex input element is passed as one {real imaginary} list;
the callback is responsible for unpacking it.
package require rbc::vector
rbc::vector create x y
x set {1 2 3}
proc adjust {gain offset value} {
expr {$gain * $value + $offset}
}
y expr {map({adjust 2.0 0.5}, x) + 1}
# y contains 3.5 5.5 7.5
The first argument can be a bare command name, a braced or quoted prefix, a Tcl variable, or a bracketed
command substitution. Prefix substitutions are evaluated once per map call. Use braces to protect fixed
arguments containing commas, spaces, or parentheses. The prefix is a list, not a script; its elements are
passed directly as command arguments without concatenation or another round of substitution.
set callback [list adjust 2.0 0.5]
rbc::vector expr {map($callback, x)}
rbc::vector expr {map([list adjust 2.0 0.5], x + 1)}
rbc::vector expr {map({apply {{value} {expr {$value * $value}}}}, x)}
A callback must return a real number or a two-element list of real numbers representing a complex value. If every result is scalar, the result vector is real, even when the input is complex. If any result is a pair (including a pair with zero imaginary part), the result vector is complex; scalar results are promoted with zero imaginary part. Empty input invokes no callbacks and returns an empty real vector. Callback results are converted to double precision; arbitrary expression strings, nonnumeric values, NaN, and infinities are rejected. A real destination still cannot store a complex expression result.
rbc::vector create z -type complex
z set {{3 4} {5 -12}}
proc conjugate {value} {
lassign $value re im
list $re [expr {-$im}]
}
rbc::vector expr {map(conjugate, z)}
# -> {3.0 -4.0} {5.0 12.0}
rbc::vector expr {map({apply {{value} {
lassign $value re im
expr {hypot($re, $im)}
}}}, z)}
# -> 5.0 13.0
Callbacks run in element order, starting at zero within the evaluated input (also for slices). An error stops iteration. Tcl callback errors retain their message and error code, with the element index added to the error stack. Invalid values and unexpected Tcl completion codes are errors too. Expression assignment occurs only after successful evaluation; callback side effects are not rolled back on failure.
The input expression is copied before callbacks start. Changing or destroying a source vector therefore does
not change those captured input values. If a callback destroys the assignment destination, evaluation reports
an error instead of assigning to a stale vector or a same-name replacement. Renaming the destination with
rbc::vector rename retains its identity and permits the assignment to complete.
map invokes Tcl once per element and is intended for custom transformations and convenience. Built-in
mathematical functions are faster for operations they already support. As with other function names, a vector
literally named map(x) must be quoted as {map(x)} or "map(x)" when used as an expression operand.
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
Fourier transforms¶
fft and ifft work with package require rbc::vector; Tk and an external FFT library are not required.
The source can be real or complex. Destinations must already exist: use either one complex vector or
two distinct real vectors. Transform commands never create vectors, interpret #auto as a request to
create one, or change a vector’s type.
package require rbc::vector
rbc::vector create signal spectrumReal spectrumImag
rbc::vector create spectrum reconstructed -type complex
signal set {1 2 3 4}
# Complete complex spectrum, followed by an inverse transform.
signal fft spectrum
spectrum ifft reconstructed
# The same spectrum stored as separate real and imaginary components.
signal fft -real spectrumReal -imag spectrumImag
spectrumReal ifft -imaginput spectrumImag reconstructed
# Reuse the two real vectors as inverse destinations.
spectrumReal ifft -imaginput spectrumImag -real spectrumReal -imag spectrumImag
A positional destination must be complex, even when the input is real. -real and -imag must be supplied
together and refer to different real vectors. Both commands also accept -imaginput for a real source;
the supplied imaginary-input vector must be real and have the same length. All references use RBC vector
lookup in the caller’s namespace, including literal names with parentheses. Partial vector ranges are
rejected; copy a range into a vector first when a transform of a subset is required.
Input/output overlap is supported with matching types. For example, spectrum fft spectrum is valid for
a complex vector; signal fft signal is an error when signal is real. Existing destination C entities,
names, types, index offsets and client bindings are retained. Data pointers may change. C clients must
reacquire data pointers after an update. Mapped-array caches and vector clients are updated as for other
vector mutations. Both split results are installed before notifications are issued.
Length and zero padding¶
The initial implementation uses a radix-2 algorithm. The default transform length is the input length,
which must be a positive power of two. A one-element transform is valid; an empty input is an error.
-length auto pads with zeros to the next power of two. -length size requires a positive power of two
at least as large as the input length. Smaller lengths are errors: input is never silently truncated.
Both real and imaginary components are padded with zeros.
signal set {1 2 3}
signal fft spectrum -length auto
# spectrum has 4 bins.
spectrum ifft reconstructed
# reconstructed is approximately {{1 0} {2 0} {3 0} {0 0}}.
The inverse returns the transform length, including padding. The original unpadded length is not stored
as vector metadata. ifft -length pads the supplied frequency-bin array before taking its inverse; it
does not crop time-domain output or reconstruct an omitted negative-frequency half-spectrum.
Sign, scaling and frequency bins¶
For N samples, forward bin k is the sum of x[j] * exp(-2*pi*i*j*k/N) over j from 0 to N-1.
The inverse uses the positive exponential and divides by N. Thus ifft(fft(x)) reproduces x up to
floating-point rounding, or x followed by zeros if padding was requested. Neither transform applies a
window, removes the mean, doubles bins, or performs amplitude, power or power-spectral-density scaling.
All N bins are returned in unshifted order: DC at index 0, positive frequencies next, then negative
frequencies. For even N, index N/2 is the Nyquist bin. For uniform sample interval dt, indices below
N/2 correspond to k/(N*dt) and indices above N/2 to (k-N)/(N*dt); the Nyquist bin represents the
shared positive/negative Nyquist frequency. These are storage positions, independent of vector offsets.
A real input has conjugate-symmetric bins; complex inputs generally do not.
rbc::vector create magnitude phase
magnitude expr {abs(spectrum)}
phase expr {arg(spectrum)}
These expressions give raw magnitude and phase in radians. Calibrated amplitude and power spectra need additional normalization. FFT assumes uniform sampling; resample adaptive simulation output before using it for a physical frequency spectrum. No sample-time vector or resampling is implicit in these commands.
Inputs must be finite. Non-finite input, non-finite results, invalid arguments, and temporary allocation failures return an error without replacing destination data. Runtime is O(N log N), with O(N) temporary storage. The operation runs synchronously and does not process Tcl events during computation.
Frequency-bin generation¶
Use an existing real vector to generate frequencies for a transform length N:
rbc::vector create frequencies
frequencies fftfreq 8 -delta 0.125
# {0 1 2 3 -4 -3 -2 -1}, in cycles per time unit.
frequencies fftfreq 8 -delta 0.125 -onesided true
# {0 1 2 3 4}
-delta is the finite, positive sample interval, defaulting to 1. Frequencies are in Hz when the interval
is in seconds, or cycles per sample when it is 1. Full ordering, the default, matches the unshifted FFT:
for even N it uses integer bins 0, ..., N/2-1, -N/2, ..., -1, and for odd N it uses
0, ..., (N-1)/2, -(N-1)/2, ..., -1. Each bin is divided by N and the sample interval.
The even-length Nyquist bin is negative in full ordering. -onesided true instead returns floor(N/2)+1
nonnegative bins, including positive Nyquist for even N. There is no Nyquist bin for odd N. Both forms return
{0} for N=1. Any positive integer length is accepted, including non-powers of two.
Supply the actual transform length, including padding, rather than the number of original samples.
This command only generates an axis: it neither computes nor truncates an FFT and it never doubles
spectral amplitudes. Pair a one-sided axis with the first floor(N/2)+1 FFT bins. Such a reduced spectrum
represents real signals through conjugate symmetry; it generally loses independent information from
complex signals.
The frequency values follow the standard FFT bin convention documented by NumPy fftfreq.
Window generation¶
Generate coefficients in an existing real vector, then multiply the signal by that vector before padding:
rbc::vector create signal weights windowed frequencies magnitude positiveMagnitude
rbc::vector create spectrum -type complex
signal set {0 1 0 -1 0 1 0 -1}
weights window hann [signal length]
windowed expr {signal * weights}
windowed fft spectrum -length auto
frequencies fftfreq [spectrum length] -delta 0.001 -onesided true
magnitude expr {abs(spectrum)}
positiveMagnitude set [magnitude range 0 [expr {[spectrum length]/2}]]
window type length ?-periodic boolean? replaces the receiving vector with the requested coefficients.
The types are rectangular, hann, hamming, blackman and bartlett. The default -periodic true
is suitable for spectral analysis. -periodic false produces a symmetric window, suitable for filter
design. For N>1 the periodic form is the first N coefficients of the symmetric N+1 window. Both forms
accept any positive length. Every length-one window is {1}. Zero and negative lengths are errors.
For sample k from 0 to N-1, set D=N for periodic windows and D=N-1 for symmetric windows. For N>1:
Rectangular:
w[k] = 1.Hann:
w[k] = 0.5 - 0.5*cos(2*pi*k/D).Hamming:
w[k] = 0.54 - 0.46*cos(2*pi*k/D).Blackman:
w[k] = 0.42 - 0.5*cos(2*pi*k/D) + 0.08*cos(4*pi*k/D).Bartlett:
w[k] = 1 - abs(2*k/D - 1).
These are the conventional window definitions; no extra normalization by sum, energy or sampled peak is applied. Some short or odd periodic windows, and even symmetric windows, do not contain a coefficient equal to 1. Symmetric Hann, Blackman and Bartlett windows have zero endpoints; the periodic form normally omits the final zero. Hamming endpoints are 0.08 in symmetric form.
The periodic/symmetric convention is also described in SciPy window documentation, that was used as a reference.
Window scaling¶
Windowing changes amplitude and energy. Use the coefficients applied to the original L samples to compute
coherent gain sum(w)/L and energy sum(w*w). Padding does not add coefficients to these sums.
set coherentGain [rbc::vector expr {mean(weights)}]
set windowSum [rbc::vector expr {sum(weights)}]
set windowEnergy [rbc::vector expr {sum(weights * weights)}]
For an isolated complex tone exactly on a frequency bin, divide its unscaled FFT magnitude by
windowSum to recover amplitude. Real-tone one-sided amplitude conventions additionally double the
interior positive-frequency bins, but not DC or the even-length Nyquist bin. Leakage or overlapping
components can still affect a tone’s measured amplitude.
A two-sided periodogram PSD convention is abs(X[k])^2 / (sampleRate * windowEnergy), where sampleRate =
1/delta. One-sided PSD for real signals doubles interior bins only. These are scaling conventions for scripts,
not additional generator behavior. Check for zero sums or energy before division: for example, a symmetric
two-sample Hann window is all zero.
Both generators preserve vector identity, type, index offset and client bindings, and return the qualified
destination name. They replace all coefficients at once, refresh caches and notify clients. Invalid input,
unrepresentable allocation sizes, allocation failures or non-finite frequency results leave data unchanged.
Frequency underflow follows ordinary double arithmetic and may round extremely small bins to zero.
The commands perform no implicit vector creation or resampling and do not interpret #auto.
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_VectorInitStubs(interp, version, 0) == NULL) {
return TCL_ERROR;
}
return TCL_OK;
}
version is the minimum Rbc package version required by the extension. Rbc_VectorInitStubs requires
rbc::vector and initializes the existing Rbc stubs table without loading Tk. Rebuild the consumer
with the updated rbcStubLib.c (or updated stub library) to use this new helper. The table layout and
vector function slots are unchanged.
Rbc_InitStubs remains available and requires the full rbc package, including Tk and the toolbar.
Both helpers use the same vector API and table; upgrading to the full package preserves vector pointers
and subscriptions. As before, each interpreter must have its package initialized before using the API.
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.
Read-only state in C (since 0.8.1)¶
Function |
Purpose |
|---|---|
|
Returns 0 or 1. |
|
Sets the state (nonzero means true); returns |
|
Returns |
These functions are appended to the stubs table. Consumers using them must request at least 0.8.1
when calling Rbc_VectorInitStubs or Rbc_InitStubs. Existing slots and signatures are unchanged.
State changes do not replace storage or send value-change notifications.
Resize, reset and recreation of a read-only destination fail before replacing storage. This includes
same-length resize requests. Failed reset calls do not take ownership of the proposed buffer or call
its free procedure. Rbc_FreeVector remains allowed regardless of the state.
Existing data accessors still return borrowed mutable pointers for compatibility. RBC cannot intercept writes through those pointers, including pointers retained before locking and externally owned storage. Native producers must check writability before modifying data, and again after any callbacks which may change the state. Readers need no such check. For example:
if (Rbc_VectorCheckWritable(interp, vecPtr) != TCL_OK) {
return TCL_ERROR;
}
/* Here vecPtr is a real vector with at least one element. */
Rbc_VectorData(vecPtr)[0] = value;
Rbc_VectorChangedRange(vecPtr, 0, 0);
Rbc_VectorChanged and Rbc_VectorChangedRange remain notifications, not write permission checks.
They report changes already made and must not be used to enforce protection after writing.
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;
}
Both Rbc_CreateVector and Rbc_CreateVectorWithType accept literal names with balanced parentheses,
including numeric names such as v(1); C constructors receive the length separately and never interpret
name suffixes as sizes. Newly created real vectors receive an automatic Tcl array mapping only when their
name contains no parentheses. Complex vectors never receive an automatic mapping. Rbc_CreateVector2
accepts a separate array name or NULL; explicit array names must not contain parentheses.
Public lookup, existence, client-token and deletion APIs accept these vector names without encoding. Lookup paths that accept ranges use the same exact-name precedence as Tcl vector references. No stubs ABI change or additional C function is required.
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