* Copyright (c) 1994 David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
* Generic value manipulation routines.
* Free a value and set its type to undefined.
register VALUE
*vp
; /* value to be freed */
int type
; /* type of value being freed */
if (vp
->v_subtype
== V_STRALLOC
)
math_error("Freeing unknown value type");
vp
->v_subtype
= V_NOSUBTYPE
;
* Copy a value from one location to another.
* This overwrites the specified new value without checking it.
register VALUE
*oldvp
; /* value to be copied from */
register VALUE
*newvp
; /* value to be copied into */
newvp
->v_file
= oldvp
->v_file
;
newvp
->v_num
= qlink(oldvp
->v_num
);
newvp
->v_com
= clink(oldvp
->v_com
);
newvp
->v_str
= oldvp
->v_str
;
if (oldvp
->v_subtype
== V_STRALLOC
) {
newvp
->v_str
= (char *)malloc(strlen(oldvp
->v_str
) + 1);
if (newvp
->v_str
== NULL
)
math_error("Cannot get memory for string copy");
strcpy(newvp
->v_str
, oldvp
->v_str
);
newvp
->v_mat
= matcopy(oldvp
->v_mat
);
newvp
->v_list
= listcopy(oldvp
->v_list
);
newvp
->v_assoc
= assoccopy(oldvp
->v_assoc
);
newvp
->v_addr
= oldvp
->v_addr
;
newvp
->v_obj
= objcopy(oldvp
->v_obj
);
math_error("Copying unknown value type");
if (oldvp
->v_type
== V_STR
) {
newvp
->v_subtype
= oldvp
->v_subtype
;
newvp
->v_subtype
= V_NOSUBTYPE
;
newvp
->v_type
= oldvp
->v_type
;
* Negate an arbitrary value.
* Result is placed in the indicated location.
vres
->v_num
= qneg(vp
->v_num
);
vres
->v_com
= cneg(vp
->v_com
);
vres
->v_mat
= matneg(vp
->v_mat
);
*vres
= objcall(OBJ_NEG
, vp
, NULL_VALUE
, NULL_VALUE
);
math_error("Illegal value for negation");
* Add two arbitrary values together.
* Result is placed in the indicated location.
switch (TWOVAL(v1
->v_type
, v2
->v_type
)) {
case TWOVAL(V_NUM
, V_NUM
):
vres
->v_num
= qadd(v1
->v_num
, v2
->v_num
);
case TWOVAL(V_COM
, V_NUM
):
vres
->v_com
= caddq(v1
->v_com
, v2
->v_num
);
case TWOVAL(V_NUM
, V_COM
):
vres
->v_com
= caddq(v2
->v_com
, v1
->v_num
);
case TWOVAL(V_COM
, V_COM
):
vres
->v_com
= cadd(v1
->v_com
, v2
->v_com
);
vres
->v_num
= qlink(c
->real
);
case TWOVAL(V_MAT
, V_MAT
):
vres
->v_mat
= matadd(v1
->v_mat
, v2
->v_mat
);
if ((v1
->v_type
!= V_OBJ
) && (v2
->v_type
!= V_OBJ
))
math_error("Non-compatible values for add");
*vres
= objcall(OBJ_ADD
, v1
, v2
, NULL_VALUE
);
* Subtract one arbitrary value from another one.
* Result is placed in the indicated location.
switch (TWOVAL(v1
->v_type
, v2
->v_type
)) {
case TWOVAL(V_NUM
, V_NUM
):
vres
->v_num
= qsub(v1
->v_num
, v2
->v_num
);
case TWOVAL(V_COM
, V_NUM
):
vres
->v_com
= csubq(v1
->v_com
, v2
->v_num
);
case TWOVAL(V_NUM
, V_COM
):
c
= csubq(v2
->v_com
, v1
->v_num
);
case TWOVAL(V_COM
, V_COM
):
vres
->v_com
= csub(v1
->v_com
, v2
->v_com
);
vres
->v_num
= qlink(c
->real
);
case TWOVAL(V_MAT
, V_MAT
):
vres
->v_mat
= matsub(v1
->v_mat
, v2
->v_mat
);
if ((v1
->v_type
!= V_OBJ
) && (v2
->v_type
!= V_OBJ
))
math_error("Non-compatible values for subtract");
*vres
= objcall(OBJ_SUB
, v1
, v2
, NULL_VALUE
);
* Multiply two arbitrary values together.
* Result is placed in the indicated location.
switch (TWOVAL(v1
->v_type
, v2
->v_type
)) {
case TWOVAL(V_NUM
, V_NUM
):
vres
->v_num
= qmul(v1
->v_num
, v2
->v_num
);
case TWOVAL(V_COM
, V_NUM
):
vres
->v_com
= cmulq(v1
->v_com
, v2
->v_num
);
case TWOVAL(V_NUM
, V_COM
):
vres
->v_com
= cmulq(v2
->v_com
, v1
->v_num
);
case TWOVAL(V_COM
, V_COM
):
vres
->v_com
= cmul(v1
->v_com
, v2
->v_com
);
case TWOVAL(V_MAT
, V_MAT
):
vres
->v_mat
= matmul(v1
->v_mat
, v2
->v_mat
);
case TWOVAL(V_MAT
, V_NUM
):
case TWOVAL(V_MAT
, V_COM
):
vres
->v_mat
= matmulval(v1
->v_mat
, v2
);
case TWOVAL(V_NUM
, V_MAT
):
case TWOVAL(V_COM
, V_MAT
):
vres
->v_mat
= matmulval(v2
->v_mat
, v1
);
if ((v1
->v_type
!= V_OBJ
) && (v2
->v_type
!= V_OBJ
))
math_error("Non-compatible values for multiply");
*vres
= objcall(OBJ_MUL
, v1
, v2
, NULL_VALUE
);
vres
->v_num
= qlink(c
->real
);
* Square an arbitrary value.
* Result is placed in the indicated location.
vres
->v_num
= qsquare(vp
->v_num
);
vres
->v_com
= csquare(vp
->v_com
);
vres
->v_num
= qlink(c
->real
);
vres
->v_mat
= matsquare(vp
->v_mat
);
*vres
= objcall(OBJ_SQUARE
, vp
, NULL_VALUE
, NULL_VALUE
);
math_error("Illegal value for squaring");
* Invert an arbitrary value.
* Result is placed in the indicated location.
vres
->v_num
= qinv(vp
->v_num
);
vres
->v_com
= cinv(vp
->v_com
);
vres
->v_mat
= matinv(vp
->v_mat
);
*vres
= objcall(OBJ_INV
, vp
, NULL_VALUE
, NULL_VALUE
);
math_error("Illegal value for inverting");
* Round an arbitrary value to the specified number of decimal places.
* Result is placed in the indicated location.
if (qisfrac(q
) || zisbig(q
->num
))
math_error("Bad number of places for round");
math_error("Bad value type for places in round");
math_error("Negative number of places in round");
vres
->v_num
= qlink(v1
->v_num
);
vres
->v_num
= qround(v1
->v_num
, places
);
vres
->v_com
= clink(v1
->v_com
);
vres
->v_com
= cround(v1
->v_com
, places
);
vres
->v_num
= qlink(c
->real
);
vres
->v_mat
= matround(v1
->v_mat
, places
);
*vres
= objcall(OBJ_ROUND
, v1
, v2
, NULL_VALUE
);
math_error("Illegal value for round");
* Round an arbitrary value to the specified number of binary places.
* Result is placed in the indicated location.
broundvalue(v1
, v2
, vres
)
if (qisfrac(q
) || zisbig(q
->num
))
math_error("Bad number of places for bround");
math_error("Bad value type for places in bround");
math_error("Negative number of places in bround");
vres
->v_num
= qlink(v1
->v_num
);
vres
->v_num
= qbround(v1
->v_num
, places
);
vres
->v_com
= clink(v1
->v_com
);
vres
->v_com
= cbround(v1
->v_com
, places
);
vres
->v_num
= qlink(c
->real
);
vres
->v_mat
= matbround(v1
->v_mat
, places
);
*vres
= objcall(OBJ_BROUND
, v1
, v2
, NULL_VALUE
);
math_error("Illegal value for bround");
* Take the integer part of an arbitrary value.
* Result is placed in the indicated location.
vres
->v_num
= qlink(vp
->v_num
);
vres
->v_num
= qint(vp
->v_num
);
vres
->v_com
= clink(vp
->v_com
);
vres
->v_com
= cint(vp
->v_com
);
vres
->v_num
= qlink(c
->real
);
vres
->v_mat
= matint(vp
->v_mat
);
*vres
= objcall(OBJ_INT
, vp
, NULL_VALUE
, NULL_VALUE
);
math_error("Illegal value for int");
* Take the fractional part of an arbitrary value.
* Result is placed in the indicated location.
vres
->v_num
= qlink(&_qzero_
);
vres
->v_num
= qfrac(vp
->v_num
);
vres
->v_num
= clink(&_qzero_
);
vres
->v_com
= cfrac(vp
->v_com
);
vres
->v_mat
= matfrac(vp
->v_mat
);
*vres
= objcall(OBJ_FRAC
, vp
, NULL_VALUE
, NULL_VALUE
);
math_error("Illegal value for frac function");
* Increment an arbitrary value by one.
* Result is placed in the indicated location.
vres
->v_num
= qinc(vp
->v_num
);
vres
->v_com
= caddq(vp
->v_com
, &_qone_
);
*vres
= objcall(OBJ_INC
, vp
, NULL_VALUE
, NULL_VALUE
);
math_error("Illegal value for incrementing");
* Decrement an arbitrary value by one.
* Result is placed in the indicated location.
vres
->v_num
= qdec(vp
->v_num
);
vres
->v_com
= caddq(vp
->v_com
, &_qnegone_
);
*vres
= objcall(OBJ_DEC
, vp
, NULL_VALUE
, NULL_VALUE
);
math_error("Illegal value for decrementing");
* Produce the 'conjugate' of an arbitrary value.
* Result is placed in the indicated location.
* (Example: complex conjugate.)
vres
->v_num
= qlink(vp
->v_num
);
vres
->v_com
= comalloc();
vres
->v_com
->real
= qlink(vp
->v_com
->real
);
vres
->v_com
->imag
= qneg(vp
->v_com
->imag
);
vres
->v_mat
= matconj(vp
->v_mat
);
*vres
= objcall(OBJ_CONJ
, vp
, NULL_VALUE
, NULL_VALUE
);
math_error("Illegal value for conjugation");
* Take the square root of an arbitrary value within the specified error.
* Result is placed in the indicated location.
math_error("Non-real epsilon for sqrt");
if (qisneg(q
) || qiszero(q
))
math_error("Illegal epsilon value for sqrt");
if (!qisneg(v1
->v_num
)) {
vres
->v_num
= qsqrt(v1
->v_num
, q
);
vres
->v_com
= csqrt(v1
->v_com
, q
);
*vres
= objcall(OBJ_SQRT
, v1
, v2
, NULL_VALUE
);
math_error("Bad value for taking square root");
vres
->v_num
= qlink(c
->real
);
* Take the Nth root of an arbitrary value within the specified error.
* Result is placed in the indicated location.
rootvalue(v1
, v2
, v3
, vres
)
VALUE
*v1
; /* value to take root of */
VALUE
*v2
; /* value specifying root to take */
VALUE
*v3
; /* value specifying error */
if ((v2
->v_type
!= V_NUM
) || (v3
->v_type
!= V_NUM
))
math_error("Non-real arguments for root");
if (qisneg(q1
) || qiszero(q1
) || qisfrac(q1
))
math_error("Non-positive or non-integral root");
if (qisneg(q2
) || qiszero(q2
))
math_error("Non-positive epsilon for root");
if (!qisneg(v1
->v_num
) || zisodd(q1
->num
)) {
vres
->v_num
= qroot(v1
->v_num
, q1
, q2
);
vres
->v_com
= croot(&ctmp
, q1
, q2
);
vres
->v_com
= croot(v1
->v_com
, q1
, q2
);
*vres
= objcall(OBJ_ROOT
, v1
, v2
, v3
);
math_error("Taking root of bad value");
* Take the absolute value of an arbitrary value within the specified error.
* Result is placed in the indicated location.
math_error("Bad epsilon type for abs");
if (qiszero(epsilon
) || qisneg(epsilon
))
math_error("Non-positive epsilon for abs");
q
= qhypot(v1
->v_com
->real
, v1
->v_com
->imag
, epsilon
);
*vres
= objcall(OBJ_ABS
, v1
, v2
, NULL_VALUE
);
math_error("Illegal value for absolute value");
* Calculate the norm of an arbitrary value.
* Result is placed in the indicated location.
* The norm is the square of the absolute value.
vres
->v_num
= qsquare(vp
->v_num
);
q1
= qsquare(vp
->v_com
->real
);
q2
= qsquare(vp
->v_com
->imag
);
vres
->v_num
= qadd(q1
, q2
);
*vres
= objcall(OBJ_NORM
, vp
, NULL_VALUE
, NULL_VALUE
);
math_error("Illegal value for norm");
* Shift a value left or right by the specified number of bits.
* Negative shift value means shift the direction opposite the selected dir.
* Right shifts are defined to lose bits off the low end of the number.
* Result is placed in the indicated location.
shiftvalue(v1
, v2
, rightshift
, vres
)
BOOL rightshift
; /* TRUE if shift right instead of left */
math_error("Non-real shift value");
math_error("Non-integral shift value");
if (v1
->v_type
!= V_OBJ
) {
if (zisbig(v2
->v_num
->num
))
math_error("Very large shift value");
vres
->v_num
= qshift(v1
->v_num
, n
);
c
= cshift(v1
->v_com
, n
);
vres
->v_num
= qlink(c
->real
);
vres
->v_mat
= matshift(v1
->v_mat
, n
);
*vres
= objcall(OBJ_SHIFT
, v1
, v2
, NULL_VALUE
);
tmp
.v_num
= qneg(v2
->v_num
);
*vres
= objcall(OBJ_SHIFT
, v1
, &tmp
, NULL_VALUE
);
math_error("Bad value for shifting");
* Scale a value by a power of two.
* Result is placed in the indicated location.
math_error("Non-real scaling factor");
math_error("Non-integral scaling factor");
if (v1
->v_type
!= V_OBJ
) {
if (zisbig(v2
->v_num
->num
))
math_error("Very large scaling factor");
vres
->v_num
= qscale(v1
->v_num
, n
);
vres
->v_com
= cscale(v1
->v_com
, n
);
vres
->v_mat
= matscale(v1
->v_mat
, n
);
*vres
= objcall(OBJ_SCALE
, v1
, v2
, NULL_VALUE
);
math_error("Bad value for scaling");
* Raise a value to an integral power.
* Result is placed in the indicated location.
math_error("Raising value to non-real power");
math_error("Raising value to non-integral power");
vres
->v_num
= qpowi(v1
->v_num
, q
);
vres
->v_com
= cpowi(v1
->v_com
, q
);
vres
->v_num
= qlink(c
->real
);
vres
->v_mat
= matpowi(v1
->v_mat
, q
);
*vres
= objcall(OBJ_POW
, v1
, v2
, NULL_VALUE
);
math_error("Illegal value for raising to integer power");
* Raise one value to another value's power, within the specified error.
* Result is placed in the indicated location.
powervalue(v1
, v2
, v3
, vres
)
VALUE
*v1
, *v2
, *v3
, *vres
;
math_error("Non-real epsilon value for power");
if (qisneg(epsilon
) || qiszero(epsilon
))
math_error("Non-positive epsilon value for power");
switch (TWOVAL(v1
->v_type
, v2
->v_type
)) {
case TWOVAL(V_NUM
, V_NUM
):
vres
->v_num
= qpower(v1
->v_num
, v2
->v_num
, epsilon
);
case TWOVAL(V_NUM
, V_COM
):
vres
->v_com
= cpower(&ctmp
, v2
->v_com
, epsilon
);
case TWOVAL(V_COM
, V_NUM
):
vres
->v_com
= cpower(v1
->v_com
, &ctmp
, epsilon
);
case TWOVAL(V_COM
, V_COM
):
vres
->v_com
= cpower(v1
->v_com
, v2
->v_com
, epsilon
);
math_error("Illegal value for raising to power");
* Here for any complex result.
vres
->v_num
= qlink(c
->real
);
* Divide one arbitrary value by another one.
* Result is placed in the indicated location.
switch (TWOVAL(v1
->v_type
, v2
->v_type
)) {
case TWOVAL(V_NUM
, V_NUM
):
vres
->v_num
= qdiv(v1
->v_num
, v2
->v_num
);
case TWOVAL(V_COM
, V_NUM
):
vres
->v_com
= cdivq(v1
->v_com
, v2
->v_num
);
case TWOVAL(V_NUM
, V_COM
):
if (qiszero(v1
->v_num
)) {
vres
->v_num
= qlink(&_qzero_
);
vres
->v_com
= cdiv(&ctmp
, v2
->v_com
);
case TWOVAL(V_COM
, V_COM
):
vres
->v_com
= cdiv(v1
->v_com
, v2
->v_com
);
vres
->v_num
= qlink(c
->real
);
case TWOVAL(V_MAT
, V_NUM
):
case TWOVAL(V_MAT
, V_COM
):
invertvalue(v2
, &tmpval
);
vres
->v_mat
= matmulval(v1
->v_mat
, &tmpval
);
if ((v1
->v_type
!= V_OBJ
) && (v2
->v_type
!= V_OBJ
))
math_error("Non-compatible values for divide");
*vres
= objcall(OBJ_DIV
, v1
, v2
, NULL_VALUE
);
* Divide one arbitrary value by another one keeping only the integer part.
* Result is placed in the indicated location.
switch (TWOVAL(v1
->v_type
, v2
->v_type
)) {
case TWOVAL(V_NUM
, V_NUM
):
vres
->v_num
= qquo(v1
->v_num
, v2
->v_num
);
case TWOVAL(V_COM
, V_NUM
):
vres
->v_com
= cquoq(v1
->v_com
, v2
->v_num
);
vres
->v_num
= qlink(c
->real
);
case TWOVAL(V_MAT
, V_NUM
):
case TWOVAL(V_MAT
, V_COM
):
vres
->v_mat
= matquoval(v1
->v_mat
, v2
);
if ((v1
->v_type
!= V_OBJ
) && (v2
->v_type
!= V_OBJ
))
math_error("Non-compatible values for quotient");
*vres
= objcall(OBJ_QUO
, v1
, v2
, NULL_VALUE
);
* Divide one arbitrary value by another one keeping only the remainder.
* Result is placed in the indicated location.
switch (TWOVAL(v1
->v_type
, v2
->v_type
)) {
case TWOVAL(V_NUM
, V_NUM
):
vres
->v_num
= qmod(v1
->v_num
, v2
->v_num
);
case TWOVAL(V_COM
, V_NUM
):
vres
->v_com
= cmodq(v1
->v_com
, v2
->v_num
);
vres
->v_num
= qlink(c
->real
);
case TWOVAL(V_MAT
, V_NUM
):
case TWOVAL(V_MAT
, V_COM
):
vres
->v_mat
= matmodval(v1
->v_mat
, v2
);
if ((v1
->v_type
!= V_OBJ
) && (v2
->v_type
!= V_OBJ
))
math_error("Non-compatible values for mod");
*vres
= objcall(OBJ_MOD
, v1
, v2
, NULL_VALUE
);
* Test an arbitrary value to see if it is equal to "zero".
* The definition of zero varies depending on the value type. For example,
* the null string is "zero", and a matrix with zero values is "zero".
* Returns TRUE if value is not equal to zero.
return !qiszero(vp
->v_num
);
return !ciszero(vp
->v_com
);
return (vp
->v_str
[0] != '\0');
return mattest(vp
->v_mat
);
return (vp
->v_list
->l_count
!= 0);
return (vp
->v_assoc
->a_count
!= 0);
return validid(vp
->v_file
);
val
= objcall(OBJ_TEST
, vp
, NULL_VALUE
, NULL_VALUE
);
* Compare two values for equality.
* Returns TRUE if the two values differ.
if ((v1
->v_type
== V_OBJ
) || (v2
->v_type
== V_OBJ
)) {
val
= objcall(OBJ_CMP
, v1
, v2
, NULL_VALUE
);
if (v1
->v_type
!= v2
->v_type
)
r
= qcmp(v1
->v_num
, v2
->v_num
);
r
= ccmp(v1
->v_com
, v2
->v_com
);
r
= ((v1
->v_str
!= v2
->v_str
) &&
((v1
->v_str
[0] - v2
->v_str
[0]) ||
strcmp(v1
->v_str
, v2
->v_str
)));
r
= matcmp(v1
->v_mat
, v2
->v_mat
);
r
= listcmp(v1
->v_list
, v2
->v_list
);
r
= assoccmp(v1
->v_assoc
, v2
->v_assoc
);
r
= (v1
->v_file
!= v2
->v_file
);
math_error("Illegal values for comparevalue");
* Compare two values for their relative values.
* Returns minus one if the first value is less than the second one,
* one if the first value is greater than the second one, and
* zero if they are equal.
if ((v1
->v_type
== V_OBJ
) || (v2
->v_type
== V_OBJ
)) {
val
= objcall(OBJ_REL
, v1
, v2
, NULL_VALUE
);
if (v1
->v_type
!= v2
->v_type
)
math_error("Relative comparison of differing types");
r
= qrel(v1
->v_num
, v2
->v_num
);
r
= strcmp(v1
->v_str
, v2
->v_str
);
math_error("Illegal value for relative comparison");
* Calculate a hash value for a value.
* The hash does not have to be a perfect one, it is only used for
* making associations faster.
return ((long) vp
->v_int
);
return hashstr(vp
->v_str
);
return objhash(vp
->v_obj
);
return listhash(vp
->v_list
);
return assochash(vp
->v_assoc
);
return mathash(vp
->v_mat
);
return ((long) vp
->v_file
);
math_error("Hashing unknown value");
* Print the value of a descriptor in one of several formats.
* If flags contains PRINT_SHORT, then elements of arrays and lists
* will not be printed. If flags contains PRINT_UNAMBIG, then quotes
* are placed around strings and the null value is explicitly printed.
qprintnum(vp
->v_num
, MODE_DEFAULT
);
if (flags
& PRINT_UNAMBIG
)
if (flags
& PRINT_UNAMBIG
)
if (flags
& PRINT_UNAMBIG
)
(void) objcall(OBJ_PRINT
, vp
, NULL_VALUE
, NULL_VALUE
);
((flags
& PRINT_SHORT
) ? 0L : maxprint
));
((flags
& PRINT_SHORT
) ? 0L : maxprint
));
((flags
& PRINT_SHORT
) ? 0L : maxprint
));
printid(vp
->v_file
, flags
);
math_error("Printing unknown value");