From f76980f59d1fde7e935a86f2af88e92ce8a96aa2 Mon Sep 17 00:00:00 2001 From: Daniel Svane Date: Sat, 29 Aug 2020 10:50:42 +0200 Subject: [PATCH] Changed number of decimals to 20 --- dist/algebrite.bundle-for-browser.js | 8081 ++++++++++++++++++++------ dist/algebrite.js | 8081 ++++++++++++++++++++------ runtime/init.coffee | 2 +- 3 files changed, 12477 insertions(+), 3687 deletions(-) diff --git a/dist/algebrite.bundle-for-browser.js b/dist/algebrite.bundle-for-browser.js index 3a23a4de..6a704077 100644 --- a/dist/algebrite.bundle-for-browser.js +++ b/dist/algebrite.bundle-for-browser.js @@ -1,22 +1,925 @@ (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i -2 + + */ + /* adj ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + m + + General description + ------------------- + Returns the adjunct of matrix m. The inverse of m is equal to adj(m) divided by det(m). + + */ + /* + Guesses a rational for each float in the passed expression + */ + /* arccos ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + Returns the inverse cosine of x. + + */ + /* arccosh ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + Returns the inverse hyperbolic cosine of x. + + */ + /* arcsin ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + Returns the inverse sine of x. + + */ + /* arcsinh ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + Returns the inverse hyperbolic sine of x. + + */ + /* arctan ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + Returns the inverse tangent of x. + + */ + /* arctanh ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + Returns the inverse hyperbolic tangent of x. + + */ + /* besselj ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x,n + + General description + ------------------- + + Returns a solution to the Bessel differential equation (Bessel function of first kind). + + Recurrence relation: + + besselj(x,n) = (2/x) (n-1) besselj(x,n-1) - besselj(x,n-2) + + besselj(x,1/2) = sqrt(2/pi/x) sin(x) + + besselj(x,-1/2) = sqrt(2/pi/x) cos(x) + + For negative n, reorder the recurrence relation as: + + besselj(x,n-2) = (2/x) (n-1) besselj(x,n-1) - besselj(x,n) + + Substitute n+2 for n to obtain + + besselj(x,n) = (2/x) (n+1) besselj(x,n+1) - besselj(x,n+2) + + Examples: + + besselj(x,3/2) = (1/x) besselj(x,1/2) - besselj(x,-1/2) + + besselj(x,-3/2) = -(1/x) besselj(x,-1/2) - besselj(x,1/2) + + */ + /* bessely ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x,n + + General description + ------------------- + + Bessel function of second kind. + + */ + /* ceiling ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + + Returns the smallest integer not less than x. + + */ + /* check ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + p + + General description + ------------------- + Returns whether the predicate p is true/false or unknown: + 0 if false, 1 if true or remains unevaluated if unknown. + Note that if "check" is passed an assignment, it turns it into a test, + i.e. check(a = b) is turned into check(a==b) + so "a" is not assigned anything. + Like in many programming languages, "check" also gives truthyness/falsyness + for numeric values. In which case, "true" is returned for non-zero values. + Potential improvements: "check" can't evaluate strings yet. + + */ + /* choose ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + n,k + + General description + ------------------- + + Returns the number of combinations of n items taken k at a time. + + For example, the number of five card hands is choose(52,5) + + ``` + n! + choose(n,k) = ------------- + k! (n - k)! + ``` + */ + /* circexp ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + + Returns expression x with circular and hyperbolic functions converted to exponential forms. Sometimes this will simplify an expression. + + */ + /* clear ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + + Completely wipes a variable from the environment (while doing x = quote(x) just unassigns it). + + */ + /* clearall ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + General description + ------------------- + + Completely wipes all variables from the environment. + + */ + /* cofactor ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + m,i,j + + General description + ------------------- + Cofactor of a matrix component. + Let c be the cofactor matrix of matrix m, i.e. tranpose(c) = adj(m). + This function returns c[i,j]. + + */ + /* conj ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + z + + General description + ------------------- + Returns the complex conjugate of z. + + */ + /* contract ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + a,i,j + + General description + ------------------- + Contract across tensor indices i.e. returns "a" summed over indices i and j. + If i and j are omitted then 1 and 2 are used. + contract(m) is equivalent to the trace of matrix m. + + */ + /* cosh ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + Returns the hyperbolic cosine of x + + ``` + exp(x) + exp(-x) + cosh(x) = ---------------- + 2 + ``` + + */ + /* deg ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + p,x + + General description + ------------------- + Returns the degree of polynomial p(x). + + */ + /* denominator ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + Returns the denominator of expression x. + + */ + /* dim ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + m,n + + General description + ------------------- + Returns the cardinality of the nth index of tensor "m". + + */ + /* do ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + a,b,... + + General description + ------------------- + Evaluates each argument from left to right. Returns the result of the last argument. + + */ + /* eigenval ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + m + + General description + ------------------- + Compute eigenvalues of m. See "eigen" for more info. + + */ + /* eigenvec ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + m + + General description + ------------------- + Compute eigenvectors of m. See "eigen" for more info. + + */ + /* erf ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Authors + ------- + philippe.billet@noos.fr + + Parameters + ---------- + x + + General description + ------------------- + Error function erf(x). + erf(-x)=erf(x) + + */ + /* + Remove terms that involve a given symbol or expression. For example... + + filter(x^2 + x + 1, x) => 1 + + filter(x^2 + x + 1, x^2) => x + 1 + */ + /* dot ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + a,b,... + + General description + ------------------- + + The inner (or dot) operator gives products of vectors, + matrices, and tensors. + + Note that for Algebrite, the elements of a vector/matrix + can only be scalars. This allows for example to flesh out + matrix multiplication using the usual multiplication. + So for example block-representations are not allowed. + + There is an aweful lot of confusion between sw packages on + what dot and inner do. + + First off, the "dot" operator is different from the + mathematical notion of dot product, which can be + slightly confusing. + + The mathematical notion of dot product is here: + http://mathworld.wolfram.com/DotProduct.html + + However, "dot" does that and a bunch of other things, + i.e. in Algebrite + dot/inner does what the dot of Mathematica does, i.e.: + + scalar product of vectors: + + inner((a, b, c), (x, y, z)) + > a x + b y + c z + + products of matrices and vectors: + + inner(((a, b), (c,d)), (x, y)) + > (a x + b y,c x + d y) + + inner((x, y), ((a, b), (c,d))) + > (a x + c y,b x + d y) + + inner((x, y), ((a, b), (c,d)), (r, s)) + > a r x + b s x + c r y + d s y + + matrix product: + + inner(((a,b),(c,d)),((r,s),(t,u))) + > ((a r + b t,a s + b u),(c r + d t,c s + d u)) + + the "dot/inner" operator is associative and + distributive but not commutative. + + In Mathematica, Inner is a generalisation of Dot where + the user can specify the multiplication and the addition + operators. + But here in Algebrite they do the same thing. + + https://reference.wolfram.com/language/ref/Dot.html + https://reference.wolfram.com/language/ref/Inner.html + + http://uk.mathworks.com/help/matlab/ref/dot.html + http://uk.mathworks.com/help/matlab/ref/mtimes.html + + */ + /* + Laguerre function + + Example + + laguerre(x,3) + + Result + + 1 3 3 2 + - --- x + --- x - 3 x + 1 + 6 2 + + The computation uses the following recurrence relation. + + L(x,0,k) = 1 + + L(x,1,k) = -x + k + 1 + + n*L(x,n,k) = (2*(n-1)+1-x+k)*L(x,n-1,k) - (n-1+k)*L(x,n-2,k) + + In the "for" loop i = n-1 so the recurrence relation becomes + + (i+1)*L(x,n,k) = (2*i+1-x+k)*L(x,n-1,k) - (i+k)*L(x,n-2,k) + */ + /* + Return the leading coefficient of a polynomial. + + Example + + leading(5x^2+x+1,x) + + Result + + 5 + + The result is undefined if P is not a polynomial. + */ + /* + Legendre function + + Example + + legendre(x,3,0) + + Result + + 5 3 3 + --- x - --- x + 2 2 + + The computation uses the following recurrence relation. + + P(x,0) = 1 + + P(x,1) = x + + n*P(x,n) = (2*(n-1)+1)*x*P(x,n-1) - (n-1)*P(x,n-2) + + In the "for" loop we have i = n-1 so the recurrence relation becomes + + (i+1)*P(x,n) = (2*i+1)*x*P(x,n-1) - i*P(x,n-2) + + For m > 0 + + P(x,n,m) = (-1)^m * (1-x^2)^(m/2) * d^m/dx^m P(x,n) + */ + /* + Convert complex z to polar form + + Input: push z + + Output: Result on stack + + polar(z) = abs(z) * exp(i * arg(z)) + */ + /* + Returns the real part of complex z + + z real(z) + - ------- + + a + i b a + + exp(i a) cos(a) + */ + /* + Taylor expansion of a function + + push(F) + push(X) + push(N) + push(A) + taylor() + */ + /* + // up to 100 blocks of 100,000 atoms + + #define M 100 + #define N 100000 + + U *mem[M] + int mcount + + U *free_list + int free_count + + U * + alloc(void) + { + U *p + if (free_count == 0) { + if (mcount == 0) + alloc_mem() + else { + gc() + if (free_count < N * mcount / 2) + alloc_mem() + } + if (free_count == 0) + stop("atom space exhausted") + } + p = free_list + free_list = free_list->u.cons.cdr + free_count-- + return p + } + */ + /* + Compare adjacent terms in s[] and combine if possible. + + Returns the number of terms remaining in s[]. + + n number of terms in s[] initially + */ + /* cross ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept, script_defined + + Parameters + ---------- + u,v + + General description + ------------------- + Returns the cross product of vectors u and v. + + */ + /* curl ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept, script_defined + + Parameters + ---------- + u + + General description + ------------------- + Returns the curl of vector u. + + */ + /* + Clear all patterns + */ + /* + if 0 + + * left brace + + for (i = 0; i < h; i++) { + if (yindex == YMAX) + break + chartab[yindex].c = '|' + chartab[yindex].x = x - 2 + chartab[yindex].y = y + i + yindex++ + } + + * right brace + + emit_x++ + + for (i = 0; i < h; i++) { + if (yindex == YMAX) + break + chartab[yindex].c = '|' + chartab[yindex].x = emit_x + chartab[yindex].y = y + i + yindex++ + } + + emit_x++ + + endif + */ + /* + For example... + + push(F) + push(X) + filter() + F = pop() + */ + /* + Symbolic addition + + Terms in a sum are combined if they are identical modulo rational + coefficients. + + For example, A + 2A becomes 3A. + + However, the sum A + sqrt(2) A is not modified. + + Combining terms can lead to second-order effects. + + For example, consider the case of + + 1/sqrt(2) A + 3/sqrt(2) A + sqrt(2) A + + The first two terms are combined to yield 2 sqrt(2) A. + + This result can now be combined with the third term to yield + + 3 sqrt(2) A + */ + /* + Table of integrals + + The symbol f is just a dummy symbol for creating a list f(A,B,C,C,...) where + + A is the template expression + + B is the result expression + + C is an optional list of conditional expressions + */ + /* + Partition a term + + Input stack: + + term (factor or product of factors) + + free variable + + Output stack: + + constant expression + + variable expression + */ + /* + Substitute new expr for old expr in expr. + + Input: push expr + + push old expr + + push new expr + + Output: Result on stack + */ var $, ABS, ADD, ADJ, AND, APPROXRATIO, ARCCOS, ARCCOSH, ARCSIN, ARCSINH, ARCTAN, ARCTANH, ARG, ASSUME_REAL_VARIABLES, ATOMIZE, AUTOEXPAND, BAKE, BESSELJ, BESSELY, BINDING, BINOMIAL, BINOM_check_args, BUF, C1, C2, C3, C4, C5, C6, CEILING, CHECK, CHOOSE, CIRCEXP, CLEAR, CLEARALL, CLEARPATTERNS, CLOCK, COEFF, COFACTOR, CONDENSE, CONJ, CONS, CONTRACT, COS, COSH, Condense, DEBUG, DEBUG_ABS, DEBUG_ARG, DEBUG_CLOCKFORM, DEBUG_IMAG, DEBUG_IS, DEBUG_POWER, DEBUG_RECT, DECOMP, DEFINT, DEGREE, DENOMINATOR, DERIVATIVE, DET, DET_check_arg, DIM, DIRAC, DIVISORS, DO, DOT, DOUBLE, DRAW, DRAWX, DSOLVE, E, EIGEN, EIGENVAL, EIGENVEC, EIG_N, EIG_check_arg, EIG_yydd, EIG_yyqq, ERF, ERFC, EVAL, EXP, EXPAND, EXPCOS, EXPSIN, Eval, Eval_Eval, Eval_abs, Eval_add, Eval_adj, Eval_and, Eval_approxratio, Eval_arccos, Eval_arccosh, Eval_arcsin, Eval_arcsinh, Eval_arctan, Eval_arctanh, Eval_arg, Eval_besselj, Eval_bessely, Eval_binding, Eval_binomial, Eval_ceiling, Eval_check, Eval_choose, Eval_circexp, Eval_clear, Eval_clearall, Eval_clearpatterns, Eval_clock, Eval_coeff, Eval_cofactor, Eval_condense, Eval_conj, Eval_cons, Eval_contract, Eval_cos, Eval_cosh, Eval_decomp, Eval_defint, Eval_degree, Eval_denominator, Eval_derivative, Eval_det, Eval_dim, Eval_dirac, Eval_divisors, Eval_do, Eval_dsolve, Eval_eigen, Eval_eigenval, Eval_eigenvec, Eval_erf, Eval_erfc, Eval_exp, Eval_expand, Eval_expcos, Eval_expsin, Eval_factor, Eval_factorial, Eval_factorpoly, Eval_filter, Eval_float, Eval_floor, Eval_for, Eval_function_reference, Eval_gamma, Eval_gcd, Eval_hermite, Eval_hilbert, Eval_imag, Eval_index, Eval_inner, Eval_integral, Eval_inv, Eval_invg, Eval_isinteger, Eval_isprime, Eval_laguerre, Eval_lcm, Eval_leading, Eval_legendre, Eval_log, Eval_lookup, Eval_mod, Eval_multiply, Eval_noexpand, Eval_not, Eval_nroots, Eval_number, Eval_numerator, Eval_operator, Eval_or, Eval_outer, Eval_pattern, Eval_patternsinfo, Eval_polar, Eval_power, Eval_predicate, Eval_prime, Eval_print, Eval_print2dascii, Eval_printcomputer, Eval_printhuman, Eval_printlatex, Eval_printlist, Eval_product, Eval_quote, Eval_quotient, Eval_rank, Eval_rationalize, Eval_real, Eval_rect, Eval_roots, Eval_round, Eval_setq, Eval_sgn, Eval_shape, Eval_silentpattern, Eval_simfac, Eval_simplify, Eval_sin, Eval_sinh, Eval_sqrt, Eval_stop, Eval_subst, Eval_sum, Eval_sym, Eval_symbolsinfo, Eval_tan, Eval_tanh, Eval_taylor, Eval_tensor, Eval_test, Eval_testeq, Eval_testge, Eval_testgt, Eval_testle, Eval_testlt, Eval_transpose, Eval_unit, Eval_user_function, Eval_zero, Evalpoly, FACTOR, FACTORIAL, FACTORPOLY, FILTER, FLOATF, FLOOR, FOR, FORCE_FIXED_PRINTOUT, FUNCTION, Find, GAMMA, GCD, HERMITE, HILBERT, IMAG, INDEX, INNER, INTEGRAL, INV, INVG, INV_check_arg, INV_decomp, ISINTEGER, ISPRIME, LAGUERRE, LAST, LAST_2DASCII_PRINT, LAST_FULL_PRINT, LAST_LATEX_PRINT, LAST_LIST_PRINT, LAST_PLAIN_PRINT, LAST_PRINT, LCM, LEADING, LEGENDRE, LOG, LOOKUP, M, MAXDIM, MAXPRIMETAB, MAX_CONSECUTIVE_APPLICATIONS_OF_ALL_RULES, MAX_CONSECUTIVE_APPLICATIONS_OF_SINGLE_RULE, MAX_FIXED_PRINTOUT_DIGITS, MAX_PROGRAM_SIZE, MEQUAL, METAA, METAB, METAX, MLENGTH, MOD, MSIGN, MULTIPLY, MZERO, N, NIL, NOT, NROOTS, NROOTS_ABS, NROOTS_DELTA, NROOTS_EPSILON, NROOTS_RANDOM, NROOTS_YMAX, NROOTS_divpoly, NSYM, NUM, NUMBER, NUMERATOR, OPERATOR, OR, OUTER, PATTERN, PATTERNSINFO, PI, POLAR, POWER, PRIME, PRINT, PRINT2DASCII, PRINTFULL, PRINTLATEX, PRINTLIST, PRINTMODE_2DASCII, PRINTMODE_COMPUTER, PRINTMODE_HUMAN, PRINTMODE_LATEX, PRINTMODE_LIST, PRINTOUTRESULT, PRINTPLAIN, PRINT_LEAVE_E_ALONE, PRINT_LEAVE_X_ALONE, PRODUCT, QUOTE, QUOTIENT, RANK, RATIONALIZE, REAL, ROOTS, ROUND, SECRETX, SELFTEST, SETQ, SGN, SHAPE, SILENTPATTERN, SIMPLIFY, SIN, SINH, SPACE_BETWEEN_COLUMNS, SPACE_BETWEEN_ROWS, SQRT, STOP, STR, SUBST, SUM, SYM, SYMBOLSINFO, SYMBOL_A, SYMBOL_A_UNDERSCORE, SYMBOL_B, SYMBOL_B_UNDERSCORE, SYMBOL_C, SYMBOL_D, SYMBOL_I, SYMBOL_IDENTITY_MATRIX, SYMBOL_J, SYMBOL_N, SYMBOL_R, SYMBOL_S, SYMBOL_T, SYMBOL_X, SYMBOL_X_UNDERSCORE, SYMBOL_Y, SYMBOL_Z, TAN, TANH, TAYLOR, TENSOR, TEST, TESTEQ, TESTGE, TESTGT, TESTLE, TESTLT, TIMING_DEBUGS, TOS, TRACE, TRANSPOSE, T_DOUBLE, T_EQ, T_FUNCTION, T_GTEQ, T_INTEGER, T_LTEQ, T_NEQ, T_NEWLINE, T_QUOTASSIGN, T_STRING, T_SYMBOL, U, UNIT, USR_SYMBOLS, VERSION, YMAX, YYE, YYRECT, ZERO, __emit_char, __emit_str, __factor_add, __factorial, __is_negative, __is_radical_number, __lcm, __legendre, __legendre2, __legendre3, __normalize_radical_factors, __rationalize_tensor, _print, abs, absValFloat, absval, absval_tensor, add, addSymbolLeftOfAssignment, addSymbolRightOfAssignment, add_all, add_factor_to_accumulator, add_numbers, add_terms, addf, adj, alloc_tensor, allocatedId, any_denominators, approxAll, approxLogs, approxLogsOfRationals, approxOneRatioOnly, approxRadicals, approxRadicalsOfRationals, approxRationalsOfLogs, approxRationalsOfPowersOfE, approxRationalsOfPowersOfPI, approxRationalsOfRadicals, approxSineOfRationalMultiplesOfPI, approxSineOfRationals, approxTrigonometric, approx_just_an_integer, approx_logarithmsOfRationals, approx_nothingUseful, approx_radicalOfRatio, approx_ratioOfRadical, approx_rationalOfE, approx_rationalOfPi, approx_rationalsOfLogarithms, approx_sine_of_pi_times_rational, approx_sine_of_rational, approxratioRecursive, arccos, arccosh, arcsin, arcsinh, arctan, arctanh, areunivarpolysfactoredorexpandedform, arg, arglist, assignmentFound, avoidCalculatingPowersIntoArctans, bake, bake_poly, bake_poly_term, besselj, bessely, bigInt, bignum_factorial, bignum_float, bignum_power_number, bignum_scan_float, bignum_scan_integer, bignum_truncate, binding, binomial, buffer, build_tensor, caaddr, caadr, caar, cadaddr, cadadr, cadar, caddaddr, caddadr, caddar, caddddr, cadddr, caddr, cadr, called_from_Algebra_block, car, cdaddr, cdadr, cdar, cddaddr, cddar, cdddaddr, cddddr, cdddr, cddr, cdr, ceiling, chainOfUserSymbolsNotFunctionsBeingEvaluated, charTabIndex, chartab, checkFloatHasWorkedOutCompletely, check_esc_flag, check_stack, check_tensor_dimensions, choose, choose_check_args, circexp, clearAlgebraEnvironment, clearRenamedVariablesToAvoidBindingToExternalScope, clear_symbols, clear_term, clearall, clockform, cmpGlyphs, cmp_args, cmp_expr, cmp_terms, cmp_terms_count, codeGen, coeff, cofactor, collectLatexStringFromReturnValue, collectUserSymbols, combine_factors, combine_gammas, combine_terms, compareState, compare_numbers, compare_rationals, compare_tensors, compatible, computeDependenciesFromAlgebra, computeResultsAndJavaScriptFromAlgebra, compute_fa, conjugate, cons, consCount, contract, convert_bignum_to_double, convert_rational_to_double, copy_tensor, cosine, cosine_of_angle, cosine_of_angle_sum, count, countOccurrencesOfSymbol, count_denominators, counter, countsize, d_scalar_scalar, d_scalar_scalar_1, d_scalar_tensor, d_tensor_scalar, d_tensor_tensor, dabs, darccos, darccosh, darcsin, darcsinh, darctan, darctanh, dbesselj0, dbesseljn, dbessely0, dbesselyn, dcos, dcosh, dd, decomp, decomp_product, decomp_sum, defineSomeHandyConstants, define_user_function, defn, defn_str, degree, denominator, derf, derfc, derivative, derivative_of_integral, det, determinant, detg, dfunction, dhermite, dirac, display, display_flag, displaychar, divide, divide_numbers, divisors, divisors_onstack, divpoly, dlog, do_clearPatterns, do_clearall, do_simplify_nested_radicals, dontCreateNewRadicalsInDenominatorWhenEvalingMultiplication, dotprod_unicode, doubleToReasonableString, dpow, dpower, dproduct, draw_flag, draw_stop_return, dsgn, dsin, dsinh, dsum, dtan, dtanh, dupl, eigen, elelmIndex, elem, emit_denominator, emit_denominators, emit_expr, emit_factor, emit_factorial_function, emit_flat_tensor, emit_fraction, emit_function, emit_index_function, emit_multiply, emit_number, emit_numerators, emit_numerical_fraction, emit_power, emit_string, emit_subexpr, emit_symbol, emit_tensor, emit_tensor_inner, emit_term, emit_top_expr, emit_unsigned_expr, emit_x, equal, equaln, equalq, erfc, errorMessage, esc_flag, evaluatingAsFloats, evaluatingPolar, exec, expand, expand_get_A, expand_get_AF, expand_get_B, expand_get_C, expand_get_CF, expand_tensor, expanding, expcos, exponential, expr_level, expsin, f1, f10, f2, f3, f4, f5, f9, f_equals_a, factor, factor_a, factor_again, factor_b, factor_number, factor_small_number, factor_term, factorial, factorpoly, factors, factpoly_expo, fill_buf, filter, filter_main, filter_sum, filter_tensor, findDependenciesInScript, findPossibleClockForm, findPossibleExponentialForm, findroot, fixup_fraction, fixup_power, flag, floatToRatioRoutine, fmt_index, fmt_level, fmt_x, frame, freeze, functionInvokationsScanningStack, gamma, gamma_of_sum, gammaf, gcd, gcd_main, gcd_numbers, gcd_polys, gcd_powers_with_same_base, gcd_product_product, gcd_product_sum, gcd_sum, gcd_sum_product, gcd_sum_sum, gen, getSimpleRoots, getStateHash, get_binding, get_factor_from_complex_root, get_factor_from_real_root, get_innerprod_factors, get_next_token, get_printname, get_size, get_token, getdisplaystr, glyph, gp, guess, hasImaginaryCoeff, hasNegativeRationalExponent, hash_addition, hash_function, hash_multiplication, hash_power, hashcode_values, hashed_itab, hermite, hilbert, i1, imag, imaginaryunit, index_function, init, initNRoots, inited, inner, inner_f, input_str, integral, integral_of_form, integral_of_product, integral_of_sum, inv, inverse, invert_number, invg, isNumberOneOverSomething, isNumericAtom, isNumericAtomOrTensor, isSimpleRoot, isSmall, isSymbolLeftOfAssignment, isSymbolReclaimable, isZeroAtom, isZeroAtomOrTensor, isZeroLikeOrNonZeroLikeOrUndetermined, isZeroTensor, is_denominator, is_factor, is_small_integer, is_square_matrix, is_usr_symbol, isadd, isalnumorunderscore, isalpha, isalphaOrUnderscore, iscomplexnumber, iscomplexnumberdouble, iscons, isdenominator, isdigit, isdouble, iseveninteger, isfactor, isfactorial, isfloating, isfraction, isidentitymatrix, isimaginarynumber, isimaginarynumberdouble, isimaginaryunit, isinnerordot, isinteger, isintegerfactor, isintegerorintegerfloat, isinv, iskeyword, isminusone, isminusoneoversqrttwo, isminusoneovertwo, ismultiply, isnegative, isnegativenumber, isnegativeterm, isnonnegativeinteger, isnpi, isone, isoneover, isoneoversqrttwo, isoneovertwo, isplusone, isplustwo, ispolyexpandedform, ispolyexpandedform_expr, ispolyexpandedform_factor, ispolyexpandedform_term, ispolyfactoredorexpandedform, ispolyfactoredorexpandedform_factor, ispolyfactoredorexpandedform_power, isposint, ispositivenumber, ispower, isquarterturn, isrational, isspace, isstr, issymbol, issymbolic, istensor, istranspose, isunderscore, isunivarpolyfactoredorexpandedform, itab, italu_hashcode, j1, laguerre, laguerre2, lastFoundSymbol, latexErrorSign, lcm, leading, legendre, length, lessp, level, list, listLength, logarithm, logbuf, lookupsTotal, lu_decomp, madd, makePositive, makeSignSameAs, make_hashed_itab, mask, mcmp, mcmpint, mdiv, mdivrem, meta_mode, mgcd, mini_solve, mint, mmod, mmul, mod, monic, move, moveTos, mp_clr_bit, mp_denominator, mp_numerator, mp_set_bit, mpow, mprime, mroot, mshiftright, msub, mtotal, multinomial_sum, multiply, multiply_all, multiply_all_noexpand, multiply_consecutive_constants, multiply_denominators, multiply_denominators_factor, multiply_denominators_term, multiply_noexpand, multiply_numbers, n_factor_number, negate, negate_expand, negate_noexpand, negate_number, new_string, newline_flag, nil_symbols, normaliseDots, normalisedCoeff, normalize_angle, nroots_a, nroots_b, nroots_c, nroots_df, nroots_dx, nroots_fa, nroots_fb, nroots_x, nroots_y, nterms, nthCadr, numerator, numericRootOfPolynomial, o, one, oneElement, one_as_double, out_buf, out_count, out_of_memory, outer, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, parse, parse_internal, parse_p1, parse_p2, parse_time_simplifications, partition, patternHasBeenFound, patternsinfo, peek, performing_roots, polar, polarRectAMinusOneBase, polycoeff, polyform, pop, pop_double, pop_frame, pop_integer, power, power_str, power_sum, power_tensor, predefinedSymbolsInGlobalScope_doNotTrackInDependencies, prime, primetab, print2dascii, printMode, print_ABS_latex, print_ARCCOS_codegen, print_ARCSIN_codegen, print_ARCTAN_codegen, print_BINOMIAL_latex, print_COS_codegen, print_DEFINT_latex, print_DOT_codegen, print_DOT_latex, print_DO_codegen, print_FOR_codegen, print_INV_codegen, print_INV_latex, print_PRODUCT_codegen, print_PRODUCT_latex, print_SETQ_codegen, print_SIN_codegen, print_SQRT_latex, print_SUM_codegen, print_SUM_latex, print_TAN_codegen, print_TESTEQ_latex, print_TESTGE_latex, print_TESTGT_latex, print_TESTLE_latex, print_TESTLT_latex, print_TEST_codegen, print_TEST_latex, print_TRANSPOSE_codegen, print_TRANSPOSE_latex, print_UNIT_codegen, print_a_over_b, print_base, print_base_of_denom, print_char, print_denom, print_double, print_expo_of_denom, print_exponent, print_expr, print_factor, print_factorial_function, print_glyphs, print_index_function, print_list, print_multiply_sign, print_number, print_power, print_str, print_subexpr, print_tensor, print_tensor_inner, print_tensor_inner_latex, print_tensor_latex, print_term, printchar, printchar_nowrap, printline, program_buf, promote_tensor, push, pushTryNotToDuplicate, push_cars, push_double, push_factor, push_frame, push_identity_matrix, push_integer, push_rational, push_symbol, push_term_factors, push_terms, push_zero_matrix, qadd, qdiv, qmul, qpow, qpowf, quickfactor, quickpower, rational, rationalize, rationalize_coefficients, real, reciprocate, rect, recursionLevelNestedRadicalsRemoval, recursiveDependencies, ref, ref1, rememberPrint, remove_negative_exponents, reset_after_error, restore, restoreMetaBindings, rewrite_args, rewrite_args_tensor, roots, roots2, roots3, run, runUserDefinedSimplifications, save, saveMetaBindings, scalar_times_tensor, scan, scan_error, scan_expression, scan_factor, scan_function_call_with_function_name, scan_function_call_without_function_name, scan_index, scan_meta, scan_power, scan_relation, scan_stmt, scan_str, scan_string, scan_subexpr, scan_symbol, scan_tensor, scan_term, scanned, scanningParameters, setM, setSignTo, set_binding, set_component, setq_indexed, sfac_product, sfac_product_f, sgn, shape, show_power_debug, sign, sign_of_term, simfac, simfac_term, simpleComplexityMeasure, simplify, simplifyForCodeGeneration, simplify_1_in_products, simplify_main, simplify_nested_radicals, simplify_polar, simplify_polarRect, simplify_rational_expressions, simplify_rectToClock, simplify_tensor, simplify_trig, simplifyfactorials, sine, sine_of_angle, sine_of_angle_sum, skipRootVariableToBeSolved, sort_stack, square, ssqrt, stack, stackAddsCount, std_symbol, step, step2, stop, strcmp, stringsEmittedByUserPrintouts, subf, subst, subtract, subtract_numbers, swap, symbol, symbolsDependencies, symbolsHavingReassignments, symbolsInExpressionsWithoutAssignments, symbolsLeftOfAssignment, symbolsRightOfAssignment, symbolsinfo, symnum, symtab, take_care_of_nested_radicals, tangent, taylor, tensor, tensor_plus_tensor, tensor_times_scalar, testApprox, test_flag, text_metric, theRandom, token, token_buf, token_str, top, top_level_eval, tos, transform, transpose, transpose_unicode, trigmode, trivial_divide, try_kth_prime, turnErrorMessageToLatex, ucmp, unfreeze, unique, unique_f, update_token_buf, userSimplificationsInListForm, userSimplificationsInStringForm, usr_symbol, verbosing, version, will_be_displayed_as_fraction, ybinomial, ycosh, ydirac, yerf, yerfc, yfloor, yindex, yround, ysinh, yyarg, yybesselj, yybessely, yyceiling, yycondense, yycontract, yycosh, yydegree, yydetg, yydivpoly, yyerf, yyerfc, yyexpand, yyfactorpoly, yyfloat, yyfloor, yyhermite, yyhermite2, yyinvg, yylcm, yylog, yymultiply, yyouter, yypower, yyrationalize, yyround, yysgn, yysimfac, yysinh, yytangent, zero, zzfloat, - hasProp = {}.hasOwnProperty, - slice = [].slice; + hasProp = {}.hasOwnProperty; bigInt = require('big-integer'); + // also change the version in the package.json file version = "1.3.1"; SELFTEST = 1; + // size of the symbol table NSYM = 1000; DEBUG = false; PRINTOUTRESULT = false; + // printing-related constants PRINTMODE_LATEX = "PRINTMODE_LATEX"; PRINTMODE_2DASCII = "PRINTMODE_2DASCII"; @@ -27,6 +930,8 @@ PRINTMODE_LIST = "PRINTMODE_LIST"; + // when the user uses the generic "print" statement + // this setting kicks-in. printMode = PRINTMODE_COMPUTER; dontCreateNewRadicalsInDenominatorWhenEvalingMultiplication = true; @@ -38,18 +943,54 @@ avoidCalculatingPowersIntoArctans = true; rational = (function() { - function rational() {} - - rational.prototype.a = null; - - rational.prototype.b = null; + // Symbolic expressions are built by connecting U structs. + + // For example, (a b + c) is built like this: + + // _______ _______ _______ + // |CONS |--->|CONS |----------------------------->|CONS | + // | | | | | | + // |_______| |_______| |_______| + // | | | + // ___v___ ___v___ _______ _______ ___v___ + // |ADD | |CONS |--->|CONS |--->|CONS | |SYM c | + // | | | | | | | | | | + // |_______| |_______| |_______| |_______| |_______| + // | | | + // ___v___ ___v___ ___v___ + // |MUL | |SYM a | |SYM b | + // | | | | | | + // |_______| |_______| |_______| + class rational {}; + + rational.prototype.a = null; // a bigInteger + + rational.prototype.b = null; // a bigInteger return rational; - })(); + }).call(this); U = (function() { - U.prototype.cons = null; + class U { + toString() { + return print_expr(this); + } + + toLatexString() { + return collectLatexStringFromReturnValue(this); + } + + constructor() { + this.cons = {}; + this.cons.car = null; + this.cons.cdr = null; + this.q = new rational(); + } + + }; + + U.prototype.cons = null; // will have a car and cdr U.prototype.printname = ""; @@ -57,35 +998,22 @@ U.prototype.tensor = null; - U.prototype.q = null; + // rational number a over b + U.prototype.q = null; // will point to a rational - U.prototype.d = 0.0; + U.prototype.d = 0.0; // a double U.prototype.k = 0; U.prototype.tag = 0; - U.prototype.toString = function() { - return print_expr(this); - }; - - U.prototype.toLatexString = function() { - return collectLatexStringFromReturnValue(this); - }; - - function U() { - this.cons = {}; - this.cons.car = null; - this.cons.cdr = null; - this.q = new rational(); - } - return U; - })(); + }).call(this); errorMessage = ""; + // the following enum is for struct U, member k CONS = 0; NUM = 1; @@ -98,6 +1026,9 @@ SYM = 5; + // the following enum is for indexing the symbol table + + // standard functions first, then nil, then everything else counter = 0; ABS = counter++; @@ -252,6 +1183,7 @@ LAGUERRE = counter++; + // LAPLACE = counter++ LCM = counter++; LEADING = counter++; @@ -372,7 +1304,9 @@ ZERO = counter++; - NIL = counter++; + // ALL THE SYMBOLS ABOVE NIL ARE KEYWORDS, + // WHICH MEANS THAT USER CANNOT REDEFINE THEM + NIL = counter++; // nil goes here, after standard functions LAST = counter++; @@ -402,7 +1336,7 @@ YYE = counter++; - DRAWX = counter++; + DRAWX = counter++; // special purpose internal symbols METAA = counter++; @@ -462,10 +1396,13 @@ C6 = counter++; - USR_SYMBOLS = counter++; + USR_SYMBOLS = counter++; // this must be last E = YYE; + // TOS cannot be arbitrarily large because the OS seg faults on deep recursion. + // For example, a circular evaluation like x=x+1 can cause a seg fault. + // At this setting (100,000) the evaluation stack overruns before seg fault. TOS = 100000; BUF = 10000; @@ -478,8 +1415,12 @@ MAX_CONSECUTIVE_APPLICATIONS_OF_SINGLE_RULE = 10; + //define _USE_MATH_DEFINES // for MS C++ MAXDIM = 24; + // needed for the mechanism to + // find all dependencies between variables + // in a script symbolsDependencies = {}; symbolsHavingReassignments = []; @@ -490,41 +1431,52 @@ predefinedSymbolsInGlobalScope_doNotTrackInDependencies = ["rationalize", "abs", "e", "i", "pi", "sin", "ceiling", "cos", "roots", "integral", "derivative", "defint", "sqrt", "eig", "cov", "deig", "dcov", "float", "floor", "product", "root", "round", "sum", "test", "unit"]; + // you can do some little simplifications + // at parse time, such as calculating away + // immediately simple operations on + // constants, removing 1s from products + // etc. parse_time_simplifications = true; chainOfUserSymbolsNotFunctionsBeingEvaluated = []; stringsEmittedByUserPrintouts = ""; + // flag use to potentially switch on/off some quirks "deep" + // in the code due to call from Algebra block. + // Currently not used. called_from_Algebra_block = false; tensor = (function() { - tensor.prototype.ndim = 0; + class tensor { + constructor() { + this.dim = (function() { + var o, ref, results; + results = []; + for (o = 0, ref = MAXDIM; (0 <= ref ? o <= ref : o >= ref); 0 <= ref ? o++ : o--) { + results.push(0); + } + return results; + })(); + this.elem = []; + } - tensor.prototype.dim = null; + }; - tensor.prototype.nelem = 0; + tensor.prototype.ndim = 0; // number of dimensions - tensor.prototype.elem = null; + tensor.prototype.dim = null; // dimension length, for each dimension - function tensor() { - this.dim = (function() { - var o, ref, results; - results = []; - for (o = 0, ref = MAXDIM; 0 <= ref ? o <= ref : o >= ref; 0 <= ref ? o++ : o--) { - results.push(0); - } - return results; - })(); - this.elem = []; - } + tensor.prototype.nelem = 0; // total number of elements + + tensor.prototype.elem = null; // an array containing all the data return tensor; - })(); + }).call(this); display = (function() { - function display() {} + class display {}; display.prototype.h = 0; @@ -532,14 +1484,14 @@ display.prototype.n = 0; - display.prototype.a = []; + display.prototype.a = []; // will contain an array of c,x,y (color,x,y) return display; - })(); + }).call(this); text_metric = (function() { - function text_metric() {} + class text_metric {}; text_metric.prototype.ascent = 0; @@ -549,9 +1501,9 @@ return text_metric; - })(); + }).call(this); - tos = 0; + tos = 0; // top of stack expanding = 0; @@ -602,45 +1554,48 @@ program_buf = ""; + // will contain the variable names symtab = []; + // will contain the contents of the variable + // in the corresponding position in symtab array binding = []; isSymbolReclaimable = []; - arglist = []; + arglist = []; // will contain U - stack = []; + stack = []; // will contain *U frame = 0; - p0 = null; + p0 = null; // will contain U - p1 = null; + p1 = null; // will contain U - p2 = null; + p2 = null; // will contain U - p3 = null; + p3 = null; // will contain U - p4 = null; + p4 = null; // will contain U - p5 = null; + p5 = null; // will contain U - p6 = null; + p6 = null; // will contain U - p7 = null; + p7 = null; // will contain U - p8 = null; + p8 = null; // will contain U - p9 = null; + p9 = null; // will contain U - zero = null; + zero = null; // will contain U - one = null; + one = null; // will contain U one_as_double = null; - imaginaryunit = null; + imaginaryunit = null; // will contain U out_buf = ""; @@ -650,7 +1605,7 @@ codeGen = false; - draw_stop_return = null; + draw_stop_return = null; // extern jmp_buf ????? userSimplificationsInListForm = []; @@ -692,18 +1647,22 @@ } }; + // because of recursion, we consider a scalar to be + // a tensor, so a numeric scalar will return true isNumericAtomOrTensor = function(p) { var a, i, n, o, ref; if (isNumericAtom(p) || p === symbol(SYMBOL_IDENTITY_MATRIX)) { return 1; } if (!istensor(p) && !isNumericAtom(p)) { + //console.log "p not an atom nor a tensor: " + p return 0; } n = p.tensor.nelem; a = p.tensor.elem; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { if (!isNumericAtomOrTensor(a[i])) { + //console.log "non-numeric element: " + a[i] return 0; } } @@ -822,6 +1781,7 @@ return car(cdr(cdr(car(cdr(cdr(p)))))); }; + // not used yet listLength = function(p) { var startCount; startCount = -1; @@ -832,6 +1792,7 @@ return startCount; }; + // not used yet nthCadr = function(p, n) { var startCount; startCount = 0; @@ -870,6 +1831,9 @@ return car(p) === symbol(INV); }; + // TODO this is a bit of a shallow check, we should + // check when we are passed an actual tensor and possibly + // cache the test result. isidentitymatrix = function(p) { return p === symbol(SYMBOL_IDENTITY_MATRIX); }; @@ -998,53 +1962,52 @@ $.SYM = SYM; - - /* abs ===================================================================== + //(docs are generated from top-level comments, keep an eye on the formatting!) + /* abs ===================================================================== + Tags ---- scripting, JS, internal, treenode, general concept - + Parameters ---------- x - + General description ------------------- Returns the absolute value of a real number, the magnitude of a complex number, or the vector length. - */ - + */ /* Absolute value of a number,or magnitude of complex z, or norm of a vector - + z abs(z) - ------ - + a a - + -a a - + (-1)^a 1 - + exp(a + i b) exp(a) - + a b abs(a) abs(b) - + a + i b sqrt(a^2 + b^2) - + Notes - + 1. Handles mixed polar and rectangular forms, e.g. 1 + exp(i pi/3) - + 2. jean-francois.debroux reports that when z=(a+i*b)/(c+i*d) then - + abs(numerator(z)) / abs(denominator(z)) - + must be used to get the correct answer. Now the operation is automatic. - */ - + */ DEBUG_ABS = false; Eval_abs = function() { @@ -1060,6 +2023,13 @@ return zzfloat(); }; + // zzfloat of an abs doesn't necessarily result in a double + // , for example if there are variables. But + // in many of the tests there should be indeed + // a float, these two lines come handy to highlight + // when that doesn't happen for those tests. + //if !isdouble(stack[tos-1]) + // stop("absValFloat should return a double and instead got: " + stack[tos-1]) abs = function() { var theArgument; theArgument = top(); @@ -1100,6 +2070,7 @@ if (DEBUG_ABS) { console.log("ABS of " + p1); } + // handle all the "number" cases first ----------------------------------------- if (isZeroAtomOrTensor(p1)) { if (DEBUG_ABS) { console.log(" abs: " + p1 + " just zero"); @@ -1142,6 +2113,17 @@ restore(); return; } + // ??? should there be a shortcut case here for the imaginary unit? + + // now handle decomposition cases ---------------------------------------------- + + // we catch the "add", "power", "multiply" cases first, + // before falling back to the + // negative/positive cases because there are some + // simplification thay we might be able to do. + // Note that for this routine to give a correct result, this + // must be a sum where a complex number appears. + // If we apply this to "a+b", we get an incorrect result. if (car(p1) === symbol(ADD) && (findPossibleClockForm(p1) || findPossibleExponentialForm(p1) || Find(p1, imaginaryunit))) { if (DEBUG_ABS) { console.log(" abs: " + p1 + " is a sum"); @@ -1149,8 +2131,9 @@ if (DEBUG_ABS) { console.log("abs of a sum"); } + // sum push(p1); - rect(); + rect(); // convert polar terms, if any p1 = pop(); push(p1); real(); @@ -1174,6 +2157,7 @@ if (DEBUG_ABS) { console.log(" abs: " + p1 + " is -1 to any power"); } + // -1 to any power if (evaluatingAsFloats) { if (DEBUG_ABS) { console.log(" abs: numeric, so result is 1.0"); @@ -1191,6 +2175,7 @@ restore(); return; } + // abs(a^b) is equal to abs(a)^b IF b is positive if (car(p1) === symbol(POWER) && ispositivenumber(caddr(p1))) { if (DEBUG_ABS) { console.log(" abs: " + p1 + " is something to the power of a positive number"); @@ -1205,10 +2190,12 @@ restore(); return; } + // abs(e^something) if (car(p1) === symbol(POWER) && cadr(p1) === symbol(E)) { if (DEBUG_ABS) { console.log(" abs: " + p1 + " is an exponential"); } + // exponential push(caddr(p1)); real(); exponential(); @@ -1222,6 +2209,7 @@ if (DEBUG_ABS) { console.log(" abs: " + p1 + " is a product"); } + // product anyFactorsYet = false; p1 = cdr(p1); while (iscons(p1)) { @@ -1243,6 +2231,7 @@ if (DEBUG_ABS) { console.log(" abs: " + p1 + " is abs of a abs"); } + // abs of a abs push_symbol(ABS); push(cadr(p1)); list(2); @@ -1252,7 +2241,6 @@ restore(); return; } - /* * Evaluation via zzfloat() * ...while this is in theory a powerful mechanism, I've commented it @@ -1260,7 +2248,7 @@ * Evaling via zzfloat() is in principle more problematic because it could * require further evaluations which could end up in further "abs" which * would end up in infinite loops. Better not use it if not necessary. - + * we look directly at the float evaluation of the argument * to see if we end up with a number, which would mean that there * is no imaginary component and we can just return the input @@ -1268,14 +2256,14 @@ push p1 zzfloat() floatEvaluation = pop() - + if (isnegativenumber(floatEvaluation)) if DEBUG_ABS then console.log " abs: " + p1 + " just a negative" push(p1) negate() restore() return - + if (ispositivenumber(floatEvaluation)) if DEBUG_ABS then console.log " abs: " + p1 + " just a positive" push(p1) @@ -1305,6 +2293,7 @@ return restore(); }; + // also called the "norm" of a vector absval_tensor = function() { if (p1.tensor.ndim !== 1) { stop("abs(tensor) with tensor rank > 1"); @@ -1319,30 +2308,6 @@ return Eval(); }; - - /* - Symbolic addition - - Terms in a sum are combined if they are identical modulo rational - coefficients. - - For example, A + 2A becomes 3A. - - However, the sum A + sqrt(2) A is not modified. - - Combining terms can lead to second-order effects. - - For example, consider the case of - - 1/sqrt(2) A + 3/sqrt(2) A + sqrt(2) A - - The first two terms are combined to yield 2 sqrt(2) A. - - This result can now be combined with the third term to yield - - 3 sqrt(2) A - */ - flag = 0; Eval_add = function() { @@ -1359,6 +2324,7 @@ return add_terms(tos - h); }; + // Add n terms, returns one expression on the stack. stackAddsCount = 0; add_terms = function(n) { @@ -1367,11 +2333,14 @@ i = 0; h = tos - n; s = h; + // ensure no infinite loop, use "for" if (DEBUG) { console.log("stack before adding terms #" + stackAddsCount); } + //if stackAddsCount == 137 + // debugger if (DEBUG) { - for (i = o = 0, ref = tos; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = tos; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { console.log(print_list(stack[i])); } } @@ -1380,6 +2349,7 @@ break; } flag = 0; + //qsort(s, n, sizeof (U *), cmp_terms) subsetOfStack = stack.slice(h, h + n); subsetOfStack.sort(cmp_terms); stack = stack.slice(0, h).concat(subsetOfStack).concat(stack.slice(h + n)); @@ -1409,41 +2379,54 @@ if (DEBUG) { console.log("stack after adding terms #" + stackAddsCount); } + //if stackAddsCount == 5 + // debugger if (DEBUG) { results = []; - for (i = j1 = 0, ref1 = tos; 0 <= ref1 ? j1 < ref1 : j1 > ref1; i = 0 <= ref1 ? ++j1 : --j1) { + for (i = j1 = 0, ref1 = tos; (0 <= ref1 ? j1 < ref1 : j1 > ref1); i = 0 <= ref1 ? ++j1 : --j1) { results.push(console.log(print_list(stack[i]))); } return results; } }; + // Compare terms for order, clobbers p1 and p2. cmp_terms_count = 0; cmp_terms = function(p1, p2) { var i, o, ref, t; cmp_terms_count++; + //if cmp_terms_count == 52 + // debugger i = 0; + // numbers can be combined if (isNumericAtom(p1) && isNumericAtom(p2)) { flag = 1; + //if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns 0" return 0; } + // congruent tensors can be combined if (istensor(p1) && istensor(p2)) { if (p1.tensor.ndim < p2.tensor.ndim) { + //if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns -1" return -1; } if (p1.tensor.ndim > p2.tensor.ndim) { + //if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns 1" return 1; } - for (i = o = 0, ref = p1.tensor.ndim; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = p1.tensor.ndim; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { if (p1.tensor.dim[i] < p2.tensor.dim[i]) { + //if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns -1" return -1; } if (p1.tensor.dim[i] > p2.tensor.dim[i]) { + //if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns 1" return 1; } } flag = 1; + //if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns 0" return 0; } if (car(p1) === symbol(MULTIPLY)) { @@ -1464,24 +2447,25 @@ } } } - t = cmp_expr(p1, p2); - if (t === 0) { - flag = 1; - } - return t; - }; - - - /* - Compare adjacent terms in s[] and combine if possible. - - Returns the number of terms remaining in s[]. - - n number of terms in s[] initially - */ + t = cmp_expr(p1, p2); + if (t === 0) { + flag = 1; + } + //if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns " + t + return t; + }; combine_terms = function(s, n) { var i, i1, j, j1, l1, m1, o, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, t; + //debugger + + // I had to turn the coffeescript for loop into + // a more mundane while loop because the i + // variable was changed from within the body, + // which is something that is not supposed to + // happen in the coffeescript 'vector' form. + // Also this means I had to add a 'i++' jus before + // the end of the body and before the "continue"s i = 0; while (i < (n - 1)) { check_esc_flag(); @@ -1494,7 +2478,7 @@ p1 = pop(); if (p1 !== symbol(NIL)) { stack[s + i] = p1; - for (j = o = ref = i + 1, ref1 = n - 1; ref <= ref1 ? o < ref1 : o > ref1; j = ref <= ref1 ? ++o : --o) { + for (j = o = ref = i + 1, ref1 = n - 1; (ref <= ref1 ? o < ref1 : o > ref1); j = ref <= ref1 ? ++o : --o) { stack[s + j] = stack[s + j + 1]; } n--; @@ -1513,13 +2497,13 @@ add_numbers(); p1 = pop(); if (isZeroAtomOrTensor(p1)) { - for (j = i1 = ref2 = i, ref3 = n - 2; ref2 <= ref3 ? i1 < ref3 : i1 > ref3; j = ref2 <= ref3 ? ++i1 : --i1) { + for (j = i1 = ref2 = i, ref3 = n - 2; (ref2 <= ref3 ? i1 < ref3 : i1 > ref3); j = ref2 <= ref3 ? ++i1 : --i1) { stack[s + j] = stack[s + j + 2]; } n -= 2; } else { stack[s + i] = p1; - for (j = j1 = ref4 = i + 1, ref5 = n - 1; ref4 <= ref5 ? j1 < ref5 : j1 > ref5; j = ref4 <= ref5 ? ++j1 : --j1) { + for (j = j1 = ref4 = i + 1, ref5 = n - 1; (ref4 <= ref5 ? j1 < ref5 : j1 > ref5); j = ref4 <= ref5 ? ++j1 : --j1) { stack[s + j] = stack[s + j + 1]; } n--; @@ -1571,7 +2555,7 @@ add_numbers(); p1 = pop(); if (isZeroAtomOrTensor(p1)) { - for (j = l1 = ref6 = i, ref7 = n - 2; ref6 <= ref7 ? l1 < ref7 : l1 > ref7; j = ref6 <= ref7 ? ++l1 : --l1) { + for (j = l1 = ref6 = i, ref7 = n - 2; (ref6 <= ref7 ? l1 < ref7 : l1 > ref7); j = ref6 <= ref7 ? ++l1 : --l1) { stack[s + j] = stack[s + j + 2]; } n -= 2; @@ -1589,11 +2573,12 @@ } multiply(); stack[s + i] = pop(); - for (j = m1 = ref8 = i + 1, ref9 = n - 1; ref8 <= ref9 ? m1 < ref9 : m1 > ref9; j = ref8 <= ref9 ? ++m1 : --m1) { + for (j = m1 = ref8 = i + 1, ref9 = n - 1; (ref8 <= ref9 ? m1 < ref9 : m1 > ref9); j = ref8 <= ref9 ? ++m1 : --m1) { stack[s + j] = stack[s + j + 1]; } n--; i--; + // this i++ is to match the while i++; } return n; @@ -1614,6 +2599,7 @@ } }; + // add two expressions add = function() { var h; save(); @@ -1632,7 +2618,7 @@ save(); s = tos - k; h = tos; - for (i = o = 0, ref = k; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = k; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push_terms(stack[s + i]); } add_terms(tos - h); @@ -1647,22 +2633,6 @@ return add(); }; - - /* adj ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - m - - General description - ------------------- - Returns the adjunct of matrix m. The inverse of m is equal to adj(m) divided by det(m). - */ - Eval_adj = function() { push(cadr(p1)); Eval(); @@ -1686,21 +2656,16 @@ p2.tensor.ndim = 2; p2.tensor.dim[0] = n; p2.tensor.dim[1] = n; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { - for (j = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { + for (j = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); j = 0 <= ref1 ? ++i1 : --i1) { cofactor(p1, n, i, j); - p2.tensor.elem[n * j + i] = pop(); + p2.tensor.elem[n * j + i] = pop(); // transpose } } push(p2); return restore(); }; - - /* - Guesses a rational for each float in the passed expression - */ - Eval_approxratio = function() { var theArgument; theArgument = cadr(p1); @@ -1716,10 +2681,10 @@ if (istensor(p1)) { p4 = alloc_tensor(p1.tensor.nelem); p4.tensor.ndim = p1.tensor.ndim; - for (i = o = 0, ref = p1.tensor.ndim; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = p1.tensor.ndim; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p4.tensor.dim[i] = p1.tensor.dim[i]; } - for (i = i1 = 0, ref1 = p1.tensor.nelem; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = p1.tensor.nelem; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { push(p1.tensor.elem[i]); approxratioRecursive(); p4.tensor.elem[i] = pop(); @@ -1758,11 +2723,22 @@ } return; } + // we didn't manage, just leave unexpressed push_symbol(APPROXRATIO); push(theArgument); return list(2); }; + // original routine by John Kennedy, see + // https://web.archive.org/web/20111027100847/http://homepage.smc.edu/kennedy_john/DEC2FRAC.PDF + // courtesy of Michael Borcherds + // who ported this to JavaScript under MIT licence + // also see + // https://github.com/geogebra/geogebra/blob/master/common/src/main/java/org/geogebra/common/kernel/algos/AlgoFractionText.java + // potential other ways to do this: + // https://rosettacode.org/wiki/Convert_decimal_number_to_rational + // http://www.homeschoolmath.net/teaching/rational_numbers.php + // http://stackoverflow.com/questions/95727/how-to-convert-floats-to-human-readable-fractions floatToRatioRoutine = function(decimal, AccuracyFactor) { var DecimalSign, FractionDenominator, FractionNumerator, PreviousDenominator, ScratchValue, Z, ret; FractionNumerator = void 0; @@ -1775,14 +2751,17 @@ if (isNaN(decimal)) { return ret; } + // return 0/0 if (decimal === 2e308) { ret[0] = 1; ret[1] = 0; + // 1/0 return ret; } if (decimal === -2e308) { ret[0] = -1; ret[1] = 0; + // -1/0 return ret; } if (decimal < 0.0) { @@ -1792,6 +2771,7 @@ } decimal = Math.abs(decimal); if (Math.abs(decimal - Math.floor(decimal)) < AccuracyFactor) { + // handles exact integers including 0 FractionNumerator = decimal * DecimalSign; FractionDenominator = 1.0; ret[0] = FractionNumerator; @@ -1799,6 +2779,7 @@ return ret; } if (decimal < 1.0e-19) { + // X = 0 already taken care of FractionNumerator = DecimalSign; FractionDenominator = 9999999999999999999.0; ret[0] = FractionNumerator; @@ -1821,6 +2802,7 @@ FractionDenominator = FractionDenominator * Math.floor(Z) + PreviousDenominator; PreviousDenominator = ScratchValue; FractionNumerator = Math.floor(decimal * FractionDenominator + 0.5); + // Rounding Function if (!(Math.abs(decimal - (FractionNumerator / FractionDenominator)) > AccuracyFactor && Z !== Math.floor(Z))) { break; } @@ -1861,27 +2843,34 @@ return ["" + Math.floor(theFloat), approx_just_an_integer, Math.floor(theFloat), 1, 2]; } console.log("precision: " + precision); + // simple radicals. bestResultSoFar = null; minimumComplexity = Number.MAX_VALUE; ref = [2, 3, 5, 6, 7, 8, 10]; for (o = 0, len = ref.length; o < len; o++) { i = ref[o]; for (j = i1 = 1; i1 <= 10; j = ++i1) { + //console.log "i,j: " + i + "," + j hypothesis = Math.sqrt(i) / j; + //console.log "hypothesis: " + hypothesis if (Math.abs(hypothesis) > 1e-10) { ratio = theFloat / hypothesis; likelyMultiplier = Math.round(ratio); + //console.log "ratio: " + ratio error = Math.abs(1 - ratio / likelyMultiplier); } else { ratio = 1; likelyMultiplier = 1; error = Math.abs(theFloat - hypothesis); } + //console.log "error: " + error if (error < 2 * precision) { complexity = simpleComplexityMeasure(likelyMultiplier, i, j); if (complexity < minimumComplexity) { + //console.log "MINIMUM MULTIPLIER SO FAR" minimumComplexity = complexity; result = likelyMultiplier + " * sqrt( " + i + " ) / " + j; + //console.log result + " error: " + error bestResultSoFar = [result, approx_ratioOfRadical, likelyMultiplier, i, j]; } } @@ -1900,29 +2889,39 @@ return ["" + Math.floor(theFloat), approx_just_an_integer, Math.floor(theFloat), 1, 2]; } console.log("precision: " + precision); + // simple radicals. bestResultSoFar = null; minimumComplexity = Number.MAX_VALUE; ref = [1, 2, 3, 5, 6, 7, 8, 10]; + // this one catches things like Math.sqrt(3/4), but + // things like Math.sqrt(1/2) are caught by the paragraph + // above (and in a better form) for (o = 0, len = ref.length; o < len; o++) { i = ref[o]; ref1 = [1, 2, 3, 5, 6, 7, 8, 10]; for (i1 = 0, len1 = ref1.length; i1 < len1; i1++) { j = ref1[i1]; + //console.log "i,j: " + i + "," + j hypothesis = Math.sqrt(i / j); + //console.log "hypothesis: " + hypothesis if (Math.abs(hypothesis) > 1e-10) { ratio = theFloat / hypothesis; likelyMultiplier = Math.round(ratio); + //console.log "ratio: " + ratio error = Math.abs(1 - ratio / likelyMultiplier); } else { ratio = 1; likelyMultiplier = 1; error = Math.abs(theFloat - hypothesis); } + //console.log "error: " + error if (error < 2 * precision) { complexity = simpleComplexityMeasure(likelyMultiplier, i, j); if (complexity < minimumComplexity) { + //console.log "MINIMUM MULTIPLIER SO FAR" minimumComplexity = complexity; result = likelyMultiplier + " * (sqrt( " + i + " / " + j + " )"; + //console.log result + " error: " + error bestResultSoFar = [result, approx_radicalOfRatio, likelyMultiplier, i, j]; } } @@ -1941,6 +2940,11 @@ return ["" + Math.floor(theFloat), approx_just_an_integer, Math.floor(theFloat), 1, 2]; } console.log("precision: " + precision); + // simple radicals. + + // we always prefer a rational of a radical of an integer + // to a radical of a rational. Radicals of rationals generate + // radicals at the denominator which we'd rather avoid approxRationalsOfRadicalsResult = approxRationalsOfRadicals(theFloat); if (approxRationalsOfRadicalsResult != null) { return approxRationalsOfRadicalsResult; @@ -1962,6 +2966,8 @@ return ["" + Math.floor(theFloat), approx_just_an_integer, Math.floor(theFloat), 1, 2]; } console.log("precision: " + precision); + // we always prefer a rational of a log to a log of + // a rational approxRationalsOfLogsResult = approxRationalsOfLogs(theFloat); if (approxRationalsOfLogsResult != null) { return approxRationalsOfLogsResult; @@ -1985,26 +2991,41 @@ console.log("precision: " + precision); bestResultSoFar = null; minimumComplexity = Number.MAX_VALUE; +// simple rationals of logs for (i = o = 2; o <= 5; i = ++o) { for (j = i1 = 1; i1 <= 5; j = ++i1) { + //console.log "i,j: " + i + "," + j hypothesis = Math.log(i) / j; + //console.log "hypothesis: " + hypothesis if (Math.abs(hypothesis) > 1e-10) { ratio = theFloat / hypothesis; likelyMultiplier = Math.round(ratio); + //console.log "ratio: " + ratio error = Math.abs(1 - ratio / likelyMultiplier); } else { ratio = 1; likelyMultiplier = 1; error = Math.abs(theFloat - hypothesis); } + //console.log "error: " + error + + // it does happen that due to roundings + // a "higher multiple" is picked, which is obviously + // unintended. + // E.g. 1 * log(1 / 3 ) doesn't match log( 3 ) BUT + // it matches -5 * log( 3 ) / 5 + // so we avoid any case where the multiplier is a multiple + // of the divisor. if (likelyMultiplier !== 1 && Math.abs(Math.floor(likelyMultiplier / j)) === Math.abs(likelyMultiplier / j)) { continue; } if (error < 2.2 * precision) { complexity = simpleComplexityMeasure(likelyMultiplier, i, j); if (complexity < minimumComplexity) { + //console.log "MINIMUM MULTIPLIER SO FAR" minimumComplexity = complexity; result = likelyMultiplier + " * log( " + i + " ) / " + j; + //console.log result + " error: " + error bestResultSoFar = [result, approx_rationalsOfLogarithms, likelyMultiplier, i, j]; } } @@ -2025,23 +3046,30 @@ console.log("precision: " + precision); bestResultSoFar = null; minimumComplexity = Number.MAX_VALUE; +// simple logs of rationals for (i = o = 1; o <= 5; i = ++o) { for (j = i1 = 1; i1 <= 5; j = ++i1) { + //console.log "i,j: " + i + "," + j hypothesis = Math.log(i / j); + //console.log "hypothesis: " + hypothesis if (Math.abs(hypothesis) > 1e-10) { ratio = theFloat / hypothesis; likelyMultiplier = Math.round(ratio); + //console.log "ratio: " + ratio error = Math.abs(1 - ratio / likelyMultiplier); } else { ratio = 1; likelyMultiplier = 1; error = Math.abs(theFloat - hypothesis); } + //console.log "error: " + error if (error < 1.96 * precision) { complexity = simpleComplexityMeasure(likelyMultiplier, i, j); if (complexity < minimumComplexity) { + //console.log "MINIMUM MULTIPLIER SO FAR" minimumComplexity = complexity; result = likelyMultiplier + " * log( " + i + " / " + j + " )"; + //console.log result + " error: " + error bestResultSoFar = [result, approx_logarithmsOfRationals, likelyMultiplier, i, j]; } } @@ -2062,23 +3090,30 @@ console.log("precision: " + precision); bestResultSoFar = null; minimumComplexity = Number.MAX_VALUE; +// simple rationals of a few powers of e for (i = o = 1; o <= 2; i = ++o) { for (j = i1 = 1; i1 <= 12; j = ++i1) { + //console.log "i,j: " + i + "," + j hypothesis = Math.pow(Math.E, i) / j; + //console.log "hypothesis: " + hypothesis if (Math.abs(hypothesis) > 1e-10) { ratio = theFloat / hypothesis; likelyMultiplier = Math.round(ratio); + //console.log "ratio: " + ratio error = Math.abs(1 - ratio / likelyMultiplier); } else { ratio = 1; likelyMultiplier = 1; error = Math.abs(theFloat - hypothesis); } + //console.log "error: " + error if (error < 2 * precision) { complexity = simpleComplexityMeasure(likelyMultiplier, i, j); if (complexity < minimumComplexity) { + //console.log "MINIMUM MULTIPLIER SO FAR" minimumComplexity = complexity; result = likelyMultiplier + " * (e ^ " + i + " ) / " + j; + //console.log result + " error: " + error bestResultSoFar = [result, approx_rationalOfE, likelyMultiplier, i, j]; } } @@ -2098,29 +3133,45 @@ } console.log("precision: " + precision); bestResultSoFar = null; + // here we do somethng a little special: since + // the powers of pi can get quite big, there might + // be multiple hypothesis where more of the + // magnitude is shifted to the multiplier, and some + // where more of the magnitude is shifted towards the + // exponent of pi. So we prefer the hypotheses with the + // lower multiplier since it's likely to insert more + // information. minimumComplexity = Number.MAX_VALUE; +// simple rationals of a few powers of PI for (i = o = 1; o <= 5; i = ++o) { for (j = i1 = 1; i1 <= 12; j = ++i1) { + //console.log "i,j: " + i + "," + j hypothesis = Math.pow(Math.PI, i) / j; + //console.log "hypothesis: " + hypothesis if (Math.abs(hypothesis) > 1e-10) { ratio = theFloat / hypothesis; likelyMultiplier = Math.round(ratio); + //console.log "ratio: " + ratio error = Math.abs(1 - ratio / likelyMultiplier); } else { ratio = 1; likelyMultiplier = 1; error = Math.abs(theFloat - hypothesis); } + //console.log "error: " + error if (error < 2 * precision) { complexity = simpleComplexityMeasure(likelyMultiplier, i, j); if (complexity < minimumComplexity) { + //console.log "MINIMUM MULTIPLIER SO FAR" minimumComplexity = complexity; result = likelyMultiplier + " * (pi ^ " + i + " ) / " + j + " )"; + //console.log result + " error: " + error bestResultSoFar = [result, approx_rationalOfPi, likelyMultiplier, i, j]; } } } } + //console.log "approxRationalsOfPowersOfPI returning: " + bestResultSoFar return bestResultSoFar; }; @@ -2134,6 +3185,7 @@ return ["" + Math.floor(theFloat), approx_just_an_integer, Math.floor(theFloat), 1, 2]; } console.log("precision: " + precision); + // we always prefer a sin of a rational without the PI approxSineOfRationalsResult = approxSineOfRationals(theFloat); if (approxSineOfRationalsResult != null) { return approxSineOfRationalsResult; @@ -2157,24 +3209,35 @@ console.log("precision: " + precision); bestResultSoFar = null; minimumComplexity = Number.MAX_VALUE; +// we only check very simple rationals because they begin to get tricky +// quickly, also they collide often with the "rational of pi" hypothesis. +// For example sin(11) is veeery close to 1 (-0.99999020655) +// (see: http://mathworld.wolfram.com/AlmostInteger.html ) +// we stop at rationals that mention up to 10 for (i = o = 1; o <= 4; i = ++o) { for (j = i1 = 1; i1 <= 4; j = ++i1) { + //console.log "i,j: " + i + "," + j fraction = i / j; hypothesis = Math.sin(fraction); + //console.log "hypothesis: " + hypothesis if (Math.abs(hypothesis) > 1e-10) { ratio = theFloat / hypothesis; likelyMultiplier = Math.round(ratio); + //console.log "ratio: " + ratio error = Math.abs(1 - ratio / likelyMultiplier); } else { ratio = 1; likelyMultiplier = 1; error = Math.abs(theFloat - hypothesis); } + //console.log "error: " + error if (error < 2 * precision) { complexity = simpleComplexityMeasure(likelyMultiplier, i, j); if (complexity < minimumComplexity) { + //console.log "MINIMUM MULTIPLIER SO FAR" minimumComplexity = complexity; result = likelyMultiplier + " * sin( " + i + "/" + j + " )"; + //console.log result + " error: " + error bestResultSoFar = [result, approx_sine_of_rational, likelyMultiplier, i, j]; } } @@ -2195,24 +3258,32 @@ console.log("precision: " + precision); bestResultSoFar = null; minimumComplexity = Number.MAX_VALUE; +// check rational multiples of pi for (i = o = 1; o <= 13; i = ++o) { for (j = i1 = 1; i1 <= 13; j = ++i1) { + //console.log "i,j: " + i + "," + j fraction = i / j; hypothesis = Math.sin(Math.PI * fraction); + //console.log "hypothesis: " + hypothesis if (Math.abs(hypothesis) > 1e-10) { ratio = theFloat / hypothesis; likelyMultiplier = Math.round(ratio); + //console.log "ratio: " + ratio error = Math.abs(1 - ratio / likelyMultiplier); } else { ratio = 1; likelyMultiplier = 1; error = Math.abs(theFloat - hypothesis); } + //console.log "error: " + error + // magic number 23 comes from the case sin(pi/10) if (error < 23 * precision) { complexity = simpleComplexityMeasure(likelyMultiplier, i, j); if (complexity < minimumComplexity) { + //console.log "MINIMUM MULTIPLIER SO FAR" minimumComplexity = complexity; result = likelyMultiplier + " * sin( " + i + "/" + j + " * pi )"; + //console.log result + " error: " + error bestResultSoFar = [result, approx_sine_of_pi_times_rational, likelyMultiplier, i, j]; } } @@ -2317,10 +3388,17 @@ var theSum; theSum = null; if (aResult instanceof Array) { + // we want PI and E to somewhat increase the + // complexity of the expression, so basically they count + // more than any integer lower than 3, i.e. we consider + // 1,2,3 to be more fundamental than PI or E. switch (aResult[1]) { case approx_sine_of_pi_times_rational: theSum = 4; break; + // exponents of PI and E need to be penalised as well + // otherwise they come to explain any big number + // so we count them just as much as the multiplier case approx_rationalOfPi: theSum = Math.pow(4, Math.abs(aResult[3])) * Math.abs(aResult[2]); break; @@ -2334,6 +3412,8 @@ } else { theSum += Math.abs(aResult) * (Math.abs(b) + Math.abs(c)); } + + // heavily discount unit constants if (aResult[2] === 1) { theSum -= 1; } else { @@ -2364,7 +3444,7 @@ for (i1 = 0, len1 = ref1.length; i1 < len1; i1++) { j = ref1[i1]; if (i === j) { - continue; + continue; // this is just 1 } console.log("testapproxRadicals testing: " + "1 * sqrt( " + i + " ) / " + j); fraction = i / j; @@ -2383,7 +3463,7 @@ for (l1 = 0, len3 = ref3.length; l1 < len3; l1++) { j = ref3[l1]; if (i === j) { - continue; + continue; // this is just 1 } console.log("testapproxRadicals testing with 4 digits: " + "1 * sqrt( " + i + " ) / " + j); fraction = i / j; @@ -2403,7 +3483,7 @@ for (n1 = 0, len5 = ref5.length; n1 < len5; n1++) { j = ref5[n1]; if (i === j) { - continue; + continue; // this is just 1 } console.log("testapproxRadicals testing: " + "1 * sqrt( " + i + " / " + j + " )"); fraction = i / j; @@ -2550,6 +3630,7 @@ } } } +// 5 digits create no problem for (i = v2 = 1; v2 <= 4; i = ++v2) { for (j = x2 = 1; x2 <= 4; j = ++x2) { console.log("testApproxAll testing with 5 digits: " + "1 * sin( " + i + "/" + j + " )"); @@ -2568,6 +3649,7 @@ } } } +// 4 digits create two collisions for (i = z2 = 1; z2 <= 4; i = ++z2) { for (j = i3 = 1; i3 <= 4; j = ++i3) { console.log("testApproxAll testing with 4 digits: " + "1 * sin( " + i + "/" + j + " )"); @@ -2638,6 +3720,8 @@ if (approxAll(value)[0] !== "1 * sqrt( 2 ) / 1") { console.log("fail testApproxAll: 1.41"); } + // if we narrow down to a particular family then we can get + // an OK guess even with few digits, expecially for really "famous" numbers value = 1.4; if (approxRadicals(value)[0] !== "1 * sqrt( 2 ) / 1") { console.log("fail approxRadicals: 1.4"); @@ -2730,10 +3814,12 @@ if (approxAll(value)[0] !== "1 * sin( 1/5 * pi )") { console.log("fail testApproxAll: Math.sqrt(10 - 2*Math.sqrt(5))/4"); } + // this has a radical form but it's too long to write value = Math.sin(Math.PI / 7); if (approxAll(value)[0] !== "1 * sin( 1/7 * pi )") { console.log("fail testApproxAll: Math.sin(Math.PI/7)"); } + // this has a radical form but it's too long to write value = Math.sin(Math.PI / 9); if (approxAll(value)[0] !== "1 * sin( 1/9 * pi )") { console.log("fail testApproxAll: Math.sin(Math.PI/9)"); @@ -2747,6 +3833,9 @@ console.log("approxTrigonometric testing: " + "1 * sin( " + i + "/" + j + " * pi )"); fraction = i / j; value = Math.sin(Math.PI * fraction); + // we specifically search for sines of rational multiples of PI + // because too many of them would be picked up as simple + // rationals. returned = approxTrigonometric(value); returnedFraction = returned[3] / returned[4]; returnedValue = returned[2] * Math.sin(Math.PI * returnedFraction); @@ -2757,6 +3846,10 @@ } for (i = l3 = 1; l3 <= 13; i = ++l3) { for (j = m3 = 1; m3 <= 13; j = ++m3) { + // with four digits, there are two collisions with the + // "simple fraction" argument hypotesis, which we prefer since + // it's a simpler expression, so let's skip those + // two tests if (i === 5 && j === 11 || i === 6 && j === 11) { continue; } @@ -2764,6 +3857,9 @@ fraction = i / j; originalValue = Math.sin(Math.PI * fraction); value = originalValue.toFixed(4); + // we specifically search for sines of rational multiples of PI + // because too many of them would be picked up as simple + // rationals. returned = approxTrigonometric(value); returnedFraction = returned[3] / returned[4]; returnedValue = returned[2] * Math.sin(Math.PI * returnedFraction); @@ -2784,22 +3880,6 @@ $.testApprox = testApprox; - - /* arccos ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - Returns the inverse cosine of x. - */ - Eval_arccos = function() { push(cadr(p1)); Eval(); @@ -2827,6 +3907,8 @@ restore(); return; } + // if p1 == 1/sqrt(2) then return 1/4*pi (45 degrees) + // second if catches the other way of saying it, sqrt(2)/2 if ((isoneoversqrttwo(p1)) || (car(p1) === symbol(MULTIPLY) && equalq(car(cdr(p1)), 1, 2) && car(car(cdr(cdr(p1)))) === symbol(POWER) && equaln(car(cdr(car(cdr(cdr(p1))))), 2) && equalq(car(cdr(cdr(car(cdr(cdr(p1)))))), 1, 2))) { if (evaluatingAsFloats) { push_double(Math.PI / 4.0); @@ -2838,6 +3920,8 @@ restore(); return; } + // if p1 == -1/sqrt(2) then return 3/4*pi (135 degrees) + // second if catches the other way of saying it, -sqrt(2)/2 if ((isminusoneoversqrttwo(p1)) || (car(p1) === symbol(MULTIPLY) && equalq(car(cdr(p1)), -1, 2) && car(car(cdr(cdr(p1)))) === symbol(POWER) && equaln(car(cdr(car(cdr(cdr(p1))))), 2) && equalq(car(cdr(cdr(car(cdr(cdr(p1)))))), 1, 2))) { if (evaluatingAsFloats) { push_double(Math.PI * 3.0 / 4.0); @@ -2910,22 +3994,6 @@ return restore(); }; - - /* arccosh ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - Returns the inverse hyperbolic cosine of x. - */ - Eval_arccosh = function() { push(cadr(p1)); Eval(); @@ -2963,22 +4031,6 @@ return restore(); }; - - /* arcsin ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - Returns the inverse sine of x. - */ - Eval_arcsin = function() { push(cadr(p1)); Eval(); @@ -3006,6 +4058,8 @@ restore(); return; } + // if p1 == 1/sqrt(2) then return 1/4*pi (45 degrees) + // second if catches the other way of saying it, sqrt(2)/2 if ((isoneoversqrttwo(p1)) || (car(p1) === symbol(MULTIPLY) && equalq(car(cdr(p1)), 1, 2) && car(car(cdr(cdr(p1)))) === symbol(POWER) && equaln(car(cdr(car(cdr(cdr(p1))))), 2) && equalq(car(cdr(cdr(car(cdr(cdr(p1)))))), 1, 2))) { push_rational(1, 4); push_symbol(PI); @@ -3013,6 +4067,8 @@ restore(); return; } + // if p1 == -1/sqrt(2) then return -1/4*pi (-45 degrees) + // second if catches the other way of saying it, -sqrt(2)/2 if ((isminusoneoversqrttwo(p1)) || (car(p1) === symbol(MULTIPLY) && equalq(car(cdr(p1)), -1, 2) && car(car(cdr(cdr(p1)))) === symbol(POWER) && equaln(car(cdr(car(cdr(cdr(p1))))), 2) && equalq(car(cdr(cdr(car(cdr(cdr(p1)))))), 1, 2))) { if (evaluatingAsFloats) { push_double(-Math.PI / 4.0); @@ -3087,22 +4143,6 @@ return restore(); }; - - /* arcsinh ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - Returns the inverse hyperbolic sine of x. - */ - Eval_arcsinh = function() { push(cadr(p1)); Eval(); @@ -3137,22 +4177,6 @@ return restore(); }; - - /* arctan ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - Returns the inverse tangent of x. - */ - Eval_arctan = function() { push(cadr(p1)); Eval(); @@ -3192,6 +4216,7 @@ restore(); return; } + // arctan(sin(a) / cos(a)) ? if (Find(p1, symbol(SIN)) && Find(p1, symbol(COS))) { push(p1); numerator(); @@ -3205,6 +4230,8 @@ return; } } + // arctan(1/sqrt(3)) -> pi/6 + // second if catches the other way of saying it, sqrt(3)/3 if ((car(p1) === symbol(POWER) && equaln(cadr(p1), 3) && equalq(caddr(p1), -1, 2)) || (car(p1) === symbol(MULTIPLY) && equalq(car(cdr(p1)), 1, 3) && car(car(cdr(cdr(p1)))) === symbol(POWER) && equaln(car(cdr(car(cdr(cdr(p1))))), 3) && equalq(car(cdr(cdr(car(cdr(cdr(p1)))))), 1, 2))) { push_rational(1, 6); if (evaluatingAsFloats) { @@ -3216,6 +4243,7 @@ restore(); return; } + // arctan(1) -> pi/4 if (equaln(p1, 1)) { push_rational(1, 4); if (evaluatingAsFloats) { @@ -3227,6 +4255,7 @@ restore(); return; } + // arctan(sqrt(3)) -> pi/3 if (car(p1) === symbol(POWER) && equaln(cadr(p1), 3) && equalq(caddr(p1), 1, 2)) { push_rational(1, 3); if (evaluatingAsFloats) { @@ -3244,22 +4273,6 @@ return restore(); }; - - /* arctanh ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - Returns the inverse hyperbolic tangent of x. - */ - Eval_arctanh = function() { push(cadr(p1)); Eval(); @@ -3297,72 +4310,6 @@ return restore(); }; - - /* arg ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - z - - General description - ------------------- - Returns the angle of complex z. - */ - - - /* - Argument (angle) of complex z - - z arg(z) - - ------ - - a 0 - - -a -pi See note 3 below - - (-1)^a a pi - - exp(a + i b) b - - a b arg(a) + arg(b) - - a + i b arctan(b/a) - - Result by quadrant - - z arg(z) - - ------ - - 1 + i 1/4 pi - - 1 - i -1/4 pi - - -1 + i 3/4 pi - - -1 - i -3/4 pi - - Notes - - 1. Handles mixed polar and rectangular forms, e.g. 1 + exp(i pi/3) - - 2. Symbols in z are assumed to be positive and real. - - 3. Negative direction adds -pi to angle. - - Example: z = (-1)^(1/3), abs(z) = 1/3 pi, abs(-z) = -2/3 pi - - 4. jean-francois.debroux reports that when z=(a+i*b)/(c+i*d) then - - arg(numerator(z)) - arg(denominator(z)) - - must be used to get the correct answer. Now the operation is - automatic. - */ - DEBUG_ARG = false; Eval_arg = function() { @@ -3384,9 +4331,12 @@ return restore(); }; + //define RE p2 + //define IM p3 yyarg = function() { save(); p1 = pop(); + // case of plain number if (ispositivenumber(p1) || p1 === symbol(PI)) { if (isdouble(p1) || evaluatingAsFloats) { push_double(0); @@ -3400,11 +4350,16 @@ push(symbol(PI)); } negate(); + // you'd think that something like + // arg(a) is always 0 when a is real but no, + // arg(a) is pi when a is negative so we have + // to leave unexpressed } else if (issymbol(p1)) { push_symbol(ARG); push(p1); list(2); } else if (car(p1) === symbol(POWER) && equaln(cadr(p1), -1)) { + // -1 to a power if (evaluatingAsFloats) { push_double(Math.PI); } else { @@ -3413,8 +4368,11 @@ push(caddr(p1)); multiply(); } else if (car(p1) === symbol(POWER) && cadr(p1) === symbol(E)) { + // exponential push(caddr(p1)); imag(); + // arg(a^(1/2)) is always equal to 1/2 * arg(a) + // this can obviously be made more generic TODO } else if (car(p1) === symbol(POWER) && isoneovertwo(caddr(p1))) { if (DEBUG_ARG) { console.log("arg of a sqrt: " + p1); @@ -3430,6 +4388,7 @@ push(caddr(p1)); multiply(); } else if (car(p1) === symbol(MULTIPLY)) { + // product of factors push_integer(0); p1 = cdr(p1); while (iscons(p1)) { @@ -3439,6 +4398,7 @@ p1 = cdr(p1); } } else if (car(p1) === symbol(ADD)) { + // sum of terms push(p1); rect(); p1 = pop(); @@ -3469,16 +4429,19 @@ push_symbol(PI); } if (isnegative(p3)) { - subtract(); + subtract(); // quadrant 1 -> 3 } else { - add(); + add(); // quadrant 4 -> 2 } } } } else { if (!isZeroAtomOrTensor(get_binding(symbol(ASSUME_REAL_VARIABLES)))) { + // if we assume all passed values are real push_integer(0); } else { + // if we don't assume all passed values are real, all + // we con do is to leave unexpressed push_symbol(ARG); push(p1); list(2); @@ -3487,6 +4450,7 @@ return restore(); }; + // pretty print bake = function() { var h, s, t, x, y, z; h = 0; @@ -3518,6 +4482,10 @@ } else if (s === 0 && t === 0 && x === 0 && y === 0 && z === 1) { p2 = symbol(SYMBOL_Z); bake_poly(); + // don't bake the contents of some constructs such as "for" + // because we don't want to evaluate the body of + // such constructs "statically", i.e. without fully running + // the loops. } else if ((iscons(p1)) && car(p1) !== symbol(FOR)) { h = tos; push(car(p1)); @@ -3566,6 +4534,7 @@ i = 0; k = 0; n = 0; + //U **a a = tos; push(p1); push(p2); @@ -3587,6 +4556,9 @@ return push(p1); }; + // p1 points to coefficient of p2 ^ k + + // k is an int bake_poly_term = function(k) { var h, n; h = 0; @@ -3594,6 +4566,7 @@ if (isZeroAtomOrTensor(p1)) { return; } + // constant term? if (k === 0) { if (car(p1) === symbol(ADD)) { p1 = cdr(p1); @@ -3607,6 +4580,7 @@ return; } h = tos; + // coefficient if (car(p1) === symbol(MULTIPLY)) { p1 = cdr(p1); while (iscons(p1)) { @@ -3616,6 +4590,7 @@ } else if (!equaln(p1, 1)) { push(p1); } + // x ^ k if (k === 1) { push(p2); } else { @@ -3633,45 +4608,6 @@ } }; - - /* besselj ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x,n - - General description - ------------------- - - Returns a solution to the Bessel differential equation (Bessel function of first kind). - - Recurrence relation: - - besselj(x,n) = (2/x) (n-1) besselj(x,n-1) - besselj(x,n-2) - - besselj(x,1/2) = sqrt(2/pi/x) sin(x) - - besselj(x,-1/2) = sqrt(2/pi/x) cos(x) - - For negative n, reorder the recurrence relation as: - - besselj(x,n-2) = (2/x) (n-1) besselj(x,n-1) - besselj(x,n) - - Substitute n+2 for n to obtain - - besselj(x,n) = (2/x) (n+1) besselj(x,n+1) - besselj(x,n+2) - - Examples: - - besselj(x,3/2) = (1/x) besselj(x,1/2) - besselj(x,-1/2) - - besselj(x,-3/2) = -(1/x) besselj(x,-1/2) - besselj(x,1/2) - */ - Eval_besselj = function() { push(cadr(p1)); Eval(); @@ -3686,6 +4622,9 @@ return restore(); }; + //define X p1 + //define N p2 + //define SGN p3 yybesselj = function() { var d, n; d = 0.0; @@ -3694,20 +4633,25 @@ p1 = pop(); push(p2); n = pop_integer(); + // numerical result if (isdouble(p1) && !isNaN(n)) { d = jn(n, p1.d); push_double(d); return; } + // bessej(0,0) = 1 if (isZeroAtomOrTensor(p1) && isZeroAtomOrTensor(p2)) { push_integer(1); return; } + // besselj(0,n) = 0 if (isZeroAtomOrTensor(p1) && !isNaN(n)) { push_integer(0); return; } + // half arguments if (p2.k === NUM && MEQUAL(p2.q.b, 2)) { + // n = 1/2 if (MEQUAL(p2.q.a, 1)) { if (evaluatingAsFloats) { push_double(2.0 / Math.PI); @@ -3725,6 +4669,7 @@ multiply(); return; } + // n = -1/2 if (MEQUAL(p2.q.a, -1)) { if (evaluatingAsFloats) { push_double(2.0 / Math.PI); @@ -3742,6 +4687,7 @@ multiply(); return; } + // besselj(x,n) = (2/x) (n-sgn(n)) besselj(x,n-sgn(n)) - besselj(x,n-2*sgn(n)) push_integer(MSIGN(p2.q.a)); p3 = pop(); push_integer(2); @@ -3767,6 +4713,7 @@ subtract(); return; } + //if 0 # test cases needed if (isnegativeterm(p1)) { push(p1); negate(); @@ -3797,28 +4744,12 @@ multiply(); return; } + //endif push(symbol(BESSELJ)); push(p1); push(p2); - return list(3); - }; - - - /* bessely ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x,n - - General description - ------------------- - - Bessel function of second kind. - */ + return list(3); + }; Eval_bessely = function() { push(cadr(p1)); @@ -3834,6 +4765,8 @@ return restore(); }; + //define X p1 + //define N p2 yybessely = function() { var d, n; d = 0.0; @@ -3865,6 +4798,9 @@ list(3); }; + //double convert_rational_to_double(U *) + //double convert_bignum_to_double(unsigned int *) + //int ge(unsigned int *, unsigned int *, int) mint = function(a) { return bigInt(a); }; @@ -3873,12 +4809,14 @@ return a.geq(Number.MIN_SAFE_INTEGER) && a.leq(Number.MAX_SAFE_INTEGER); }; + // b is +1 or -1, a is a bigint setSignTo = function(a, b) { if (a.isPositive()) { if (b < 0) { return a.multiply(bigInt(-1)); } } else { + // a is negative if (b > 0) { return a.multiply(bigInt(-1)); } @@ -3892,6 +4830,7 @@ return a.multiply(bigInt(-1)); } } else { + // a is negative if (b.isPositive()) { return a.multiply(bigInt(-1)); } @@ -3906,12 +4845,12 @@ return a; }; - + // n is an int /* mtotal = 0 MP_MIN_SIZE = 2 MP_MAX_FREE = 1000 - + mnew = (n) -> if (n < MP_MIN_SIZE) n = MP_MIN_SIZE @@ -3920,16 +4859,16 @@ else p = [] #(unsigned int *) malloc((n + 3) * sizeof (int)) #if (p == 0) - * stop("malloc failure") + * stop("malloc failure") p[0] = n mtotal += n return p[3] */ - - + // p is the index of array of ints + // !!! array wasn't passed here /* free_stack = [] - + mfree = (array, p) -> p -= 3 mtotal -= array[p] @@ -3937,44 +4876,44 @@ free_stack[mfreecount++] = p else free(p) - */ - + */ + // convert int to bignum + // n is an int /* mint = (n) -> p = mnew(1) if (n < 0) - * !!! this is FU - * MSIGN(p) = -1 + * !!! this is FU + * MSIGN(p) = -1 fu = true else - * !!! this is FU + * !!! this is FU #MSIGN(p) = 1 fu = true - * !!! this is FU + * !!! this is FU #MLENGTH(p) = 1 p[0] = Math.abs(n) return p */ + // copy bignum - + // a is an array of ints /* mcopy = (a) -> #unsigned int *b - + b = mnew(MLENGTH(a)) - - * !!! fu + + * !!! fu #MSIGN(b) = MSIGN(a) #MLENGTH(b) = MLENGTH(a) - + for i in [0...MLENGTH(a)] b[i] = a[i] - + return b */ - - /* * * ge not invoked from anywhere - is you need ge @@ -3995,11 +4934,11 @@ else return 0 */ - add_numbers = function() { var a, b, theResult; a = 1.0; b = 1.0; + //if DEBUG then console.log("add_numbers adding numbers: " + print_list(stack[tos - 1]) + " and " + print_list(stack[tos - 2])) if (isrational(stack[tos - 1]) && isrational(stack[tos - 2])) { qadd(); return; @@ -4102,6 +5041,7 @@ invert_number = function() { var a, b; + //unsigned int *a, *b save(); p1 = pop(); if (isZeroAtomOrTensor(p1)) { @@ -4124,15 +5064,18 @@ return restore(); }; + // a and b are Us compare_rationals = function(a, b) { var ab, ba, t; t = 0; + //unsigned int *ab, *ba ab = mmul(a.q.a, b.q.b); ba = mmul(a.q.b, b.q.a); t = mcmp(ab, ba); return t; }; + // a and b are Us compare_numbers = function(a, b) { var x, y; x = 0.0; @@ -4186,6 +5129,7 @@ bignum_truncate = function() { var a; + //unsigned int *a save(); p1 = pop(); a = mdiv(p1.q.a, p1.q.b); @@ -4229,13 +5173,16 @@ return restore(); }; + // expo is an integer bignum_power_number = function(expo) { var a, b, t; + //unsigned int *a, *b, *t save(); p1 = pop(); a = mpow(p1.q.a, Math.abs(expo)); b = mpow(p1.q.b, Math.abs(expo)); if (expo < 0) { + // swap a and b t = a; a = b; b = t; @@ -4250,10 +5197,12 @@ return restore(); }; + // p an array of ints convert_bignum_to_double = function(p) { return p.toJSNumber(); }; + // p is a U convert_rational_to_double = function(p) { var quotientAndRemainder, result; if (p.q == null) { @@ -4264,6 +5213,7 @@ return result; }; + // n an integer push_integer = function(n) { if (DEBUG) { console.log("pushing integer " + n); @@ -4277,6 +5227,7 @@ return restore(); }; + // d a double push_double = function(d) { save(); p1 = new U(); @@ -4286,8 +5237,8 @@ return restore(); }; + // a,b parts of a rational push_rational = function(a, b) { - /* save() p1 = new U() @@ -4332,6 +5283,7 @@ return n; }; + // p is a U, flag is an int print_double = function(p, flag) { var accumulator, buf; accumulator = ""; @@ -4344,14 +5296,18 @@ return accumulator; }; + // s is a string bignum_scan_integer = function(s) { var a, scounter, sign_; + //unsigned int *a + //char sign save(); scounter = 0; sign_ = s[scounter]; if (sign_ === '+' || sign_ === '-') { scounter++; } + // !!!! some mess in here, added an argument a = bigInt(s.substring(scounter)); p1 = new U(); p1.k = NUM; @@ -4364,10 +5320,18 @@ return restore(); }; + // s a string bignum_scan_float = function(s) { return push_double(parseFloat(s)); }; + // gives the capability of printing the unsigned + // value. This is handy because printing of the sign + // might be taken care of "upstream" + // e.g. when printing a base elevated to a negative exponent + // prints the inverse of the base powered to the unsigned + // exponent. + // p is a U print_number = function(p, signed) { var aAsString, accumulator, buf, denominatorString; accumulator = ""; @@ -4412,6 +5376,8 @@ save(); p2 = pop(); p1 = pop(); + // if (!isinteger(p1) || !isinteger(p2)) + // stop("integer args expected for gcd") p3 = new U(); p3.k = NUM; p3.q.a = mgcd(p1.q.a, p2.q.a); @@ -4447,6 +5413,9 @@ return push_double(d); }; + //static unsigned int *__factorial(int) + + // n is an int bignum_factorial = function(n) { save(); p1 = new U(); @@ -4457,9 +5426,11 @@ return restore(); }; + // n is an int __factorial = function(n) { var a, b, i, o, ref, t; i = 0; + //unsigned int *a, *b, *t if (n === 0 || n === 1) { a = bigInt(1); return a; @@ -4467,7 +5438,7 @@ a = bigInt(2); b = bigInt(0); if (3 <= n) { - for (i = o = 3, ref = n; 3 <= ref ? o <= ref : o >= ref; i = 3 <= ref ? ++o : --o) { + for (i = o = 3, ref = n; (3 <= ref ? o <= ref : o >= ref); i = 3 <= ref ? ++o : --o) { b = bigInt(i); t = mmul(a, b); a = t; @@ -4478,22 +5449,36 @@ mask = [0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000, 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000, 0x00800000, 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000]; + // unsigned int *x, unsigned int k mp_set_bit = function(x, k) { console.log("not implemented yet"); debugger; return x[k / 32] |= mask[k % 32]; }; + // unsigned int *x, unsigned int k mp_clr_bit = function(x, k) { console.log("not implemented yet"); debugger; return x[k / 32] &= ~mask[k % 32]; }; + // unsigned int *a mshiftright = function(a) { return a = a.shiftRight(); }; + // Binomial coefficient + + // Input: tos-2 n + + // tos-1 k + + // Output: Binomial coefficient on stack + + // binomial(n, k) = n! / k! / (n - k)! + + // The binomial coefficient vanishes for k < 0 or k > n. (A=B, p. 19) Eval_binomial = function() { push(cadr(p1)); Eval(); @@ -4508,6 +5493,8 @@ return restore(); }; + //define N p1 + //define K p2 ybinomial = function() { p2 = pop(); p1 = pop(); @@ -4539,23 +5526,6 @@ } }; - - /* ceiling ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - - Returns the smallest integer not less than x. - */ - Eval_ceiling = function() { push(cadr(p1)); Eval(); @@ -4600,31 +5570,6 @@ } }; - - /* choose ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - n,k - - General description - ------------------- - - Returns the number of combinations of n items taken k at a time. - - For example, the number of five card hands is choose(52,5) - - ``` - n! - choose(n,k) = ------------- - k! (n - k)! - ``` - */ - Eval_choose = function() { push(cadr(p1)); Eval(); @@ -4633,6 +5578,10 @@ return choose(); }; + // Result vanishes for k < 0 or k > n. (A=B, p. 19) + + //define N p1 + //define K p2 choose = function() { save(); p2 = pop(); @@ -4667,27 +5616,11 @@ } }; - - /* circexp ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - - Returns expression x with circular and hyperbolic functions converted to exponential forms. Sometimes this will simplify an expression. - */ - Eval_circexp = function() { push(cadr(p1)); Eval(); circexp(); + // normalize return Eval(); }; @@ -4792,7 +5725,7 @@ push(p1); copy_tensor(); p1 = pop(); - for (i = o = 0, ref = p1.tensor.nelem; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = p1.tensor.nelem; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push(p1.tensor.elem[i]); circexp(); p1.tensor.elem[i] = pop(); @@ -4805,20 +5738,6 @@ return restore(); }; - - /* clearall ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - - General description - ------------------- - - Completely wipes all variables from the environment. - */ - Eval_clearall = function() { do_clearall(); return push(symbol(NIL)); @@ -4834,15 +5753,19 @@ return codeGen = false; }; + // clearall from application GUI code clearall = function() { return run("clearall"); }; + // this transformation is done in run.coffee, see there + // for more info. clearRenamedVariablesToAvoidBindingToExternalScope = function() { var i, o, ref, results; results = []; - for (i = o = 0, ref = symtab.length; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = symtab.length; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { if (symtab[i].printname.indexOf("AVOID_BINDING_TO_EXTERNAL_SCOPE_VALUE") !== -1) { + // just clear it symtab[i].k = SYM; symtab[i].printname = ""; binding[i] = symtab[i]; @@ -4854,31 +5777,18 @@ return results; }; - - /* clear ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - - Completely wipes a variable from the environment (while doing x = quote(x) just unassigns it). - */ - Eval_clear = function() { var indexFound, variableToBeCleared; p2 = cdr(p1); while (iscons(p2)) { variableToBeCleared = car(p2); + //console.log variableToBeCleared + "" if (variableToBeCleared.k !== SYM) { stop("symbol error"); } + //console.log "getting binding of " + p.toString() + //if p.toString() == "aaa" + // debugger indexFound = symtab.indexOf(variableToBeCleared); symtab[indexFound].k = SYM; symtab[indexFound].printname = ""; @@ -4889,19 +5799,26 @@ return push(symbol(NIL)); }; - /* Convert complex z to clock form - + Input: push z - + Output: Result on stack - + clock(z) = abs(z) * (-1) ^ (arg(z) / pi) - - For example, clock(exp(i pi/3)) gives the result (-1)^(1/3) - */ + For example, clock(exp(i pi/3)) gives the result (-1)^(1/3) + */ + // P.S. I couldn't find independent definition/aknowledgment + // of the naming "clock form" anywhere on the web, seems like a + // naming specific to eigenmath. + // Clock form is another way to express a complex number, and + // it has three advantages + // 1) it's uniform with how for example + // i is expressed i.e. (-1)^(1/2) + // 2) it's very compact + // 3) it's a straighforward notation for roots of 1 and -1 DEBUG_CLOCKFORM = false; Eval_clock = function() { @@ -4912,12 +5829,17 @@ clockform = function() { save(); + //if 1 p1 = pop(); push(p1); abs(); if (DEBUG_CLOCKFORM) { console.log("clockform: abs of " + p1 + " : " + stack[tos - 1]); } + // pushing the expression (-1)^... but note + // that we can't use "power", as "power" evaluates + // clock forms into rectangular form (see "-1 ^ rational" + // section in power) push_symbol(POWER); push_integer(-1); push(p1); @@ -4942,7 +5864,7 @@ if (DEBUG_CLOCKFORM) { console.log("clockform: multiply : " + stack[tos - 1]); } - + //else /* p1 = pop() push(p1) @@ -4954,26 +5876,29 @@ multiply() power() multiply() - */ + */ + //endif return restore(); }; - /* coeff ===================================================================== - + Tags ---- scripting, JS, internal, treenode, general concept - + Parameters ---------- p,x,n - + General description ------------------- Returns the coefficient of x^n in polynomial p. The x argument can be omitted for polynomials in x. - */ + */ + //define P p1 + //define X p2 + //define N p3 Eval_coeff = function() { push(cadr(p1)); Eval(); @@ -4984,9 +5909,9 @@ p3 = pop(); p2 = pop(); p1 = pop(); - if (p3 === symbol(NIL)) { + if (p3 === symbol(NIL)) { // p3 is N # only 2 args? p3 = p2; - p2 = symbol(SYMBOL_X); + p2 = symbol(SYMBOL_X); // p2 is X } push(p1); push(p2); @@ -4997,6 +5922,21 @@ return filter(); }; + //----------------------------------------------------------------------------- + + // Put polynomial coefficients on the stack + + // Input: tos-2 p(x) (the polynomial) + + // tos-1 x (the variable) + + // Output: Returns number of coefficients on stack + + // tos-n Coefficient of x^0 + + // tos-1 Coefficient of x^(n-1) + + //----------------------------------------------------------------------------- coeff = function() { var h, n, prev_expanding; save(); @@ -5026,28 +5966,11 @@ expanding = 1; divide(); expanding = prev_expanding; + //console.log("just divided: " + stack[tos-1].toString()) p1 = pop(); } }; - - /* cofactor ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - m,i,j - - General description - ------------------- - Cofactor of a matrix component. - Let c be the cofactor matrix of matrix m, i.e. tranpose(c) = adj(m). - This function returns c[i,j]. - */ - Eval_cofactor = function() { var doNothing, i, j, n; i = 0; @@ -5081,8 +6004,8 @@ var i, i1, j, o, ref, ref1; i = 0; j = 0; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { - for (j = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { + for (j = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); j = 0 <= ref1 ? ++i1 : --i1) { if (i !== row && j !== col) { push(p.tensor.elem[n * i + j]); } @@ -5094,6 +6017,7 @@ } }; + // Condense an expression by factoring common terms. Eval_condense = function() { push(cadr(p1)); Eval(); @@ -5111,19 +6035,26 @@ }; yycondense = function() { + //expanding = 0 p1 = pop(); if (car(p1) !== symbol(ADD)) { push(p1); return; } + // get gcd of all terms p3 = cdr(p1); push(car(p3)); p3 = cdr(p3); while (iscons(p3)) { push(car(p3)); + //console.log "calculating gcd between: " + stack[tos - 1] + " and " + stack[tos - 2] gcd(); + //console.log "partial gcd: " + stack[tos - 1] p3 = cdr(p3); } + //console.log "condense: this is the gcd of all the terms: " + stack[tos - 1] + + // divide each term by gcd inverse(); p2 = pop(); push(zero); @@ -5131,37 +6062,27 @@ while (iscons(p3)) { push(p2); push(car(p3)); + //multiply() multiply_noexpand(); add(); p3 = cdr(p3); } + // We multiplied above w/o expanding so some factors cancelled. + + // Now we expand which normalizes the result and, in some cases, + // simplifies it too (see test case H). yyexpand(); + // multiply result by gcd push(p2); return divide(); }; - - /* conj ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - z - - General description - ------------------- - Returns the complex conjugate of z. - */ - Eval_conj = function() { push(cadr(p1)); Eval(); p1 = pop(); push(p1); - if (!Find(p1, imaginaryunit)) { + if (!Find(p1, imaginaryunit)) { // example: (-1)^(1/3) polar(); conjugate(); return clockform(); @@ -5170,6 +6091,8 @@ } }; + // careful is you pass this one an expression with + // i (instead of (-1)^(1/2)) then this doesn't work! conjugate = function() { push(imaginaryunit); push(imaginaryunit); @@ -5178,6 +6101,7 @@ return Eval(); }; + // Cons two things on the stack. consCount = 0; cons = function() { @@ -5186,6 +6110,9 @@ if (DEBUG) { console.log("cons tos: " + tos + " # " + consCount); } + //if consCount == 444 + // debugger + // auto var ok, no opportunity for garbage collection after p = alloc() p = new U(); p.k = CONS; p.cons.cdr = pop(); @@ -5194,34 +6121,15 @@ console.log("something wrong p == its cdr"); } p.cons.car = pop(); - /* console.log "cons new cdr.k = " + p.cons.cdr.k + "\nor more in detail:" console.log print_list p.cons.cdr console.log "cons new car.k = " + p.cons.car.k + "\nor more in detail:" console.log print_list p.cons.car - */ + */ return push(p); }; - - /* contract ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - a,i,j - - General description - ------------------- - Contract across tensor indices i.e. returns "a" summed over indices i and j. - If i and j are omitted then 1 and 2 are used. - contract(m) is equivalent to the trace of matrix m. - */ - Eval_contract = function() { push(cadr(p1)); Eval(); @@ -5277,40 +6185,48 @@ l--; m--; n = p1.tensor.dim[l]; + // nelem is the number of elements in "b" nelem = 1; - for (i = o = 0, ref = ndim; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = ndim; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { if (i !== l && i !== m) { nelem *= p1.tensor.dim[i]; } } + //console.log "nelem:" + nelem p2 = alloc_tensor(nelem); + //console.log "p2:" + p2 p2.tensor.ndim = ndim - 2; j = 0; - for (i = i1 = 0, ref1 = ndim; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = ndim; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { if (i !== l && i !== m) { p2.tensor.dim[j++] = p1.tensor.dim[i]; } } a = p1.tensor.elem; b = p2.tensor.elem; - for (i = j1 = 0, ref2 = ndim; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { +//console.log "a: " + a +//console.log "b: " + b + for (i = j1 = 0, ref2 = ndim; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { ai[i] = 0; an[i] = p1.tensor.dim[i]; } - for (i = l1 = 0, ref3 = nelem; 0 <= ref3 ? l1 < ref3 : l1 > ref3; i = 0 <= ref3 ? ++l1 : --l1) { + for (i = l1 = 0, ref3 = nelem; (0 <= ref3 ? l1 < ref3 : l1 > ref3); i = 0 <= ref3 ? ++l1 : --l1) { push(zero); - for (j = m1 = 0, ref4 = n; 0 <= ref4 ? m1 < ref4 : m1 > ref4; j = 0 <= ref4 ? ++m1 : --m1) { + for (j = m1 = 0, ref4 = n; (0 <= ref4 ? m1 < ref4 : m1 > ref4); j = 0 <= ref4 ? ++m1 : --m1) { ai[l] = j; ai[m] = j; h = 0; - for (k = n1 = 0, ref5 = ndim; 0 <= ref5 ? n1 < ref5 : n1 > ref5; k = 0 <= ref5 ? ++n1 : --n1) { + for (k = n1 = 0, ref5 = ndim; (0 <= ref5 ? n1 < ref5 : n1 > ref5); k = 0 <= ref5 ? ++n1 : --n1) { h = (h * an[k]) + ai[k]; } push(a[h]); + //console.log "a[h]: " + a[h] add(); } + //console.log "tos: " + stack[tos-1] b[i] = pop(); - for (j = o1 = ref6 = ndim - 1; ref6 <= 0 ? o1 <= 0 : o1 >= 0; j = ref6 <= 0 ? ++o1 : --o1) { +//console.log "b[i]: " + b[i] + for (j = o1 = ref6 = ndim - 1; (ref6 <= 0 ? o1 <= 0 : o1 >= 0); j = ref6 <= 0 ? ++o1 : --o1) { if (j === l || j === m) { continue; } @@ -5327,22 +6243,22 @@ } }; - + //console.log "returning: " + stack[tos-1] /* cos ===================================================================== - + Tags ---- scripting, JS, internal, treenode, general concept - + Parameters ---------- x - + General description ------------------- Returns the cosine of x. - */ + */ Eval_cos = function() { push(cadr(p1)); Eval(); @@ -5360,11 +6276,15 @@ return restore(); }; + // Use angle sum formula for special angles. + + //define A p3 + //define B p4 cosine_of_angle_sum = function() { p2 = cdr(p1); while (iscons(p2)) { p4 = car(p2); - if (isnpi(p4)) { + if (isnpi(p4)) { // p4 is B push(p1); push(p4); subtract(); @@ -5401,11 +6321,15 @@ push_double(d); return; } + // cosine function is symmetric, cos(-x) = cos(x) if (isnegative(p1)) { push(p1); negate(); p1 = pop(); } + // cos(arctan(x)) = 1 / sqrt(1 + x^2) + + // see p. 173 of the CRC Handbook of Mathematical Sciences if (car(p1) === symbol(ARCTAN)) { push_integer(1); push(cadr(p1)); @@ -5416,6 +6340,15 @@ power(); return; } + // multiply by 180/pi to go from radians to degrees. + // we go from radians to degrees because it's much + // easier to calculate symbolic results of most (not all) "classic" + // angles (e.g. 30,45,60...) if we calculate the degrees + // and the we do a switch on that. + // Alternatively, we could look at the fraction of pi + // (e.g. 60 degrees is 1/3 pi) but that's more + // convoluted as we'd need to look at both numerator and + // denominator. push(p1); push_integer(180); multiply(); @@ -5426,6 +6359,9 @@ } divide(); n = pop_integer(); + // most "good" (i.e. compact) trigonometric results + // happen for a round number of degrees. There are some exceptions + // though, e.g. 22.5 degrees, which we don't capture here. if (n < 0 || isNaN(n)) { push(symbol(COS)); push(p1); @@ -5481,28 +6417,6 @@ } }; - - /* cosh ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - Returns the hyperbolic cosine of x - - ``` - exp(x) + exp(-x) - cosh(x) = ---------------- - 2 - ``` - */ - Eval_cosh = function() { push(cadr(p1)); Eval(); @@ -5540,6 +6454,19 @@ return list(2); }; + // this function extract parts subtrees from a tree. + // It is used in two + // places that have to do with pattern matching. + // One is for integrals, where an expression or its + // subparts are matched against cases in an + // integrals table. + // Another one is for applyging tranformation patterns + // defined via PATTERN, again patterns are applied to + // either the whole expression or any of its parts. + + // unclear to me at the moment + // why this is exposed as something that can + // be evalled. Never called. Eval_decomp = function() { var h; save(); @@ -5576,6 +6503,7 @@ return push(toBePushed); }; + // returns constant expressions on the stack decomp = function(generalTransform) { save(); p2 = pop(); @@ -5583,6 +6511,7 @@ if (DEBUG) { console.log("DECOMPOSING " + p1); } + // is the entire expression constant? if (generalTransform) { if (!iscons(p1)) { if (DEBUG) { @@ -5598,20 +6527,25 @@ console.log(" entire expression is constant"); } pushTryNotToDuplicate(p1); + //push(p1); # may need later for pushing both +a, -a + //negate() restore(); return; } } + // sum? if (isadd(p1)) { decomp_sum(generalTransform); restore(); return; } + // product? if (ismultiply(p1)) { decomp_product(generalTransform); restore(); return; } + // naive decomp if not sum or product if (DEBUG) { console.log(" naive decomp"); } @@ -5620,6 +6554,10 @@ console.log("startig p3: " + p3); } while (iscons(p3)) { + // for a general transformations, + // we want to match any part of the tree so + // we need to push the subtree as well + // as recurse to its parts if (generalTransform) { push(car(p3)); } @@ -5646,6 +6584,7 @@ console.log(" decomposing the sum "); } h = 0; + // decomp terms involving x p3 = cdr(p1); while (iscons(p3)) { if (Find(car(p3), p2) || generalTransform) { @@ -5655,6 +6594,7 @@ } p3 = cdr(p3); } + // add together all constant terms h = tos; p3 = cdr(p1); while (iscons(p3)) { @@ -5668,7 +6608,7 @@ p3 = pop(); pushTryNotToDuplicate(p3); push(p3); - return negate(); + return negate(); // need both +a, -a for some integrals } }; @@ -5678,6 +6618,7 @@ console.log(" decomposing the product "); } h = 0; + // decomp factors involving x p3 = cdr(p1); while (iscons(p3)) { if (Find(car(p3), p2) || generalTransform) { @@ -5687,6 +6628,7 @@ } p3 = cdr(p3); } + // multiply together all constant factors h = tos; p3 = cdr(p1); while (iscons(p3)) { @@ -5700,18 +6642,70 @@ } }; + //p3 = pop(); # may need later for pushing both +a, -a + //push(p3) + //push(p3) + //negate() + // Store a function definition + + // Example: + + // f(x,y)=x^y + + // For this definition, p1 points to the following structure. + + // p1 + // | + // ___v__ ______ ______ + // |CONS |->|CONS |--------------------->|CONS | + // |______| |______| |______| + // | | | + // ___v__ ___v__ ______ ______ ___v__ ______ ______ + // |SETQ | |CONS |->|CONS |->|CONS | |CONS |->|CONS |->|CONS | + // |______| |______| |______| |______| |______| |______| |______| + // | | | | | | + // ___v__ ___v__ ___v__ ___v__ ___v__ ___v__ + // |SYM f | |SYM x | |SYM y | |POWER | |SYM x | |SYM y | + // |______| |______| |______| |______| |______| |______| + + // the result (in f) is a FUNCTION node + // that contains both the body and the argument list. + + // We have + + // caadr(p1) points to the function name i.e. f + // cdadr(p1) points to the arguments i.e. the list (x y) + // caddr(p1) points to the function body i.e. (power x y) + + //define F p3 # F points to the function name + //define A p4 # A points to the argument list + //define B p5 # B points to the function body define_user_function = function() { p3 = caadr(p1); p4 = cdadr(p1); p5 = caddr(p1); - if (!issymbol(p3)) { + if (!issymbol(p3)) { // p3 is F stop("function name?"); } - if (car(p5) === symbol(EVAL)) { + // evaluate function body (maybe) + if (car(p5) === symbol(EVAL)) { // p5 is B push(cadr(p5)); Eval(); - p5 = pop(); + p5 = pop(); // p5 is B } + + // note how, unless explicitly forced by an eval, + // (handled by the if just above) + // we don't eval/simplify + // the body. + // Why? because it's the easiest way + // to solve scope problems i.e. + // x = 0 + // f(x) = x + 1 + // f(4) # would reply 1 + // which would need to otherwise + // be solved by some scope device + // somehow push_symbol(FUNCTION); push(p5); push(p4); @@ -5725,17 +6719,16 @@ return push(p1); }; - /* defint ===================================================================== - + Tags ---- scripting, JS, internal, treenode, general concept - + Parameters ---------- f,x,a,b[,y,c,d...] - + General description ------------------- Returns the definite integral of f with respect to x evaluated from "a" to b. @@ -5743,62 +6736,69 @@ integrals"), for example a double integral (which can represent for example a volume under a surface), or a triple integral, etc. For example, defint(f,x,a,b,y,c,d). - */ + */ + //define F p2 + //define X p3 + //define A p4 + //define B p5 Eval_defint = function() { push(cadr(p1)); Eval(); - p2 = pop(); + p2 = pop(); // p2 is F p1 = cddr(p1); + // defint can handle multiple + // integrals, so we loop over the + // multiple integrals here while (iscons(p1)) { push(car(p1)); p1 = cdr(p1); Eval(); - p3 = pop(); + p3 = pop(); // p3 is X push(car(p1)); p1 = cdr(p1); Eval(); - p4 = pop(); + p4 = pop(); // p4 is A push(car(p1)); p1 = cdr(p1); Eval(); - p5 = pop(); + p5 = pop(); // p5 is B + + // obtain the primitive of F against the + // specified variable X + // note that the primitive changes over + // the calculation of the multiple + // integrals. push(p2); push(p3); integral(); - p2 = pop(); + p2 = pop(); // contains the antiderivative of F + + // evaluate the integral in A push(p2); push(p3); push(p5); subst(); Eval(); + // evaluate the integral in B push(p2); push(p3); push(p4); subst(); Eval(); + // integral between B and A is the + // subtraction. Note that this could + // be a number but also a function. + // and we might have to integrate this + // number/function again doing the while + // loop again if this is a multiple + // integral. subtract(); p2 = pop(); } return push(p2); }; - - /* deg ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - p,x - - General description - ------------------- - Returns the degree of polynomial p(x). - */ - Eval_degree = function() { push(cadr(p1)); Eval(); @@ -5813,6 +6813,24 @@ return degree(); }; + //----------------------------------------------------------------------------- + + // Find the degree of a polynomial + + // Input: tos-2 p(x) + + // tos-1 x + + // Output: Result on stack + + // Note: Finds the largest numerical power of x. Does not check for + // weirdness in p(x). + + //----------------------------------------------------------------------------- + + //define POLY p1 + //define X p2 + //define DEGREE p3 degree = function() { save(); p2 = pop(); @@ -5844,22 +6862,6 @@ } }; - - /* denominator ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - Returns the denominator of expression x. - */ - Eval_denominator = function() { push(cadr(p1)); Eval(); @@ -5870,6 +6872,7 @@ var h, theArgument; h = 0; theArgument = pop(); + //console.trace "denominator of: " + theArgument if (car(theArgument) === symbol(ADD)) { push(theArgument); rationalize(); @@ -5895,12 +6898,27 @@ } }; + // derivative + + //define F p3 + //define X p4 + //define N p5 Eval_derivative = function() { var doNothing, i, i1, n, o, ref, ref1; + // evaluate 1st arg to get function F i = 0; p1 = cdr(p1); push(car(p1)); Eval(); + // evaluate 2nd arg and then... + + // example result of 2nd arg what to do + + // d(f) nil guess X, N = nil + // d(f,2) 2 guess X, N = 2 + // d(f,x) x X = x, N = nil + // d(f,x,2) x X = x, N = 2 + // d(f,x,y) x X = x, N = y p1 = cdr(p1); push(car(p1)); Eval(); @@ -5921,7 +6939,8 @@ p4 = pop(); p3 = pop(); while (1) { - if (isNumericAtom(p5)) { + // p5 (N) might be a symbol instead of a number + if (isNumericAtom(p5)) { // p5 is N push(p5); n = pop_integer(); if (isNaN(n)) { @@ -5932,47 +6951,58 @@ } push(p3); if (n >= 0) { - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push(p4); derivative(); } } else { n = -n; - for (i = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { push(p4); integral(); } } p3 = pop(); - if (p5 === symbol(NIL)) { + if (p5 === symbol(NIL)) { // p5 is N break; } - if (isNumericAtom(p5)) { + // otherwise... + + // N arg1 what to do + + // number nil break + // number number N = arg1, continue + // number symbol X = arg1, N = arg2, continue + + // symbol nil X = N, N = nil, continue + // symbol number X = N, N = arg1, continue + // symbol symbol X = N, N = arg1, continue + if (isNumericAtom(p5)) { // p5 is N p1 = cdr(p1); push(car(p1)); Eval(); p5 = pop(); - if (p5 === symbol(NIL)) { - break; + if (p5 === symbol(NIL)) { // p5 is N + break; // arglist exhausted } - if (isNumericAtom(p5)) { - doNothing = 1; + if (isNumericAtom(p5)) { // p5 is N + doNothing = 1; // N = arg1 } else { p4 = p5; p1 = cdr(p1); push(car(p1)); Eval(); - p5 = pop(); + p5 = pop(); // p5 is N # N = arg2 } } else { p4 = p5; p1 = cdr(p1); push(car(p1)); Eval(); - p5 = pop(); + p5 = pop(); // p5 is N # N = arg1 } } - return push(p3); + return push(p3); // p3 is F # final result }; derivative = function() { @@ -6002,6 +7032,8 @@ if (issymbol(p2)) { return d_scalar_scalar_1(); } else { + // Example: d(sin(cos(x)),cos(x)) + // Replace cos(x) <- X, find derivative, then do X <- cos(x) push(p1); push(p2); push(symbol(SECRETX)); @@ -6010,11 +7042,12 @@ derivative(); push(symbol(SECRETX)); push(p2); - return subst(); + return subst(); // cos(X) -> cos(cos(x)) } }; d_scalar_scalar_1 = function() { + // d(x,x)? if (equal(p1, p2)) { push(one); return; @@ -6153,9 +7186,9 @@ j = 0; n = 0; n = length(p1) - 1; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p3 = cdr(p1); - for (j = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (j = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); j = 0 <= ref1 ? ++i1 : --i1) { push(car(p3)); if (i === j) { push(p2); @@ -6168,6 +7201,22 @@ return add_all(n); }; + //----------------------------------------------------------------------------- + + // v + // y = u + + // log y = v log u + + // 1 dy v du dv + // - -- = - -- + (log u) -- + // y dx u dx dx + + // dy v v du dv + // -- = u (- -- + (log u) --) + // dx u dx dx + + //----------------------------------------------------------------------------- dpower = function() { push(caddr(p1)); push(cadr(p1)); @@ -6195,12 +7244,25 @@ return divide(); }; + // derivative of derivative + + // example: d(d(f(x,y),y),x) + + // p1 = d(f(x,y),y) + + // p2 = x + + // cadr(p1) = f(x,y) + + // caddr(p1) = y dd = function() { + // d(f(x,y),x) push(cadr(p1)); push(p2); derivative(); p3 = pop(); if (car(p3) === symbol(DERIVATIVE)) { + // sort dx terms push_symbol(DERIVATIVE); push_symbol(DERIVATIVE); push(cadr(p3)); @@ -6221,6 +7283,7 @@ } }; + // derivative of a generic function dfunction = function() { p3 = cdr(p1); if (p3 === symbol(NIL) || Find(p3, p2)) { @@ -6292,6 +7355,11 @@ return negate(); }; + // Without simplify With simplify + + // d(arctan(y/x),x) -y/(x^2*(y^2/x^2+1)) -y/(x^2+y^2) + + // d(arctan(y/x),y) 1/(x*(y^2/x^2+1)) x/(x^2+y^2) darctan = function() { push(cadr(p1)); push(p2); @@ -6530,28 +7598,6 @@ return push(cadr(p1)); }; - - /* det ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - m - - General description - ------------------- - Returns the determinant of matrix m. - Uses Gaussian elimination for numerical matrices. - - Example: - - det(((1,2),(3,4))) - > -2 - */ - DET_check_arg = function() { if (!istensor(p1)) { return 0; @@ -6568,6 +7614,7 @@ var a, i, i1, n, o, ref, ref1; i = 0; n = 0; + //U **a save(); p1 = pop(); if (DET_check_arg() === 0) { @@ -6579,7 +7626,7 @@ } n = p1.tensor.nelem; a = p1.tensor.elem; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { if (!isNumericAtom(a[i])) { break; } @@ -6587,7 +7634,7 @@ if (i === n) { yydetg(); } else { - for (i = i1 = 0, ref1 = p1.tensor.nelem; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = p1.tensor.nelem; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { push(p1.tensor.elem[i]); } determinant(p1.tensor.dim[0]); @@ -6595,6 +7642,7 @@ return restore(); }; + // determinant of n * n matrix elements on the stack determinant = function(n) { var a, breakFromOutherWhile, h, i, i1, j, k, o, q, ref, ref1, s, sign_, t; h = 0; @@ -6606,8 +7654,13 @@ sign_ = 0; t = 0; a = []; + //int *a, *c, *d h = tos - n * n; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { +//a = (int *) malloc(3 * n * sizeof (int)) + + //if (a == NULL) +// out_of_memory() + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { a[i] = i; a[i + n] = 0; a[i + n + n] = 1; @@ -6620,12 +7673,13 @@ } else { push_integer(-1); } - for (i = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { k = n * a[i] + i; push(stack[h + k]); - multiply(); + multiply(); // FIXME -- problem here } add(); + // next permutation (Knuth's algorithm P) j = n - 1; s = 0; breakFromOutherWhile = false; @@ -6661,6 +7715,20 @@ return moveTos(h + 1); }; + //----------------------------------------------------------------------------- + + // Input: Matrix on stack + + // Output: Determinant on stack + + // Note: + + // Uses Gaussian elimination which is faster for numerical matrices. + + // Gaussian Elimination works by walking down the diagonal and clearing + // out the columns below it. + + //----------------------------------------------------------------------------- detg = function() { save(); p1 = pop(); @@ -6680,7 +7748,7 @@ i = 0; n = 0; n = p1.tensor.dim[0]; - for (i = o = 0, ref = n * n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n * n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push(p1.tensor.elem[i]); } lu_decomp(n); @@ -6688,6 +7756,17 @@ return push(p1); }; + //----------------------------------------------------------------------------- + + // Input: n * n matrix elements on stack + + // Output: p1 determinant + + // p2 mangled + + // upper diagonal matrix on stack + + //----------------------------------------------------------------------------- M = function(h, n, i, j) { return stack[h + n * i + j]; }; @@ -6704,9 +7783,11 @@ j = 0; h = tos - n * n; p1 = one; - for (d = o = 0, ref = n - 1; 0 <= ref ? o < ref : o > ref; d = 0 <= ref ? ++o : --o) { + for (d = o = 0, ref = n - 1; (0 <= ref ? o < ref : o > ref); d = 0 <= ref ? ++o : --o) { + // diagonal element zero? if (equal(M(h, n, d, d), zero)) { - for (i = i1 = ref1 = d + 1, ref2 = n; ref1 <= ref2 ? i1 < ref2 : i1 > ref2; i = ref1 <= ref2 ? ++i1 : --i1) { +// find a new row + for (i = i1 = ref1 = d + 1, ref2 = n; (ref1 <= ref2 ? i1 < ref2 : i1 > ref2); i = ref1 <= ref2 ? ++i1 : --i1) { if (!equal(M(h, n, i, d), zero)) { break; } @@ -6715,27 +7796,33 @@ p1 = zero; break; } - for (j = j1 = ref3 = d, ref4 = n; ref3 <= ref4 ? j1 < ref4 : j1 > ref4; j = ref3 <= ref4 ? ++j1 : --j1) { +// exchange rows + for (j = j1 = ref3 = d, ref4 = n; (ref3 <= ref4 ? j1 < ref4 : j1 > ref4); j = ref3 <= ref4 ? ++j1 : --j1) { p2 = M(h, n, d, j); setM(h, n, d, j, M(h, n, i, j)); setM(h, n, i, j, p2); } + // negate det push(p1); negate(); p1 = pop(); } + // update det push(p1); push(M(h, n, d, d)); multiply(); p1 = pop(); - for (i = l1 = ref5 = d + 1, ref6 = n; ref5 <= ref6 ? l1 < ref6 : l1 > ref6; i = ref5 <= ref6 ? ++l1 : --l1) { +// update lower diagonal matrix + for (i = l1 = ref5 = d + 1, ref6 = n; (ref5 <= ref6 ? l1 < ref6 : l1 > ref6); i = ref5 <= ref6 ? ++l1 : --l1) { + // multiplier push(M(h, n, i, d)); push(M(h, n, d, d)); divide(); negate(); p2 = pop(); + // update one row setM(h, n, i, d, zero); - for (j = m1 = ref7 = d + 1, ref8 = n; ref7 <= ref8 ? m1 < ref8 : m1 > ref8; j = ref7 <= ref8 ? ++m1 : --m1) { + for (j = m1 = ref7 = d + 1, ref8 = n; (ref7 <= ref8 ? m1 < ref8 : m1 > ref8); j = ref7 <= ref8 ? ++m1 : --m1) { push(M(h, n, d, j)); push(p2); multiply(); @@ -6745,12 +7832,21 @@ } } } + // last diagonal element push(p1); push(M(h, n, n - 1, n - 1)); multiply(); return p1 = pop(); }; + //----------------------------------------------------------------------------- + + // Author : philippe.billet@noos.fr + + // Dirac function dirac(x) + // dirac(-x)=dirac(x) + // dirac(b-a)=dirac(a-b) + //----------------------------------------------------------------------------- Eval_dirac = function() { push(cadr(p1)); Eval(); @@ -6763,6 +7859,7 @@ return restore(); }; + //define p1 p1 ydirac = function() { p1 = pop(); if (isdouble(p1)) { @@ -6806,6 +7903,15 @@ return list(2); }; + //----------------------------------------------------------------------------- + + // Generate all divisors of a term + + // Input: Term on stack (factor * factor * ...) + + // Output: Divisors on stack + + //----------------------------------------------------------------------------- divisors = function() { var h, i, n, o, ref, subsetOfStack; i = 0; @@ -6815,13 +7921,14 @@ h = tos - 1; divisors_onstack(); n = tos - h; + //qsort(stack + h, n, sizeof (U *), __cmp) subsetOfStack = stack.slice(h, h + n); subsetOfStack.sort(cmp_expr); stack = stack.slice(0, h).concat(subsetOfStack).concat(stack.slice(h + n)); p1 = alloc_tensor(n); p1.tensor.ndim = 1; p1.tensor.dim[0] = n; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p1.tensor.elem[i] = stack[h + i]; } moveTos(h); @@ -6838,12 +7945,17 @@ save(); p1 = pop(); h = tos; + // push all of the term's factors if (isNumericAtom(p1)) { push(p1); factor_small_number(); } else if (car(p1) === symbol(ADD)) { push(p1); __factor_add(); + //printf(">>>\n") + //for (i = h; i < tos; i++) + //print(stdout, stack[i]) + //printf("<<<\n") } else if (car(p1) === symbol(MULTIPLY)) { p1 = cdr(p1); if (isNumericAtom(car(p1))) { @@ -6870,16 +7982,44 @@ push(one); } k = tos; + // contruct divisors by recursive descent push(one); gen(h, k); + // move n = tos - k; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { stack[h + i] = stack[k + i]; } moveTos(h + n); return restore(); }; + //----------------------------------------------------------------------------- + + // Generate divisors + + // Input: Base-exponent pairs on stack + + // h first pair + + // k just past last pair + + // Output: Divisors on stack + + // For example, factor list 2 2 3 1 results in 6 divisors, + + // 1 + // 3 + // 2 + // 6 + // 4 + // 12 + + //----------------------------------------------------------------------------- + + //define ACCUM p1 + //define BASE p2 + //define EXPO p3 gen = function(h, k) { var expo, i, o, ref; expo = 0; @@ -6896,7 +8036,7 @@ push(p3); expo = pop_integer(); if (!isNaN(expo)) { - for (i = o = 0, ref = Math.abs(expo); 0 <= ref ? o <= ref : o >= ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = Math.abs(expo); (0 <= ref ? o <= ref : o >= ref); i = 0 <= ref ? ++o : --o) { push(p1); push(p2); push_integer(sign(expo) * i); @@ -6908,9 +8048,22 @@ return restore(); }; + //----------------------------------------------------------------------------- + + // Factor ADD expression + + // Input: Expression on stack + + // Output: Factors on stack + + // Each factor consists of two expressions, the factor itself followed + // by the exponent. + + //----------------------------------------------------------------------------- __factor_add = function() { save(); p1 = pop(); + // get gcd of all terms p3 = cdr(p1); push(car(p3)); p3 = cdr(p3); @@ -6919,6 +8072,7 @@ gcd(); p3 = cdr(p3); } + // check gcd p2 = pop(); if (isplusone(p2)) { push(p1); @@ -6926,6 +8080,7 @@ restore(); return; } + // push factored gcd if (isNumericAtom(p2)) { push(p2); factor_small_number(); @@ -6948,6 +8103,7 @@ push(p2); push(one); } + // divide each term by gcd push(p2); inverse(); p2 = pop(); @@ -6964,6 +8120,7 @@ return restore(); }; + // power function for double precision floating point dpow = function() { var a, b, base, expo, result, theta; a = 0.0; @@ -6974,9 +8131,11 @@ theta = 0.0; expo = pop_double(); base = pop_double(); + // divide by zero? if (base === 0.0 && expo < 0.0) { stop("divide by zero"); } + // nonnegative base or integer power? if (base >= 0.0 || (expo % 1.0) === 0.0) { result = Math.pow(base, expo); push_double(result); @@ -6984,6 +8143,7 @@ } result = Math.pow(Math.abs(base), expo); theta = Math.PI * expo; + // this ensures the real part is 0.0 instead of a tiny fraction if ((expo % 0.5) === 0.0) { a = 0.0; b = Math.sin(theta); @@ -6998,17 +8158,16 @@ return add(); }; - /* eigen ===================================================================== - + Tags ---- scripting, JS, internal, treenode, general concept - + Parameters ---------- m - + General description ------------------- Compute eigenvalues and eigenvectors. Matrix m must be both numerical and symmetric. @@ -7016,68 +8175,70 @@ The eigenvec function returns a matrix with the eigenvectors arranged as row vectors. The eigen function does not return anything but stores the eigenvalue matrix in D and the eigenvector matrix in Q. - + Input: stack[tos - 1] symmetric matrix - + Output: D diagnonal matrix Q eigenvector matrix - + D and Q have the property that - + A == dot(transpose(Q),D,Q) - + where A is the original matrix. - + The eigenvalues are on the diagonal of D. The eigenvectors are row vectors in Q. - + The eigenvalue relation: - + A X = lambda X - + can be checked as follows: - + lambda = D[1,1] X = Q[1] dot(A,X) - lambda X - + Example 1. Check the relation AX = lambda X where lambda is an eigenvalue and X is the associated eigenvector. - + Enter: - + A = hilbert(3) - + eigen(A) - + lambda = D[1,1] - + X = Q[1] - + dot(A,X) - lambda X - + Result: - + -1.16435e-14 - + -6.46705e-15 - + -4.55191e-15 - + Example 2: Check the relation A = QTDQ. - + Enter: - + A - dot(transpose(Q),D,Q) - + Result: - + 6.27365e-12 -1.58236e-11 1.81902e-11 - + -1.58236e-11 -1.95365e-11 2.56514e-12 - + 1.81902e-11 2.56514e-12 1.32627e-11 - */ + */ + //define D(i, j) yydd[EIG_N * (i) + (j)] + //define Q(i, j) yyqq[EIG_N * (i) + (j)] EIG_N = 0; EIG_yydd = []; @@ -7096,22 +8257,6 @@ return push(symbol(NIL)); }; - - /* eigenval ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - m - - General description - ------------------- - Compute eigenvalues of m. See "eigen" for more info. - */ - Eval_eigenval = function() { if (EIG_check_arg() === 0) { push_symbol(EIGENVAL); @@ -7123,22 +8268,6 @@ return push(p2); }; - - /* eigenvec ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - m - - General description - ------------------- - Compute eigenvectors of m. See "eigen" for more info. - */ - Eval_eigenvec = function() { if (EIG_check_arg() === 0) { push_symbol(EIGENVEC); @@ -7166,15 +8295,15 @@ stop("eigen: argument is not a square matrix"); } EIG_N = p1.tensor.dim[0]; - for (i = o = 0, ref = EIG_N; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { - for (j = i1 = 0, ref1 = EIG_N; 0 <= ref1 ? i1 < ref1 : i1 > ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (i = o = 0, ref = EIG_N; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { + for (j = i1 = 0, ref1 = EIG_N; (0 <= ref1 ? i1 < ref1 : i1 > ref1); j = 0 <= ref1 ? ++i1 : --i1) { if (!isdouble(p1.tensor.elem[EIG_N * i + j])) { stop("eigen: matrix is not numerical"); } } } - for (i = j1 = 0, ref2 = EIG_N - 1; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { - for (j = l1 = ref3 = i + 1, ref4 = EIG_N; ref3 <= ref4 ? l1 < ref4 : l1 > ref4; j = ref3 <= ref4 ? ++l1 : --l1) { + for (i = j1 = 0, ref2 = EIG_N - 1; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { + for (j = l1 = ref3 = i + 1, ref4 = EIG_N; (ref3 <= ref4 ? l1 < ref4 : l1 > ref4); j = ref3 <= ref4 ? ++l1 : --l1) { if (Math.abs(p1.tensor.elem[EIG_N * i + j].d - p1.tensor.elem[EIG_N * j + i].d) > 1e-10) { stop("eigen: matrix is not symmetrical"); } @@ -7183,30 +8312,52 @@ return 1; }; + //----------------------------------------------------------------------------- + + // Input: p1 matrix + + // Output: p2 eigenvalues + + // p3 eigenvectors + + //----------------------------------------------------------------------------- eigen = function(op) { var i, i1, j, j1, l1, m1, n1, o, o1, q1, r1, ref, ref1, ref10, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, results, s1; i = 0; j = 0; - for (i = o = 0, ref = EIG_N * EIG_N; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { +// malloc working vars + + //EIG_yydd = (double *) malloc(n * n * sizeof (double)) + for (i = o = 0, ref = EIG_N * EIG_N; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { EIG_yydd[i] = 0.0; } - for (i = i1 = 0, ref1 = EIG_N * EIG_N; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { +//if (EIG_yydd == NULL) +// stop("malloc failure") + + //EIG_yyqq = (double *) malloc(n * n * sizeof (double)) + for (i = i1 = 0, ref1 = EIG_N * EIG_N; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { EIG_yyqq[i] = 0.0; } - for (i = j1 = 0, ref2 = EIG_N; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { +//if (EIG_yyqq == NULL) +// stop("malloc failure") + + // initialize D + for (i = j1 = 0, ref2 = EIG_N; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { EIG_yydd[EIG_N * i + i] = p1.tensor.elem[EIG_N * i + i].d; - for (j = l1 = ref3 = i + 1, ref4 = EIG_N; ref3 <= ref4 ? l1 < ref4 : l1 > ref4; j = ref3 <= ref4 ? ++l1 : --l1) { + for (j = l1 = ref3 = i + 1, ref4 = EIG_N; (ref3 <= ref4 ? l1 < ref4 : l1 > ref4); j = ref3 <= ref4 ? ++l1 : --l1) { EIG_yydd[EIG_N * i + j] = p1.tensor.elem[EIG_N * i + j].d; EIG_yydd[EIG_N * j + i] = p1.tensor.elem[EIG_N * i + j].d; } } - for (i = m1 = 0, ref5 = EIG_N; 0 <= ref5 ? m1 < ref5 : m1 > ref5; i = 0 <= ref5 ? ++m1 : --m1) { +// initialize Q + for (i = m1 = 0, ref5 = EIG_N; (0 <= ref5 ? m1 < ref5 : m1 > ref5); i = 0 <= ref5 ? ++m1 : --m1) { EIG_yyqq[EIG_N * i + i] = 1.0; - for (j = n1 = ref6 = i + 1, ref7 = EIG_N; ref6 <= ref7 ? n1 < ref7 : n1 > ref7; j = ref6 <= ref7 ? ++n1 : --n1) { + for (j = n1 = ref6 = i + 1, ref7 = EIG_N; (ref6 <= ref7 ? n1 < ref7 : n1 > ref7); j = ref6 <= ref7 ? ++n1 : --n1) { EIG_yyqq[EIG_N * i + j] = 0.0; EIG_yyqq[EIG_N * j + i] = 0.0; } } +// step up to 100 times for (i = o1 = 0; o1 < 100; i = ++o1) { if (step() === 0) { break; @@ -7215,27 +8366,29 @@ if (i === 100) { printstr("\nnote: eigen did not converge\n"); } + // p2 = D if (op === EIGEN || op === EIGENVAL) { push(p1); copy_tensor(); p2 = pop(); - for (i = q1 = 0, ref8 = EIG_N; 0 <= ref8 ? q1 < ref8 : q1 > ref8; i = 0 <= ref8 ? ++q1 : --q1) { - for (j = r1 = 0, ref9 = EIG_N; 0 <= ref9 ? r1 < ref9 : r1 > ref9; j = 0 <= ref9 ? ++r1 : --r1) { + for (i = q1 = 0, ref8 = EIG_N; (0 <= ref8 ? q1 < ref8 : q1 > ref8); i = 0 <= ref8 ? ++q1 : --q1) { + for (j = r1 = 0, ref9 = EIG_N; (0 <= ref9 ? r1 < ref9 : r1 > ref9); j = 0 <= ref9 ? ++r1 : --r1) { push_double(EIG_yydd[EIG_N * i + j]); p2.tensor.elem[EIG_N * i + j] = pop(); } } } + // p3 = Q if (op === EIGEN || op === EIGENVEC) { push(p1); copy_tensor(); p3 = pop(); results = []; - for (i = s1 = 0, ref10 = EIG_N; 0 <= ref10 ? s1 < ref10 : s1 > ref10; i = 0 <= ref10 ? ++s1 : --s1) { + for (i = s1 = 0, ref10 = EIG_N; (0 <= ref10 ? s1 < ref10 : s1 > ref10); i = 0 <= ref10 ? ++s1 : --s1) { results.push((function() { var ref11, results1, t1; results1 = []; - for (j = t1 = 0, ref11 = EIG_N; 0 <= ref11 ? t1 < ref11 : t1 > ref11; j = 0 <= ref11 ? ++t1 : --t1) { + for (j = t1 = 0, ref11 = EIG_N; (0 <= ref11 ? t1 < ref11 : t1 > ref11); j = 0 <= ref11 ? ++t1 : --t1) { push_double(EIG_yyqq[EIG_N * i + j]); results1.push(p3.tensor.elem[EIG_N * i + j] = pop()); } @@ -7246,13 +8399,212 @@ } }; + // free working vars + + //----------------------------------------------------------------------------- + + // Example: p = 1, q = 3 + + // c 0 s 0 + + // 0 1 0 0 + // G = + // -s 0 c 0 + + // 0 0 0 1 + + // The effect of multiplying G times A is... + + // row 1 of A = c (row 1 of A ) + s (row 3 of A ) + // n+1 n n + + // row 3 of A = c (row 3 of A ) - s (row 1 of A ) + // n+1 n n + + // In terms of components the overall effect is... + + // row 1 = c row 1 + s row 3 + + // A[1,1] = c A[1,1] + s A[3,1] + + // A[1,2] = c A[1,2] + s A[3,2] + + // A[1,3] = c A[1,3] + s A[3,3] + + // A[1,4] = c A[1,4] + s A[3,4] + + // row 3 = c row 3 - s row 1 + + // A[3,1] = c A[3,1] - s A[1,1] + + // A[3,2] = c A[3,2] - s A[1,2] + + // A[3,3] = c A[3,3] - s A[1,3] + + // A[3,4] = c A[3,4] - s A[1,4] + + // T + // The effect of multiplying A times G is... + + // col 1 of A = c (col 1 of A ) + s (col 3 of A ) + // n+1 n n + + // col 3 of A = c (col 3 of A ) - s (col 1 of A ) + // n+1 n n + + // In terms of components the overall effect is... + + // col 1 = c col 1 + s col 3 + + // A[1,1] = c A[1,1] + s A[1,3] + + // A[2,1] = c A[2,1] + s A[2,3] + + // A[3,1] = c A[3,1] + s A[3,3] + + // A[4,1] = c A[4,1] + s A[4,3] + + // col 3 = c col 3 - s col 1 + + // A[1,3] = c A[1,3] - s A[1,1] + + // A[2,3] = c A[2,3] - s A[2,1] + + // A[3,3] = c A[3,3] - s A[3,1] + + // A[4,3] = c A[4,3] - s A[4,1] + + // What we want to do is just compute the upper triangle of A since we + // know the lower triangle is identical. + + // In other words, we just want to update components A[i,j] where i < j. + + //----------------------------------------------------------------------------- + + // Example: p = 2, q = 5 + + // p q + + // j=1 j=2 j=3 j=4 j=5 j=6 + + // i=1 . A[1,2] . . A[1,5] . + + // p i=2 A[2,1] A[2,2] A[2,3] A[2,4] A[2,5] A[2,6] + + // i=3 . A[3,2] . . A[3,5] . + + // i=4 . A[4,2] . . A[4,5] . + + // q i=5 A[5,1] A[5,2] A[5,3] A[5,4] A[5,5] A[5,6] + + // i=6 . A[6,2] . . A[6,5] . + + //----------------------------------------------------------------------------- + + // This is what B = GA does: + + // row 2 = c row 2 + s row 5 + + // B[2,1] = c * A[2,1] + s * A[5,1] + // B[2,2] = c * A[2,2] + s * A[5,2] + // B[2,3] = c * A[2,3] + s * A[5,3] + // B[2,4] = c * A[2,4] + s * A[5,4] + // B[2,5] = c * A[2,5] + s * A[5,5] + // B[2,6] = c * A[2,6] + s * A[5,6] + + // row 5 = c row 5 - s row 2 + + // B[5,1] = c * A[5,1] + s * A[2,1] + // B[5,2] = c * A[5,2] + s * A[2,2] + // B[5,3] = c * A[5,3] + s * A[2,3] + // B[5,4] = c * A[5,4] + s * A[2,4] + // B[5,5] = c * A[5,5] + s * A[2,5] + // B[5,6] = c * A[5,6] + s * A[2,6] + + // T + // This is what BG does: + + // col 2 = c col 2 + s col 5 + + // B[1,2] = c * A[1,2] + s * A[1,5] + // B[2,2] = c * A[2,2] + s * A[2,5] + // B[3,2] = c * A[3,2] + s * A[3,5] + // B[4,2] = c * A[4,2] + s * A[4,5] + // B[5,2] = c * A[5,2] + s * A[5,5] + // B[6,2] = c * A[6,2] + s * A[6,5] + + // col 5 = c col 5 - s col 2 + + // B[1,5] = c * A[1,5] - s * A[1,2] + // B[2,5] = c * A[2,5] - s * A[2,2] + // B[3,5] = c * A[3,5] - s * A[3,2] + // B[4,5] = c * A[4,5] - s * A[4,2] + // B[5,5] = c * A[5,5] - s * A[5,2] + // B[6,5] = c * A[6,5] - s * A[6,2] + + //----------------------------------------------------------------------------- + + // Step 1: Just do upper triangle (i < j), B[2,5] = 0 + + // B[1,2] = c * A[1,2] + s * A[1,5] + + // B[2,3] = c * A[2,3] + s * A[5,3] + // B[2,4] = c * A[2,4] + s * A[5,4] + // B[2,6] = c * A[2,6] + s * A[5,6] + + // B[1,5] = c * A[1,5] - s * A[1,2] + // B[3,5] = c * A[3,5] - s * A[3,2] + // B[4,5] = c * A[4,5] - s * A[4,2] + + // B[5,6] = c * A[5,6] + s * A[2,6] + + //----------------------------------------------------------------------------- + + // Step 2: Transpose where i > j since A[i,j] == A[j,i] + + // B[1,2] = c * A[1,2] + s * A[1,5] + + // B[2,3] = c * A[2,3] + s * A[3,5] + // B[2,4] = c * A[2,4] + s * A[4,5] + // B[2,6] = c * A[2,6] + s * A[5,6] + + // B[1,5] = c * A[1,5] - s * A[1,2] + // B[3,5] = c * A[3,5] - s * A[2,3] + // B[4,5] = c * A[4,5] - s * A[2,4] + + // B[5,6] = c * A[5,6] + s * A[2,6] + + //----------------------------------------------------------------------------- + + // Step 3: Same as above except reorder + + // k < p (k = 1) + + // A[1,2] = c * A[1,2] + s * A[1,5] + // A[1,5] = c * A[1,5] - s * A[1,2] + + // p < k < q (k = 3..4) + + // A[2,3] = c * A[2,3] + s * A[3,5] + // A[3,5] = c * A[3,5] - s * A[2,3] + + // A[2,4] = c * A[2,4] + s * A[4,5] + // A[4,5] = c * A[4,5] - s * A[2,4] + + // q < k (k = 6) + + // A[2,6] = c * A[2,6] + s * A[5,6] + // A[5,6] = c * A[5,6] - s * A[2,6] + + //----------------------------------------------------------------------------- step = function() { var count, i, i1, j, o, ref, ref1, ref2; i = 0; j = 0; count = 0; - for (i = o = 0, ref = EIG_N - 1; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { - for (j = i1 = ref1 = i + 1, ref2 = EIG_N; ref1 <= ref2 ? i1 < ref2 : i1 > ref2; j = ref1 <= ref2 ? ++i1 : --i1) { +// for each upper triangle "off-diagonal" component do step2 + for (i = o = 0, ref = EIG_N - 1; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { + for (j = i1 = ref1 = i + 1, ref2 = EIG_N; (ref1 <= ref2 ? i1 < ref2 : i1 > ref2); j = ref1 <= ref2 ? ++i1 : --i1) { if (EIG_yydd[EIG_N * i + j] !== 0.0) { step2(i, j); count++; @@ -7271,6 +8623,9 @@ cc = 0.0; s = 0.0; ss = 0.0; + // compute c and s + + // from Numerical Recipes (except they have a_qq - a_pp) theta = 0.5 * (EIG_yydd[EIG_N * p + p] - EIG_yydd[EIG_N * q + q]) / EIG_yydd[EIG_N * p + q]; t = 1.0 / (Math.abs(theta) + Math.sqrt(theta * theta + 1.0)); if (theta < 0.0) { @@ -7278,19 +8633,28 @@ } c = 1.0 / Math.sqrt(t * t + 1.0); s = t * c; - for (k = o = 0, ref = EIG_N; 0 <= ref ? o < ref : o > ref; k = 0 <= ref ? ++o : --o) { +// D = GD + + // which means "add rows" + for (k = o = 0, ref = EIG_N; (0 <= ref ? o < ref : o > ref); k = 0 <= ref ? ++o : --o) { cc = EIG_yydd[EIG_N * p + k]; ss = EIG_yydd[EIG_N * q + k]; EIG_yydd[EIG_N * p + k] = c * cc + s * ss; EIG_yydd[EIG_N * q + k] = c * ss - s * cc; } - for (k = i1 = 0, ref1 = EIG_N; 0 <= ref1 ? i1 < ref1 : i1 > ref1; k = 0 <= ref1 ? ++i1 : --i1) { +// D = D transpose(G) + + // which means "add columns" + for (k = i1 = 0, ref1 = EIG_N; (0 <= ref1 ? i1 < ref1 : i1 > ref1); k = 0 <= ref1 ? ++i1 : --i1) { cc = EIG_yydd[EIG_N * k + p]; ss = EIG_yydd[EIG_N * k + q]; EIG_yydd[EIG_N * k + p] = c * cc + s * ss; EIG_yydd[EIG_N * k + q] = c * ss - s * cc; } - for (k = j1 = 0, ref2 = EIG_N; 0 <= ref2 ? j1 < ref2 : j1 > ref2; k = 0 <= ref2 ? ++j1 : --j1) { +// Q = GQ + + // which means "add rows" + for (k = j1 = 0, ref2 = EIG_N; (0 <= ref2 ? j1 < ref2 : j1 > ref2); k = 0 <= ref2 ? ++j1 : --j1) { cc = EIG_yyqq[EIG_N * p + k]; ss = EIG_yyqq[EIG_N * q + k]; EIG_yyqq[EIG_N * p + k] = c * cc + s * ss; @@ -7300,27 +8664,6 @@ return EIG_yydd[EIG_N * q + p] = 0.0; }; - - /* erf ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Authors - ------- - philippe.billet@noos.fr - - Parameters - ---------- - x - - General description - ------------------- - Error function erf(x). - erf(-x)=erf(x) - */ - Eval_erf = function() { push(cadr(p1)); Eval(); @@ -7359,6 +8702,15 @@ list(2); }; + //----------------------------------------------------------------------------- + + // Author : philippe.billet@noos.fr + + // erfc(x) + + // GW Added erfc() from Numerical Recipes in C + + //----------------------------------------------------------------------------- Eval_erfc = function() { push(cadr(p1)); Eval(); @@ -7389,6 +8741,7 @@ list(2); }; + // from Numerical Recipes in C erfc = function(x) { var ans, t, z; if (x === 0) { @@ -7407,6 +8760,11 @@ } }; + // Evaluate an expression, for example... + + // push(p1) + // Eval() + // p2 = pop() Eval = function() { var willEvaluateAsFloats; check_esc_flag(); @@ -7451,6 +8809,15 @@ Eval_sym = function() { var cycleString, i, o, positionIfSymbolAlreadyBeingEvaluated, ref, ref1; + // note that function calls are not processed here + // because, since they have an argument (at least an empty one) + // they are actually CONs, which is a branch of the + // switch before the one that calls this function + + // bare keyword? + // If it's a keyword, then we don't look + // at the binding array, because keywords + // are not redefinable. if (iskeyword(p1)) { push(p1); push(symbol(LAST)); @@ -7461,16 +8828,29 @@ push_double(Math.PI); return; } + // Evaluate symbol's binding p2 = get_binding(p1); if (DEBUG) { console.log("looked up: " + p1 + " which contains: " + p2); } push(p2); + // differently from standard Lisp, + // here the evaluation is not + // one-step only, rather it keeps evaluating + // "all the way" until a symbol is + // defined as itself. + // Uncomment these two lines to get Lisp + // behaviour (and break most tests) if (p1 !== p2) { + // detect recursive lookup of symbols, which would otherwise + // cause a stack overflow. + // Note that recursive functions will still work because + // as mentioned at the top, this method doesn't look + // up and evaluate function calls. positionIfSymbolAlreadyBeingEvaluated = chainOfUserSymbolsNotFunctionsBeingEvaluated.indexOf(p1); if (positionIfSymbolAlreadyBeingEvaluated !== -1) { cycleString = ""; - for (i = o = ref = positionIfSymbolAlreadyBeingEvaluated, ref1 = chainOfUserSymbolsNotFunctionsBeingEvaluated.length; ref <= ref1 ? o < ref1 : o > ref1; i = ref <= ref1 ? ++o : --o) { + for (i = o = ref = positionIfSymbolAlreadyBeingEvaluated, ref1 = chainOfUserSymbolsNotFunctionsBeingEvaluated.length; (ref <= ref1 ? o < ref1 : o > ref1); i = ref <= ref1 ? ++o : --o) { cycleString += chainOfUserSymbolsNotFunctionsBeingEvaluated[i].printname + " -> "; } cycleString += p1.printname; @@ -7486,6 +8866,15 @@ Eval_cons = function() { var cons_head; cons_head = car(p1); + // normally the cons_head is a symbol, + // but sometimes in the case of + // functions we don't have a symbol, + // we have to evaluate something to get to the + // symbol. For example if a function is inside + // a tensor, then we need to evaluate an index + // access first to get to the function. + // In those cases, we find an EVAL here, + // so we proceed to EVAL if (car(cons_head) === symbol(EVAL)) { Eval_user_function(); return; @@ -7618,6 +9007,10 @@ return Eval_floor(); case FOR: return Eval_for(); + // this is invoked only when we + // evaluate a function that is NOT being called + // e.g. when f is a function as we do + // g = f case FUNCTION: return Eval_function_reference(); case GAMMA: @@ -7646,6 +9039,7 @@ return Eval_isprime(); case LAGUERRE: return Eval_laguerre(); + // when LAPLACE then Eval_laplace() case LCM: return Eval_lcm(); case LEADING: @@ -7771,35 +9165,16 @@ return push(get_binding(cadr(p1))); }; - - /* check ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - p - - General description - ------------------- - Returns whether the predicate p is true/false or unknown: - 0 if false, 1 if true or remains unevaluated if unknown. - Note that if "check" is passed an assignment, it turns it into a test, - i.e. check(a = b) is turned into check(a==b) - so "a" is not assigned anything. - Like in many programming languages, "check" also gives truthyness/falsyness - for numeric values. In which case, "true" is returned for non-zero values. - Potential improvements: "check" can't evaluate strings yet. - */ - Eval_check = function() { var checkResult; + // check the argument checkResult = isZeroLikeOrNonZeroLikeOrUndetermined(cadr(p1)); if (checkResult == null) { + // returned null: unknown result + // leave the whole check unevalled return push(p1); } else { + // returned 1 or 0 return push_integer(checkResult); } }; @@ -7810,24 +9185,9 @@ return det(); }; - - /* dim ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - m,n - - General description - ------------------- - Returns the cardinality of the nth index of tensor "m". - */ - Eval_dim = function() { var n; + //int n push(cadr(p1)); Eval(); p2 = pop(); @@ -7839,7 +9199,7 @@ n = 1; } if (!istensor(p2)) { - return push_integer(1); + return push_integer(1); // dim of scalar is 1 } else if (n < 1 || n > p2.tensor.ndim) { return push(p1); } else { @@ -7853,22 +9213,6 @@ return divisors(); }; - - /* do ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - a,b,... - - General description - ------------------- - Evaluates each argument from left to right. Returns the result of the last argument. - */ - Eval_do = function() { var results; push(car(p1)); @@ -7893,6 +9237,7 @@ return dsolve(); }; + // for example, Eval(f,x,2) Eval_Eval = function() { push(cadr(p1)); Eval(); @@ -7908,6 +9253,8 @@ return Eval(); }; + // exp evaluation: it replaces itself with + // a POWER(E,something) node and evals that one Eval_exp = function() { push(cadr(p1)); Eval(); @@ -7958,6 +9305,9 @@ var h, orig, theTensor; h = tos; orig = p1; + + // look into the head of the list, + // when evaluated it should be a tensor p1 = cdr(p1); push(car(p1)); Eval(); @@ -7966,15 +9316,22 @@ stop("trying to access a scalar as a tensor"); } if (!istensor(theTensor)) { + // the tensor is not allocated yet, so + // leaving the expression unevalled moveTos(h); push(orig); return; } + // we examined the head of the list which + // was the tensor, now look into + // the indexes p1 = cdr(p1); while (iscons(p1)) { push(car(p1)); Eval(); if (!isintegerorintegerfloat(stack[tos - 1])) { + // index with something other than + // an integer moveTos(h); push(orig); return; @@ -8047,10 +9404,12 @@ return list(tos - h); }; + // quote definition Eval_quote = function() { return push(cadr(p1)); }; + // rank definition Eval_rank = function() { push(cadr(p1)); Eval(); @@ -8062,11 +9421,30 @@ } }; + // Evaluates the right side and assigns the + // result of the evaluation to the left side. + // It's called setq because it stands for "set quoted" from Lisp, + // see: + // http://stackoverflow.com/questions/869529/difference-between-set-setq-and-setf-in-common-lisp + // Note that this also takes case of assigning to a tensor + // element, which is something that setq wouldn't do + // in list, see comments further down below. + + // Example: + // f = x + // // f evaluates to x, so x is assigned to g really + // // rather than actually f being assigned to g + // g = f + // f = y + // g + // > x Eval_setq = function() { + // case of tensor if (caadr(p1) === symbol(INDEX)) { setq_indexed(); return; } + // case of function definition if (iscons(cadr(p1))) { define_user_function(); return; @@ -8078,14 +9456,52 @@ Eval(); p2 = pop(); set_binding(cadr(p1), p2); + // An assignment returns nothing. + // This is unlike most programming languages + // where an assignment does return the + // assigned value. + // TODO Could be changed. return push(symbol(NIL)); }; + // Here "setq" is a misnomer because + // setq wouldn't work in Lisp to set array elements + // since setq stands for "set quoted" and you wouldn't + // quote an array element access. + // You'd rather use setf, which is a macro that can + // assign a value to anything. + // (setf (aref YourArray 2) "blue") + // see + // http://stackoverflow.com/questions/18062016/common-lisp-how-to-set-an-element-in-a-2d-array + //----------------------------------------------------------------------------- + + // Example: a[1] = b + + // p1 *-------*-----------------------* + // | | | + // setq *-------*-------* b + // | | | + // index a 1 + + // cadadr(p1) -> a + + //----------------------------------------------------------------------------- setq_indexed = function() { var h; p4 = cadadr(p1); console.log("p4: " + p4); if (!issymbol(p4)) { + // this is likely to happen when one tries to + // do assignments like these + // 1[2] = 3 + // or + // f(x)[1] = 2 + // or + // [[1,2],[3,4]][5] = 6 + + // In other words, one can only do + // a straight assignment like + // existingMatrix[index] = something stop("indexed assignment: expected a symbol name"); } h = tos; @@ -8122,9 +9538,13 @@ push(cadr(p1)); Eval(); subst(); - return Eval(); + return Eval(); // normalize }; + + // always returns a matrix with rank 2 + // i.e. two dimensions, + // the passed parameter is the size Eval_unit = function() { var i, n, o, ref; i = 0; @@ -8144,7 +9564,7 @@ p1.tensor.ndim = 2; p1.tensor.dim[0] = n; p1.tensor.dim[1] = n; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p1.tensor.elem[n * i + i] = one; } check_tensor_dimensions(p1); @@ -8159,10 +9579,26 @@ return expanding = prev_expanding; }; + // like Eval() except "=" (assignment) is treated + // as "==" (equality test) + // This is because + // * this allows users to be lazy and just + // use "=" instead of "==" as per more common + // mathematical notation + // * in many places we don't expect an assignment + // e.g. we don't expect to test the zero-ness + // of an assignment or the truth value of + // an assignment + // Note that these are questionable assumptions + // as for example in most programming languages one + // can indeed test the value of an assignment (the + // value is just the evaluation of the right side) Eval_predicate = function() { save(); p1 = top(); if (car(p1) === symbol(SETQ)) { + // replace the assignment in the + // head with an equality test pop(); push_symbol(TESTEQ); push(cadr(p1)); @@ -8173,9 +9609,21 @@ return restore(); }; + // Partial fraction expansion + + // Example + + // expand(1/(x^3+x^2),x) + + // 1 1 1 + // ---- - --- + ------- + // 2 x x + 1 + // x Eval_expand = function() { + // 1st arg push(cadr(p1)); Eval(); + // 2nd arg push(caddr(p1)); Eval(); p2 = pop(); @@ -8187,6 +9635,14 @@ return expand(); }; + //define A p2 + //define B p3 + //define C p4 + //define F p5 + //define P p6 + //define Q p7 + //define T p8 + //define X p9 expand = function() { var prev_expanding; save(); @@ -8197,6 +9653,7 @@ restore(); return; } + // if sum of terms then sum over the expansion of each term if (car(p5) === symbol(ADD)) { push_integer(0); p1 = cdr(p5); @@ -8210,43 +9667,56 @@ restore(); return; } + // B = numerator push(p5); numerator(); p3 = pop(); + // A = denominator push(p5); denominator(); p2 = pop(); remove_negative_exponents(); + // Q = quotient push(p3); push(p2); push(p9); + // if the denominator is one then always bail out + // also bail out if the denominator is not one but + // it's not anything recognizable as a polynomial. if (isone(p3) || isone(p2)) { if (!ispolyexpandedform(p2, p9) || isone(p2)) { pop(); pop(); pop(); push(p5); + // p5 is the original input, leave unchanged restore(); return; } } divpoly(); p7 = pop(); + // remainder B = B - A * Q push(p3); push(p2); push(p7); multiply(); subtract(); p3 = pop(); + // if the remainder is zero then we're done if (isZeroAtomOrTensor(p3)) { push(p7); restore(); return; } + // A = factor(A) + + //console.log("expand - to be factored: " + p2) push(p2); push(p9); factorpoly(); p2 = pop(); + //console.log("expand - factored to: " + p2) expand_get_C(); expand_get_B(); expand_get_A(); @@ -8281,7 +9751,7 @@ push(p5); copy_tensor(); p5 = pop(); - for (i = o = 0, ref = p5.tensor.nelem; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = p5.tensor.nelem; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push(p5.tensor.elem[i]); push(p9); expand(); @@ -8301,8 +9771,9 @@ factors(p2); factors(p3); n = tos - h; + // find the smallest exponent j = 0; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p1 = stack[h + i]; if (car(p1) !== symbol(POWER)) { continue; @@ -8323,12 +9794,14 @@ if (j === 0) { return; } + // A = A / X^j push(p2); push(p9); push_integer(-j); power(); multiply(); p2 = pop(); + // B = B / X^j push(p3); push(p9); push_integer(-j); @@ -8337,12 +9810,68 @@ return p3 = pop(); }; + // Returns the expansion coefficient matrix C. + + // Example: + + // B 1 + // --- = ----------- + // A 2 + // x (x + 1) + + // We have + + // B Y1 Y2 Y3 + // --- = ---- + ---- + ------- + // A 2 x x + 1 + // x + + // Our task is to solve for the unknowns Y1, Y2, and Y3. + + // Multiplying both sides by A yields + + // AY1 AY2 AY3 + // B = ----- + ----- + ------- + // 2 x x + 1 + // x + + // Let + + // A A A + // W1 = ---- W2 = --- W3 = ------- + // 2 x x + 1 + // x + + // Then the coefficient matrix C is + + // coeff(W1,x,0) coeff(W2,x,0) coeff(W3,x,0) + + // C = coeff(W1,x,1) coeff(W2,x,1) coeff(W3,x,1) + + // coeff(W1,x,2) coeff(W2,x,2) coeff(W3,x,2) + + // It follows that + + // coeff(B,x,0) Y1 + + // coeff(B,x,1) = C Y2 + + // coeff(B,x,2) = Y3 + + // Hence + + // Y1 coeff(B,x,0) + // -1 + // Y2 = C coeff(B,x,1) + + // Y3 coeff(B,x,2) expand_get_C = function() { var a, h, i, i1, j, n, o, prev_expanding, ref, ref1; h = 0; i = 0; j = 0; n = 0; + //U **a h = tos; if (car(p2) === symbol(MULTIPLY)) { p1 = cdr(p2); @@ -8365,8 +9894,8 @@ p4.tensor.dim[0] = n; p4.tensor.dim[1] = n; a = h; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { - for (j = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { + for (j = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); j = 0 <= ref1 ? ++i1 : --i1) { push(stack[a + j]); push(p9); push_integer(i); @@ -8383,6 +9912,70 @@ return moveTos(tos - n); }; + // The following table shows the push order for simple roots, repeated roots, + // and inrreducible factors. + + // Factor F Push 1st Push 2nd Push 3rd Push 4th + + // A + // x --- + // x + + // 2 A A + // x ---- --- + // 2 x + // x + + // A + // x + 1 ------- + // x + 1 + + // 2 A A + // (x + 1) ---------- ------- + // 2 x + 1 + // (x + 1) + + // 2 A Ax + // x + x + 1 ------------ ------------ + // 2 2 + // x + x + 1 x + x + 1 + + // 2 2 A Ax A Ax + // (x + x + 1) --------------- --------------- ------------ ------------ + // 2 2 2 2 2 2 + // (x + x + 1) (x + x + 1) x + x + 1 x + x + 1 + + // For T = A/F and F = P^N we have + + // Factor F Push 1st Push 2nd Push 3rd Push 4th + + // x T + + // 2 + // x T TP + + // x + 1 T + + // 2 + // (x + 1) T TP + + // 2 + // x + x + 1 T TX + + // 2 2 + // (x + x + 1) T TX TP TPX + + // Hence we want to push in the order + + // T * (P ^ i) * (X ^ j) + + // for all i, j such that + + // i = 0, 1, ..., N - 1 + + // j = 0, 1, ..., deg(P) - 1 + + // where index j runs first. expand_get_CF = function() { var d, i, j, n, o, prev_expanding, ref, results; d = 0; @@ -8409,11 +10002,11 @@ degree(); d = pop_integer(); results = []; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { results.push((function() { var i1, ref1, results1; results1 = []; - for (j = i1 = 0, ref1 = d; 0 <= ref1 ? i1 < ref1 : i1 > ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (j = i1 = 0, ref1 = d; (0 <= ref1 ? i1 < ref1 : i1 > ref1); j = 0 <= ref1 ? ++i1 : --i1) { push(p8); push(p6); push_integer(i); @@ -8436,6 +10029,7 @@ return results; }; + // Returns T = A/F where F is a factor of A. trivial_divide = function() { var h; h = 0; @@ -8445,7 +10039,7 @@ while (iscons(p0)) { if (!equal(car(p0), p5)) { push(car(p0)); - Eval(); + Eval(); // force expansion of (x+1)^2, f.e. } p0 = cdr(p0); } @@ -8456,6 +10050,7 @@ return p8 = pop(); }; + // Returns the expansion coefficient vector B. expand_get_B = function() { var i, n, o, prev_expanding, ref; i = 0; @@ -8467,7 +10062,7 @@ p8 = alloc_tensor(n); p8.tensor.ndim = 1; p8.tensor.dim[0] = n; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push(p3); push(p9); push_integer(i); @@ -8483,6 +10078,7 @@ return p3 = p8; }; + // Returns the expansion fractions in A. expand_get_A = function() { var h, i, n, o, ref; h = 0; @@ -8510,7 +10106,7 @@ p8 = alloc_tensor(n); p8.tensor.ndim = 1; p8.tensor.dim[0] = n; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p8.tensor.elem[i] = stack[h + i]; } moveTos(h); @@ -8536,11 +10132,11 @@ degree(); d = pop_integer(); results = []; - for (i = o = ref = n; ref <= 0 ? o < 0 : o > 0; i = ref <= 0 ? ++o : --o) { + for (i = o = ref = n; (ref <= 0 ? o < 0 : o > 0); i = ref <= 0 ? ++o : --o) { results.push((function() { var i1, ref1, results1; results1 = []; - for (j = i1 = 0, ref1 = d; 0 <= ref1 ? i1 < ref1 : i1 > ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (j = i1 = 0, ref1 = d; (0 <= ref1 ? i1 < ref1 : i1 > ref1); j = 0 <= ref1 ? ++i1 : --i1) { push(p5); push_integer(i); power(); @@ -8556,6 +10152,7 @@ return results; }; + // Do the exponential cosine function. Eval_expcos = function() { push(cadr(p1)); Eval(); @@ -8582,6 +10179,7 @@ return restore(); }; + // Do the exponential sine function. Eval_expsin = function() { push(cadr(p1)); Eval(); @@ -8612,6 +10210,7 @@ return restore(); }; + // factor a polynomial or integer Eval_factor = function() { var results; push(cadr(p1)); @@ -8625,6 +10224,7 @@ push(p2); } factor(); + // more factoring? p1 = cdddr(p1); results = []; while (iscons(p1)) { @@ -8684,7 +10284,7 @@ p1 = pop(); if (isinteger(p1)) { push(p1); - factor_number(); + factor_number(); // see pollard.cpp } else { push(p1); push(p2); @@ -8693,6 +10293,7 @@ return restore(); }; + // for factoring small integers (2^32 or less) factor_small_number = function() { var d, expo, i, n, o, ref; i = 0; @@ -8704,7 +10305,7 @@ if (n < 0) { n = -n; } - for (i = o = 0, ref = MAXPRIMETAB; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = MAXPRIMETAB; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { d = primetab[i]; if (d > n / d) { break; @@ -8744,6 +10345,25 @@ return restore(); }; + // simplification rules for factorials (m < n) + + // (e + 1) * factorial(e) -> factorial(e + 1) + + // factorial(e) / e -> factorial(e - 1) + + // e / factorial(e) -> 1 / factorial(e - 1) + + // factorial(e + n) + // ---------------- -> (e + m + 1)(e + m + 2)...(e + n) + // factorial(e + m) + + // factorial(e + m) 1 + // ---------------- -> -------------------------------- + // factorial(e + n) (e + m + 1)(e + m + 2)...(e + n) + + // this function is not actually used, but + // all these simplifications + // do happen automatically via simplify simplifyfactorials = function() { var x; x = 0; @@ -8788,11 +10408,11 @@ p1 = cdr(p1); n++; } - for (i = o = 0, ref = n - 1; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n - 1; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { if (stack[s + i] === symbol(NIL)) { continue; } - for (j = i1 = ref1 = i + 1, ref2 = n; ref1 <= ref2 ? i1 < ref2 : i1 > ref2; j = ref1 <= ref2 ? ++i1 : --i1) { + for (j = i1 = ref1 = i + 1, ref2 = n; (ref1 <= ref2 ? i1 < ref2 : i1 > ref2); j = ref1 <= ref2 ? ++i1 : --i1) { if (stack[s + j] === symbol(NIL)) { continue; } @@ -8800,7 +10420,7 @@ } } push(one); - for (i = j1 = 0, ref3 = n; 0 <= ref3 ? j1 < ref3 : j1 > ref3; i = 0 <= ref3 ? ++j1 : --j1) { + for (i = j1 = 0, ref3 = n; (0 <= ref3 ? j1 < ref3 : j1 > ref3); i = 0 <= ref3 ? ++j1 : --j1) { if (stack[s + i] === symbol(NIL)) { continue; } @@ -8831,6 +10451,7 @@ p4 = one; } if (isfactorial(p1) && isfactorial(p2)) { + // Determine if the powers cancel. push(p3); push(p4); add(); @@ -8839,6 +10460,9 @@ if (n !== 0) { return; } + // Find the difference between the two factorial args. + + // For example, the difference between (a + 2)! and a! is 2. push(cadr(p1)); push(cadr(p2)); subtract(); @@ -8857,7 +10481,7 @@ p4 = p5; } push(one); - for (i = o = 1, ref = n; 1 <= ref ? o <= ref : o >= ref; i = 1 <= ref ? ++o : --o) { + for (i = o = 1, ref = n; (1 <= ref ? o <= ref : o >= ref); i = 1 <= ref ? ++o : --o) { push(cadr(p2)); push_integer(i); add(); @@ -8870,6 +10494,16 @@ } }; + // Factor a polynomial + + //define POLY p1 + //define X p2 + //define Z p3 + //define A p4 + //define B p5 + //define Q p6 + //define RESULT p7 + //define FACTOR p8 polycoeff = 0; factpoly_expo = 0; @@ -8899,6 +10533,15 @@ return restore(); }; + //----------------------------------------------------------------------------- + + // Input: tos-2 true polynomial + + // tos-1 free variable + + // Output: factored polynomial on stack + + //----------------------------------------------------------------------------- yyfactorpoly = function() { var checkingTheDivision, dividend, foundComplexRoot, foundRealRoot, h, i, i1, j1, l1, o, prev_expanding, previousFactorisation, ref, ref1, ref2, ref3, remainingPoly, whichRootsAreWeFinding; h = 0; @@ -8915,6 +10558,7 @@ push(p2); factpoly_expo = coeff() - 1; rationalize_coefficients(h); + // for univariate polynomials we could do factpoly_expo > 1 whichRootsAreWeFinding = "real"; remainingPoly = null; while (factpoly_expo > 0) { @@ -8924,6 +10568,7 @@ push_integer(0); p5 = pop(); } else { + //console.log("trying to find a " + whichRootsAreWeFinding + " root") if (whichRootsAreWeFinding === "real") { foundRealRoot = get_factor_from_real_root(); } else if (whichRootsAreWeFinding === "complex") { @@ -8935,16 +10580,19 @@ whichRootsAreWeFinding = "complex"; continue; } else { - push(p4); - push(p2); + // build the 1-degree polynomial out of the + // real solution that was just found. + push(p4); // A + push(p2); // x multiply(); - push(p5); + push(p5); // B add(); p8 = pop(); if (DEBUG) { console.log("success\nFACTOR=" + p8); } - + // factor out negative sign (not req'd because p4 > 1) + //if 0 /* if (isnegativeterm(p4)) push(p8) @@ -8953,19 +10601,30 @@ push(p7) negate_noexpand() p7 = pop() - */ + */ + //endif + + // p7 is the part of the polynomial that was factored so far, + // add the newly found factor to it. Note that we are not actually + // multiplying the polynomials fully, we are just leaving them + // expressed as (P1)*(P2), we are not expanding the product. push(p7); push(p8); multiply_noexpand(); p7 = pop(); + // ok now on stack we have the coefficients of the + // remaining part of the polynomial still to factor. + // Divide it by the newly-found factor so that + // the stack then contains the coefficients of the + // polynomial part still left to factor. yydivpoly(); while (factpoly_expo && isZeroAtomOrTensor(stack[polycoeff + factpoly_expo])) { factpoly_expo--; } push(zero); - for (i = o = 0, ref = factpoly_expo; 0 <= ref ? o <= ref : o >= ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = factpoly_expo; (0 <= ref ? o <= ref : o >= ref); i = 0 <= ref ? ++o : --o) { push(stack[polycoeff + i]); - push(p2); + push(p2); // the free variable push_integer(i); power(); multiply(); @@ -8973,23 +10632,32 @@ } remainingPoly = pop(); } + //console.log("real branch remainingPoly: " + remainingPoly) } else if (whichRootsAreWeFinding === "complex") { if (foundComplexRoot === 0) { break; } else { - push(p4); - push(p2); + // build the 2-degree polynomial out of the + // real solution that was just found. + push(p4); // A + push(p2); // x subtract(); - push(p4); + //console.log("first factor: " + stack[tos-1].toString()) + push(p4); // A conjugate(); - push(p2); + push(p2); // x subtract(); + //console.log("second factor: " + stack[tos-1].toString()) multiply(); + //if (factpoly_expo > 0 && isnegativeterm(stack[polycoeff+factpoly_expo])) + // negate() + // negate_noexpand() p8 = pop(); if (DEBUG) { console.log("success\nFACTOR=" + p8); } - + // factor out negative sign (not req'd because p4 > 1) + //if 0 /* if (isnegativeterm(p4)) push(p8) @@ -8998,18 +10666,25 @@ push(p7) negate_noexpand() p7 = pop() - */ + */ + //endif + + // p7 is the part of the polynomial that was factored so far, + // add the newly found factor to it. Note that we are not actually + // multiplying the polynomials fully, we are just leaving them + // expressed as (P1)*(P2), we are not expanding the product. push(p7); previousFactorisation = pop(); + //console.log("previousFactorisation: " + previousFactorisation) push(p7); push(p8); multiply_noexpand(); p7 = pop(); if (remainingPoly == null) { push(zero); - for (i = i1 = 0, ref1 = factpoly_expo; 0 <= ref1 ? i1 <= ref1 : i1 >= ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = factpoly_expo; (0 <= ref1 ? i1 <= ref1 : i1 >= ref1); i = 0 <= ref1 ? ++i1 : --i1) { push(stack[polycoeff + i]); - push(p2); + push(p2); // the free variable push_integer(i); power(); multiply(); @@ -9017,17 +10692,25 @@ } remainingPoly = pop(); } + //console.log("original polynomial (dividend): " + remainingPoly) dividend = remainingPoly; + //push(dividend) + //degree() + //startingDegree = pop() push(dividend); - push(p8); - push(p2); + //console.log("dividing " + stack[tos-1].toString() + " by " + p8) + push(p8); // divisor + push(p2); // X divpoly(); remainingPoly = pop(); push(remainingPoly); - push(p8); + push(p8); // divisor multiply(); checkingTheDivision = pop(); if (!equal(checkingTheDivision, dividend)) { + //push(dividend) + //gcd_sum() + //console.log("gcd top of stack: " + stack[tos-1].toString()) if (DEBUG) { console.log("we found a polynomial based on complex root and its conj but it doesn't divide the poly, quitting"); } @@ -9047,17 +10730,21 @@ restore(); return; } - - /* - if compare_numbers(startingDegree, remainingDegree) - * ok even if we found a complex root that - * together with the conjugate generates a poly in Z, - * that doesn't mean that the division would end up in Z. - * Example: 1+x^2+x^4+x^6 has +i and -i as one of its roots - * so a factor is 1+x^2 ( = (x+i)*(x-i)) - * BUT - */ - for (i = j1 = 0, ref2 = factpoly_expo; 0 <= ref2 ? j1 <= ref2 : j1 >= ref2; i = 0 <= ref2 ? ++j1 : --j1) { +//console.log("result: (still to be factored) " + remainingPoly) + + //push(remainingPoly) +//degree() +//remainingDegree = pop() +/* +if compare_numbers(startingDegree, remainingDegree) + * ok even if we found a complex root that + * together with the conjugate generates a poly in Z, + * that doesn't mean that the division would end up in Z. + * Example: 1+x^2+x^4+x^6 has +i and -i as one of its roots + * so a factor is 1+x^2 ( = (x+i)*(x-i)) + * BUT + */ + for (i = j1 = 0, ref2 = factpoly_expo; (0 <= ref2 ? j1 <= ref2 : j1 >= ref2); i = 0 <= ref2 ? ++j1 : --j1) { pop(); } push(remainingPoly); @@ -9067,10 +10754,13 @@ } } } + //console.log("factpoly_expo: " + factpoly_expo) + + // build the remaining unfactored part of the polynomial push(zero); - for (i = l1 = 0, ref3 = factpoly_expo; 0 <= ref3 ? l1 <= ref3 : l1 >= ref3; i = 0 <= ref3 ? ++l1 : --l1) { + for (i = l1 = 0, ref3 = factpoly_expo; (0 <= ref3 ? l1 <= ref3 : l1 >= ref3); i = 0 <= ref3 ? ++l1 : --l1) { push(stack[polycoeff + i]); - push(p2); + push(p2); // the free variable push_integer(i); power(); multiply(); @@ -9086,9 +10776,16 @@ yycondense(); expanding = prev_expanding; p1 = pop(); + //console.log("new poly with extracted common factor: " + p1) + //debugger + + // factor out negative sign if (factpoly_expo > 0 && isnegativeterm(stack[polycoeff + factpoly_expo])) { push(p1); + //prev_expanding = expanding + //expanding = 1 negate(); + //expanding = prev_expanding p1 = pop(); push(p7); negate_noexpand(); @@ -9109,20 +10806,23 @@ rationalize_coefficients = function(h) { var i, i1, o, ref, ref1, ref2, ref3; i = 0; + // LCM of all polynomial coefficients p7 = one; - for (i = o = ref = h, ref1 = tos; ref <= ref1 ? o < ref1 : o > ref1; i = ref <= ref1 ? ++o : --o) { + for (i = o = ref = h, ref1 = tos; (ref <= ref1 ? o < ref1 : o > ref1); i = ref <= ref1 ? ++o : --o) { push(stack[i]); denominator(); push(p7); lcm(); p7 = pop(); } - for (i = i1 = ref2 = h, ref3 = tos; ref2 <= ref3 ? i1 < ref3 : i1 > ref3; i = ref2 <= ref3 ? ++i1 : --i1) { +// multiply each coefficient by RESULT + for (i = i1 = ref2 = h, ref3 = tos; (ref2 <= ref3 ? i1 < ref3 : i1 > ref3); i = ref2 <= ref3 ? ++i1 : --i1) { push(p7); push(stack[i]); multiply(); stack[i] = pop(); } + // reciprocate RESULT push(p7); reciprocate(); p7 = pop(); @@ -9131,6 +10831,7 @@ } }; + //console.log print_list(p7) get_factor_from_real_root = function() { var a0, an, h, i, i1, j, j1, l1, m1, na0, nan, o, ref, ref1, ref2, ref3, ref4, rootsTries_i, rootsTries_j; i = 0; @@ -9142,7 +10843,7 @@ nan = 0; if (DEBUG) { push(zero); - for (i = o = 0, ref = factpoly_expo; 0 <= ref ? o <= ref : o >= ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = factpoly_expo; (0 <= ref ? o <= ref : o >= ref); i = 0 <= ref ? ++o : --o) { push(stack[polycoeff + i]); push(p2); push_integer(i); @@ -9164,16 +10865,18 @@ na0 = tos - a0; if (DEBUG) { console.log("divisors of base term"); - for (i = i1 = 0, ref1 = na0; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = na0; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { console.log(", " + stack[a0 + i]); } console.log("divisors of leading term"); - for (i = j1 = 0, ref2 = nan; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = nan; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { console.log(", " + stack[an + i]); } } - for (rootsTries_i = l1 = 0, ref3 = nan; 0 <= ref3 ? l1 < ref3 : l1 > ref3; rootsTries_i = 0 <= ref3 ? ++l1 : --l1) { - for (rootsTries_j = m1 = 0, ref4 = na0; 0 <= ref4 ? m1 < ref4 : m1 > ref4; rootsTries_j = 0 <= ref4 ? ++m1 : --m1) { +// try roots + for (rootsTries_i = l1 = 0, ref3 = nan; (0 <= ref3 ? l1 < ref3 : l1 > ref3); rootsTries_i = 0 <= ref3 ? ++l1 : --l1) { + for (rootsTries_j = m1 = 0, ref4 = na0; (0 <= ref4 ? m1 < ref4 : m1 > ref4); rootsTries_j = 0 <= ref4 ? ++m1 : --m1) { + //if DEBUG then console.log "nan: " + nan + " na0: " + na0 + " i: " + rootsTries_i + " j: " + rootsTries_j p4 = stack[an + rootsTries_i]; p5 = stack[a0 + rootsTries_j]; push(p5); @@ -9249,6 +10952,8 @@ } h = tos; an = tos; + // trying -1^(2/3) which generates a polynomial in Z + // generates x^2 + 2x + 1 push_integer(-1); push_rational(2, 3); power(); @@ -9271,6 +10976,9 @@ } return 1; } + // trying 1^(2/3) which generates a polynomial in Z + // http://www.wolframalpha.com/input/?i=(1)%5E(2%2F3) + // generates x^2 - 2x + 1 push_integer(1); push_rational(2, 3); power(); @@ -9293,6 +11001,8 @@ } return 1; } +// trying some simple complex numbers. All of these +// generate polynomials in Z for (rootsTries_i = o = -10; o <= 10; rootsTries_i = ++o) { for (rootsTries_j = i1 = 1; i1 <= 5; rootsTries_j = ++i1) { push_integer(rootsTries_i); @@ -9302,10 +11012,12 @@ add(); rect(); p4 = pop(); + //console.log("complex root finding: trying simple complex combination: " + p4) push(p4); p3 = pop(); push(p3); Evalpoly(); + //console.log("complex root finding result: " + p6) if (isZeroAtomOrTensor(p6)) { moveTos(h); if (DEBUG) { @@ -9322,11 +11034,26 @@ return 0; }; + //----------------------------------------------------------------------------- + + // Divide a polynomial by Ax+B + + // Input: on stack: polycoeff Dividend coefficients + + // factpoly_expo Degree of dividend + + // A (p4) As above + + // B (p5) As above + + // Output: on stack: polycoeff Contains quotient coefficients + + //----------------------------------------------------------------------------- yydivpoly = function() { var i, o, ref; i = 0; p6 = zero; - for (i = o = ref = factpoly_expo; ref <= 0 ? o < 0 : o > 0; i = ref <= 0 ? ++o : --o) { + for (i = o = ref = factpoly_expo; (ref <= 0 ? o < 0 : o > 0); i = ref <= 0 ? ++o : --o) { push(stack[polycoeff + i]); stack[polycoeff + i] = p6; push(p4); @@ -9345,11 +11072,12 @@ } }; + //console.log print_list(p6) Evalpoly = function() { var i, o, ref; i = 0; push(zero); - for (i = o = ref = factpoly_expo; ref <= 0 ? o <= 0 : o >= 0; i = ref <= 0 ? ++o : --o) { + for (i = o = ref = factpoly_expo; (ref <= 0 ? o <= 0 : o >= 0); i = ref <= 0 ? ++o : --o) { push(p3); multiply(); push(stack[polycoeff + i]); @@ -9362,6 +11090,24 @@ return p6 = pop(); }; + // Push expression factors onto the stack. For example... + + // Input + + // 2 + // 3x + 2x + 1 + + // Output on stack + + // [ 3 ] + // [ x^2 ] + // [ 2 ] + // [ x ] + // [ 1 ] + + // but not necessarily in that order. Returns the number of factors. + + // Local U *p is OK here because no functional path to garbage collector. factors = function(p) { var h; h = tos; @@ -9377,29 +11123,21 @@ return tos - h; }; + // Local U *p is OK here because no functional path to garbage collector. push_term_factors = function(p) { var results; if (car(p) === symbol(MULTIPLY)) { p = cdr(p); results = []; while (iscons(p)) { - push(car(p)); - results.push(p = cdr(p)); - } - return results; - } else { - return push(p); - } - }; - - - /* - Remove terms that involve a given symbol or expression. For example... - - filter(x^2 + x + 1, x) => 1 - - filter(x^2 + x + 1, x^2) => x + 1 - */ + push(car(p)); + results.push(p = cdr(p)); + } + return results; + } else { + return push(p); + } + }; Eval_filter = function() { var results; @@ -9417,16 +11155,6 @@ return results; }; - - /* - For example... - - push(F) - push(X) - filter() - F = pop() - */ - filter = function() { save(); p2 = pop(); @@ -9469,10 +11197,10 @@ n = p1.tensor.nelem; p3 = alloc_tensor(n); p3.tensor.ndim = p1.tensor.ndim; - for (i = o = 0, ref = p1.tensor.ndim; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = p1.tensor.ndim; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p3.tensor.dim[i] = p1.tensor.dim[i]; } - for (i = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { push(p1.tensor.elem[i]); push(p2); filter(); @@ -9512,6 +11240,9 @@ zzfloat = function() { save(); evaluatingAsFloats++; + //p1 = pop() + //push(cadr(p1)) + //push(p1) Eval(); yyfloat(); Eval(); @@ -9519,6 +11250,12 @@ return restore(); }; + // zzfloat doesn't necessarily result in a double + // , for example if there are variables. But + // in many of the tests there should be indeed + // a float, this line comes handy to highlight + // when that doesn't happen for those tests. + //checkFloatHasWorkedOutCompletely(stack[tos-1]) yyfloat = function() { var h, i, o, ref; i = 0; @@ -9538,7 +11275,7 @@ push(p1); copy_tensor(); p1 = pop(); - for (i = o = 0, ref = p1.tensor.nelem; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = p1.tensor.nelem; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push(p1.tensor.elem[i]); yyfloat(); p1.tensor.elem[i] = pop(); @@ -9600,21 +11337,25 @@ } }; - + // 'for' function /* x=0 y=2 for(do(x=sqrt(2+x),y=2*y/x),k,1,9) float(y) - + X: k B: 1...9 - + 1st parameter is the body 2nd parameter is the variable to loop with 3rd and 4th are the limits - */ + */ + //define A p3 + //define B p4 + //define I p5 + //define X p6 Eval_for = function() { var i, j, k, loopingVariable, o, ref, ref1; i = 0; @@ -9638,8 +11379,10 @@ push(p1); return; } + // remember contents of the index + // variable so we can put it back after the loop p4 = get_binding(loopingVariable); - for (i = o = ref = j, ref1 = k; ref <= ref1 ? o <= ref1 : o >= ref1; i = ref <= ref1 ? ++o : --o) { + for (i = o = ref = j, ref1 = k; (ref <= ref1 ? o <= ref1 : o >= ref1); i = ref <= ref1 ? ++o : --o) { push_integer(i); p5 = pop(); set_binding(loopingVariable, p5); @@ -9647,10 +11390,19 @@ Eval(); pop(); } + // put back the index variable to original content set_binding(loopingVariable, p4); + // return value return push_symbol(NIL); }; + //----------------------------------------------------------------------------- + + // Author : philippe.billet@noos.fr + + // Gamma function gamma(x) + + //----------------------------------------------------------------------------- Eval_gamma = function() { push(cadr(p1)); Eval(); @@ -9664,6 +11416,7 @@ }; gammaf = function() { + // double d p1 = pop(); if (isrational(p1) && MEQUAL(p1.q.a, 1) && MEQUAL(p1.q.b, 2)) { if (evaluatingAsFloats) { @@ -9687,6 +11440,12 @@ multiply(); return; } + + // if (p1->k == DOUBLE) { + // d = exp(lgamma(p1.d)) + // push_double(d) + // return + // } if (isnegativeterm(p1)) { if (evaluatingAsFloats) { push_double(Math.PI); @@ -9744,6 +11503,10 @@ } }; + // Greatest common denominator + // can also be run on polynomials, however + // it works only on the integers and it works + // by factoring the polynomials (not Euclidean algorithm) Eval_gcd = function() { var results; p1 = cdr(p1); @@ -9826,6 +11589,7 @@ }; gcd_polys = function(polyVar) { + // gcd of factors push(p1); push(polyVar); factorpoly(); @@ -9843,7 +11607,21 @@ if (DEBUG) { console.log("p2:" + p2.toString()); } + // In case one of two polynomials can be factored, + // (and only in that case), then + // we'll need to run gcd_factors on the two polynomials. + // (In case neither of them can be factored there is no gcd). + // However, gcd_factors expects two _products_ , and + // in case _one_ of the polynomials can't be factored it will look + // like a sum instead of a product. + // So, we'll have to make that sum to look like a factor: + // let's just turn it into a product with 1. + + // in case one of the two polys has been factored... if (car(p1) === symbol(MULTIPLY) || car(p2) === symbol(MULTIPLY)) { + // then make sure that if one of them is a single + // factor, we take the sum and wrap it into a + // multiplication by 1 if (car(p1) !== symbol(MULTIPLY)) { push_symbol(MULTIPLY); push(p1); @@ -9888,14 +11666,14 @@ gcd_powers_with_same_base = function() { if (car(p1) === symbol(POWER)) { - p3 = caddr(p1); - p1 = cadr(p1); + p3 = caddr(p1); // exponent + p1 = cadr(p1); // base } else { p3 = one; } if (car(p2) === symbol(POWER)) { - p4 = caddr(p2); - p2 = cadr(p2); + p4 = caddr(p2); // exponent + p2 = cadr(p2); // base } else { p4 = one; } @@ -9903,6 +11681,7 @@ push(one); return; } + // are both exponents numerical? if (isNumericAtom(p3) && isNumericAtom(p4)) { push(p1); if (lessp(p3, p4)) { @@ -9913,12 +11692,14 @@ power(); return; } + // are the exponents multiples of eah other? push(p3); push(p4); divide(); p5 = pop(); if (isNumericAtom(p5)) { push(p1); + // choose the smallest exponent if (car(p3) === symbol(MULTIPLY) && isNumericAtom(cadr(p3))) { p5 = cadr(p3); } else { @@ -9945,6 +11726,7 @@ push(one); return; } + // can't be equal because of test near beginning push(p1); if (isnegativenumber(p5)) { push(p3); @@ -9954,6 +11736,7 @@ return power(); }; + // in this case gcd is used as a composite function, i.e. gcd(gcd(gcd... gcd_sum_sum = function() { if (length(p1) !== length(p2)) { push(one); @@ -10040,6 +11823,7 @@ return results; }; + // Guess which symbol to use for derivative, integral, etc. guess = function() { var p; p = pop(); @@ -10059,12 +11843,30 @@ } }; + //----------------------------------------------------------------------------- + + // Hermite polynomial + + // Input: tos-2 x (can be a symbol or expr) + + // tos-1 n + + // Output: Result on stack + + //----------------------------------------------------------------------------- hermite = function() { save(); yyhermite(); return restore(); }; + // uses the recurrence relation H(x,n+1)=2*x*H(x,n)-2*n*H(x,n-1) + + //define X p1 + //define N p2 + //define Y p3 + //define Y1 p4 + //define Y0 p5 yyhermite = function() { var n; n = 0; @@ -10100,7 +11902,7 @@ push_integer(0); p4 = pop(); results = []; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p5 = p4; p4 = pop(); push(p1); @@ -10116,6 +11918,25 @@ return results; }; + //----------------------------------------------------------------------------- + + // Create a Hilbert matrix + + // Input: Dimension on stack + + // Output: Hilbert matrix on stack + + // Example: + + // > hilbert(5) + // ((1,1/2,1/3,1/4),(1/2,1/3,1/4,1/5),(1/3,1/4,1/5,1/6),(1/4,1/5,1/6,1/7)) + + //----------------------------------------------------------------------------- + + //define A p1 + //define N p2 + + //define AELEM(i, j) A->u.tensor->elem[i * n + j] hilbert = function() { var i, i1, j, n, o, ref, ref1; i = 0; @@ -10134,8 +11955,8 @@ } push_zero_matrix(n, n); p1 = pop(); - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { - for (j = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { + for (j = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); j = 0 <= ref1 ? ++i1 : --i1) { push_integer(i + j + 1); inverse(); p1.tensor.elem[i * n + j] = pop(); @@ -10145,18 +11966,6 @@ return restore(); }; - - /* - Returns the coefficient of the imaginary part of complex z - - z imag(z) - - ------- - - a + i b b - - exp(i a) sin(a) - */ - DEBUG_IMAG = false; Eval_imag = function() { @@ -10192,6 +12001,10 @@ return restore(); }; + // n is the total number of things on the stack. The first thing on the stack + // is the object to be indexed, followed by the indices themselves. + + // called by Eval_index index_function = function(n) { var i, i1, j1, k, l1, m, m1, ndim, nelem, o, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, s, t; i = 0; @@ -10209,7 +12022,7 @@ stop("too many indices for tensor"); } k = 0; - for (i = o = 0, ref = m; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = m; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push(stack[s + i + 1]); t = pop_integer(); if (t < 1 || t > p1.tensor.dim[i]) { @@ -10223,19 +12036,19 @@ restore(); return; } - for (i = i1 = ref1 = m, ref2 = ndim; ref1 <= ref2 ? i1 < ref2 : i1 > ref2; i = ref1 <= ref2 ? ++i1 : --i1) { + for (i = i1 = ref1 = m, ref2 = ndim; (ref1 <= ref2 ? i1 < ref2 : i1 > ref2); i = ref1 <= ref2 ? ++i1 : --i1) { k = k * p1.tensor.dim[i] + 0; } nelem = 1; - for (i = j1 = ref3 = m, ref4 = ndim; ref3 <= ref4 ? j1 < ref4 : j1 > ref4; i = ref3 <= ref4 ? ++j1 : --j1) { + for (i = j1 = ref3 = m, ref4 = ndim; (ref3 <= ref4 ? j1 < ref4 : j1 > ref4); i = ref3 <= ref4 ? ++j1 : --j1) { nelem *= p1.tensor.dim[i]; } p2 = alloc_tensor(nelem); p2.tensor.ndim = ndim - m; - for (i = l1 = ref5 = m, ref6 = ndim; ref5 <= ref6 ? l1 < ref6 : l1 > ref6; i = ref5 <= ref6 ? ++l1 : --l1) { + for (i = l1 = ref5 = m, ref6 = ndim; (ref5 <= ref6 ? l1 < ref6 : l1 > ref6); i = ref5 <= ref6 ? ++l1 : --l1) { p2.tensor.dim[i - m] = p1.tensor.dim[i]; } - for (i = m1 = 0, ref7 = nelem; 0 <= ref7 ? m1 < ref7 : m1 > ref7; i = 0 <= ref7 ? ++m1 : --m1) { + for (i = m1 = 0, ref7 = nelem; (0 <= ref7 ? m1 < ref7 : m1 > ref7); i = 0 <= ref7 ? ++m1 : --m1) { p2.tensor.elem[i] = p1.tensor.elem[k + i]; } check_tensor_dimensions(p1); @@ -10245,6 +12058,29 @@ return restore(); }; + //----------------------------------------------------------------------------- + + // Input: n Number of args on stack + + // tos-n Right-hand value + + // tos-n+1 Left-hand value + + // tos-n+2 First index + + // . + // . + // . + + // tos-1 Last index + + // Output: Result on stack + + //----------------------------------------------------------------------------- + + //define LVALUE p1 + //define RVALUE p2 + //define TMP p3 set_component = function(n) { var i, i1, j1, k, l1, m, m1, n1, ndim, o, ref, ref1, ref2, ref3, ref4, ref5, ref6, s, t; i = 0; @@ -10259,7 +12095,7 @@ s = tos - n; p2 = stack[s]; p1 = stack[s + 1]; - if (!istensor(p1)) { + if (!istensor(p1)) { // p1 is LVALUE stop("error in indexed assign: assigning to something that is not a tensor"); } ndim = p1.tensor.ndim; @@ -10268,7 +12104,7 @@ stop("error in indexed assign"); } k = 0; - for (i = o = 0, ref = m; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = m; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push(stack[s + i + 2]); t = pop_integer(); if (t < 1 || t > p1.tensor.dim[i]) { @@ -10276,22 +12112,23 @@ } k = k * p1.tensor.dim[i] + t - 1; } - for (i = i1 = ref1 = m, ref2 = ndim; ref1 <= ref2 ? i1 < ref2 : i1 > ref2; i = ref1 <= ref2 ? ++i1 : --i1) { + for (i = i1 = ref1 = m, ref2 = ndim; (ref1 <= ref2 ? i1 < ref2 : i1 > ref2); i = ref1 <= ref2 ? ++i1 : --i1) { k = k * p1.tensor.dim[i] + 0; } + // copy p3 = alloc_tensor(p1.tensor.nelem); p3.tensor.ndim = p1.tensor.ndim; - for (i = j1 = 0, ref3 = p1.tensor.ndim; 0 <= ref3 ? j1 < ref3 : j1 > ref3; i = 0 <= ref3 ? ++j1 : --j1) { + for (i = j1 = 0, ref3 = p1.tensor.ndim; (0 <= ref3 ? j1 < ref3 : j1 > ref3); i = 0 <= ref3 ? ++j1 : --j1) { p3.tensor.dim[i] = p1.tensor.dim[i]; } - for (i = l1 = 0, ref4 = p1.tensor.nelem; 0 <= ref4 ? l1 < ref4 : l1 > ref4; i = 0 <= ref4 ? ++l1 : --l1) { + for (i = l1 = 0, ref4 = p1.tensor.nelem; (0 <= ref4 ? l1 < ref4 : l1 > ref4); i = 0 <= ref4 ? ++l1 : --l1) { p3.tensor.elem[i] = p1.tensor.elem[i]; } check_tensor_dimensions(p1); check_tensor_dimensions(p3); p1 = p3; if (ndim === m) { - if (istensor(p2)) { + if (istensor(p2)) { // p2 is RVALUE stop("error in indexed assign"); } p1.tensor.elem[k] = p2; @@ -10301,18 +12138,21 @@ restore(); return; } - if (!istensor(p2)) { + if (!istensor(p2)) { // p2 is RVALUE stop("error in indexed assign"); } - if (ndim - m !== p2.tensor.ndim) { + if (ndim - m !== p2.tensor.ndim) { // p2 is RVALUE stop("error in indexed assign"); } - for (i = m1 = 0, ref5 = p2.tensor.ndim; 0 <= ref5 ? m1 < ref5 : m1 > ref5; i = 0 <= ref5 ? ++m1 : --m1) { +// p2 is RVALUE + for (i = m1 = 0, ref5 = p2.tensor.ndim; (0 <= ref5 ? m1 < ref5 : m1 > ref5); i = 0 <= ref5 ? ++m1 : --m1) { if (p1.tensor.dim[m + i] !== p2.tensor.dim[i]) { stop("error in indexed assign"); } } - for (i = n1 = 0, ref6 = p2.tensor.nelem; 0 <= ref6 ? n1 < ref6 : n1 > ref6; i = 0 <= ref6 ? ++n1 : --n1) { +// p2 is RVALUE +// copy rvalue + for (i = n1 = 0, ref6 = p2.tensor.nelem; (0 <= ref6 ? n1 < ref6 : n1 > ref6); i = 0 <= ref6 ? ++n1 : --n1) { p1.tensor.elem[k + i] = p2.tensor.elem[i]; } check_tensor_dimensions(p1); @@ -10322,80 +12162,17 @@ return restore(); }; - - /* dot ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - a,b,... - - General description - ------------------- - - The inner (or dot) operator gives products of vectors, - matrices, and tensors. - - Note that for Algebrite, the elements of a vector/matrix - can only be scalars. This allows for example to flesh out - matrix multiplication using the usual multiplication. - So for example block-representations are not allowed. - - There is an aweful lot of confusion between sw packages on - what dot and inner do. - - First off, the "dot" operator is different from the - mathematical notion of dot product, which can be - slightly confusing. - - The mathematical notion of dot product is here: - http://mathworld.wolfram.com/DotProduct.html - - However, "dot" does that and a bunch of other things, - i.e. in Algebrite - dot/inner does what the dot of Mathematica does, i.e.: - - scalar product of vectors: - - inner((a, b, c), (x, y, z)) - > a x + b y + c z - - products of matrices and vectors: - - inner(((a, b), (c,d)), (x, y)) - > (a x + b y,c x + d y) - - inner((x, y), ((a, b), (c,d))) - > (a x + c y,b x + d y) - - inner((x, y), ((a, b), (c,d)), (r, s)) - > a r x + b s x + c r y + d s y - - matrix product: - - inner(((a,b),(c,d)),((r,s),(t,u))) - > ((a r + b t,a s + b u),(c r + d t,c s + d u)) - - the "dot/inner" operator is associative and - distributive but not commutative. - - In Mathematica, Inner is a generalisation of Dot where - the user can specify the multiplication and the addition - operators. - But here in Algebrite they do the same thing. - - https://reference.wolfram.com/language/ref/Dot.html - https://reference.wolfram.com/language/ref/Inner.html - - http://uk.mathworks.com/help/matlab/ref/dot.html - http://uk.mathworks.com/help/matlab/ref/mtimes.html - */ - Eval_inner = function() { var difference, i, i1, j1, l1, moretheArguments, o, operands, ref, ref1, ref2, ref3, refinedOperands, results, secondArgument, shift, theArguments; + + // if there are more than two arguments then + // reduce it to a more standard version + // of two arguments, which means we need to + // transform the arguments into a tree of + // inner products e.g. + // inner(a,b,c) becomes inner(a,inner(b,c)) + // this is so we can get to a standard binary-tree + // version that is simpler to manipulate. theArguments = []; theArguments.push(car(cdr(p1))); secondArgument = car(cdr(cdr(p1))); @@ -10407,12 +12184,13 @@ theArguments.push(car(moretheArguments)); moretheArguments = cdr(moretheArguments); } + // make it so e.g. inner(a,b,c) becomes inner(a,inner(b,c)) if (theArguments.length > 2) { push_symbol(INNER); push(theArguments[theArguments.length - 2]); push(theArguments[theArguments.length - 1]); list(3); - for (i = o = 2, ref = theArguments.length; 2 <= ref ? o < ref : o > ref; i = 2 <= ref ? ++o : --o) { + for (i = o = 2, ref = theArguments.length; (2 <= ref ? o < ref : o > ref); i = 2 <= ref ? ++o : --o) { push_symbol(INNER); swap(); push(theArguments[theArguments.length - i - 1]); @@ -10423,10 +12201,17 @@ Eval_inner(); return; } + // TODO we have to take a look at the whole + // sequence of operands and make simplifications + // on that... operands = []; get_innerprod_factors(p1, operands); + //console.log "printing operands --------" + //for i in [0...operands.length] + // console.log "operand " + i + " : " + operands[i] refinedOperands = []; - for (i = i1 = 0, ref1 = operands.length; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { +// removing all identity matrices + for (i = i1 = 0, ref1 = operands.length; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { if (operands[i] === symbol(SYMBOL_IDENTITY_MATRIX)) { continue; } else { @@ -10436,8 +12221,14 @@ operands = refinedOperands; refinedOperands = []; if (operands.length > 1) { + // removing all consecutive pairs of inverses + // so we can answer that inv(a)·a results in the + // identity matrix. We want to catch symbolic inverses + // not numeric inverses, those will just take care + // of themselves when multiplied shift = 0; - for (i = j1 = 0, ref2 = operands.length; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = operands.length; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { + //console.log "comparing if " + operands[i+shift] + " and " + operands[i+shift+1] + " are inverses of each other" if ((i + shift + 1) <= (operands.length - 1)) { if (!(isNumericAtomOrTensor(operands[i + shift]) || isNumericAtomOrTensor(operands[i + shift + 1]))) { push(operands[i + shift]); @@ -10447,6 +12238,7 @@ Eval(); subtract(); difference = pop(); + //console.log "result: " + difference if (isZeroAtomOrTensor(difference)) { shift += 1; } else { @@ -10458,7 +12250,9 @@ } else { break; } + //console.log "i: " + i + " shift: " + shift + " operands.length: " + operands.length if (i + shift === operands.length - 2) { + //console.log "adding last operand 2 " refinedOperands.push(operands[operands.length - 1]); } if (i + shift >= operands.length - 1) { @@ -10467,9 +12261,19 @@ } operands = refinedOperands; } + //console.log "refined operands --------" + //for i in [0...refinedOperands.length] + // console.log "refined operand " + i + " : " + refinedOperands[i] + + //console.log "stack[tos-1]: " + stack[tos-1] + + // now rebuild the arguments, just using the + // refined operands push(symbol(INNER)); + //console.log "rebuilding the argument ----" if (operands.length > 0) { - for (i = l1 = 0, ref3 = operands.length; 0 <= ref3 ? l1 < ref3 : l1 > ref3; i = 0 <= ref3 ? ++l1 : --l1) { + for (i = l1 = 0, ref3 = operands.length; (0 <= ref3 ? l1 < ref3 : l1 > ref3); i = 0 <= ref3 ? ++l1 : --l1) { + //console.log "pushing " + operands[i] push(operands[i]); } } else { @@ -10477,6 +12281,7 @@ push(symbol(SYMBOL_IDENTITY_MATRIX)); return; } + //console.log "list(operands.length): " + (operands.length+1) list(operands.length + 1); p1 = pop(); p1 = cdr(p1); @@ -10493,11 +12298,19 @@ return results; }; + // inner definition inner = function() { var arg1, arg2, arg3, subtractionResult; save(); p2 = pop(); p1 = pop(); + // more in general, when a and b are scalars, + // inner(a*M1, b*M2) is equal to + // a*b*inner(M1,M2), but of course we can only + // "bring out" in a and b the scalars, because + // it's the only commutative part. + // that's going to be trickier to do in general + // but let's start with just the signs. if (isnegativeterm(p2) && isnegativeterm(p1)) { push(p2); negate(); @@ -10506,9 +12319,15 @@ negate(); p1 = pop(); } + // since inner is associative, + // put it in a canonical form i.e. + // inner(inner(a,b),c) -> + // inner(a,inner(b,c)) + // so that we can recognise when they + // are equal. if (isinnerordot(p1)) { - arg1 = car(cdr(p1)); - arg2 = car(cdr(cdr(p1))); + arg1 = car(cdr(p1)); //a + arg2 = car(cdr(cdr(p1))); //b arg3 = p2; p1 = arg1; push(arg2); @@ -10516,6 +12335,9 @@ inner(); p2 = pop(); } + // Check if one of the operands is the identity matrix + // we could maybe use Eval_testeq here but + // this seems to suffice? if (p1 === symbol(SYMBOL_IDENTITY_MATRIX)) { push(p2); restore(); @@ -10540,6 +12362,8 @@ return; } } + // if either operand is a sum then distribute + // (if we are in expanding mode) if (expanding && isadd(p1)) { p1 = cdr(p1); push(zero); @@ -10568,14 +12392,34 @@ } push(p1); push(p2); + // there are 8 remaining cases here, since each of the + // two arguments can only be a scalar/tensor/unknown + // and the tensor - tensor case was caught + // upper in the code if (istensor(p1) && isNumericAtom(p2)) { + // one case covered by this branch: + // tensor - scalar tensor_times_scalar(); } else if (isNumericAtom(p1) && istensor(p2)) { + // one case covered by this branch: + // scalar - tensor scalar_times_tensor(); } else { if (isNumericAtom(p1) || isNumericAtom(p2)) { + // three cases covered by this branch: + // unknown - scalar + // scalar - unknown + // scalar - scalar + // in these cases a normal multiplication + // will be OK multiply(); } else { + // three cases covered by this branch: + // unknown - unknown + // unknown - tensor + // tensor - unknown + // in this case we can't use normal + // multiplication. pop(); pop(); push_symbol(INNER); @@ -10590,6 +12434,7 @@ return restore(); }; + // inner product of tensors p1 and p2 inner_f = function() { var a, ak, b, bk, c, i, i1, j, j1, k, l1, m1, n, n1, ndim, o, o1, ref, ref1, ref2, ref3, ref4, ref5, ref6; i = 0; @@ -10604,22 +12449,38 @@ } a = p1.tensor.elem; b = p2.tensor.elem; + //--------------------------------------------------------------------- + + // ak is the number of rows in tensor A + + // bk is the number of columns in tensor B + + // Example: + + // A[3][3][4] B[4][4][3] + + // 3 3 ak = 3 * 3 = 9 + + // 4 3 bk = 4 * 3 = 12 + + //--------------------------------------------------------------------- ak = 1; - for (i = o = 0, ref = p1.tensor.ndim - 1; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = p1.tensor.ndim - 1; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { ak *= p1.tensor.dim[i]; } bk = 1; - for (i = i1 = 1, ref1 = p2.tensor.ndim; 1 <= ref1 ? i1 < ref1 : i1 > ref1; i = 1 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 1, ref1 = p2.tensor.ndim; (1 <= ref1 ? i1 < ref1 : i1 > ref1); i = 1 <= ref1 ? ++i1 : --i1) { bk *= p2.tensor.dim[i]; } p3 = alloc_tensor(ak * bk); c = p3.tensor.elem; - for (i = j1 = 0, ref2 = ak; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { - for (j = l1 = 0, ref3 = n; 0 <= ref3 ? l1 < ref3 : l1 > ref3; j = 0 <= ref3 ? ++l1 : --l1) { +// new method copied from ginac http://www.ginac.de/ + for (i = j1 = 0, ref2 = ak; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { + for (j = l1 = 0, ref3 = n; (0 <= ref3 ? l1 < ref3 : l1 > ref3); j = 0 <= ref3 ? ++l1 : --l1) { if (isZeroAtomOrTensor(a[i * n + j])) { continue; } - for (k = m1 = 0, ref4 = bk; 0 <= ref4 ? m1 < ref4 : m1 > ref4; k = 0 <= ref4 ? ++m1 : --m1) { + for (k = m1 = 0, ref4 = bk; (0 <= ref4 ? m1 < ref4 : m1 > ref4); k = 0 <= ref4 ? ++m1 : --m1) { push(a[i * n + j]); push(b[j * bk + k]); multiply(); @@ -10629,22 +12490,51 @@ } } } + //--------------------------------------------------------------------- + + // Note on understanding "k * bk + j" + + // k * bk because each element of a column is bk locations apart + + // + j because the beginnings of all columns are in the first bk + // locations + + // Example: n = 2, bk = 6 + + // b111 <- 1st element of 1st column + // b112 <- 1st element of 2nd column + // b113 <- 1st element of 3rd column + // b121 <- 1st element of 4th column + // b122 <- 1st element of 5th column + // b123 <- 1st element of 6th column + + // b211 <- 2nd element of 1st column + // b212 <- 2nd element of 2nd column + // b213 <- 2nd element of 3rd column + // b221 <- 2nd element of 4th column + // b222 <- 2nd element of 5th column + // b223 <- 2nd element of 6th column + + //--------------------------------------------------------------------- if (ndim === 0) { return push(p3.tensor.elem[0]); } else { p3.tensor.ndim = ndim; j = 0; - for (i = n1 = 0, ref5 = p1.tensor.ndim - 1; 0 <= ref5 ? n1 < ref5 : n1 > ref5; i = 0 <= ref5 ? ++n1 : --n1) { + for (i = n1 = 0, ref5 = p1.tensor.ndim - 1; (0 <= ref5 ? n1 < ref5 : n1 > ref5); i = 0 <= ref5 ? ++n1 : --n1) { p3.tensor.dim[i] = p1.tensor.dim[i]; } j = p1.tensor.ndim - 1; - for (i = o1 = 0, ref6 = p2.tensor.ndim - 1; 0 <= ref6 ? o1 < ref6 : o1 > ref6; i = 0 <= ref6 ? ++o1 : --o1) { + for (i = o1 = 0, ref6 = p2.tensor.ndim - 1; (0 <= ref6 ? o1 < ref6 : o1 > ref6); i = 0 <= ref6 ? ++o1 : --o1) { p3.tensor.dim[j + i] = p2.tensor.dim[i + 1]; } return push(p3); } }; + // Algebrite.run('c·(b+a)ᵀ·inv((a+b)ᵀ)·d').toString(); + // Algebrite.run('c*(b+a)ᵀ·inv((a+b)ᵀ)·d').toString(); + // Algebrite.run('(c·(b+a)ᵀ)·(inv((a+b)ᵀ)·d)').toString(); get_innerprod_factors = function(tree, factors_accumulator) { if (!iscons(tree)) { add_factor_to_accumulator(tree, factors_accumulator); @@ -10655,6 +12545,7 @@ return; } if (isinnerordot(tree)) { + // console.log "there is inner at top, recursing on the operands" get_innerprod_factors(car(cdr(tree)), factors_accumulator); get_innerprod_factors(cdr(cdr(tree)), factors_accumulator); return; @@ -10664,32 +12555,369 @@ add_factor_to_accumulator = function(tree, factors_accumulator) { if (tree !== symbol(NIL)) { + // console.log ">> adding to factors_accumulator: " + tree return factors_accumulator.push(tree); } }; - - /* - Table of integrals - - The symbol f is just a dummy symbol for creating a list f(A,B,C,C,...) where - - A is the template expression - - B is the result expression - - C is an optional list of conditional expressions - */ - - itab = ["f(a,a*x)", "f(1/x,log(x))", "f(x^a,x^(a+1)/(a+1))", "f(x^(-2),-x^(-1))", "f(x^(-1/2),2*x^(1/2))", "f(x^(1/2),2/3*x^(3/2))", "f(x,x^2/2)", "f(x^2,x^3/3)", "f(exp(a*x),1/a*exp(a*x))", "f(exp(a*x+b),1/a*exp(a*x+b))", "f(x*exp(a*x^2),exp(a*x^2)/(2*a))", "f(x*exp(a*x^2+b),exp(a*x^2+b)/(2*a))", "f(log(a*x),x*log(a*x)-x)", "f(a^x,a^x/log(a),or(not(number(a)),a>0))", "f(1/(a+x^2),1/sqrt(a)*arctan(x/sqrt(a)),or(not(number(a)),a>0))", "f(1/(a-x^2),1/sqrt(a)*arctanh(x/sqrt(a)))", "f(1/sqrt(a-x^2),arcsin(x/(sqrt(a))))", "f(1/sqrt(a+x^2),log(x+sqrt(a+x^2)))", "f(1/(a+b*x),1/b*log(a+b*x))", "f(1/(a+b*x)^2,-1/(b*(a+b*x)))", "f(1/(a+b*x)^3,-1/(2*b)*1/(a+b*x)^2)", "f(x/(a+b*x),x/b-a*log(a+b*x)/b/b)", "f(x/(a+b*x)^2,1/b^2*(log(a+b*x)+a/(a+b*x)))", "f(x^2/(a+b*x),1/b^2*(1/2*(a+b*x)^2-2*a*(a+b*x)+a^2*log(a+b*x)))", "f(x^2/(a+b*x)^2,1/b^3*(a+b*x-2*a*log(a+b*x)-a^2/(a+b*x)))", "f(x^2/(a+b*x)^3,1/b^3*(log(a+b*x)+2*a/(a+b*x)-1/2*a^2/(a+b*x)^2))", "f(1/x*1/(a+b*x),-1/a*log((a+b*x)/x))", "f(1/x*1/(a+b*x)^2,1/a*1/(a+b*x)-1/a^2*log((a+b*x)/x))", "f(1/x*1/(a+b*x)^3,1/a^3*(1/2*((2*a+b*x)/(a+b*x))^2+log(x/(a+b*x))))", "f(1/x^2*1/(a+b*x),-1/(a*x)+b/a^2*log((a+b*x)/x))", "f(1/x^3*1/(a+b*x),(2*b*x-a)/(2*a^2*x^2)+b^2/a^3*log(x/(a+b*x)))", "f(1/x^2*1/(a+b*x)^2,-(a+2*b*x)/(a^2*x*(a+b*x))+2*b/a^3*log((a+b*x)/x))", "f(1/(a+b*x^2),1/sqrt(a*b)*arctan(x*sqrt(a*b)/a),or(not(number(a*b)),a*b>0))", "f(1/(a+b*x^2),1/(2*sqrt(-a*b))*log((a+x*sqrt(-a*b))/(a-x*sqrt(-a*b))),or(not(number(a*b)),a*b<0))", "f(x/(a+b*x^2),1/2*1/b*log(a+b*x^2))", "f(x^2/(a+b*x^2),x/b-a/b*integral(1/(a+b*x^2),x))", "f(1/(a+b*x^2)^2,x/(2*a*(a+b*x^2))+1/2*1/a*integral(1/(a+b*x^2),x))", "f(1/x*1/(a+b*x^2),1/2*1/a*log(x^2/(a+b*x^2)))", "f(1/x^2*1/(a+b*x^2),-1/(a*x)-b/a*integral(1/(a+b*x^2),x))", "f(1/(a+b*x^3),1/3*1/a*(a/b)^(1/3)*(1/2*log(((a/b)^(1/3)+x)^3/(a+b*x^3))+sqrt(3)*arctan((2*x-(a/b)^(1/3))*(a/b)^(-1/3)/sqrt(3))))", "f(x^2/(a+b*x^3),1/3*1/b*log(a+b*x^3))", "f(x/(a+b*x^4),1/2*sqrt(b/a)/b*arctan(x^2*sqrt(b/a)),or(not(number(a*b)),a*b>0))", "f(x/(a+b*x^4),1/4*sqrt(-b/a)/b*log((x^2-sqrt(-a/b))/(x^2+sqrt(-a/b))),or(not(number(a*b)),a*b<0))", "f(x^3/(a+b*x^4),1/4*1/b*log(a+b*x^4))", "f(sqrt(a+b*x),2/3*1/b*sqrt((a+b*x)^3))", "f(x*sqrt(a+b*x),-2*(2*a-3*b*x)*sqrt((a+b*x)^3)/15/b^2)", "f(x^2*sqrt(a+b*x),2*(8*a^2-12*a*b*x+15*b^2*x^2)*sqrt((a+b*x)^3)/105/b^3)", "f(sqrt(a+b*x)/x,2*sqrt(a+b*x)+a*integral(1/x*1/sqrt(a+b*x),x))", "f(sqrt(a+b*x)/x^2,-sqrt(a+b*x)/x+b/2*integral(1/x*1/sqrt(a+b*x),x))", "f(1/sqrt(a+b*x),2*sqrt(a+b*x)/b)", "f(x/sqrt(a+b*x),-2/3*(2*a-b*x)*sqrt(a+b*x)/b^2)", "f(x^2/sqrt(a+b*x),2/15*(8*a^2-4*a*b*x+3*b^2*x^2)*sqrt(a+b*x)/b^3)", "f(1/x*1/sqrt(a+b*x),1/sqrt(a)*log((sqrt(a+b*x)-sqrt(a))/(sqrt(a+b*x)+sqrt(a))),or(not(number(a)),a>0))", "f(1/x*1/sqrt(a+b*x),2/sqrt(-a)*arctan(sqrt(-(a+b*x)/a)),or(not(number(a)),a<0))", "f(1/x^2*1/sqrt(a+b*x),-sqrt(a+b*x)/a/x-1/2*b/a*integral(1/x*1/sqrt(a+b*x),x))", "f(sqrt(x^2+a),1/2*(x*sqrt(x^2+a)+a*log(x+sqrt(x^2+a))))", "f(1/sqrt(x^2+a),log(x+sqrt(x^2+a)))", "f(1/x*1/sqrt(x^2+a),arcsec(x/sqrt(-a))/sqrt(-a),or(not(number(a)),a<0))", "f(1/x*1/sqrt(x^2+a),-1/sqrt(a)*log((sqrt(a)+sqrt(x^2+a))/x),or(not(number(a)),a>0))", "f(sqrt(x^2+a)/x,sqrt(x^2+a)-sqrt(a)*log((sqrt(a)+sqrt(x^2+a))/x),or(not(number(a)),a>0))", "f(sqrt(x^2+a)/x,sqrt(x^2+a)-sqrt(-a)*arcsec(x/sqrt(-a)),or(not(number(a)),a<0))", "f(x/sqrt(x^2+a),sqrt(x^2+a))", "f(x*sqrt(x^2+a),1/3*sqrt((x^2+a)^3))", "f(sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/4*(x*sqrt((x^2+a^(1/3))^3)+3/2*a^(1/3)*x*sqrt(x^2+a^(1/3))+3/2*a^(2/3)*log(x+sqrt(x^2+a^(1/3)))))", "f(sqrt(-a+x^6-3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/4*(x*sqrt((x^2-a^(1/3))^3)-3/2*a^(1/3)*x*sqrt(x^2-a^(1/3))+3/2*a^(2/3)*log(x+sqrt(x^2-a^(1/3)))))", "f(1/sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),x/a^(1/3)/sqrt(x^2+a^(1/3)))", "f(x/sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),-1/sqrt(x^2+a^(1/3)))", "f(x*sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/5*sqrt((x^2+a^(1/3))^5))", "f(x^2*sqrt(x^2+a),1/4*x*sqrt((x^2+a)^3)-1/8*a*x*sqrt(x^2+a)-1/8*a^2*log(x+sqrt(x^2+a)))", "f(x^3*sqrt(x^2+a),(1/5*x^2-2/15*a)*sqrt((x^2+a)^3),and(number(a),a>0))", "f(x^3*sqrt(x^2+a),sqrt((x^2+a)^5)/5-a*sqrt((x^2+a)^3)/3,and(number(a),a<0))", "f(x^2/sqrt(x^2+a),1/2*x*sqrt(x^2+a)-1/2*a*log(x+sqrt(x^2+a)))", "f(x^3/sqrt(x^2+a),1/3*sqrt((x^2+a)^3)-a*sqrt(x^2+a))", "f(1/x^2*1/sqrt(x^2+a),-sqrt(x^2+a)/a/x)", "f(1/x^3*1/sqrt(x^2+a),-1/2*sqrt(x^2+a)/a/x^2+1/2*log((sqrt(a)+sqrt(x^2+a))/x)/a^(3/2),or(not(number(a)),a>0))", "f(1/x^3*1/sqrt(x^2-a),1/2*sqrt(x^2-a)/a/x^2+1/2*1/(a^(3/2))*arcsec(x/(a^(1/2))),or(not(number(a)),a>0))", "f(x^2*sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/6*x*sqrt((x^2+a^(1/3))^5)-1/24*a^(1/3)*x*sqrt((x^2+a^(1/3))^3)-1/16*a^(2/3)*x*sqrt(x^2+a^(1/3))-1/16*a*log(x+sqrt(x^2+a^(1/3))),or(not(number(a)),a>0))", "f(x^2*sqrt(-a-3*a^(1/3)*x^4+3*a^(2/3)*x^2+x^6),1/6*x*sqrt((x^2-a^(1/3))^5)+1/24*a^(1/3)*x*sqrt((x^2-a^(1/3))^3)-1/16*a^(2/3)*x*sqrt(x^2-a^(1/3))+1/16*a*log(x+sqrt(x^2-a^(1/3))),or(not(number(a)),a>0))", "f(x^3*sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/7*sqrt((x^2+a^(1/3))^7)-1/5*a^(1/3)*sqrt((x^2+a^(1/3))^5),or(not(number(a)),a>0))", "f(x^3*sqrt(-a-3*a^(1/3)*x^4+3*a^(2/3)*x^2+x^6),1/7*sqrt((x^2-a^(1/3))^7)+1/5*a^(1/3)*sqrt((x^2-a^(1/3))^5),or(not(number(a)),a>0))", "f(1/(x-a)/sqrt(x^2-a^2),-sqrt(x^2-a^2)/a/(x-a))", "f(1/(x+a)/sqrt(x^2-a^2),sqrt(x^2-a^2)/a/(x+a))", "f(sqrt(a-x^2),1/2*(x*sqrt(a-x^2)+a*arcsin(x/sqrt(abs(a)))))", "f(1/x*1/sqrt(a-x^2),-1/sqrt(a)*log((sqrt(a)+sqrt(a-x^2))/x),or(not(number(a)),a>0))", "f(sqrt(a-x^2)/x,sqrt(a-x^2)-sqrt(a)*log((sqrt(a)+sqrt(a-x^2))/x),or(not(number(a)),a>0))", "f(x/sqrt(a-x^2),-sqrt(a-x^2))", "f(x*sqrt(a-x^2),-1/3*sqrt((a-x^2)^3))", "f(x^2*sqrt(a-x^2),-x/4*sqrt((a-x^2)^3)+1/8*a*(x*sqrt(a-x^2)+a*arcsin(x/sqrt(a))),or(not(number(a)),a>0))", "f(x^3*sqrt(a-x^2),(-1/5*x^2-2/15*a)*sqrt((a-x^2)^3),or(not(number(a)),a>0))", "f(x^2/sqrt(a-x^2),-x/2*sqrt(a-x^2)+a/2*arcsin(x/sqrt(a)),or(not(number(a)),a>0))", "f(1/x^2*1/sqrt(a-x^2),-sqrt(a-x^2)/a/x,or(not(number(a)),a>0))", "f(sqrt(a-x^2)/x^2,-sqrt(a-x^2)/x-arcsin(x/sqrt(a)),or(not(number(a)),a>0))", "f(sqrt(a-x^2)/x^3,-1/2*sqrt(a-x^2)/x^2+1/2*log((sqrt(a)+sqrt(a-x^2))/x)/sqrt(a),or(not(number(a)),a>0))", "f(sqrt(a-x^2)/x^4,-1/3*sqrt((a-x^2)^3)/a/x^3,or(not(number(a)),a>0))", "f(sqrt(a*x^2+b),x*sqrt(a*x^2+b)/2+b*log(x*sqrt(a)+sqrt(a*x^2+b))/2/sqrt(a),and(number(a),a>0))", "f(sqrt(a*x^2+b),x*sqrt(a*x^2+b)/2+b*arcsin(x*sqrt(-a/b))/2/sqrt(-a),and(number(a),a<0))", "f(sin(a*x),-cos(a*x)/a)", "f(cos(a*x),sin(a*x)/a)", "f(tan(a*x),-log(cos(a*x))/a)", "f(1/tan(a*x),log(sin(a*x))/a)", "f(1/cos(a*x),log(tan(pi/4+a*x/2))/a)", "f(1/sin(a*x),log(tan(a*x/2))/a)", "f(sin(a*x)^2,x/2-sin(2*a*x)/(4*a))", "f(sin(a*x)^3,-cos(a*x)*(sin(a*x)^2+2)/(3*a))", "f(sin(a*x)^4,3/8*x-sin(2*a*x)/(4*a)+sin(4*a*x)/(32*a))", "f(cos(a*x)^2,x/2+sin(2*a*x)/(4*a))", "f(cos(a*x)^3,sin(a*x)*(cos(a*x)^2+2)/(3*a))", "f(cos(a*x)^4,3/8*x+sin(2*a*x)/(4*a)+sin(4*a*x)/(32*a))", "f(1/sin(a*x)^2,-1/(a*tan(a*x)))", "f(1/cos(a*x)^2,tan(a*x)/a)", "f(sin(a*x)*cos(a*x),sin(a*x)^2/(2*a))", "f(sin(a*x)^2*cos(a*x)^2,-sin(4*a*x)/(32*a)+x/8)", "f(sin(a*x)/cos(a*x)^2,1/(a*cos(a*x)))", "f(sin(a*x)^2/cos(a*x),(log(tan(pi/4+a*x/2))-sin(a*x))/a)", "f(cos(a*x)/sin(a*x)^2,-1/(a*sin(a*x)))", "f(1/(sin(a*x)*cos(a*x)),log(tan(a*x))/a)", "f(1/(sin(a*x)*cos(a*x)^2),(1/cos(a*x)+log(tan(a*x/2)))/a)", "f(1/(sin(a*x)^2*cos(a*x)),(log(tan(pi/4+a*x/2))-1/sin(a*x))/a)", "f(1/(sin(a*x)^2*cos(a*x)^2),-2/(a*tan(2*a*x)))", "f(sin(a+b*x),-cos(a+b*x)/b)", "f(cos(a+b*x),sin(a+b*x)/b)", "f(1/(b+b*sin(a*x)),-tan(pi/4-a*x/2)/a/b)", "f(1/(b-b*sin(a*x)),tan(pi/4+a*x/2)/a/b)", "f(1/(b+b*cos(a*x)),tan(a*x/2)/a/b)", "f(1/(b-b*cos(a*x)),-1/tan(a*x/2)/a/b)", "f(1/(a+b*sin(x)),1/sqrt(b^2-a^2)*log((a*tan(x/2)+b-sqrt(b^2-a^2))/(a*tan(x/2)+b+sqrt(b^2-a^2))),b^2-a^2)", "f(1/(a+b*cos(x)),1/sqrt(b^2-a^2)*log((sqrt(b^2-a^2)*tan(x/2)+a+b)/(sqrt(b^2-a^2)*tan(x/2)-a-b)),b^2-a^2)", "f(x*sin(a*x),sin(a*x)/a^2-x*cos(a*x)/a)", "f(x^2*sin(a*x),2*x*sin(a*x)/a^2-(a^2*x^2-2)*cos(a*x)/a^3)", "f(x*cos(a*x),cos(a*x)/a^2+x*sin(a*x)/a)", "f(x^2*cos(a*x),2*x*cos(a*x)/a^2+(a^2*x^2-2)*sin(a*x)/a^3)", "f(arcsin(a*x),x*arcsin(a*x)+sqrt(1-a^2*x^2)/a)", "f(arccos(a*x),x*arccos(a*x)-sqrt(1-a^2*x^2)/a)", "f(arctan(a*x),x*arctan(a*x)-1/2*log(1+a^2*x^2)/a)", "f(x*log(a*x),x^2*log(a*x)/2-x^2/4)", "f(x^2*log(a*x),x^3*log(a*x)/3-1/9*x^3)", "f(log(x)^2,x*log(x)^2-2*x*log(x)+2*x)", "f(1/x*1/(a+log(x)),log(a+log(x)))", "f(log(a*x+b),(a*x+b)*log(a*x+b)/a-x)", "f(log(a*x+b)/x^2,a/b*log(x)-(a*x+b)*log(a*x+b)/b/x)", "f(sinh(x),cosh(x))", "f(cosh(x),sinh(x))", "f(tanh(x),log(cosh(x)))", "f(x*sinh(x),x*cosh(x)-sinh(x))", "f(x*cosh(x),x*sinh(x)-cosh(x))", "f(sinh(x)^2,sinh(2*x)/4-x/2)", "f(tanh(x)^2,x-tanh(x))", "f(cosh(x)^2,sinh(2*x)/4+x/2)", "f(x^3*exp(a*x^2),exp(a*x^2)*(x^2/a-1/(a^2))/2)", "f(x^3*exp(a*x^2+b),exp(a*x^2)*exp(b)*(x^2/a-1/(a^2))/2)", "f(exp(a*x^2),-i*sqrt(pi)*erf(i*sqrt(a)*x)/sqrt(a)/2)", "f(erf(a*x),x*erf(a*x)+exp(-a^2*x^2)/a/sqrt(pi))", "f(x^2*(1-x^2)^(3/2),(x*sqrt(1-x^2)*(-8*x^4+14*x^2-3)+3*arcsin(x))/48)", "f(x^2*(1-x^2)^(5/2),(x*sqrt(1-x^2)*(48*x^6-136*x^4+118*x^2-15)+15*arcsin(x))/384)", "f(x^4*(1-x^2)^(3/2),(-x*sqrt(1-x^2)*(16*x^6-24*x^4+2*x^2+3)+3*arcsin(x))/128)", "f(x*exp(a*x),exp(a*x)*(a*x-1)/(a^2))", "f(x*exp(a*x+b),exp(a*x+b)*(a*x-1)/(a^2))", "f(x^2*exp(a*x),exp(a*x)*(a^2*x^2-2*a*x+2)/(a^3))", "f(x^2*exp(a*x+b),exp(a*x+b)*(a^2*x^2-2*a*x+2)/(a^3))", "f(x^3*exp(a*x),exp(a*x)*x^3/a-3/a*integral(x^2*exp(a*x),x))", "f(x^3*exp(a*x+b),exp(a*x+b)*x^3/a-3/a*integral(x^2*exp(a*x+b),x))", 0]; - + itab = [ + // 1 + "f(a,a*x)", + // 9 (need a caveat for 7 so we can put 9 after 7) + "f(1/x,log(x))", + // 7 + "f(x^a,x^(a+1)/(a+1))", + // five specialisations of case 7 for speed. + // Covers often-occurring exponents: each of + // these case ends up in a dedicated entry, so we + // only have to do one sure-shot match. + "f(x^(-2),-x^(-1))", + "f(x^(-1/2),2*x^(1/2))", + "f(x^(1/2),2/3*x^(3/2))", + "f(x,x^2/2)", + "f(x^2,x^3/3)", + // 12 + "f(exp(a*x),1/a*exp(a*x))", + "f(exp(a*x+b),1/a*exp(a*x+b))", + "f(x*exp(a*x^2),exp(a*x^2)/(2*a))", + "f(x*exp(a*x^2+b),exp(a*x^2+b)/(2*a))", + // 14 + "f(log(a*x),x*log(a*x)-x)", + // 15 + "f(a^x,a^x/log(a),or(not(number(a)),a>0))", + // 16 + "f(1/(a+x^2),1/sqrt(a)*arctan(x/sqrt(a)),or(not(number(a)),a>0))", + // 17 + "f(1/(a-x^2),1/sqrt(a)*arctanh(x/sqrt(a)))", + // 19 + "f(1/sqrt(a-x^2),arcsin(x/(sqrt(a))))", + // 20 + "f(1/sqrt(a+x^2),log(x+sqrt(a+x^2)))", + // 27 + "f(1/(a+b*x),1/b*log(a+b*x))", + // 28 + "f(1/(a+b*x)^2,-1/(b*(a+b*x)))", + // 29 + "f(1/(a+b*x)^3,-1/(2*b)*1/(a+b*x)^2)", + // 30 + "f(x/(a+b*x),x/b-a*log(a+b*x)/b/b)", + // 31 + "f(x/(a+b*x)^2,1/b^2*(log(a+b*x)+a/(a+b*x)))", + // 33 + "f(x^2/(a+b*x),1/b^2*(1/2*(a+b*x)^2-2*a*(a+b*x)+a^2*log(a+b*x)))", + // 34 + "f(x^2/(a+b*x)^2,1/b^3*(a+b*x-2*a*log(a+b*x)-a^2/(a+b*x)))", + // 35 + "f(x^2/(a+b*x)^3,1/b^3*(log(a+b*x)+2*a/(a+b*x)-1/2*a^2/(a+b*x)^2))", + // 37 + "f(1/x*1/(a+b*x),-1/a*log((a+b*x)/x))", + // 38 + "f(1/x*1/(a+b*x)^2,1/a*1/(a+b*x)-1/a^2*log((a+b*x)/x))", + // 39 + "f(1/x*1/(a+b*x)^3,1/a^3*(1/2*((2*a+b*x)/(a+b*x))^2+log(x/(a+b*x))))", + // 40 + "f(1/x^2*1/(a+b*x),-1/(a*x)+b/a^2*log((a+b*x)/x))", + // 41 + "f(1/x^3*1/(a+b*x),(2*b*x-a)/(2*a^2*x^2)+b^2/a^3*log(x/(a+b*x)))", + // 42 + "f(1/x^2*1/(a+b*x)^2,-(a+2*b*x)/(a^2*x*(a+b*x))+2*b/a^3*log((a+b*x)/x))", + // 60 + "f(1/(a+b*x^2),1/sqrt(a*b)*arctan(x*sqrt(a*b)/a),or(not(number(a*b)),a*b>0))", + // 61 + "f(1/(a+b*x^2),1/(2*sqrt(-a*b))*log((a+x*sqrt(-a*b))/(a-x*sqrt(-a*b))),or(not(number(a*b)),a*b<0))", + // 62 is the same as 60 + // 63 + "f(x/(a+b*x^2),1/2*1/b*log(a+b*x^2))", + //64 + "f(x^2/(a+b*x^2),x/b-a/b*integral(1/(a+b*x^2),x))", + //65 + "f(1/(a+b*x^2)^2,x/(2*a*(a+b*x^2))+1/2*1/a*integral(1/(a+b*x^2),x))", + //66 is covered by 61 + //70 + "f(1/x*1/(a+b*x^2),1/2*1/a*log(x^2/(a+b*x^2)))", + //71 + "f(1/x^2*1/(a+b*x^2),-1/(a*x)-b/a*integral(1/(a+b*x^2),x))", + //74 + "f(1/(a+b*x^3),1/3*1/a*(a/b)^(1/3)*(1/2*log(((a/b)^(1/3)+x)^3/(a+b*x^3))+sqrt(3)*arctan((2*x-(a/b)^(1/3))*(a/b)^(-1/3)/sqrt(3))))", + //76 + "f(x^2/(a+b*x^3),1/3*1/b*log(a+b*x^3))", + // float(defint(1/(2+3*X^4),X,0,pi)) gave wrong result. + // Also, the tests related to the indefinite integral + // fail since we rationalise expressions "better", so I'm thinking + // to take this out completely as it seemed to give the + // wrong results in the first place. + //77 + //"f(1/(a+b*x^4),1/2*1/a*(a/b/4)^(1/4)*(1/2*log((x^2+2*(a/b/4)^(1/4)*x+2*(a/b/4)^(1/2))/(x^2-2*(a/b/4)^(1/4)*x+2*(a/b/4)^(1/2)))+arctan(2*(a/b/4)^(1/4)*x/(2*(a/b/4)^(1/2)-x^2))),or(not(number(a*b)),a*b>0))", + //78 + //"f(1/(a+b*x^4),1/2*(-a/b)^(1/4)/a*(1/2*log((x+(-a/b)^(1/4))/(x-(-a/b)^(1/4)))+arctan(x*(-a/b)^(-1/4))),or(not(number(a*b)),a*b<0))", + //79 + "f(x/(a+b*x^4),1/2*sqrt(b/a)/b*arctan(x^2*sqrt(b/a)),or(not(number(a*b)),a*b>0))", + //80 + "f(x/(a+b*x^4),1/4*sqrt(-b/a)/b*log((x^2-sqrt(-a/b))/(x^2+sqrt(-a/b))),or(not(number(a*b)),a*b<0))", + // float(defint(X^2/(2+3*X^4),X,0,pi)) gave wrong result. + // Also, the tests related to the indefinite integral + // fail since we rationalise expressions "better", so I'm thinking + // to take this out completely as it seemed to give the + // wrong results in the first place. + //81 + //"f(x^2/(a+b*x^4),1/4*1/b*(a/b/4)^(-1/4)*(1/2*log((x^2-2*(a/b/4)^(1/4)*x+2*sqrt(a/b/4))/(x^2+2*(a/b/4)^(1/4)*x+2*sqrt(a/b/4)))+arctan(2*(a/b/4)^(1/4)*x/(2*sqrt(a/b/4)-x^2))),or(not(number(a*b)),a*b>0))", + //82 + //"f(x^2/(a+b*x^4),1/4*1/b*(-a/b)^(-1/4)*(log((x-(-a/b)^(1/4))/(x+(-a/b)^(1/4)))+2*arctan(x*(-a/b)^(-1/4))),or(not(number(a*b)),a*b<0))", + //83 + "f(x^3/(a+b*x^4),1/4*1/b*log(a+b*x^4))", + //124 + "f(sqrt(a+b*x),2/3*1/b*sqrt((a+b*x)^3))", + //125 + "f(x*sqrt(a+b*x),-2*(2*a-3*b*x)*sqrt((a+b*x)^3)/15/b^2)", + //126 + "f(x^2*sqrt(a+b*x),2*(8*a^2-12*a*b*x+15*b^2*x^2)*sqrt((a+b*x)^3)/105/b^3)", + //128 + "f(sqrt(a+b*x)/x,2*sqrt(a+b*x)+a*integral(1/x*1/sqrt(a+b*x),x))", + //129 + "f(sqrt(a+b*x)/x^2,-sqrt(a+b*x)/x+b/2*integral(1/x*1/sqrt(a+b*x),x))", + //131 + "f(1/sqrt(a+b*x),2*sqrt(a+b*x)/b)", + //132 + "f(x/sqrt(a+b*x),-2/3*(2*a-b*x)*sqrt(a+b*x)/b^2)", + //133 + "f(x^2/sqrt(a+b*x),2/15*(8*a^2-4*a*b*x+3*b^2*x^2)*sqrt(a+b*x)/b^3)", + //135 + "f(1/x*1/sqrt(a+b*x),1/sqrt(a)*log((sqrt(a+b*x)-sqrt(a))/(sqrt(a+b*x)+sqrt(a))),or(not(number(a)),a>0))", + //136 + "f(1/x*1/sqrt(a+b*x),2/sqrt(-a)*arctan(sqrt(-(a+b*x)/a)),or(not(number(a)),a<0))", + //137 + "f(1/x^2*1/sqrt(a+b*x),-sqrt(a+b*x)/a/x-1/2*b/a*integral(1/x*1/sqrt(a+b*x),x))", + //156 + "f(sqrt(x^2+a),1/2*(x*sqrt(x^2+a)+a*log(x+sqrt(x^2+a))))", + //157 + "f(1/sqrt(x^2+a),log(x+sqrt(x^2+a)))", + //158 + "f(1/x*1/sqrt(x^2+a),arcsec(x/sqrt(-a))/sqrt(-a),or(not(number(a)),a<0))", + //159 + "f(1/x*1/sqrt(x^2+a),-1/sqrt(a)*log((sqrt(a)+sqrt(x^2+a))/x),or(not(number(a)),a>0))", + //160 + "f(sqrt(x^2+a)/x,sqrt(x^2+a)-sqrt(a)*log((sqrt(a)+sqrt(x^2+a))/x),or(not(number(a)),a>0))", + //161 + "f(sqrt(x^2+a)/x,sqrt(x^2+a)-sqrt(-a)*arcsec(x/sqrt(-a)),or(not(number(a)),a<0))", + //162 + "f(x/sqrt(x^2+a),sqrt(x^2+a))", + //163 + "f(x*sqrt(x^2+a),1/3*sqrt((x^2+a)^3))", + //164 need an unexpanded version? + "f(sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/4*(x*sqrt((x^2+a^(1/3))^3)+3/2*a^(1/3)*x*sqrt(x^2+a^(1/3))+3/2*a^(2/3)*log(x+sqrt(x^2+a^(1/3)))))", + // match doesn't work for the following + "f(sqrt(-a+x^6-3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/4*(x*sqrt((x^2-a^(1/3))^3)-3/2*a^(1/3)*x*sqrt(x^2-a^(1/3))+3/2*a^(2/3)*log(x+sqrt(x^2-a^(1/3)))))", + //165 + "f(1/sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),x/a^(1/3)/sqrt(x^2+a^(1/3)))", + //166 + "f(x/sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),-1/sqrt(x^2+a^(1/3)))", + //167 + "f(x*sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/5*sqrt((x^2+a^(1/3))^5))", + //168 + "f(x^2*sqrt(x^2+a),1/4*x*sqrt((x^2+a)^3)-1/8*a*x*sqrt(x^2+a)-1/8*a^2*log(x+sqrt(x^2+a)))", + //169 + "f(x^3*sqrt(x^2+a),(1/5*x^2-2/15*a)*sqrt((x^2+a)^3),and(number(a),a>0))", + //170 + "f(x^3*sqrt(x^2+a),sqrt((x^2+a)^5)/5-a*sqrt((x^2+a)^3)/3,and(number(a),a<0))", + //171 + "f(x^2/sqrt(x^2+a),1/2*x*sqrt(x^2+a)-1/2*a*log(x+sqrt(x^2+a)))", + //172 + "f(x^3/sqrt(x^2+a),1/3*sqrt((x^2+a)^3)-a*sqrt(x^2+a))", + //173 + "f(1/x^2*1/sqrt(x^2+a),-sqrt(x^2+a)/a/x)", + //174 + "f(1/x^3*1/sqrt(x^2+a),-1/2*sqrt(x^2+a)/a/x^2+1/2*log((sqrt(a)+sqrt(x^2+a))/x)/a^(3/2),or(not(number(a)),a>0))", + //175 + "f(1/x^3*1/sqrt(x^2-a),1/2*sqrt(x^2-a)/a/x^2+1/2*1/(a^(3/2))*arcsec(x/(a^(1/2))),or(not(number(a)),a>0))", + //176+ + "f(x^2*sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/6*x*sqrt((x^2+a^(1/3))^5)-1/24*a^(1/3)*x*sqrt((x^2+a^(1/3))^3)-1/16*a^(2/3)*x*sqrt(x^2+a^(1/3))-1/16*a*log(x+sqrt(x^2+a^(1/3))),or(not(number(a)),a>0))", + //176- + "f(x^2*sqrt(-a-3*a^(1/3)*x^4+3*a^(2/3)*x^2+x^6),1/6*x*sqrt((x^2-a^(1/3))^5)+1/24*a^(1/3)*x*sqrt((x^2-a^(1/3))^3)-1/16*a^(2/3)*x*sqrt(x^2-a^(1/3))+1/16*a*log(x+sqrt(x^2-a^(1/3))),or(not(number(a)),a>0))", + //177+ + "f(x^3*sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/7*sqrt((x^2+a^(1/3))^7)-1/5*a^(1/3)*sqrt((x^2+a^(1/3))^5),or(not(number(a)),a>0))", + //177- + "f(x^3*sqrt(-a-3*a^(1/3)*x^4+3*a^(2/3)*x^2+x^6),1/7*sqrt((x^2-a^(1/3))^7)+1/5*a^(1/3)*sqrt((x^2-a^(1/3))^5),or(not(number(a)),a>0))", + //196 + "f(1/(x-a)/sqrt(x^2-a^2),-sqrt(x^2-a^2)/a/(x-a))", + //197 + "f(1/(x+a)/sqrt(x^2-a^2),sqrt(x^2-a^2)/a/(x+a))", + //200+ + "f(sqrt(a-x^2),1/2*(x*sqrt(a-x^2)+a*arcsin(x/sqrt(abs(a)))))", + //201 (seems to be handled somewhere else) + //202 + "f(1/x*1/sqrt(a-x^2),-1/sqrt(a)*log((sqrt(a)+sqrt(a-x^2))/x),or(not(number(a)),a>0))", + //203 + "f(sqrt(a-x^2)/x,sqrt(a-x^2)-sqrt(a)*log((sqrt(a)+sqrt(a-x^2))/x),or(not(number(a)),a>0))", + //204 + "f(x/sqrt(a-x^2),-sqrt(a-x^2))", + //205 + "f(x*sqrt(a-x^2),-1/3*sqrt((a-x^2)^3))", + //210 + "f(x^2*sqrt(a-x^2),-x/4*sqrt((a-x^2)^3)+1/8*a*(x*sqrt(a-x^2)+a*arcsin(x/sqrt(a))),or(not(number(a)),a>0))", + //211 + "f(x^3*sqrt(a-x^2),(-1/5*x^2-2/15*a)*sqrt((a-x^2)^3),or(not(number(a)),a>0))", + //214 + "f(x^2/sqrt(a-x^2),-x/2*sqrt(a-x^2)+a/2*arcsin(x/sqrt(a)),or(not(number(a)),a>0))", + //215 + "f(1/x^2*1/sqrt(a-x^2),-sqrt(a-x^2)/a/x,or(not(number(a)),a>0))", + //216 + "f(sqrt(a-x^2)/x^2,-sqrt(a-x^2)/x-arcsin(x/sqrt(a)),or(not(number(a)),a>0))", + //217 + "f(sqrt(a-x^2)/x^3,-1/2*sqrt(a-x^2)/x^2+1/2*log((sqrt(a)+sqrt(a-x^2))/x)/sqrt(a),or(not(number(a)),a>0))", + //218 + "f(sqrt(a-x^2)/x^4,-1/3*sqrt((a-x^2)^3)/a/x^3,or(not(number(a)),a>0))", + // 273 + "f(sqrt(a*x^2+b),x*sqrt(a*x^2+b)/2+b*log(x*sqrt(a)+sqrt(a*x^2+b))/2/sqrt(a),and(number(a),a>0))", + // 274 + "f(sqrt(a*x^2+b),x*sqrt(a*x^2+b)/2+b*arcsin(x*sqrt(-a/b))/2/sqrt(-a),and(number(a),a<0))", + // 290 + "f(sin(a*x),-cos(a*x)/a)", + // 291 + "f(cos(a*x),sin(a*x)/a)", + // 292 + "f(tan(a*x),-log(cos(a*x))/a)", + // 293 + "f(1/tan(a*x),log(sin(a*x))/a)", + // 294 + "f(1/cos(a*x),log(tan(pi/4+a*x/2))/a)", + // 295 + "f(1/sin(a*x),log(tan(a*x/2))/a)", + // 296 + "f(sin(a*x)^2,x/2-sin(2*a*x)/(4*a))", + // 297 + "f(sin(a*x)^3,-cos(a*x)*(sin(a*x)^2+2)/(3*a))", + // 298 + "f(sin(a*x)^4,3/8*x-sin(2*a*x)/(4*a)+sin(4*a*x)/(32*a))", + // 302 + "f(cos(a*x)^2,x/2+sin(2*a*x)/(4*a))", + // 303 + "f(cos(a*x)^3,sin(a*x)*(cos(a*x)^2+2)/(3*a))", + // 304 + "f(cos(a*x)^4,3/8*x+sin(2*a*x)/(4*a)+sin(4*a*x)/(32*a))", + // 308 + "f(1/sin(a*x)^2,-1/(a*tan(a*x)))", + // 312 + "f(1/cos(a*x)^2,tan(a*x)/a)", + // 318 + "f(sin(a*x)*cos(a*x),sin(a*x)^2/(2*a))", + // 320 + "f(sin(a*x)^2*cos(a*x)^2,-sin(4*a*x)/(32*a)+x/8)", + // 326 + "f(sin(a*x)/cos(a*x)^2,1/(a*cos(a*x)))", + // 327 + "f(sin(a*x)^2/cos(a*x),(log(tan(pi/4+a*x/2))-sin(a*x))/a)", + // 328 + "f(cos(a*x)/sin(a*x)^2,-1/(a*sin(a*x)))", + // 329 + "f(1/(sin(a*x)*cos(a*x)),log(tan(a*x))/a)", + // 330 + "f(1/(sin(a*x)*cos(a*x)^2),(1/cos(a*x)+log(tan(a*x/2)))/a)", + // 331 + "f(1/(sin(a*x)^2*cos(a*x)),(log(tan(pi/4+a*x/2))-1/sin(a*x))/a)", + // 333 + "f(1/(sin(a*x)^2*cos(a*x)^2),-2/(a*tan(2*a*x)))", + // 335 + "f(sin(a+b*x),-cos(a+b*x)/b)", + // 336 + "f(cos(a+b*x),sin(a+b*x)/b)", + // 337+ (with the addition of b) + "f(1/(b+b*sin(a*x)),-tan(pi/4-a*x/2)/a/b)", + // 337- (with the addition of b) + "f(1/(b-b*sin(a*x)),tan(pi/4+a*x/2)/a/b)", + // 338 (with the addition of b) + "f(1/(b+b*cos(a*x)),tan(a*x/2)/a/b)", + // 339 (with the addition of b) + "f(1/(b-b*cos(a*x)),-1/tan(a*x/2)/a/b)", + // 340 + "f(1/(a+b*sin(x)),1/sqrt(b^2-a^2)*log((a*tan(x/2)+b-sqrt(b^2-a^2))/(a*tan(x/2)+b+sqrt(b^2-a^2))),b^2-a^2)", // check that b^2-a^2 is not zero + // 341 + "f(1/(a+b*cos(x)),1/sqrt(b^2-a^2)*log((sqrt(b^2-a^2)*tan(x/2)+a+b)/(sqrt(b^2-a^2)*tan(x/2)-a-b)),b^2-a^2)", // check that b^2-a^2 is not zero + // 389 + "f(x*sin(a*x),sin(a*x)/a^2-x*cos(a*x)/a)", + // 390 + "f(x^2*sin(a*x),2*x*sin(a*x)/a^2-(a^2*x^2-2)*cos(a*x)/a^3)", + // 393 + "f(x*cos(a*x),cos(a*x)/a^2+x*sin(a*x)/a)", + // 394 + "f(x^2*cos(a*x),2*x*cos(a*x)/a^2+(a^2*x^2-2)*sin(a*x)/a^3)", + // 441 + "f(arcsin(a*x),x*arcsin(a*x)+sqrt(1-a^2*x^2)/a)", + // 442 + "f(arccos(a*x),x*arccos(a*x)-sqrt(1-a^2*x^2)/a)", + // 443 + "f(arctan(a*x),x*arctan(a*x)-1/2*log(1+a^2*x^2)/a)", + // 485 (with addition of a) + // however commenting out since it's a duplicate of 14 + // "f(log(a*x),x*log(a*x)-x)", + // 486 (with addition of a) + "f(x*log(a*x),x^2*log(a*x)/2-x^2/4)", + // 487 (with addition of a) + "f(x^2*log(a*x),x^3*log(a*x)/3-1/9*x^3)", + // 489 + "f(log(x)^2,x*log(x)^2-2*x*log(x)+2*x)", + // 493 (with addition of a) + "f(1/x*1/(a+log(x)),log(a+log(x)))", + // 499 + "f(log(a*x+b),(a*x+b)*log(a*x+b)/a-x)", + // 500 + "f(log(a*x+b)/x^2,a/b*log(x)-(a*x+b)*log(a*x+b)/b/x)", + // 554 + "f(sinh(x),cosh(x))", + // 555 + "f(cosh(x),sinh(x))", + // 556 + "f(tanh(x),log(cosh(x)))", + // 560 + "f(x*sinh(x),x*cosh(x)-sinh(x))", + // 562 + "f(x*cosh(x),x*sinh(x)-cosh(x))", + // 566 + "f(sinh(x)^2,sinh(2*x)/4-x/2)", + // 569 + "f(tanh(x)^2,x-tanh(x))", + // 572 + "f(cosh(x)^2,sinh(2*x)/4+x/2)", + // ? + "f(x^3*exp(a*x^2),exp(a*x^2)*(x^2/a-1/(a^2))/2)", + // ? + "f(x^3*exp(a*x^2+b),exp(a*x^2)*exp(b)*(x^2/a-1/(a^2))/2)", + // ? + "f(exp(a*x^2),-i*sqrt(pi)*erf(i*sqrt(a)*x)/sqrt(a)/2)", + // ? + "f(erf(a*x),x*erf(a*x)+exp(-a^2*x^2)/a/sqrt(pi))", + // these are needed for the surface integral in the manual + "f(x^2*(1-x^2)^(3/2),(x*sqrt(1-x^2)*(-8*x^4+14*x^2-3)+3*arcsin(x))/48)", + "f(x^2*(1-x^2)^(5/2),(x*sqrt(1-x^2)*(48*x^6-136*x^4+118*x^2-15)+15*arcsin(x))/384)", + "f(x^4*(1-x^2)^(3/2),(-x*sqrt(1-x^2)*(16*x^6-24*x^4+2*x^2+3)+3*arcsin(x))/128)", + "f(x*exp(a*x),exp(a*x)*(a*x-1)/(a^2))", + "f(x*exp(a*x+b),exp(a*x+b)*(a*x-1)/(a^2))", + "f(x^2*exp(a*x),exp(a*x)*(a^2*x^2-2*a*x+2)/(a^3))", + "f(x^2*exp(a*x+b),exp(a*x+b)*(a^2*x^2-2*a*x+2)/(a^3))", + "f(x^3*exp(a*x),exp(a*x)*x^3/a-3/a*integral(x^2*exp(a*x),x))", + "f(x^3*exp(a*x+b),exp(a*x+b)*x^3/a-3/a*integral(x^2*exp(a*x+b),x))", + 0 + ]; + + //define F p3 + //define X p4 + //define N p5 Eval_integral = function() { var doNothing, i, i1, n, o, ref, ref1; i = 0; n = 0; + // evaluate 1st arg to get function F p1 = cdr(p1); push(car(p1)); Eval(); + // evaluate 2nd arg and then... + + // example result of 2nd arg what to do + + // integral(f) nil guess X, N = nil + // integral(f,2) 2 guess X, N = 2 + // integral(f,x) x X = x, N = nil + // integral(f,x,2) x X = x, N = 2 + // integral(f,x,y) x X = x, N = y p1 = cdr(p1); push(car(p1)); Eval(); @@ -10710,6 +12938,7 @@ p4 = pop(); p3 = pop(); while (1) { + // N might be a symbol instead of a number if (isNumericAtom(p5)) { push(p5); n = pop_integer(); @@ -10721,47 +12950,59 @@ } push(p3); if (n >= 0) { - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push(p4); integral(); } } else { n = -n; - for (i = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { push(p4); derivative(); } } p3 = pop(); + // if N is nil then arglist is exhausted if (p5 === symbol(NIL)) { break; } + // otherwise... + + // N arg1 what to do + + // number nil break + // number number N = arg1, continue + // number symbol X = arg1, N = arg2, continue + + // symbol nil X = N, N = nil, continue + // symbol number X = N, N = arg1, continue + // symbol symbol X = N, N = arg1, continue if (isNumericAtom(p5)) { p1 = cdr(p1); push(car(p1)); Eval(); p5 = pop(); if (p5 === symbol(NIL)) { - break; + break; // arglist exhausted } if (isNumericAtom(p5)) { - doNothing = 1; + doNothing = 1; // N = arg1 } else { p4 = p5; p1 = cdr(p1); push(car(p1)); Eval(); - p5 = pop(); + p5 = pop(); // N = arg2 } } else { p4 = p5; p1 = cdr(p1); push(car(p1)); Eval(); - p5 = pop(); + p5 = pop(); // N = arg1 } } - return push(p3); + return push(p3); // final result }; integral = function() { @@ -10809,7 +13050,7 @@ partition(); p1 = pop(); integral_of_form(); - return multiply(); + return multiply(); // multiply constant part }; integral_of_form = function() { @@ -10817,14 +13058,16 @@ hc = italu_hashcode(p1, p2).toFixed(6); tab = hashed_itab[hc]; if (!tab) { + // debugger + // italu_hashcode(p1, p2) push_symbol(INTEGRAL); push(p1); push(p2); list(3); return; } - push(p1); - push(p2); + push(p1); // free variable + push(p2); // input expression transform(tab, false); p3 = pop(); if (p3 === symbol(NIL)) { @@ -10837,6 +13080,12 @@ } }; + // Implementation of hash codes based on ITALU (An Integral Table Look-Up) + // https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19680004891.pdf + // see Appendix A, page 153 + + // The first two values are from the ITALU paper. + // The others are just arbitrary constants. hashcode_values = { 'x': 0.95532, 'constexp': 1.43762, @@ -10908,6 +13157,9 @@ if (Find(term, x)) { term_hash = italu_hashcode(term, x); } else { + // The original algorithm would skip this, + // but recording that it was present helps + // prevent collisions. term_hash = hashcode_values.constant; } term_set[term_hash.toFixed(6)] = true; @@ -10944,6 +13196,7 @@ if (Find(power, x)) { exp_hash = italu_hashcode(power, x); } else { + // constant to constant = constant if (base_hash === hashcode_values.constant) { return hashcode_values.constant; } @@ -10986,6 +13239,10 @@ $.make_hashed_itab = make_hashed_itab; + // pre-calculated hashed integral table. + // in case the integral table is changed, use this + // Algebrite.make_hashed_itab() + // and copy the resulting JSON in here. hashed_itab = { "1.144166": ["f(a,a*x)"], "1.046770": ["f(1/x,log(x))"], @@ -11113,6 +13370,27 @@ "1.242392": ["f(x^3*exp(a*x+b),exp(a*x+b)*x^3/a-3/a*integral(x^2*exp(a*x+b),x))"] }; + //----------------------------------------------------------------------------- + + // Input: Matrix on stack (must have two dimensions but + // it can be non-numerical) + + // Output: Inverse on stack + + // Example: + + // > inv(((1,2),(3,4)) + // ((-2,1),(3/2,-1/2)) + + // > inv(((a,b),(c,d)) + // ((d / (a d - b c),-b / (a d - b c)),(-c / (a d - b c),a / (a d - b c))) + + // Note: + + // THIS IS DIFFERENT FROM INVERSE OF AN EXPRESSION (inv) + // Uses Gaussian elimination for numerical matrices. + + //----------------------------------------------------------------------------- INV_check_arg = function() { if (!istensor(p1)) { return 0; @@ -11129,18 +13407,29 @@ var accumulator, eachEntry, i, n, o, ref; i = 0; n = 0; + //U **a save(); p1 = pop(); + // an inv just goes away when + // applied to another inv if (isinv(p1)) { push(car(cdr(p1))); restore(); return; } + // inverse goes away in case + // of identity matrix if (isidentitymatrix(p1)) { push(p1); restore(); return; } + // distribute the inverse of a dot + // if in expanding mode + // note that the distribution happens + // in reverse. + // The dot operator is not + // commutative, so, it matters. if (expanding && isinnerordot(p1)) { p1 = cdr(p1); accumulator = []; @@ -11148,7 +13437,7 @@ accumulator.push(car(p1)); p1 = cdr(p1); } - for (eachEntry = o = ref = accumulator.length - 1; ref <= 0 ? o <= 0 : o >= 0; eachEntry = ref <= 0 ? ++o : --o) { + for (eachEntry = o = ref = accumulator.length - 1; (ref <= 0 ? o <= 0 : o >= 0); eachEntry = ref <= 0 ? ++o : --o) { push(accumulator[eachEntry]); inv(); if (eachEntry !== accumulator.length - 1) { @@ -11196,6 +13485,7 @@ return restore(); }; + // inverse using gaussian elimination yyinvg = function() { var h, i, i1, j, j1, l1, n, o, ref, ref1, ref2, ref3; h = 0; @@ -11204,8 +13494,8 @@ n = 0; n = p1.tensor.dim[0]; h = tos; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { - for (j = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { + for (j = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); j = 0 <= ref1 ? ++i1 : --i1) { if (i === j) { push(one); } else { @@ -11213,7 +13503,7 @@ } } } - for (i = j1 = 0, ref2 = n * n; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = n * n; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { push(p1.tensor.elem[i]); } INV_decomp(n); @@ -11221,13 +13511,29 @@ p1.tensor.ndim = 2; p1.tensor.dim[0] = n; p1.tensor.dim[1] = n; - for (i = l1 = 0, ref3 = n * n; 0 <= ref3 ? l1 < ref3 : l1 > ref3; i = 0 <= ref3 ? ++l1 : --l1) { + for (i = l1 = 0, ref3 = n * n; (0 <= ref3 ? l1 < ref3 : l1 > ref3); i = 0 <= ref3 ? ++l1 : --l1) { p1.tensor.elem[i] = stack[h + i]; } moveTos(tos - 2 * n * n); return push(p1); }; + //----------------------------------------------------------------------------- + + // Input: n * n unit matrix on stack + + // n * n operand on stack + + // Output: n * n inverse matrix on stack + + // n * n garbage on stack + + // p2 mangled + + //----------------------------------------------------------------------------- + + //define A(i, j) stack[a + n * (i) + (j)] + //define U(i, j) stack[u + n * (i) + (j)] INV_decomp = function(n) { var a, d, i, i1, j, j1, l1, o, ref, ref1, ref2, ref3, ref4, results, u; a = 0; @@ -11238,9 +13544,11 @@ a = tos - n * n; u = a - n * n; results = []; - for (d = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; d = 0 <= ref ? ++o : --o) { + for (d = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); d = 0 <= ref ? ++o : --o) { + // diagonal element zero? if (equal(stack[a + n * d + d], zero)) { - for (i = i1 = ref1 = d + 1, ref2 = n; ref1 <= ref2 ? i1 < ref2 : i1 > ref2; i = ref1 <= ref2 ? ++i1 : --i1) { +// find a new row + for (i = i1 = ref1 = d + 1, ref2 = n; (ref1 <= ref2 ? i1 < ref2 : i1 > ref2); i = ref1 <= ref2 ? ++i1 : --i1) { if (!equal(stack[a + n * i + d], zero)) { break; } @@ -11248,7 +13556,8 @@ if (i === n) { stop("inverse of singular matrix"); } - for (j = j1 = 0, ref3 = n; 0 <= ref3 ? j1 < ref3 : j1 > ref3; j = 0 <= ref3 ? ++j1 : --j1) { +// exchange rows + for (j = j1 = 0, ref3 = n; (0 <= ref3 ? j1 < ref3 : j1 > ref3); j = 0 <= ref3 ? ++j1 : --j1) { p2 = stack[a + n * d + j]; stack[a + n * d + j] = stack[a + n * i + j]; stack[a + n * i + j] = p2; @@ -11257,8 +13566,9 @@ stack[u + n * i + j] = p2; } } + // multiply the pivot row by 1 / pivot p2 = stack[a + n * d + d]; - for (j = l1 = 0, ref4 = n; 0 <= ref4 ? l1 < ref4 : l1 > ref4; j = 0 <= ref4 ? ++l1 : --l1) { + for (j = l1 = 0, ref4 = n; (0 <= ref4 ? l1 < ref4 : l1 > ref4); j = 0 <= ref4 ? ++l1 : --l1) { if (j > d) { push(stack[a + n * d + j]); push(p2); @@ -11272,16 +13582,19 @@ } results.push((function() { var m1, ref5, results1; +// clear out the column above and below the pivot results1 = []; - for (i = m1 = 0, ref5 = n; 0 <= ref5 ? m1 < ref5 : m1 > ref5; i = 0 <= ref5 ? ++m1 : --m1) { + for (i = m1 = 0, ref5 = n; (0 <= ref5 ? m1 < ref5 : m1 > ref5); i = 0 <= ref5 ? ++m1 : --m1) { if (i === d) { continue; } + // multiplier p2 = stack[a + n * i + d]; results1.push((function() { var n1, ref6, results2; +// add pivot row to i-th row results2 = []; - for (j = n1 = 0, ref6 = n; 0 <= ref6 ? n1 < ref6 : n1 > ref6; j = 0 <= ref6 ? ++n1 : --n1) { + for (j = n1 = 0, ref6 = n; (0 <= ref6 ? n1 < ref6 : n1 > ref6); j = 0 <= ref6 ? ++n1 : --n1) { if (j > d) { push(stack[a + n * i + j]); push(stack[a + n * d + j]); @@ -11308,6 +13621,10 @@ DEBUG_IS = false; + // p is a U + // this routine is a simple check on whether we have + // a basic zero in our hands. It doesn't perform any + // calculations or simplifications. isZeroAtom = function(p) { switch (p.k) { case NUM: @@ -11323,12 +13640,16 @@ return 0; }; + // p is a U + // this routine is a simple check on whether we have + // a basic zero in our hands. It doesn't perform any + // calculations or simplifications. isZeroTensor = function(p) { var i, o, ref; if (p.k !== TENSOR) { return 0; } - for (i = o = 0, ref = p.tensor.nelem; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = p.tensor.nelem; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { if (!isZeroAtomOrTensor(p.tensor.elem[i])) { return 0; } @@ -11336,35 +13657,86 @@ return 1; }; + // p is a U + // this routine is a simple check on whether we have + // a basic zero in our hands. It doesn't perform any + // calculations or simplifications. isZeroAtomOrTensor = function(p) { return isZeroAtom(p) || isZeroTensor(p); }; + // This is a key routine to try to determine whether + // the argument looks like zero/false, or non-zero/true, + // or undetermined. + // This is useful in two instances: + // * to determine if a predicate is true/false + // * to determine if particular quantity is zero + // Note that if one wants to check if we have a simple + // zero atom or tensor in our hands, then the isZeroAtomOrTensor + // routine is sufficient. isZeroLikeOrNonZeroLikeOrUndetermined = function(valueOrPredicate) { var evalledArgument; + // push the argument push(valueOrPredicate); + // just like Eval but turns assignments into + // equality checks Eval_predicate(); evalledArgument = pop(); + // OK first check if we already have + // a simple zero (or simple zero tensor) if (isZeroAtomOrTensor(evalledArgument)) { return 0; } + // also check if we have a simple numeric value, or a tensor + // full of simple numeric values (i.e. straight doubles or fractions). + // In such cases, since we + // just excluded they are zero, then we take it as + // a "true" if (isNumericAtomOrTensor(evalledArgument)) { return 1; } + // if we are here we are in the case of value that + // is not a zero and not a simple numeric value. + // e.g. stuff like + // 'sqrt(2)', or 'sin(45)' or '1+i', or 'a' + // so in such cases let's try to do a float() + // so we might get down to a simple numeric value + // in some of those cases push(evalledArgument); zzfloat(); evalledArgument = pop(); + // anything that could be calculated down to a simple + // numeric value is now indeed either a + // double OR a double with an imaginary component + // e.g. 2.0 or 2.4 + i*5.6 + // (Everything else are things that don't have a numeric + // value e.g. 'a+b') + + // So, let's take care of the case where we have + // a simple numeric value with NO imaginary component, + // things like sqrt(2) or sin(PI) + // by doing the simple numeric + // values checks again if (isZeroAtomOrTensor(evalledArgument)) { return 0; } if (isNumericAtomOrTensor(evalledArgument)) { return 1; } + // here we still have cases of simple numeric values + // WITH an imaginary component e.g. '1+i', + // or things that don't have a numeric value e.g. 'a' + + // so now let's take care of the imaginary numbers: + // since we JUST have to spot "zeros" we can just + // calculate the absolute value and re-do all the checks + // we just did if (Find(evalledArgument, imaginaryunit)) { push(evalledArgument); absValFloat(); Eval_predicate(); evalledArgument = pop(); + // re-do the simple-number checks... if (isZeroAtomOrTensor(evalledArgument)) { return 0; } @@ -11372,9 +13744,15 @@ return 1; } } + // here we have stuff that is not reconducible to any + // numeric value (or tensor with numeric values) e.g. + // 'a+b', so it just means that we just don't know the + // truth value, so we have + // to leave the whole thing unevalled return null; }; + // p is a U isnegativenumber = function(p) { switch (p.k) { case NUM: @@ -11390,6 +13768,7 @@ return 0; }; + // p is a U ispositivenumber = function(p) { switch (p.k) { case NUM: @@ -11405,6 +13784,7 @@ return 0; }; + // p is a U isplustwo = function(p) { switch (p.k) { case NUM: @@ -11420,6 +13800,7 @@ return 0; }; + // p is a U isplusone = function(p) { switch (p.k) { case NUM: @@ -11488,6 +13869,7 @@ } }; + // -------------------------------------- isunivarpolyfactoredorexpandedform = function(p, x) { if (x == null) { push(p); @@ -11502,6 +13884,10 @@ } }; + // -------------------------------------- + // sometimes we want to check if we have a poly in our + // hands, however it's in factored form and we don't + // want to expand it. ispolyfactoredorexpandedform = function(p, x) { return ispolyfactoredorexpandedform_factor(p, x); }; @@ -11541,6 +13927,7 @@ } }; + // -------------------------------------- ispolyexpandedform = function(p, x) { if (Find(p, x)) { return ispolyexpandedform_expr(p, x); @@ -11597,6 +13984,7 @@ } }; + // -------------------------------------- isnegativeterm = function(p) { if (isnegativenumber(p)) { return 1; @@ -11686,6 +14074,11 @@ } }; + // returns 1 if there's a symbol somewhere. + // not used anywhere. Note that PI and POWER are symbols, + // so for example 2^3 would be symbolic + // while -1^(1/2) i.e. 'i' is not, so this can + // be tricky to use. issymbolic = function(p) { if (issymbol(p)) { return 1; @@ -11700,6 +14093,7 @@ } }; + // i.e. 2, 2^3, etc. isintegerfactor = function(p) { if (isinteger(p) || car(p) === symbol(POWER) && isinteger(cadr(p)) && isinteger(caddr(p))) { return 1; @@ -11732,6 +14126,7 @@ } }; + // p is a U, n an int equaln = function(p, n) { switch (p.k) { case NUM: @@ -11747,6 +14142,7 @@ return 0; }; + // p is a U, a and b ints equalq = function(p, a, b) { switch (p.k) { case NUM: @@ -11762,6 +14158,7 @@ return 0; }; + // p == 1/2 ? isoneovertwo = function(p) { if (equalq(p, 1, 2)) { return 1; @@ -11770,6 +14167,7 @@ } }; + // p == -1/2 ? isminusoneovertwo = function(p) { if (equalq(p, -1, 2)) { return 1; @@ -11778,6 +14176,7 @@ } }; + // p == 1/sqrt(2) ? isoneoversqrttwo = function(p) { if (car(p) === symbol(POWER) && equaln(cadr(p), 2) && equalq(caddr(p), -1, 2)) { return 1; @@ -11786,6 +14185,7 @@ } }; + // p == -1/sqrt(2) ? isminusoneoversqrttwo = function(p) { if (car(p) === symbol(MULTIPLY) && equaln(cadr(p), -1) && isoneoversqrttwo(caddr(p)) && length(p) === 3) { return 1; @@ -11815,6 +14215,19 @@ } }; + // n/2 * i * pi ? + + // return value: + + // 0 no + + // 1 1 + + // 2 -1 + + // 3 i + + // 4 -i isquarterturn = function(p) { var minussign, n; n = 0; @@ -11878,6 +14291,13 @@ return n; }; + // special multiple of pi? + + // returns for the following multiples of pi... + + // -4/2 -3/2 -2/2 -1/2 1/2 2/2 3/2 4/2 + + // 4 1 2 3 1 2 3 4 isnpi = function(p) { var doNothing, n; n = 0; @@ -11959,38 +14379,14 @@ } }; - - /* - Laguerre function - - Example - - laguerre(x,3) - - Result - - 1 3 3 2 - - --- x + --- x - 3 x + 1 - 6 2 - - The computation uses the following recurrence relation. - - L(x,0,k) = 1 - - L(x,1,k) = -x + k + 1 - - n*L(x,n,k) = (2*(n-1)+1-x+k)*L(x,n-1,k) - (n-1+k)*L(x,n-2,k) - - In the "for" loop i = n-1 so the recurrence relation becomes - - (i+1)*L(x,n,k) = (2*i+1-x+k)*L(x,n-1,k) - (i+k)*L(x,n-2,k) - */ - Eval_laguerre = function() { + // 1st arg push(cadr(p1)); Eval(); + // 2nd arg push(caddr(p1)); Eval(); + // 3rd arg push(cadddr(p1)); Eval(); p2 = pop(); @@ -12002,6 +14398,12 @@ return laguerre(); }; + //define X p1 + //define N p2 + //define K p3 + //define Y p4 + //define Y0 p5 + //define Y1 p6 laguerre = function() { var n; n = 0; @@ -12042,7 +14444,7 @@ push_integer(0); p6 = pop(); results = []; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p5 = p6; p6 = pop(); push_integer(2 * i + 1); @@ -12064,6 +14466,7 @@ return results; }; + // Find the least common multiple of two expressions. Eval_lcm = function() { var results; p1 = cdr(p1); @@ -12103,21 +14506,6 @@ return inverse(); }; - - /* - Return the leading coefficient of a polynomial. - - Example - - leading(5x^2+x+1,x) - - Result - - 5 - - The result is undefined if P is not a polynomial. - */ - Eval_leading = function() { push(cadr(p1)); Eval(); @@ -12132,60 +14520,35 @@ return leading(); }; + //define P p1 + //define X p2 + //define N p3 leading = function() { save(); p2 = pop(); p1 = pop(); - push(p1); + push(p1); // N = degree of P push(p2); degree(); p3 = pop(); - push(p1); + push(p1); // divide through by X ^ N push(p2); push(p3); power(); divide(); - push(p2); + push(p2); // remove terms that depend on X filter(); return restore(); }; - - /* - Legendre function - - Example - - legendre(x,3,0) - - Result - - 5 3 3 - --- x - --- x - 2 2 - - The computation uses the following recurrence relation. - - P(x,0) = 1 - - P(x,1) = x - - n*P(x,n) = (2*(n-1)+1)*x*P(x,n-1) - (n-1)*P(x,n-2) - - In the "for" loop we have i = n-1 so the recurrence relation becomes - - (i+1)*P(x,n) = (2*i+1)*x*P(x,n-1) - i*P(x,n-2) - - For m > 0 - - P(x,n,m) = (-1)^m * (1-x^2)^(m/2) * d^m/dx^m P(x,n) - */ - Eval_legendre = function() { + // 1st arg push(cadr(p1)); Eval(); + // 2nd arg push(caddr(p1)); Eval(); + // 3rd arg (optional) push(cadddr(p1)); Eval(); p2 = pop(); @@ -12197,6 +14560,12 @@ return legendre(); }; + //define X p1 + //define N p2 + //define M p3 + //define Y p4 + //define Y0 p5 + //define Y1 p6 legendre = function() { save(); __legendre(); @@ -12243,7 +14612,18 @@ push_integer(1); push_integer(0); p6 = pop(); - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { +// i=1 p5 = 0 +// p6 = 1 +// ((2*i+1)*x*p6 - i*p5) / i = x + +// i=2 p5 = 1 +// p6 = x +// ((2*i+1)*x*p6 - i*p5) / i = -1/2 + 3/2*x^2 + +// i=3 p5 = x +// p6 = -1/2 + 3/2*x^2 +// ((2*i+1)*x*p6 - i*p5) / i = -3/2*x + 5/2*x^3 + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p5 = p6; p6 = pop(); push_integer(2 * i + 1); @@ -12259,13 +14639,14 @@ divide(); } results = []; - for (i = i1 = 0, ref1 = m; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = m; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { push(p1); results.push(derivative()); } return results; }; + // moveTos tos * (-1)^m * (1-x^2)^(m/2) __legendre3 = function(m) { if (m === 0) { return; @@ -12294,17 +14675,28 @@ } }; + // Create a list from n things on the stack. + + // n is an integer list = function(n) { var listIterator, o, ref, results; listIterator = 0; push(symbol(NIL)); results = []; - for (listIterator = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; listIterator = 0 <= ref ? ++o : --o) { + for (listIterator = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); listIterator = 0 <= ref ? ++o : --o) { results.push(cons()); } return results; }; + // Natural logarithm. + + // Note that we use the mathematics / Javascript / Mathematica + // convention that "log" is indeed the natural logarithm. + + // In engineering, biology, astronomy, "log" can stand instead + // for the "common" logarithm i.e. base 10. Also note that Google + // calculations use log for the common logarithm. Eval_log = function() { push(cadr(p1)); Eval(); @@ -12348,6 +14740,7 @@ push_double(d); return; } + // rational number and not an integer? if (isfraction(p1)) { push(p1); numerator(); @@ -12358,6 +14751,7 @@ subtract(); return; } + // log(a ^ b) --> b log(a) if (car(p1) === symbol(POWER)) { push(caddr(p1)); push(cadr(p1)); @@ -12365,6 +14759,7 @@ multiply(); return; } + // log(a * b) --> log(a) + log(b) if (car(p1) === symbol(MULTIPLY)) { push_integer(0); p1 = cdr(p1); @@ -12381,14 +14776,131 @@ return list(2); }; + // now this might be a little confusing, so a + // clarification is in order. + // First off, at the scripting level most things + // as they are handled get evalled. + // That means that they are recursively "calculated" + // as much as possible, i.e. variables are recursively + // looked up for their values, operators are applied, + // functions are ivoked, etc. + // I.e. while scripting, most things are + // evalled all the times. + // e.g. if I type + // x = 1+1 + // then x is actually assigned 2, not 1+1 + // Something that helps a little is "quote", e.g. + // If I assign + // x = quote(1+1) + // then x actually contains 1+1, not 2. + // But then x is evaluated as soon as I type + // x // gives "2" as x is evaluated + + // Evaluation is great, but sometimes one wants + // to look at the actual structure of an expression + // or a content of a variable, without those + // being evaluated first. + + // for example I might type + // x = a + b + // a = 1 + // b = 2 + // and from this point on printing the actual + // structure of x is impossible, because from + // now on any evaluation of x will give "3" + // You might say "but you have x defined up there, + // what's the point of printing it out?", to which + // the answer is that one might do further + // substitutions or transformations of special kind + // to x. One might want to look at the structure + // and it might be complex or impossible. + + // So this function does that. + // If it's passed a variable, then it + // DOES NOT eval the variable, RATHER + // it prints the content of the variable without + // evaluating it. + // In the other cases it works like "quote" e.g. + // it just gives the argument as is, again without + // evaluating it. + + // In the following examples, for brevity, I just + // use + // x = quote(1+2) + // instead of this: + // x = a + b + // a = 1 + // b = 2 + // to put a structure in x that is easy to see whether + // it's avaulated or not. + + // So lookup allows this: + // x = quote(1+2) + // print(lookup(x)) # gives 1+2 + + // Note that there would be potentially a way + // to achieve a similar result, you could do: + // x = quote(quote(1+2)) + // print(x) + // but you can't always control x to contain + // two quotes like that... + // note how two "quotes" are needed because + // if you just put one, then + // x would indeed contain 1+2 instead of 3, + // but then print would evaluate that to 3: + // x = quote(1+2) # now x contains 1+2, not 3 + // print(x) # but x evaluated here to 3 + + // Other workarounds would not work: + // x = quote(1+2) + // print(quote(x)) + // would not work because quote(x) literally means 'x' + // so 'x' is printed instead of its content. + + // Note also that lookup allows you to copy + // the structure of a variable to another: + // x = a + b + // a = 1 + // b = 2 + // now: + // y = x # y contains the number 3 and prints to 3 + // y = lookup(x) # y contains "a+b" and prints to 3 + // y = quote(x) # y contains "x" and prints to 3 + // note that in the first and second case y is + // independent from x, i.e. changing x doesn't change y + // while in the last case it is. + + // Another similar simple example is when doing something + // like this: + // x = y + // y = z + // x + // => gives z + // lookup(x) + // => gives y + // i.e. lookup allows you to see the immediate + // content of x, rather than the evaluation which + // would end up in x -> y -> z + // Note that if you invert the order of the assignments i.e. + // y = z + // x = y + // Then at this point x immediately contains z, since the + // assignment x = y is not quoted, hence y is evaluated to z + // when assigned to x. + // lookup(x) + // => gives z Eval_lookup = function() { p1 = cadr(p1); if (!iscons(p1) && cadr(p1).k === SYM) { p1 = get_binding(p1); } - return push(p1); + return push(p1); // Bignum addition and subtraction }; + + //static unsigned int *addf(unsigned int *, unsigned int *) + //static unsigned int *subf(unsigned int *, unsigned int *) + //static int ucmp(unsigned int *, unsigned int *) madd = function(a, b) { return a.add(b); }; @@ -12405,14 +14917,35 @@ return a.subtract(b); }; + // unsigned compare ucmp = function(a, b) { return a.compareAbs(b); }; + //----------------------------------------------------------------------------- + + // Bignum GCD + + // Uses the binary GCD algorithm. + + // See "The Art of Computer Programming" p. 338. + + // mgcd always returns a positive value + + // mgcd(0, 0) = 0 + + // mgcd(u, 0) = |u| + + // mgcd(0, v) = |v| + + //----------------------------------------------------------------------------- mgcd = function(u, v) { return bigInt.gcd(u, v); }; + //if SELFTEST + + // s is a string new_string = function(s) { save(); p1 = new U(); @@ -12426,6 +14959,7 @@ return stop("out of memory"); }; + // both ints push_zero_matrix = function(i, j) { push(alloc_tensor(i * j)); stack[tos - 1].tensor.ndim = 2; @@ -12437,7 +14971,7 @@ var i, o, ref; push_zero_matrix(n, n); i = 0; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { stack[tos - 1].tensor.elem[i * n + i] = one; } return check_tensor_dimensions(stack[tos - 1]); @@ -12460,6 +14994,11 @@ return restore(); }; + // see cmp_expr definition, this + // function alone just does simple structure comparison + // or compares numbers (either rationals or integers or doubles) + // but can't be used alone to test + // more complex mathematical equalities... equal = function(p1, p2) { if (cmp_expr(p1, p2) === 0) { return 1; @@ -12486,6 +15025,19 @@ } }; + // compares whether two expressions + // have the same structure. + // For example this method alone + // would compare "1+1" and "2" + // as different. + // It just so happens though that one oftens + // evaluates the two sides before passing them + // to this function, so chances are that the two + // sides have the same normal form. + // Even a simple evaluation might not cut it + // though... a simplification of both sides + // would then help. And even that might not + // cut it in some cases... cmp_expr = function(p1, p2) { var n; n = 0; @@ -12534,6 +15086,7 @@ if (istensor(p2)) { return 1; } + // recursion here while (iscons(p1) && iscons(p2)) { n = cmp_expr(car(p1), car(p2)); if (n !== 0) { @@ -12616,8 +15169,13 @@ return power(); }; + //__cmp = (p1, p2) -> + // return cmp_expr(p1, p2) + + // n an integer sort_stack = function(n) { var h, subsetOfStack; + //qsort(stack + tos - n, n, sizeof (U *), __cmp) h = tos - n; subsetOfStack = stack.slice(h, h + n); subsetOfStack.sort(cmp_expr); @@ -12628,6 +15186,7 @@ $.length = length; + // Bignum multiplication and division mmul = function(a, b) { return a.multiply(b); }; @@ -12636,7 +15195,7 @@ return a.divide(b); }; - + // a = a + b /* static void addf(unsigned int *a, unsigned int *b, int len) @@ -12649,9 +15208,9 @@ t >>= 32 } } - + // a = a - b - + static void subf(unsigned int *a, unsigned int *b, int len) { @@ -12663,11 +15222,11 @@ t >>= 32 } } - + // a = b * c - + // 0xffffffff + 0xffffffff * 0xffffffff == 0xffffffff00000000 - + static void mulf(unsigned int *a, unsigned int *b, int len, unsigned int c) { @@ -12680,18 +15239,23 @@ } a[i] = (unsigned int) t } - */ - + */ mmod = function(a, b) { return a.mod(b); }; + // return both quotient and remainder of a/b + // we'd have this method as divmod(number) + // but obviously doesn't change the passed parameters mdivrem = function(a, b) { var toReturn; toReturn = a.divmod(b); return [toReturn.quotient, toReturn.remainder]; }; + //if SELFTEST + + // small integer tests Eval_mod = function() { push(cadr(p1)); Eval(); @@ -12746,16 +15310,40 @@ return restore(); }; + // Bignum power + + // a is a bigint, n is a small normal int mpow = function(a, n) { return a.pow(n); }; + //if SELFTEST + + // Bignum prime test (returns 1 if prime, 0 if not) + + // Uses Algorithm P (probabilistic primality test) from p. 395 of + // "The Art of Computer Programming, Volume 2" by Donald E. Knuth. mprime = function(n) { return n.isProbablePrime(); }; + //if SELFTEST + + //----------------------------------------------------------------------------- + + // Bignum root + + // Returns null pointer if not perfect root. + + // The sign of the radicand is ignored. + + //----------------------------------------------------------------------------- mroot = function(n, index) { var i, j, k, o, ref, x, y; + // this doesn't quite work + //return n.pow(1/index + 0.0000000000000001) + + // sign of radicand ignored n = n.abs(); i = 0; j = 0; @@ -12763,6 +15351,7 @@ if (index === 0) { stop("root index is zero"); } + // count number of bits k = 0; while (n.shiftRight(k) > 0) { k++; @@ -12770,19 +15359,24 @@ if (k === 0) { return mint(0); } + // initial guess k = Math.floor((k - 1) / index); j = Math.floor(k / 32 + 1); x = bigInt(j); - for (i = o = 0, ref = j; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = j; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { + // zero-out the ith bit x = x.and(bigInt(1).shiftLeft(i).not()); } while (k >= 0) { + // set the kth bit x = x.or(bigInt(1).shiftLeft(k)); y = mpow(x, index); switch (mcmp(y, n)) { case 0: return x; case 1: + //mp_clr_bit(x, k) + // clear the kth bit x = x.and(bigInt(1).shiftLeft(k).not()); } k--; @@ -12790,6 +15384,20 @@ return 0; }; + //if SELFTEST + + // Symbolic multiplication + + // multiplication is commutative, so it can't be used + // e.g. on two matrices. + // But it can be used, say, on a scalar and a matrix., + // so the output of a multiplication is not + // always a scalar. + + //extern void append(void) + //static void parse_p1(void) + //static void parse_p2(void) + //static void __normalize_radical_factors(int) Eval_multiply = function() { var results; push(cadr(p1)); @@ -12805,6 +15413,9 @@ return results; }; + // this one doesn't eval the factors, + // so you pass i*(-1)^(1/2), it wouldnt't + // give -1, because i is not evalled multiply = function() { if (esc_flag) { stop("escape key stop"); @@ -12823,9 +15434,11 @@ h = 0; i = 0; n = 0; + // pop operands p2 = pop(); p1 = pop(); h = tos; + // is either operand zero? if (isZeroAtom(p1) || isZeroAtom(p2)) { if (evaluatingAsFloats) { push_double(0.0); @@ -12834,6 +15447,9 @@ } return; } + // is either operand a sum? + + //console.log("yymultiply: expanding: " + expanding) if (expanding && isadd(p1)) { p1 = cdr(p1); if (evaluatingAsFloats) { @@ -12872,12 +15488,14 @@ scalar_times_tensor(); return; } + // tensor times scalar? if (istensor(p1) && !istensor(p2)) { push(p1); push(p2); tensor_times_scalar(); return; } + // adjust operands if (car(p1) === symbol(MULTIPLY)) { p1 = cdr(p1); } else { @@ -12892,6 +15510,7 @@ list(1); p2 = pop(); } + // handle numerical coefficients if (isNumericAtom(car(p1)) && isNumericAtom(car(p2))) { push(car(p1)); push(car(p2)); @@ -12914,6 +15533,14 @@ parse_p1(); parse_p2(); while (iscons(p1) && iscons(p2)) { + // if (car(p1)->gamma && car(p2)->gamma) { + // combine_gammas(h) + // p1 = cdr(p1) + // p2 = cdr(p2) + // parse_p1() + // parse_p2() + // continue + // } if (caar(p1) === symbol(OPERATOR) && caar(p2) === symbol(OPERATOR)) { push_symbol(OPERATOR); push(cdar(p1)); @@ -12948,6 +15575,7 @@ stop("internal error 2"); } } + // push remaining factors, if any while (iscons(p1)) { push(car(p1)); p1 = cdr(p1); @@ -12956,19 +15584,36 @@ push(car(p2)); p2 = cdr(p2); } + // normalize radical factors + + // example: 2*2(-1/2) -> 2^(1/2) + + // must be done after merge because merge may produce radical + + // example: 2^(1/2-a)*2^a -> 2^(1/2) __normalize_radical_factors(h); + // this hack should not be necessary, unless power returns a multiply + + //for (i = h; i < tos; i++) { + // if (car(stack[i]) == symbol(MULTIPLY)) { + // multiply_all(tos - h) + // return + // } + //} if (expanding) { - for (i = o = ref = h, ref1 = tos; ref <= ref1 ? o < ref1 : o > ref1; i = ref <= ref1 ? ++o : --o) { + for (i = o = ref = h, ref1 = tos; (ref <= ref1 ? o < ref1 : o > ref1); i = ref <= ref1 ? ++o : --o) { if (isadd(stack[i])) { multiply_all(tos - h); return; } } } + // n is the number of result factors on the stack n = tos - h; if (n === 1) { return; } + // discard integer 1 if (isrational(stack[h]) && equaln(stack[h], 1)) { if (n === 2) { p7 = pop(); @@ -12987,6 +15632,13 @@ return cons(); }; + // Decompose a factor into base and power. + + // input: car(p1) factor + + // output: p3 factor's base + + // p5 factor's power (possibly 1) parse_p1 = function() { p3 = car(p1); p5 = evaluatingAsFloats ? one_as_double : one; @@ -12996,6 +15648,13 @@ } }; + // Decompose a factor into base and power. + + // input: car(p2) factor + + // output: p4 factor's base + + // p6 factor's power (possibly 1) parse_p2 = function() { p4 = car(p2); p6 = evaluatingAsFloats ? one_as_double : one; @@ -13005,6 +15664,7 @@ } }; + // h an integer combine_factors = function(h) { push(p4); push(p5); @@ -13018,6 +15678,7 @@ multiply_numbers(); return stack[h] = pop(); } else if (car(p7) === symbol(MULTIPLY)) { + // power can return number * factor (i.e. -1 * i) if (isNumericAtom(cadr(p7)) && cdddr(p7) === symbol(NIL)) { push(stack[h]); push(cadr(p7)); @@ -13034,6 +15695,9 @@ gp = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, -6, -7, -8, -3, -4, -5, 13, 14, 15, -16, 9, 10, 11, -12], [0, 0, 6, -1, -11, 10, -2, -15, 14, 12, -5, 4, -9, 16, -8, 7, -13], [0, 0, 7, 11, -1, -9, 15, -2, -13, 5, 12, -3, -10, 8, 16, -6, -14], [0, 0, 8, -10, 9, -1, -14, 13, -2, -4, 3, 12, -11, -7, 6, 16, -15], [0, 0, 3, 2, 15, -14, 1, 11, -10, 16, -8, 7, 13, 12, -5, 4, 9], [0, 0, 4, -15, 2, 13, -11, 1, 9, 8, 16, -6, 14, 5, 12, -3, 10], [0, 0, 5, 14, -13, 2, 10, -9, 1, -7, 6, 16, 15, -4, 3, 12, 11], [0, 0, 13, 12, -5, 4, 16, -8, 7, -1, -11, 10, -3, -2, -15, 14, -6], [0, 0, 14, 5, 12, -3, 8, 16, -6, 11, -1, -9, -4, 15, -2, -13, -7], [0, 0, 15, -4, 3, 12, -7, 6, 16, -10, 9, -1, -5, -14, 13, -2, -8], [0, 0, 16, -9, -10, -11, -13, -14, -15, -3, -4, -5, 1, -6, -7, -8, 2], [0, 0, 9, -16, 8, -7, -12, 5, -4, -2, -15, 14, 6, -1, -11, 10, 3], [0, 0, 10, -8, -16, 6, -5, -12, 3, 15, -2, -13, 7, 11, -1, -9, 4], [0, 0, 11, 7, -6, -16, 4, -3, -12, -14, 13, -2, 8, -10, 9, -1, 5], [0, 0, 12, 13, 14, 15, 9, 10, 11, -6, -7, -8, -2, -3, -4, -5, -1]]; + //if 0 + + // h an int combine_gammas = function(h) { var n; n = gp[Math.floor(p1.gamma)][Math.floor(p2.gamma)]; @@ -13048,6 +15712,11 @@ } }; + // this is useful for example when you are just adding/removing + // factors from an already factored quantity. + // e.g. if you factored x^2 + 3x + 2 into (x+1)(x+2) + // and you want to divide by (x+1) , i.e. you multiply by (x-1)^-1, + // then there is no need to expand. multiply_noexpand = function() { var prev_expanding; prev_expanding = expanding; @@ -13056,6 +15725,9 @@ return expanding = prev_expanding; }; + // multiply n factors on stack + + // n an integer multiply_all = function(n) { var h, i, o, ref; i = 0; @@ -13068,7 +15740,7 @@ } h = tos - n; push(stack[h]); - for (i = o = 1, ref = n; 1 <= ref ? o < ref : o > ref; i = 1 <= ref ? ++o : --o) { + for (i = o = 1, ref = n; (1 <= ref ? o < ref : o > ref); i = 1 <= ref ? ++o : --o) { push(stack[h + i]); multiply(); } @@ -13076,6 +15748,7 @@ return moveTos(h + 1); }; + // n an integer multiply_all_noexpand = function(n) { var prev_expanding; prev_expanding = expanding; @@ -13084,6 +15757,15 @@ return expanding = prev_expanding; }; + //----------------------------------------------------------------------------- + + // Symbolic division, or numeric division if doubles are found. + + // Input: Dividend and divisor on stack + + // Output: Quotient on stack + + //----------------------------------------------------------------------------- divide = function() { if (isNumericAtom(stack[tos - 2]) && isNumericAtom(stack[tos - 1])) { return divide_numbers(); @@ -13093,6 +15775,7 @@ } }; + // this is different from inverse of a matrix (inv)! inverse = function() { if (isNumericAtom(stack[tos - 1])) { return invert_number(); @@ -13135,13 +15818,48 @@ return expanding = prev_expanding; }; + //----------------------------------------------------------------------------- + + // Normalize radical factors + + // Input: stack[h] Coefficient factor, possibly 1 + + // stack[h + 1] Second factor + + // stack[tos - 1] Last factor + + // Output: Reduced coefficent and normalized radicals (maybe) + + // Example: 2*2^(-1/2) -> 2^(1/2) + + // (power number number) is guaranteed to have the following properties: + + // 1. Base is an integer + + // 2. Absolute value of exponent < 1 + + // These properties are assured by the power function. + + //----------------------------------------------------------------------------- + + //define A p1 + //define B p2 + + //define BASE p3 + //define EXPO p4 + + //define TMP p5 + + // h is an int __normalize_radical_factors = function(h) { var i, i1, j1, o, ref, ref1, ref2, ref3, ref4, ref5; i = 0; + // if coeff is 1 or floating then don't bother if (isplusone(stack[h]) || isminusone(stack[h]) || isdouble(stack[h])) { return; } - for (i = o = ref = h + 1, ref1 = tos; ref <= ref1 ? o < ref1 : o > ref1; i = ref <= ref1 ? ++o : --o) { +// if no radicals then don't bother + for (i = o = ref = h + 1, ref1 = tos; (ref <= ref1 ? o < ref1 : o > ref1); i = ref <= ref1 ? ++o : --o) { if (__is_radical_number(stack[i])) { break; } @@ -13149,12 +15867,15 @@ if (i === tos) { return; } + // ok, try to simplify save(); + // numerator push(stack[h]); mp_numerator(); + //console.log("__normalize_radical_factors numerator: " + stack[tos-1]) p1 = pop(); - for (i = i1 = ref2 = h + 1, ref3 = tos; ref2 <= ref3 ? i1 < ref3 : i1 > ref3; i = ref2 <= ref3 ? ++i1 : --i1) { - if (isplusone(p1) || isminusone(p1)) { + for (i = i1 = ref2 = h + 1, ref3 = tos; (ref2 <= ref3 ? i1 < ref3 : i1 > ref3); i = ref2 <= ref3 ? ++i1 : --i1) { + if (isplusone(p1) || isminusone(p1)) { // p1 is A break; } if (!__is_radical_number(stack[i])) { @@ -13162,16 +15883,18 @@ } p3 = cadr(stack[i]); p4 = caddr(stack[i]); - if (!isnegativenumber(p4)) { + if (!isnegativenumber(p4)) { //p4 is EXPO continue; } + // numerator divisible by p3 (base)? push(p1); push(p3); divide(); p5 = pop(); - if (!isinteger(p5)) { + if (!isinteger(p5)) { //p5 is TMP continue; } + // reduce numerator p1 = p5; push_symbol(POWER); push(p3); @@ -13181,11 +15904,13 @@ list(3); stack[i] = pop(); } + // denominator push(stack[h]); mp_denominator(); + //console.log("__normalize_radical_factors denominator: " + stack[tos-1]) p2 = pop(); - for (i = j1 = ref4 = h + 1, ref5 = tos; ref4 <= ref5 ? j1 < ref5 : j1 > ref5; i = ref4 <= ref5 ? ++j1 : --j1) { - if (isplusone(p2)) { + for (i = j1 = ref4 = h + 1, ref5 = tos; (ref4 <= ref5 ? j1 < ref5 : j1 > ref5); i = ref4 <= ref5 ? ++j1 : --j1) { + if (isplusone(p2)) { // p2 is B break; } if (!__is_radical_number(stack[i])) { @@ -13193,16 +15918,21 @@ } p3 = cadr(stack[i]); p4 = caddr(stack[i]); - if (isnegativenumber(p4)) { + if (isnegativenumber(p4)) { //p4 is EXPO continue; } + // denominator divisible by p3? #p3 is BASE push(p2); push(p3); divide(); p5 = pop(); - if (!isinteger(p5)) { + if (!isinteger(p5)) { //p5 is TMP continue; } + //console.log("__new radical p5: " + p5.toString()) + //console.log("__new radical top stack: " + stack[tos-1]) + + // reduce denominator p2 = p5; push_symbol(POWER); push(p3); @@ -13211,6 +15941,13 @@ subtract(); if (dontCreateNewRadicalsInDenominatorWhenEvalingMultiplication) { if (isinteger(p3) && !isinteger(stack[tos - 1]) && isnegativenumber(stack[tos - 1])) { + // bail out, + // we want to avoid going ahead with the subtraction of + // the exponents, because that would turn a perfectly good + // integer exponent in the denominator into a fractional one + // i.e. a radical. + // Note that this only prevents new radicals ending up + // in the denominator, it doesn't fix existing ones. pop(); pop(); pop(); @@ -13221,9 +15958,11 @@ break; } } + //console.log("__new radical exponent: " + stack[tos-1]) list(3); stack[i] = pop(); } + // reconstitute the coefficient push(p1); push(p2); divide(); @@ -13231,7 +15970,11 @@ return restore(); }; + // don't include i + + // p is a U __is_radical_number = function(p) { + // don't use i if (car(p) === symbol(POWER) && isNumericAtom(cadr(p)) && isNumericAtom(caddr(p)) && !isminusone(cadr(p))) { return 1; } else { @@ -13239,6 +15982,25 @@ } }; + //----------------------------------------------------------------------------- + + // > a*hilbert(2) + // ((a,1/2*a),(1/2*a,1/3*a)) + + // Note that "a" is presumed to be a scalar. Is this correct? + + // Yes, because "*" has no meaning if "a" is a tensor. + // To multiply tensors, "dot" or "outer" should be used. + + // > dot(a,hilbert(2)) + // dot(a,((1,1/2),(1/2,1/3))) + + // In this case "a" could be a scalar or tensor so the result is not + // expanded. + + //----------------------------------------------------------------------------- + + // find the roots of a polynomial numerically NROOTS_YMAX = 101; NROOTS_DELTA = 1.0e-6; @@ -13249,14 +16011,17 @@ return Math.sqrt(z.r * z.r + z.i * z.i); }; + // random between -2 and 2 theRandom = 0.0; NROOTS_RANDOM = function() { + //theRandom += 0.2 + //return theRandom return 4.0 * Math.random() - 2.0; }; numericRootOfPolynomial = (function() { - function numericRootOfPolynomial() {} + class numericRootOfPolynomial {}; numericRootOfPolynomial.prototype.r = 0.0; @@ -13264,7 +16029,7 @@ return numericRootOfPolynomial; - })(); + }).call(this); nroots_a = new numericRootOfPolynomial(); @@ -13284,7 +16049,7 @@ nroots_c = []; - for (initNRoots = o = 0, ref = NROOTS_YMAX; 0 <= ref ? o < ref : o > ref; initNRoots = 0 <= ref ? ++o : --o) { + for (initNRoots = o = 0, ref = NROOTS_YMAX; (0 <= ref ? o < ref : o > ref); initNRoots = 0 <= ref ? ++o : --o) { nroots_c[initNRoots] = new numericRootOfPolynomial(); } @@ -13309,14 +16074,17 @@ if (!ispolyexpandedform(p1, p2)) { stop("nroots: polynomial?"); } + // mark the stack h = tos; + // get the coefficients push(p1); push(p2); n = coeff(); if (n > NROOTS_YMAX) { stop("nroots: degree?"); } - for (i = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { +// convert the coefficients to real and imaginary doubles + for (i = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { push(stack[h + i]); real(); yyfloat(); @@ -13333,7 +16101,9 @@ nroots_c[i].r = p1.d; nroots_c[i].i = p2.d; } + // pop the coefficients moveTos(h); + // n is the number of coefficients, n = deg(p) + 1 monic(n); for (k = j1 = ref2 = n; j1 > 1; k = j1 += -1) { findroot(k); @@ -13350,13 +16120,14 @@ add(); NROOTS_divpoly(k); } + // now make n equal to the number of roots n = tos - h; if (n > 1) { sort_stack(n); p1 = alloc_tensor(n); p1.tensor.ndim = 1; p1.tensor.dim[0] = n; - for (i = l1 = 0, ref3 = n; 0 <= ref3 ? l1 < ref3 : l1 > ref3; i = 0 <= ref3 ? ++l1 : --l1) { + for (i = l1 = 0, ref3 = n; (0 <= ref3 ? l1 < ref3 : l1 > ref3); i = 0 <= ref3 ? ++l1 : --l1) { p1.tensor.elem[i] = stack[h + i]; } moveTos(h); @@ -13364,6 +16135,7 @@ } }; + // divide the polynomial by its leading coefficient monic = function(n) { var i1, k, ref1, t; k = 0; @@ -13371,7 +16143,7 @@ nroots_y.r = nroots_c[n - 1].r; nroots_y.i = nroots_c[n - 1].i; t = nroots_y.r * nroots_y.r + nroots_y.i * nroots_y.i; - for (k = i1 = 0, ref1 = n - 1; 0 <= ref1 ? i1 < ref1 : i1 > ref1; k = 0 <= ref1 ? ++i1 : --i1) { + for (k = i1 = 0, ref1 = n - 1; (0 <= ref1 ? i1 < ref1 : i1 > ref1); k = 0 <= ref1 ? ++i1 : --i1) { nroots_c[k].r = (nroots_c[k].r * nroots_y.r + nroots_c[k].i * nroots_y.i) / t; nroots_c[k].i = (nroots_c[k].i * nroots_y.r - nroots_c[k].r * nroots_y.i) / t; } @@ -13379,6 +16151,7 @@ return nroots_c[n - 1].i = 0.0; }; + // uses the secant method findroot = function(n) { var i1, j, j1, k, nrabs, t; j = 0; @@ -13422,16 +16195,20 @@ nroots_fb.r = nroots_x.r; nroots_fb.i = nroots_x.i; } + // dx = nroots_b - nroots_a nroots_dx.r = nroots_b.r - nroots_a.r; nroots_dx.i = nroots_b.i - nroots_a.i; + // df = fb - fa nroots_df.r = nroots_fb.r - nroots_fa.r; nroots_df.i = nroots_fb.i - nroots_fa.i; + // y = dx / df t = nroots_df.r * nroots_df.r + nroots_df.i * nroots_df.i; if (t === 0.0) { break; } nroots_y.r = (nroots_dx.r * nroots_df.r + nroots_dx.i * nroots_df.i) / t; nroots_y.i = (nroots_dx.i * nroots_df.r - nroots_dx.r * nroots_df.i) / t; + // a = b - y * fb nroots_a.r = nroots_b.r - (nroots_y.r * nroots_fb.r - nroots_y.i * nroots_fb.i); nroots_a.i = nroots_b.i - (nroots_y.r * nroots_fb.i + nroots_y.i * nroots_fb.r); } @@ -13443,25 +16220,30 @@ var i1, k, ref1, results, t; k = 0; t = 0.0; + // x = a nroots_x.r = nroots_a.r; nroots_x.i = nroots_a.i; + // fa = c0 + c1 * x nroots_fa.r = nroots_c[0].r + nroots_c[1].r * nroots_x.r - nroots_c[1].i * nroots_x.i; nroots_fa.i = nroots_c[0].i + nroots_c[1].r * nroots_x.i + nroots_c[1].i * nroots_x.r; results = []; - for (k = i1 = 2, ref1 = n; 2 <= ref1 ? i1 < ref1 : i1 > ref1; k = 2 <= ref1 ? ++i1 : --i1) { + for (k = i1 = 2, ref1 = n; (2 <= ref1 ? i1 < ref1 : i1 > ref1); k = 2 <= ref1 ? ++i1 : --i1) { + // x = a * x t = nroots_a.r * nroots_x.r - nroots_a.i * nroots_x.i; nroots_x.i = nroots_a.r * nroots_x.i + nroots_a.i * nroots_x.r; nroots_x.r = t; + // fa += c[k] * x nroots_fa.r += nroots_c[k].r * nroots_x.r - nroots_c[k].i * nroots_x.i; results.push(nroots_fa.i += nroots_c[k].r * nroots_x.i + nroots_c[k].i * nroots_x.r); } return results; }; + // divide the polynomial by x - a NROOTS_divpoly = function(n) { var i1, j1, k, ref1, ref2, results; k = 0; - for (k = i1 = ref1 = n - 1; ref1 <= 0 ? i1 < 0 : i1 > 0; k = ref1 <= 0 ? ++i1 : --i1) { + for (k = i1 = ref1 = n - 1; (ref1 <= 0 ? i1 < 0 : i1 > 0); k = ref1 <= 0 ? ++i1 : --i1) { nroots_c[k - 1].r += nroots_c[k].r * nroots_a.r - nroots_c[k].i * nroots_a.i; nroots_c[k - 1].i += nroots_c[k].i * nroots_a.r + nroots_c[k].r * nroots_a.i; } @@ -13469,7 +16251,7 @@ stop("nroots: residual error"); } results = []; - for (k = j1 = 0, ref2 = n - 1; 0 <= ref2 ? j1 < ref2 : j1 > ref2; k = 0 <= ref2 ? ++j1 : --j1) { + for (k = j1 = 0, ref2 = n - 1; (0 <= ref2 ? j1 < ref2 : j1 > ref2); k = 0 <= ref2 ? ++j1 : --j1) { nroots_c[k].r = nroots_c[k + 1].r; results.push(nroots_c[k].i = nroots_c[k + 1].i); } @@ -13488,12 +16270,16 @@ theArgument = pop(); if (car(theArgument) === symbol(ADD)) { push(theArgument); + //console.trace "rationalising " rationalize(); theArgument = pop(); } + //console.log "rationalised: " + theArgument if (car(theArgument) === symbol(MULTIPLY) && !isplusone(car(cdr(theArgument)))) { h = tos; theArgument = cdr(theArgument); + //console.log "theArgument inside multiply: " + theArgument + //console.log "first term: " + car(theArgument) while (iscons(theArgument)) { push(car(theArgument)); numerator(); @@ -13510,6 +16296,7 @@ } }; + // Outer product of tensors Eval_outer = function() { var results; p1 = cdr(p1); @@ -13560,16 +16347,16 @@ nelem = p1.tensor.nelem * p2.tensor.nelem; p3 = alloc_tensor(nelem); p3.tensor.ndim = ndim; - for (i = i1 = 0, ref1 = p1.tensor.ndim; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = p1.tensor.ndim; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { p3.tensor.dim[i] = p1.tensor.dim[i]; } j = i; - for (i = j1 = 0, ref2 = p2.tensor.ndim; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = p2.tensor.ndim; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { p3.tensor.dim[j + i] = p2.tensor.dim[i]; } k = 0; - for (i = l1 = 0, ref3 = p1.tensor.nelem; 0 <= ref3 ? l1 < ref3 : l1 > ref3; i = 0 <= ref3 ? ++l1 : --l1) { - for (j = m1 = 0, ref4 = p2.tensor.nelem; 0 <= ref4 ? m1 < ref4 : m1 > ref4; j = 0 <= ref4 ? ++m1 : --m1) { + for (i = l1 = 0, ref3 = p1.tensor.nelem; (0 <= ref3 ? l1 < ref3 : l1 > ref3); i = 0 <= ref3 ? ++l1 : --l1) { + for (j = m1 = 0, ref4 = p2.tensor.nelem; (0 <= ref4 ? m1 < ref4 : m1 > ref4); j = 0 <= ref4 ? ++m1 : --m1) { push(p1.tensor.elem[i]); push(p2.tensor.elem[j]); multiply(); @@ -13579,23 +16366,6 @@ return push(p3); }; - - /* - Partition a term - - Input stack: - - term (factor or product of factors) - - free variable - - Output stack: - - constant expression - - variable expression - */ - partition = function() { save(); p2 = pop(); @@ -13623,15 +16393,16 @@ return restore(); }; - /* Add a pattern i.e. a substitution rule. Substitution rule needs a template as first argument and what to transform it to as second argument. Optional third argument is a boolean test which adds conditions to when the rule is applied. - */ - + */ + // same as Eval_pattern but only leaves + // NIL on stack at return, hence gives no + // printout Eval_silentpattern = function() { Eval_pattern(); pop(); @@ -13656,13 +16427,19 @@ if (equal(firstArgument, secondArgument)) { stop("recursive pattern"); } + // console.log "Eval_pattern of " + cdr(p1) + // this is likely to create garbage collection + // problems in the C version as it's an + // untracked reference stringKey = "template: " + print_list(firstArgument); stringKey += " tests: " + print_list(thirdArgument); if (DEBUG) { console.log("pattern stringkey: " + stringKey); } patternPosition = userSimplificationsInStringForm.indexOf(stringKey); + // if pattern is not there yet, add it, otherwise replace it if (patternPosition === -1) { + //console.log "adding pattern because it doesn't exist: " + cdr(p1) userSimplificationsInStringForm.push(stringKey); userSimplificationsInListForm.push(cdr(p1)); } else { @@ -13672,23 +16449,25 @@ userSimplificationsInStringForm[patternPosition] = stringKey; userSimplificationsInListForm[patternPosition] = cdr(p1); } + // return the pattern node itself so we can + // give some printout feedback push_symbol(PATTERN); push(cdr(p1)); return list(2); }; - - /* - Clear all patterns - */ - do_clearPatterns = function() { userSimplificationsInListForm = []; return userSimplificationsInStringForm = []; }; Eval_clearpatterns = function() { + // this is likely to create garbage collection + // problems in the C version as it's an + // untracked reference do_clearPatterns(); + + // return nothing return push_symbol(NIL); }; @@ -13712,17 +16491,6 @@ return patternsinfoToBePrinted; }; - - /* - Convert complex z to polar form - - Input: push z - - Output: Result on stack - - polar(z) = abs(z) * exp(i * arg(z)) - */ - Eval_polar = function() { push(cadr(p1)); Eval(); @@ -13730,6 +16498,10 @@ }; polar = function() { + // there are points where we turn polar + // representations into rect, we set a "stack flag" + // here to avoid that, so we don't undo the + // work that we are trying to do. evaluatingPolar++; save(); p1 = pop(); @@ -13745,6 +16517,7 @@ return restore(); }; + // Factor using the Pollard rho method n_factor_number = 0; factor_number = function() { @@ -13752,6 +16525,7 @@ h = 0; save(); p1 = pop(); + // 0 or 1? if (equaln(p1, 0) || equaln(p1, 1) || equaln(p1, -1)) { push(p1); restore(); @@ -13769,6 +16543,9 @@ return restore(); }; + // factor using table look-up, then switch to rho method if necessary + + // From TAOCP Vol. 2 by Knuth, p. 380 (Algorithm A) factor_a = function() { var i1, k; k = 0; @@ -13778,6 +16555,7 @@ } for (k = i1 = 0; i1 < 10000; k = ++i1) { try_kth_prime(k); + // if n_factor_number is 1 then we're done if (n_factor_number.compare(1) === 0) { return; } @@ -13786,18 +16564,20 @@ }; try_kth_prime = function(k) { - var count, d, q, r, ref1; + var count, d, q, r; count = 0; d = mint(primetab[k]); count = 0; while (1) { + // if n_factor_number is 1 then we're done if (n_factor_number.compare(1) === 0) { if (count) { push_factor(d, count); } return; } - ref1 = mdivrem(n_factor_number, d), q = ref1[0], r = ref1[1]; + [q, r] = mdivrem(n_factor_number, d); + // continue looping while remainder is zero if (r.isZero()) { count++; n_factor_number = q; @@ -13808,12 +16588,15 @@ if (count) { push_factor(d, count); } + // q = n_factor_number/d, hence if q < d then + // n_factor_number < d^2 so n_factor_number is prime if (mcmp(q, d) === -1) { push_factor(n_factor_number, 1); return n_factor_number = mint(1); } }; + // From TAOCP Vol. 2 by Knuth, p. 385 (Algorithm B) factor_b = function() { var bigint_one, g, k, l, t, x, xprime; k = 0; @@ -13832,6 +16615,7 @@ if (esc_flag) { stop("esc"); } + // g = gcd(x' - x, n_factor_number) t = msub(xprime, x); t = setSignTo(t, 1); g = mgcd(t, n_factor_number); @@ -13841,6 +16625,7 @@ l *= 2; k = l; } + // x = (x ^ 2 + 1) mod n_factor_number t = mmul(x, x); x = madd(t, bigint_one); t = mmod(x, n_factor_number); @@ -13851,10 +16636,13 @@ if (mcmp(g, n_factor_number) === 0) { return -1; } + // n_factor_number = n_factor_number / g t = mdiv(n_factor_number, g); n_factor_number = t; + // x = x mod n_factor_number t = mmod(x, n_factor_number); x = t; + // xprime = xprime mod n_factor_number t = mmod(xprime, n_factor_number); xprime = t; break; @@ -13880,16 +16668,6 @@ } }; - - /* Power function - - Input: push Base - - push Exponent - - Output: Result on stack - */ - DEBUG_POWER = false; Eval_power = function() { @@ -13915,13 +16693,18 @@ debugger; } n = 0; - p2 = pop(); - p1 = pop(); + p2 = pop(); // exponent + p1 = pop(); // base inputExp = p2; inputBase = p1; + //debugger if (DEBUG_POWER) { console.log("POWER: " + p1 + " ^ " + p2); } + // first, some very basic simplifications right away + + // 1 ^ a -> 1 + // a ^ 0 -> 1 if (equal(p1, one) || isZeroAtomOrTensor(p2)) { if (evaluatingAsFloats) { push_double(1.0); @@ -13933,6 +16716,7 @@ } return; } + // a ^ 1 -> a if (equal(p2, one)) { push(p1); if (DEBUG_POWER) { @@ -13940,6 +16724,7 @@ } return; } + // -1 ^ -1 -> -1 if (isminusone(p1) && isminusone(p2)) { if (evaluatingAsFloats) { push_double(1.0); @@ -13952,6 +16737,7 @@ } return; } + // -1 ^ 1/2 -> i if (isminusone(p1) && (isoneovertwo(p2))) { push(imaginaryunit); if (DEBUG_POWER) { @@ -13959,6 +16745,7 @@ } return; } + // -1 ^ -1/2 -> -i if (isminusone(p1) && isminusoneovertwo(p2)) { push(imaginaryunit); negate(); @@ -13967,6 +16754,7 @@ } return; } + // -1 ^ rational if (isminusone(p1) && !isdouble(p1) && isrational(p2) && !isinteger(p2) && ispositivenumber(p2) && !evaluatingAsFloats) { if (DEBUG_POWER) { console.log(" power: -1 ^ rational"); @@ -13991,12 +16779,16 @@ console.log(" trick applied : " + stack[tos - 1]); } } + // evaluates clock form into + // rectangular form. This seems to give + // slightly better form to some test results. rect(); if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } return; } + // both base and exponent are rational numbers? if (isrational(p1) && isrational(p2)) { if (DEBUG_POWER) { console.log(" power: isrational(p1) && isrational(p2)"); @@ -14009,6 +16801,7 @@ } return; } + // both base and exponent are either rational or double? if (isNumericAtom(p1) && isNumericAtom(p2)) { if (DEBUG_POWER) { console.log(" power: both base and exponent are either rational or double "); @@ -14034,6 +16827,8 @@ } return; } + // if we only assume variables to be real, then |a|^2 = a^2 + // (if x is complex this doesn't hold e.g. i, which makes 1 and -1 if (car(p1) === symbol(ABS) && iseveninteger(p2) && !isZeroAtomOrTensor(get_binding(symbol(ASSUME_REAL_VARIABLES)))) { if (DEBUG_POWER) { console.log(" power: even power of absolute of real value "); @@ -14046,6 +16841,7 @@ } return; } + // e^log(...) if (p1 === symbol(E) && car(p2) === symbol(LOG)) { push(cadr(p2)); if (DEBUG_POWER) { @@ -14053,6 +16849,7 @@ } return; } + // e^some_float if (p1 === symbol(E) && isdouble(p2)) { if (DEBUG_POWER) { console.log(" power: p1 == symbol(E) && isdouble(p2) "); @@ -14063,6 +16860,9 @@ } return; } + // complex number in exponential form, get it to rectangular + // but only if we are not in the process of calculating a polar form, + // otherwise we'd just undo the work we want to do if (p1 === symbol(E) && Find(p2, imaginaryunit) !== 0 && Find(p2, symbol(PI)) !== 0 && !evaluatingPolar) { push_symbol(POWER); push(p1); @@ -14081,6 +16881,11 @@ return; } } + // (a * b) ^ c -> (a ^ c) * (b ^ c) + // note that we can't in general do this, for example + // sqrt(x*y) != x^(1/2) y^(1/2) (counterexample" x = -1 and y = -1) + // BUT we can carve-out here some cases where this + // transformation is correct if (car(p1) === symbol(MULTIPLY) && isinteger(p2)) { if (DEBUG_POWER) { console.log(" power: (a * b) ^ c -> (a ^ c) * (b ^ c) "); @@ -14102,11 +16907,18 @@ } return; } + // (a ^ b) ^ c -> a ^ (b * c) + // note that we can't in general do this, for example + // sqrt(x^y) != x^(1/2 y) (counterexample x = -1) + // BUT we can carve-out here some cases where this + // transformation is correct + + // simple numeric check to see if a is a number > 0 is_a_moreThanZero = false; if (isNumericAtom(cadr(p1))) { is_a_moreThanZero = sign(compare_numbers(cadr(p1), zero)); } - if (car(p1) === symbol(POWER) && (isinteger(p2) || is_a_moreThanZero)) { + if (car(p1) === symbol(POWER) && (isinteger(p2) || is_a_moreThanZero)) { // when a is >= 0 push(cadr(p1)); push(caddr(p1)); push(p2); @@ -14138,6 +16950,8 @@ } return; } + // when expanding, + // (a + b) ^ n -> (a + b) * (a + b) ... if (expanding && isadd(p1) && isNumericAtom(p2)) { push(p2); n = pop_integer(); @@ -14152,6 +16966,7 @@ return; } } + // sin(x) ^ 2n -> (1 - cos(x) ^ 2) ^ n if (trigmode === 1 && car(p1) === symbol(SIN) && iseveninteger(p2)) { if (DEBUG_POWER) { console.log(" power: trigmode == 1 && car(p1) == symbol(SIN) && iseveninteger(p2) "); @@ -14171,6 +16986,7 @@ } return; } + // cos(x) ^ 2n -> (1 - sin(x) ^ 2) ^ n if (trigmode === 2 && car(p1) === symbol(COS) && iseveninteger(p2)) { if (DEBUG_POWER) { console.log(" power: trigmode == 2 && car(p1) == symbol(COS) && iseveninteger(p2) "); @@ -14190,15 +17006,25 @@ } return; } + // complex number? (just number, not expression) if (iscomplexnumber(p1)) { if (DEBUG_POWER) { console.log(" power - handling the case (a + ib) ^ n"); } + // integer power? + + // n will be negative here, positive n already handled if (isinteger(p2)) { + // / \ n + // -n | a - ib | + // (a + ib) = | -------- | + // | 2 2 | + // \ a + b / push(p1); conjugate(); p3 = pop(); push(p3); + // gets the denominator push(p3); push(p1); multiply(); @@ -14213,6 +17039,7 @@ } return; } + // noninteger or floating power? if (isNumericAtom(p2)) { push(p1); abs(); @@ -14224,13 +17051,23 @@ push(p2); multiply(); if (evaluatingAsFloats || (iscomplexnumberdouble(p1) && isdouble(p2))) { + // remember that the "double" type is + // toxic, i.e. it propagates, so we do + // need to evaluate PI to its actual double + // value push_double(Math.PI); } else { + //console.log("power pushing PI when p1 is: " + p1 + " and p2 is:" + p2) push(symbol(PI)); } divide(); power(); multiply(); + // if we calculate the power making use of arctan: + // * it prevents nested radicals from being simplified + // * results become really hard to manipulate afterwards + // * we can't go back to other forms. + // so leave the power as it is. if (avoidCalculatingPowersIntoArctans) { if (Find(stack[tos - 1], symbol(ARCTAN))) { pop(); @@ -14246,6 +17083,21 @@ return; } } + + //push(p1) + //abs() + //push(p2) + //power() + //push(symbol(E)) + //push(p1) + //arg() + //push(p2) + //multiply() + //push(imaginaryunit) + //multiply() + //power() + //multiply() + if (simplify_polar()) { if (DEBUG_POWER) { console.log(" power: using simplify_polar"); @@ -14264,17 +17116,45 @@ } }; + //----------------------------------------------------------------------------- + + // Compute the power of a sum + + // Input: p1 sum + + // n exponent + + // Output: Result on stack + + // Note: + + // Uses the multinomial series (see Math World) + + // n n! n1 n2 nk + // (a1 + a2 + ... + ak) = sum (--------------- a1 a2 ... ak ) + // n1! n2! ... nk! + + // The sum is over all n1 ... nk such that n1 + n2 + ... + nk = n. + + //----------------------------------------------------------------------------- + + // first index is the term number 0..k-1, second index is the exponent 0..n + + //define A(i, j) frame[(i) * (n + 1) + (j)] power_sum = function(n) { var a, i, i1, j, j1, k, l1, ref1, ref2, ref3; a = []; i = 0; j = 0; k = 0; + // number of terms in the sum k = length(p1) - 1; + // local frame push_frame(k * (n + 1)); + // array of powers p1 = cdr(p1); - for (i = i1 = 0, ref1 = k; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { - for (j = j1 = 0, ref2 = n; 0 <= ref2 ? j1 <= ref2 : j1 >= ref2; j = 0 <= ref2 ? ++j1 : --j1) { + for (i = i1 = 0, ref1 = k; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { + for (j = j1 = 0, ref2 = n; (0 <= ref2 ? j1 <= ref2 : j1 >= ref2); j = 0 <= ref2 ? ++j1 : --j1) { push(car(p1)); push_integer(j); power(); @@ -14285,7 +17165,7 @@ push_integer(n); factorial(); p1 = pop(); - for (i = l1 = 0, ref3 = k; 0 <= ref3 ? l1 < ref3 : l1 > ref3; i = 0 <= ref3 ? ++l1 : --l1) { + for (i = l1 = 0, ref3 = k; (0 <= ref3 ? l1 < ref3 : l1 > ref3); i = 0 <= ref3 ? ++l1 : --l1) { a[i] = 0; } push(zero); @@ -14293,30 +17173,64 @@ return pop_frame(k * (n + 1)); }; + //----------------------------------------------------------------------------- + + // Compute multinomial sum + + // Input: k number of factors + + // n overall exponent + + // a partition array + + // i partition array index + + // m partition remainder + + // p1 n! + + // A factor array + + // Output: Result on stack + + // Note: + + // Uses recursive descent to fill the partition array. + + //----------------------------------------------------------------------------- + + //int k, int n, int *a, int i, int m multinomial_sum = function(k, n, a, i, m) { var i1, j, j1, l1, ref1, ref2, ref3; j = 0; if (i < k - 1) { - for (j = i1 = 0, ref1 = m; 0 <= ref1 ? i1 <= ref1 : i1 >= ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (j = i1 = 0, ref1 = m; (0 <= ref1 ? i1 <= ref1 : i1 >= ref1); j = 0 <= ref1 ? ++i1 : --i1) { a[i] = j; multinomial_sum(k, n, a, i + 1, m - j); } return; } a[i] = m; + // coefficient push(p1); - for (j = j1 = 0, ref2 = k; 0 <= ref2 ? j1 < ref2 : j1 > ref2; j = 0 <= ref2 ? ++j1 : --j1) { + for (j = j1 = 0, ref2 = k; (0 <= ref2 ? j1 < ref2 : j1 > ref2); j = 0 <= ref2 ? ++j1 : --j1) { push_integer(a[j]); factorial(); divide(); } - for (j = l1 = 0, ref3 = k; 0 <= ref3 ? l1 < ref3 : l1 > ref3; j = 0 <= ref3 ? ++l1 : --l1) { +// factors + for (j = l1 = 0, ref3 = k; (0 <= ref3 ? l1 < ref3 : l1 > ref3); j = 0 <= ref3 ? ++l1 : --l1) { push(stack[frame + j * (n + 1) + a[j]]); multiply(); } return add(); }; + // exp(n/2 i pi) ? + + // p2 is the exponent expression + + // clobbers p3 simplify_polar = function() { var doNothing, n; n = 0; @@ -14374,6 +17288,15 @@ return 0; }; + //----------------------------------------------------------------------------- + + // Look up the nth prime + + // Input: n on stack (0 < n < 10001) + + // Output: nth prime on stack + + //----------------------------------------------------------------------------- Eval_prime = function() { push(cadr(p1)); Eval(); @@ -14395,28 +17318,41 @@ codeGen = false; + // this is only invoked when user invokes + // "print" explicitly Eval_print = function() { stringsEmittedByUserPrintouts += _print(cdr(p1), printMode); return push(symbol(NIL)); }; + // this is only invoked when user invokes + // "print2dascii" explicitly Eval_print2dascii = function() { stringsEmittedByUserPrintouts += _print(cdr(p1), PRINTMODE_2DASCII); return push(symbol(NIL)); }; + // this is only invoked when user invokes + // "printcomputer" explicitly Eval_printcomputer = function() { stringsEmittedByUserPrintouts += _print(cdr(p1), PRINTMODE_COMPUTER); return push(symbol(NIL)); }; + // this is only invoked when user invokes + // "printlatex" explicitly Eval_printlatex = function() { stringsEmittedByUserPrintouts += _print(cdr(p1), PRINTMODE_LATEX); return push(symbol(NIL)); }; + // this is only invoked when user invokes + // "printhuman" explicitly Eval_printhuman = function() { var original_test_flag; + // test flag needs to be suspended + // because otherwise "printcomputer" mode + // will happen. original_test_flag = test_flag; test_flag = 0; stringsEmittedByUserPrintouts += _print(cdr(p1), PRINTMODE_HUMAN); @@ -14424,6 +17360,8 @@ return push(symbol(NIL)); }; + // this is only invoked when user invokes + // "printlist" explicitly Eval_printlist = function() { var beenPrinted; beenPrinted = _print(cdr(p1), PRINTMODE_LIST); @@ -14438,15 +17376,6 @@ push(car(p)); Eval(); p2 = pop(); - - /* - if (issymbol(car(p)) && car(p) != p2) - push_symbol(SETQ); - push(car(p)); - push(p2); - list(3); - p2 = pop(); - */ origPrintMode = printMode; if (passedPrintMode === PRINTMODE_COMPUTER) { printMode = PRINTMODE_COMPUTER; @@ -14503,6 +17432,7 @@ originalCodeGen = codeGen; codeGen = false; returnedString = print_expr(p); + // some variables might contain underscores, escape those returnedString = returnedString.replace(/_/g, "\\_"); printMode = origPrintMode; codeGen = originalCodeGen; @@ -14522,12 +17452,12 @@ print_base_of_denom = function(p1) { var accumulator; accumulator = ""; - if (isfraction(p1) || car(p1) === symbol(ADD) || car(p1) === symbol(MULTIPLY) || car(p1) === symbol(POWER) || lessp(p1, zero)) { + if (isfraction(p1) || car(p1) === symbol(ADD) || car(p1) === symbol(MULTIPLY) || car(p1) === symbol(POWER) || lessp(p1, zero)) { // p1 is BASE accumulator += print_char('('); accumulator += print_expr(p1); accumulator += print_char(')'); } else { - accumulator += print_expr(p1); + accumulator += print_expr(p1); // p1 is BASE } return accumulator; }; @@ -14535,30 +17465,39 @@ print_expo_of_denom = function(p2) { var accumulator; accumulator = ""; - if (isfraction(p2) || car(p2) === symbol(ADD) || car(p2) === symbol(MULTIPLY) || car(p2) === symbol(POWER)) { + if (isfraction(p2) || car(p2) === symbol(ADD) || car(p2) === symbol(MULTIPLY) || car(p2) === symbol(POWER)) { // p2 is EXPO accumulator += print_char('('); accumulator += print_expr(p2); accumulator += print_char(')'); } else { - accumulator += print_expr(p2); + accumulator += print_expr(p2); // p2 is EXPO } return accumulator; }; + // prints stuff after the divide symbol "/" + + // d is the number of denominators + + //define BASE p1 + //define EXPO p2 print_denom = function(p, d) { var accumulator; accumulator = ""; save(); p1 = cadr(p); p2 = caddr(p); - if (isminusone(p2)) { + if (isminusone(p2)) { // p2 is EXPO accumulator += print_base_of_denom(p1); restore(); return accumulator; } - if (d === 1) { + if (d === 1) { // p2 is EXPO accumulator += print_char('('); } + // prepare the exponent + // (needs to be negated) + // before printing it out push(p2); negate(); p2 = pop(); @@ -14570,6 +17509,8 @@ return accumulator; }; + //define A p3 + //define B p4 print_a_over_b = function(p) { var accumulator, d, doNothing, n; accumulator = ""; @@ -14577,6 +17518,7 @@ n = 0; d = 0; save(); + // count numerators and denominators n = 0; d = 0; p1 = cdr(p); @@ -14589,16 +17531,16 @@ push(p2); mp_denominator(); p4 = pop(); - if (!isplusone(p3)) { + if (!isplusone(p3)) { // p3 is A n++; } - if (!isplusone(p4)) { + if (!isplusone(p4)) { // p4 is B d++; } p1 = cdr(p1); } else { p3 = one; - p4 = one; + p4 = one; // p4 is B } while (iscons(p1)) { p2 = car(p1); @@ -14609,6 +17551,7 @@ } p1 = cdr(p1); } + //debugger if (printMode === PRINTMODE_LATEX) { accumulator += print_str('\\frac{'); } @@ -14620,7 +17563,7 @@ if (isrational(car(p1))) { p1 = cdr(p1); } - if (!isplusone(p3)) { + if (!isplusone(p3)) { // p3 is A accumulator += print_factor(p3); flag = 1; } @@ -14653,7 +17596,7 @@ if (isrational(car(p1))) { p1 = cdr(p1); } - if (!isplusone(p4)) { + if (!isplusone(p4)) { // p4 is B accumulator += print_factor(p4); flag = 1; } @@ -14736,13 +17679,29 @@ } if (car(p) === symbol(MULTIPLY)) { p = cdr(p); + // coeff -1? if (isminusone(car(p))) { + // print_char('-') p = cdr(p); } previousFactorWasANumber = false; + // print the first factor ------------ if (isNumericAtom(car(p))) { previousFactorWasANumber = true; } + // this numberOneOverSomething thing is so that + // we show things of the form + // numericFractionOfForm1/something * somethingElse + // as + // somethingElse / something + // so for example 1/2 * sqrt(2) is rendered as + // sqrt(2)/2 + // rather than the first form, which looks confusing. + // NOTE that you might want to avoid this + // when printing polynomials, as it could be nicer + // to show the numeric coefficients well separated from + // the variable, but we'll see when we'll + // come to it if it's an issue. numberOneOverSomething = false; if (printMode === PRINTMODE_LATEX && iscons(cdr(p)) && isNumberOneOverSomething(car(p))) { numberOneOverSomething = true; @@ -14755,9 +17714,20 @@ accumulator += print_factor(car(p)); } p = cdr(p); + // print all the other factors ------- while (iscons(p)) { + // check if we end up having a case where two numbers + // are next to each other. In those cases, latex needs + // to insert a \cdot otherwise they end up + // right next to each other and read like one big number if (printMode === PRINTMODE_LATEX) { if (previousFactorWasANumber) { + // if what comes next is a power and the base + // is a number, then we are in the case + // of consecutive numbers. + // Note that sqrt() i.e when exponent is 1/2 + // doesn't count because the radical gives + // a nice graphical separation already. if (caar(p) === symbol(POWER)) { if (isNumericAtom(car(cdr(car(p))))) { if (!isfraction(car(cdr(cdr(car(p)))))) { @@ -14981,7 +17951,7 @@ accumulator += print_expr(functionBody); accumulator += print_str(" \\,"); p = originalIntegral; - for (i = i1 = 1, ref1 = numberOfIntegrals; 1 <= ref1 ? i1 <= ref1 : i1 >= ref1; i = 1 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 1, ref1 = numberOfIntegrals; (1 <= ref1 ? i1 <= ref1 : i1 >= ref1); i = 1 <= ref1 ? ++i1 : --i1) { theVariable = cdr(p); accumulator += print_str(" \\mathrm{d} "); accumulator += print_expr(car(theVariable)); @@ -15000,21 +17970,37 @@ return accumulator; }; + // j scans the dimensions + // k is an increment for all the printed elements + // since they are all together in sequence in one array print_tensor_inner = function(p, j, k) { - var accumulator, i, i1, j1, ref1, ref2, ref3, retString; + var accumulator, i, i1, j1, ref1, ref2, retString; accumulator = ""; accumulator += print_str("["); + // only the last dimension prints the actual elements + // e.g. in a matrix, the first dimension contains + // vectors, not elements, and the second dimension + // actually contains the elements + + // if not the last dimension, we are just printing wrappers + // and recursing down i.e. we print the next dimension if (j < p.tensor.ndim - 1) { - for (i = i1 = 0, ref1 = p.tensor.dim[j]; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { - ref2 = print_tensor_inner(p, j + 1, k), k = ref2[0], retString = ref2[1]; + for (i = i1 = 0, ref1 = p.tensor.dim[j]; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { + [k, retString] = print_tensor_inner(p, j + 1, k); accumulator += retString; + // add separator between elements dimensions + // "above" the inner-most dimension if (i !== p.tensor.dim[j] - 1) { accumulator += print_str(","); } } } else { - for (i = j1 = 0, ref3 = p.tensor.dim[j]; 0 <= ref3 ? j1 < ref3 : j1 > ref3; i = 0 <= ref3 ? ++j1 : --j1) { +// if we reached the last dimension, we print the actual +// elements + for (i = j1 = 0, ref2 = p.tensor.dim[j]; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { accumulator += print_expr(p.tensor.elem[k]); + // add separator between elements in the + // inner-most dimension if (i !== p.tensor.dim[j] - 1) { accumulator += print_str(","); } @@ -15034,29 +18020,51 @@ return accumulator; }; + // firstLevel is needed because printing a matrix + // is not exactly an elegant recursive procedure: + // the vector on the first level prints the latex + // "wrap", while the vectors that make up the + // rows don't. so it's a bit asymmetric and this + // flag helps. + // j scans the dimensions + // k is an increment for all the printed elements + // since they are all together in sequence in one array print_tensor_inner_latex = function(firstLevel, p, j, k) { - var accumulator, i, i1, j1, ref1, ref2, ref3, retString; + var accumulator, i, i1, j1, ref1, ref2, retString; accumulator = ""; + // open the outer latex wrap if (firstLevel) { accumulator += "\\begin{bmatrix} "; } + // only the last dimension prints the actual elements + // e.g. in a matrix, the first dimension contains + // vectors, not elements, and the second dimension + // actually contains the elements + + // if not the last dimension, we are just printing wrappers + // and recursing down i.e. we print the next dimension if (j < p.tensor.ndim - 1) { - for (i = i1 = 0, ref1 = p.tensor.dim[j]; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { - ref2 = print_tensor_inner_latex(0, p, j + 1, k), k = ref2[0], retString = ref2[1]; + for (i = i1 = 0, ref1 = p.tensor.dim[j]; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { + [k, retString] = print_tensor_inner_latex(0, p, j + 1, k); accumulator += retString; if (i !== p.tensor.dim[j] - 1) { + // add separator between rows accumulator += print_str(" \\\\ "); } } } else { - for (i = j1 = 0, ref3 = p.tensor.dim[j]; 0 <= ref3 ? j1 < ref3 : j1 > ref3; i = 0 <= ref3 ? ++j1 : --j1) { +// if we reached the last dimension, we print the actual +// elements + for (i = j1 = 0, ref2 = p.tensor.dim[j]; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { accumulator += print_expr(p.tensor.elem[k]); + // separator between elements in each row if (i !== p.tensor.dim[j] - 1) { accumulator += print_str(" & "); } k++; } } + // close the outer latex wrap if (firstLevel) { accumulator += " \\end{bmatrix}"; } @@ -15092,6 +18100,9 @@ accumulator = "\\left\\{ \\begin{array}{ll}"; p = cdr(p); while (iscons(p)) { + // odd number of parameters means that the + // last argument becomes the default case + // i.e. the one without a test. if (cdr(p) === symbol(NIL)) { accumulator += "{"; accumulator += print_expr(car(p)); @@ -15104,6 +18115,8 @@ accumulator += "} & if & "; accumulator += print_expr(car(p)); accumulator += " \\\\\\\\"; + // test unsuccessful, continue to the + // next pair of test,value p = cddr(p); } accumulator = accumulator.substring(0, accumulator.length - 4); @@ -15116,6 +18129,9 @@ p = cdr(p); howManyIfs = 0; while (iscons(p)) { + // odd number of parameters means that the + // last argument becomes the default case + // i.e. the one without a test. if (cdr(p) === symbol(NIL)) { accumulator += "else {"; accumulator += "return (" + print_expr(car(p)) + ");"; @@ -15128,6 +18144,8 @@ accumulator += "if (" + print_expr(car(p)) + "){"; accumulator += "return (" + print_expr(cadr(p)) + ");"; accumulator += "}"; + // test unsuccessful, continue to the + // next pair of test,value howManyIfs++; p = cddr(p); } @@ -15278,9 +18296,11 @@ print_power = function(base, exponent) { var accumulator, denomExponent, newExponent, numExponent; accumulator = ""; + //debugger if (DEBUG) { console.log("power base: " + base + " " + " exponent: " + exponent); } + // quick check is this is actually a square root. if (isoneovertwo(exponent)) { if (equaln(base, 2)) { if (codeGen) { @@ -15328,6 +18348,13 @@ return accumulator; } if ((equaln(get_binding(symbol(PRINT_LEAVE_X_ALONE)), 0)) || base.printname !== "x") { + // if the exponent is negative then + // we invert the base BUT we don't do + // that if the base is "e", because for + // example when trigonometric functions are + // expressed in terms of exponential functions + // that would be really confusing, one wants to + // keep "e" as the base and the negative exponent if (base !== symbol(E)) { if (isminusone(exponent)) { if (printMode === PRINTMODE_LATEX) { @@ -15395,8 +18422,17 @@ } } if (printMode === PRINTMODE_LATEX && isplusone(exponent)) { + // if we are in latex mode we turn many + // radicals into a radix sign with a power + // underneath, and the power is often one + // (e.g. square root turns into a radical + // with a power one underneath) so handle + // this case simply here, just print the base accumulator += print_expr(base); } else { + // print the base, + // determining if it needs to be + // wrapped in parentheses or not if (isadd(base) || isnegativenumber(base)) { accumulator += print_str('('); accumulator += print_expr(base); @@ -15416,12 +18452,19 @@ } else { accumulator += print_factor(base); } + // print the power symbol + //debugger if (printMode === PRINTMODE_HUMAN && !test_flag) { + //print_str(" ^ ") accumulator += print_str(power_str); } else { accumulator += print_str("^"); } + // print the exponent if (printMode === PRINTMODE_LATEX) { + // in latex mode, one can omit the curly braces + // wrapping the exponent if the exponent is only + // one character long if (print_expr(exponent).length > 1) { accumulator += print_str("{"); accumulator += print_expr(exponent); @@ -15466,6 +18509,7 @@ print_factor = function(p, omitParens) { var accumulator, base, exponent, fbody, parameters, returned; + // debugger accumulator = ""; if (isNumericAtom(p)) { accumulator += print_number(p, false); @@ -15522,6 +18566,21 @@ accumulator += print_power(base, exponent); return accumulator; } + // if (car(p) == _list) { + // print_str("{") + // p = cdr(p) + // if (iscons(p)) { + // print_expr(car(p)) + // p = cdr(p) + // } + // while (iscons(p)) { + // print_str(",") + // print_expr(car(p)) + // p = cdr(p) + // } + // print_str("}") + // return + // } if (car(p) === symbol(FUNCTION)) { fbody = cadr(p); if (!codeGen) { @@ -15562,6 +18621,7 @@ accumulator += print_ABS_latex(p); return accumulator; } else if (car(p) === symbol(SQRT) && printMode === PRINTMODE_LATEX) { + //debugger accumulator += print_SQRT_latex(p); return accumulator; } else if (car(p) === symbol(TRANSPOSE)) { @@ -15637,6 +18697,10 @@ accumulator += print_SUM_codegen(p); return accumulator; } + //else if car(p) == symbol(QUOTE) + // if printMode == PRINTMODE_LATEX + // print_expr(cadr(p)) + // return accumulator } else if (car(p) === symbol(PRODUCT)) { if (printMode === PRINTMODE_LATEX) { accumulator += print_PRODUCT_latex(p); @@ -15744,6 +18808,10 @@ } } if (iscons(p)) { + //if (car(p) == symbol(FORMAL) && cadr(p)->k == SYM) { + // print_str(((struct symbol *) cadr(p))->name) + // return + //} accumulator += print_factor(car(p)); p = cdr(p); if (!omitParens) { @@ -15811,8 +18879,10 @@ accumulator += ')'; break; case STR: + //print_str("\"") accumulator += p.str; break; + //print_str("\"") case NUM: case DOUBLE: accumulator += print_number(p, true); @@ -15852,9 +18922,13 @@ } }; + // don't consider the leading fraction + // we want 2/3*a*b*c instead of 2*a*b*c/3 any_denominators = function(p) { var q; p = cdr(p); + // if (isfraction(car(p))) + // return 1 while (iscons(p)) { q = car(p); if (is_denominator(q)) { @@ -15865,31 +18939,49 @@ return 0; }; - /* - + Prints in "2d", e.g. instead of 1/(x+1)^2 : - + 1 ---------- 2 (1 + x) - + Note that although this looks more natural, a) it's not parsable and b) it can be occasionally be ambiguous, such as: - + 1 ---- 2 x - + is 1/x^2 but it also looks a little like x^(1/2) - */ + */ + //----------------------------------------------------------------------------- + + // Examples: + + // 012345678 + // -2 ......... + // -1 ......... + // 0 ..hello.. x=2, y=0, h=1, w=5 + // 1 ......... + // 2 ......... + + // 012345678 + // -2 ......... + // -1 ..355.... + // 0 ..---.... x=2, y=-1, h=3, w=3 + // 1 ..113.... + // 2 ......... + + //----------------------------------------------------------------------------- YMAX = 10000; glyph = (function() { - function glyph() {} + class glyph {}; glyph.prototype.c = 0; @@ -15899,11 +18991,12 @@ return glyph; - })(); + }).call(this); + // will contain glyphs chartab = []; - for (charTabIndex = i1 = 0, ref1 = YMAX; 0 <= ref1 ? i1 < ref1 : i1 > ref1; charTabIndex = 0 <= ref1 ? ++i1 : --i1) { + for (charTabIndex = i1 = 0, ref1 = YMAX; (0 <= ref1 ? i1 < ref1 : i1 > ref1); charTabIndex = 0 <= ref1 ? ++i1 : --i1) { chartab[charTabIndex] = new glyph(); } @@ -15917,6 +19010,9 @@ display_flag = 0; + // this is not really the translated version, + // the original is in window.cpp and is + // rather more complex printchar_nowrap = function(character) { var accumulator; accumulator = ""; @@ -15929,7 +19025,7 @@ }; print2dascii = function(p) { - var beenPrinted, h, ref2, w, y; + var beenPrinted, h, w, y; h = 0; w = 0; y = 0; @@ -15938,7 +19034,8 @@ level = 0; emit_x = 0; emit_top_expr(p); - ref2 = get_size(0, yindex), h = ref2[0], w = ref2[1], y = ref2[2]; + // if too wide then print flat + [h, w, y] = get_size(0, yindex); if (w > 100) { printline(p); restore(); @@ -15986,6 +19083,10 @@ }; emit_expr = function(p) { + // if (level > 0) { + // printexpr(p) + // return + // } expr_level++; if (car(p) === symbol(ADD)) { p = cdr(p); @@ -16026,6 +19127,8 @@ var results; if (car(p) === symbol(ADD)) { p = cdr(p); + // if (__is_negative(car(p))) + // __emit_char('-') emit_term(car(p)); p = cdr(p); results = []; @@ -16044,6 +19147,8 @@ } return results; } else { + // if (__is_negative(p)) + // __emit_char('-') return emit_term(p); } }; @@ -16084,6 +19189,10 @@ var count, q; count = 0; p = cdr(p); + // if (isfraction(car(p))) { + // count++ + // p = cdr(p) + // } while (iscons(p)) { q = car(p); if (isdenominator(q)) { @@ -16094,6 +19203,7 @@ return count; }; + // n is the number of denominators, not counting a fraction like 1/2 emit_multiply = function(p, n) { var results; if (n === 0) { @@ -16113,6 +19223,7 @@ } else { emit_numerators(p); __emit_char('/'); + // need grouping if more than one denominator if (n > 1 || isfraction(cadr(p))) { __emit_char('('); emit_denominators(p); @@ -16123,6 +19234,10 @@ } }; + //define A p3 + //define B p4 + + // sign of term has already been emitted emit_fraction = function(p, d) { var count, doNothing, k1, k2, n, x; count = 0; @@ -16140,14 +19255,16 @@ p3 = pop(); push(cadr(p)); mp_denominator(); - p4 = pop(); + p4 = pop(); // p4 is B } if (isdouble(cadr(p))) { push(cadr(p)); absval(); - p3 = pop(); + p3 = pop(); // p3 is A } - if (isplusone(p3)) { + + // count numerators + if (isplusone(p3)) { // p3 is A n = 0; } else { n = 1; @@ -16165,14 +19282,17 @@ } p1 = cdr(p1); } + // emit numerators x = emit_x; k1 = yindex; count = 0; - if (!isplusone(p3)) { + if (!isplusone(p3)) { // p3 is A emit_number(p3, 0); count++; } + // skip over "multiply" p1 = cdr(p); + // skip over numerical coefficient, already handled if (isNumericAtom(car(p1))) { p1 = cdr(p1); } @@ -16196,9 +19316,10 @@ if (count === 0) { __emit_char('1'); } + // emit denominators k2 = yindex; count = 0; - if (!isplusone(p4)) { + if (!isplusone(p4)) { // p4 is B emit_number(p4, 0); count++; d++; @@ -16222,6 +19343,7 @@ return restore(); }; + // p points to a multiply emit_numerators = function(p) { var doNothing, n; save(); @@ -16263,6 +19385,7 @@ return restore(); }; + // p points to a multiply emit_denominators = function(p) { var n; save(); @@ -16292,6 +19415,7 @@ emit_factor = function(p) { if (istensor(p)) { if (level === 0) { + //emit_tensor(p) emit_flat_tensor(p); } else { emit_flat_tensor(p); @@ -16311,6 +19435,9 @@ return; } if (iscons(p)) { + //if (car(p) == symbol(FORMAL) && cadr(p).k == SYM) + // emit_symbol(cadr(p)) + //else emit_function(p); return; } @@ -16344,7 +19471,7 @@ push(p); mp_denominator(); p4 = pop(); - if (isplusone(p4)) { + if (isplusone(p4)) { // p4 is B emit_number(p3, 0); restore(); return; @@ -16353,11 +19480,12 @@ k1 = yindex; emit_number(p3, 0); k2 = yindex; - emit_number(p4, 0); + emit_number(p4, 0); // p4 is B fixup_fraction(x, k1, k2); return restore(); }; + // if it's a factor then it doesn't need parens around it, i.e. 1/sin(theta)^2 isfactor = function(p) { if (iscons(p) && car(p) !== symbol(ADD) && car(p) !== symbol(MULTIPLY) && car(p) !== symbol(POWER)) { return 1; @@ -16412,12 +19540,15 @@ } return; } + // special case: 1 over something if (__is_negative(caddr(p))) { x = emit_x; k1 = yindex; __emit_char('1'); k2 = yindex; + //level++ emit_denominator(p, 1); + //level-- fixup_fraction(x, k1, k2); return; } @@ -16434,10 +19565,14 @@ return fixup_power(k1, k2); }; + // if n == 1 then emit as expr (no parens) + + // p is a power emit_denominator = function(p, n) { var k1, k2; k1 = 0; k2 = 0; + // special case: 1 over something if (isminusone(caddr(p))) { if (n === 1) { emit_expr(cadr(p)); @@ -16447,12 +19582,14 @@ return; } k1 = yindex; + // emit base if (isfactor(cadr(p))) { emit_factor(cadr(p)); } else { emit_subexpr(cadr(p)); } k2 = yindex; + // emit exponent, don't emit minus sign level++; emit_unsigned_expr(caddr(p)); level--; @@ -16480,6 +19617,7 @@ p = cdr(p); while (iscons(p)) { __emit_char(','); + //__emit_char(' ') emit_expr(car(p)); p = cdr(p); } @@ -16533,7 +19671,7 @@ } pPrintName = get_printname(p); results = []; - for (i = j1 = 0, ref2 = pPrintName.length; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = pPrintName.length; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { results.push(__emit_char(pPrintName[i])); } return results; @@ -16544,14 +19682,14 @@ i = 0; pString = p.str; __emit_char('"'); - for (i = j1 = 0, ref2 = pString.length; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = pString.length; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { __emit_char(pString[i]); } return __emit_char('"'); }; fixup_fraction = function(x, k1, k2) { - var dx, dy, h1, h2, i, j1, ref2, ref3, ref4, results, w, w1, w2, y, y1, y2; + var dx, dy, h1, h2, i, j1, ref2, results, w, w1, w2, y, y1, y2; dx = 0; dy = 0; i = 0; @@ -16563,14 +19701,15 @@ h2 = 0; w2 = 0; y2 = 0; - ref2 = get_size(k1, k2), h1 = ref2[0], w1 = ref2[1], y1 = ref2[2]; - ref3 = get_size(k2, yindex), h2 = ref3[0], w2 = ref3[1], y2 = ref3[2]; + [h1, w1, y1] = get_size(k1, k2); + [h2, w2, y2] = get_size(k2, yindex); if (w2 > w1) { - dx = (w2 - w1) / 2; + dx = (w2 - w1) / 2; // shift numerator right } else { dx = 0; } dx++; + // this is how much is below the baseline y = y1 + h1 - 1; dy = -y - 1; move(k1, k2, dx, dy); @@ -16590,14 +19729,14 @@ w += 2; emit_x = x; results = []; - for (i = j1 = 0, ref4 = w; 0 <= ref4 ? j1 < ref4 : j1 > ref4; i = 0 <= ref4 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = w; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { results.push(__emit_char('-')); } return results; }; fixup_power = function(k1, k2) { - var dy, h1, h2, ref2, ref3, w1, w2, y1, y2; + var dy, h1, h2, w1, w2, y1, y2; dy = 0; h1 = 0; w1 = 0; @@ -16605,9 +19744,11 @@ h2 = 0; w2 = 0; y2 = 0; - ref2 = get_size(k1, k2), h1 = ref2[0], w1 = ref2[1], y1 = ref2[2]; - ref3 = get_size(k2, yindex), h2 = ref3[0], w2 = ref3[1], y2 = ref3[2]; + [h1, w1, y1] = get_size(k1, k2); + [h2, w2, y2] = get_size(k2, yindex); + // move superscript to baseline dy = -y2 - h2 + 1; + // now move above base dy += y1 - 1; return move(k2, yindex, 0, dy); }; @@ -16616,13 +19757,14 @@ var i, j1, ref2, ref3, results; i = 0; results = []; - for (i = j1 = ref2 = j, ref3 = k; ref2 <= ref3 ? j1 < ref3 : j1 > ref3; i = ref2 <= ref3 ? ++j1 : --j1) { + for (i = j1 = ref2 = j, ref3 = k; (ref2 <= ref3 ? j1 < ref3 : j1 > ref3); i = ref2 <= ref3 ? ++j1 : --j1) { chartab[i].x += dx; results.push(chartab[i].y += dy); } return results; }; + // finds the bounding rectangle and vertical position get_size = function(j, k) { var h, i, j1, max_x, max_y, min_x, min_y, ref2, ref3, w, y; i = 0; @@ -16630,7 +19772,7 @@ max_x = chartab[j].x; min_y = chartab[j].y; max_y = chartab[j].y; - for (i = j1 = ref2 = j + 1, ref3 = k; ref2 <= ref3 ? j1 < ref3 : j1 > ref3; i = ref2 <= ref3 ? ++j1 : --j1) { + for (i = j1 = ref2 = j + 1, ref3 = k; (ref2 <= ref3 ? j1 < ref3 : j1 > ref3); i = ref2 <= ref3 ? ++j1 : --j1) { if (chartab[i].x < min_x) { min_x = chartab[i].x; } @@ -16672,7 +19814,7 @@ var i, j1, ref2, results; i = 0; results = []; - for (i = j1 = 0, ref2 = s.length; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = s.length; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { results.push(__emit_char(s[i])); } return results; @@ -16688,7 +19830,7 @@ if (tmpString[0] === '-' && emit_sign === 0) { tmpString = tmpString.substring(1); } - for (i = j1 = 0, ref2 = tmpString.length; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = tmpString.length; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { __emit_char(tmpString[i]); } tmpString = p.q.b.toString(); @@ -16697,7 +19839,7 @@ } __emit_char('/'); results = []; - for (i = l1 = 0, ref3 = tmpString.length; 0 <= ref3 ? l1 < ref3 : l1 > ref3; i = 0 <= ref3 ? ++l1 : --l1) { + for (i = l1 = 0, ref3 = tmpString.length; (0 <= ref3 ? l1 < ref3 : l1 > ref3); i = 0 <= ref3 ? ++l1 : --l1) { results.push(__emit_char(tmpString[i])); } return results; @@ -16708,13 +19850,14 @@ tmpString = tmpString.substring(1); } results1 = []; - for (i = m1 = 0, ref4 = tmpString.length; 0 <= ref4 ? m1 < ref4 : m1 > ref4; i = 0 <= ref4 ? ++m1 : --m1) { + for (i = m1 = 0, ref4 = tmpString.length; (0 <= ref4 ? m1 < ref4 : m1 > ref4); i = 0 <= ref4 ? ++m1 : --m1) { results1.push(__emit_char(tmpString[i])); } return results1; } }; + // a and b are glyphs cmpGlyphs = function(a, b) { if (a.y < b.y) { return -1; @@ -16735,12 +19878,18 @@ var accumulator, i, j1, ref2, subsetOfStack, x, y; i = 0; accumulator = ""; + + // now sort the glyphs by their vertical positions, + // since we are going to build a string where obviously the + // "upper" line has to printed out first, followed by + // a new line, followed by the other lines. + //qsort(chartab, yindex, sizeof (struct glyph), __cmp) subsetOfStack = chartab.slice(0, yindex); subsetOfStack.sort(cmpGlyphs); chartab = [].concat(subsetOfStack).concat(chartab.slice(yindex)); x = 0; y = chartab[0].y; - for (i = j1 = 0, ref2 = yindex; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = yindex; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { while (chartab[i].y > y) { accumulator += printchar('\n'); x = 0; @@ -16772,12 +19921,13 @@ tmpBuffer = buffer; sIndex = 0; i = 0; + //qsort(chartab, yindex, sizeof (struct glyph), __cmp) subsetOfStack = chartab.slice(0, yindex); subsetOfStack.sort(cmpGlyphs); chartab = [].concat(subsetOfStack).concat(chartab.slice(yindex)); x = 0; y = chartab[0].y; - for (i = j1 = 0, ref2 = yindex; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = yindex; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { while (chartab[i].y > y) { tmpBuffer[sIndex++] = '\n'; x = 0; @@ -16796,7 +19946,7 @@ N = 100; oneElement = (function() { - function oneElement() {} + class oneElement {}; oneElement.prototype.x = 0; @@ -16812,12 +19962,12 @@ return oneElement; - })(); + }).call(this); elem = []; for (elelmIndex = j1 = 0; j1 < 10000; elelmIndex = ++j1) { - elem[elelmIndex] = new oneElement; + elem[elelmIndex] = new oneElement(); } SPACE_BETWEEN_COLUMNS = 3; @@ -16825,7 +19975,7 @@ SPACE_BETWEEN_ROWS = 1; emit_tensor = function(p) { - var col, dx, dy, eh, ew, h, i, l1, m1, n, n1, ncol, nrow, o1, ref2, ref3, ref4, ref5, ref6, row, w, x, y; + var col, dx, dy, eh, ew, h, i, l1, m1, n, n1, ncol, nrow, o1, ref2, ref3, ref4, ref5, row, w, x, y; i = 0; n = 0; nrow = 0; @@ -16855,17 +20005,24 @@ emit_flat_tensor(p); return; } + // horizontal coordinate of the matrix + + //if 0 + //emit_x += 2; # make space for left paren + //endif x = emit_x; - for (i = l1 = 0, ref2 = n; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { +// emit each element + for (i = l1 = 0, ref2 = n; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { elem[i].index = yindex; elem[i].x = emit_x; emit_expr(p.tensor.elem[i]); elem[i].count = yindex - elem[i].index; - ref3 = get_size(elem[i].index, yindex), elem[i].h = ref3[0], elem[i].w = ref3[1], elem[i].y = ref3[2]; + [elem[i].h, elem[i].w, elem[i].y] = get_size(elem[i].index, yindex); } + // find element height and width eh = 0; ew = 0; - for (i = m1 = 0, ref4 = n; 0 <= ref4 ? m1 < ref4 : m1 > ref4; i = 0 <= ref4 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = n; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { if (elem[i].h > eh) { eh = elem[i].h; } @@ -16873,61 +20030,37 @@ ew = elem[i].w; } } + // this is the overall height of the matrix h = nrow * eh + (nrow - 1) * SPACE_BETWEEN_ROWS; + // this is the overall width of the matrix w = ncol * ew + (ncol - 1) * SPACE_BETWEEN_COLUMNS; + // this is the vertical coordinate of the matrix y = -(h / 2); - for (row = n1 = 0, ref5 = nrow; 0 <= ref5 ? n1 < ref5 : n1 > ref5; row = 0 <= ref5 ? ++n1 : --n1) { - for (col = o1 = 0, ref6 = ncol; 0 <= ref6 ? o1 < ref6 : o1 > ref6; col = 0 <= ref6 ? ++o1 : --o1) { +// move elements around + for (row = n1 = 0, ref4 = nrow; (0 <= ref4 ? n1 < ref4 : n1 > ref4); row = 0 <= ref4 ? ++n1 : --n1) { + for (col = o1 = 0, ref5 = ncol; (0 <= ref5 ? o1 < ref5 : o1 > ref5); col = 0 <= ref5 ? ++o1 : --o1) { i = row * ncol + col; + // first move to upper left corner of matrix dx = x - elem[i].x; dy = y - elem[i].y; move(elem[i].index, elem[i].index + elem[i].count, dx, dy); + // now move to official position dx = 0; if (col > 0) { dx = col * (ew + SPACE_BETWEEN_COLUMNS); } dy = 0; - if (row > 0) { - dy = row * (eh + SPACE_BETWEEN_ROWS); - } - dx += (ew - elem[i].w) / 2; - dy += (eh - elem[i].h) / 2; - move(elem[i].index, elem[i].index + elem[i].count, dx, dy); - } - } - return emit_x = x + w; - - /* - if 0 - - * left brace - - for (i = 0; i < h; i++) { - if (yindex == YMAX) - break - chartab[yindex].c = '|' - chartab[yindex].x = x - 2 - chartab[yindex].y = y + i - yindex++ - } - - * right brace - - emit_x++ - - for (i = 0; i < h; i++) { - if (yindex == YMAX) - break - chartab[yindex].c = '|' - chartab[yindex].x = emit_x - chartab[yindex].y = y + i - yindex++ + if (row > 0) { + dy = row * (eh + SPACE_BETWEEN_ROWS); + } + // small correction for horizontal centering + dx += (ew - elem[i].w) / 2; + // small correction for vertical centering + dy += (eh - elem[i].h) / 2; + move(elem[i].index, elem[i].index + elem[i].count, dx, dy); } - - emit_x++ - - endif - */ + } + return emit_x = x + w; }; emit_flat_tensor = function(p) { @@ -16938,7 +20071,7 @@ var i, l1, ref2; i = 0; __emit_char('('); - for (i = l1 = 0, ref2 = p.tensor.dim[j]; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p.tensor.dim[j]; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { if (j + 1 === p.tensor.ndim) { emit_expr(p.tensor.elem[k]); k = k + 1; @@ -16953,16 +20086,27 @@ return k; }; + // 'product' function + + //define A p3 + //define B p4 + //define I p5 + //define X p6 + + // leaves the product at the top of the stack Eval_product = function() { var body, i, indexVariable, j, k, l1, oldIndexVariableValue, ref2, ref3; i = 0; j = 0; k = 0; + // 1st arg body = cadr(p1); + // 2nd arg (index) indexVariable = caddr(p1); if (!issymbol(indexVariable)) { stop("sum: 2nd arg?"); } + // 3rd arg (lower limit) push(cadddr(p1)); Eval(); j = pop_integer(); @@ -16970,6 +20114,7 @@ push(p1); return; } + // 4th arg (upper limit) push(caddddr(p1)); Eval(); k = pop_integer(); @@ -16977,9 +20122,11 @@ push(p1); return; } + // remember contents of the index + // variable so we can put it back after the loop oldIndexVariableValue = get_binding(indexVariable); push_integer(1); - for (i = l1 = ref2 = j, ref3 = k; ref2 <= ref3 ? l1 <= ref3 : l1 >= ref3; i = ref2 <= ref3 ? ++l1 : --l1) { + for (i = l1 = ref2 = j, ref3 = k; (ref2 <= ref3 ? l1 <= ref3 : l1 >= ref3); i = ref2 <= ref3 ? ++l1 : --l1) { push_integer(i); p5 = pop(); set_binding(indexVariable, p5); @@ -16994,35 +20141,71 @@ console.log("product - result: " + stack[tos - 1].toString()); } } + // put back the index variable to original content return set_binding(indexVariable, oldIndexVariableValue); }; + // Add rational numbers + + // Input: tos-2 addend + + // tos-1 addend + + // Output: sum on stack qadd = function() { var gcdBetweenNumeratorAndDenominator, qadd_ab, qadd_ba, qadd_denominator, qadd_frac1, qadd_frac2, qadd_numerator, resultSum; + // a, qadd_ab, b, qadd_ba, c are all bigNum + // we are adding the fractions qadd_frac1 + qadd_frac2 i.e. + // qadd_frac1.q.a/qadd_frac1.q.b + qadd_frac2.q.a/qadd_frac2.q.b qadd_frac2 = pop(); qadd_frac1 = pop(); qadd_ab = mmul(qadd_frac1.q.a, qadd_frac2.q.b); qadd_ba = mmul(qadd_frac1.q.b, qadd_frac2.q.a); qadd_numerator = madd(qadd_ab, qadd_ba); + //mfree(qadd_ab) + //mfree(qadd_ba) + + // zero? if (MZERO(qadd_numerator)) { + //console.log "qadd IS ZERO" + //mfree(qadd_numerator) push(zero); return; } qadd_denominator = mmul(qadd_frac1.q.b, qadd_frac2.q.b); gcdBetweenNumeratorAndDenominator = mgcd(qadd_numerator, qadd_denominator); + //console.log "gcd("+qadd_numerator+","+qadd_denominator+"): " + gcdBetweenNumeratorAndDenominator gcdBetweenNumeratorAndDenominator = makeSignSameAs(gcdBetweenNumeratorAndDenominator, qadd_denominator); + //console.log "qadd qadd_denominator: " + qadd_denominator + //console.log "qadd gcdBetweenNumeratorAndDenominator: " + gcdBetweenNumeratorAndDenominator resultSum = new U(); resultSum.k = NUM; resultSum.q.a = mdiv(qadd_numerator, gcdBetweenNumeratorAndDenominator); resultSum.q.b = mdiv(qadd_denominator, gcdBetweenNumeratorAndDenominator); + //console.log "qadd resultSum.q.a: " + resultSum.q.a + //console.log "qadd resultSum.q.b: " + resultSum.q.b + + //mfree(qadd_numerator) + //mfree(qadd_denominator) + //mfree(gcdBetweenNumeratorAndDenominator) return push(resultSum); }; + //console.log "qadd result: " + resultSum + + // Divide rational numbers + + // Input: tos-2 dividend + + // tos-1 divisor + + // Output: quotient on stack qdiv = function() { var aa, bb, c; save(); p2 = pop(); p1 = pop(); + // zero? if (MZERO(p2.q.a)) { stop("divide by zero"); } @@ -17043,11 +20226,19 @@ return restore(); }; + // Multiply rational numbers + + // Input: tos-2 multiplicand + + // tos-1 multiplier + + // Output: product on stack qmul = function() { var aa, bb, c; save(); p2 = pop(); p1 = pop(); + // zero? if (MZERO(p1.q.a) || MZERO(p2.q.a)) { push(zero); restore(); @@ -17061,44 +20252,55 @@ p1.k = NUM; p1.q.a = mdiv(aa, c); p1.q.b = mdiv(bb, c); + //mfree(aa) + //mfree(bb) push(p1); return restore(); }; + // Rational power function qpow = function() { save(); qpowf(); return restore(); }; + //define BASE p1 + //define EXPO p2 qpowf = function() { var a, b, expo, t, x, y; expo = 0; + //unsigned int a, b, *t, *x, *y p2 = pop(); p1 = pop(); - if (isplusone(p1) || isZeroAtomOrTensor(p2)) { + if (isplusone(p1) || isZeroAtomOrTensor(p2)) { // p1 is BASE # p2 is EXPO push_integer(1); return; } - if (isminusone(p1) && isoneovertwo(p2)) { + // if (-1)^(1/2) -> leave it as is + if (isminusone(p1) && isoneovertwo(p2)) { // p1 is BASE # p2 is EXPO push(imaginaryunit); return; } - if (isZeroAtomOrTensor(p1)) { - if (isnegativenumber(p2)) { + // if base is zero then return 0 + if (isZeroAtomOrTensor(p1)) { // p1 is BASE + if (isnegativenumber(p2)) { // p2 is EXPO stop("divide by zero"); } push(zero); return; } - if (isplusone(p2)) { + // if exponent is 1 then return base + if (isplusone(p2)) { // p2 is EXPO push(p1); return; } - if (isinteger(p2)) { + // if exponent is integer then power + if (isinteger(p2)) { // p2 is EXPO push(p2); expo = pop_integer(); if (isNaN(expo)) { + // expo greater than 32 bits push_symbol(POWER); push(p1); push(p2); @@ -17121,12 +20323,16 @@ push(p3); return; } - if (isminusone(p1)) { + // from here on out the exponent is NOT an integer + + // if base is -1 then normalize polar angle + if (isminusone(p1)) { // p1 is BASE push(p2); normalize_angle(); return; } - if (isnegativenumber(p1)) { + // if base is negative then (-N)^M -> N^M * (-1)^M + if (isnegativenumber(p1)) { // p1 is BASE push(p1); negate(); push(p2); @@ -17137,7 +20343,7 @@ multiply(); return; } - if (!isinteger(p1)) { + if (!isinteger(p1)) { // p1 is BASE push(p1); mp_numerator(); push(p2); @@ -17150,15 +20356,18 @@ multiply(); return; } - if (is_small_integer(p1)) { + // At this point p1 (BASE) is a positive integer. + + // If p1 (BASE) is small then factor it. + if (is_small_integer(p1)) { // p1 is BASE push(p1); push(p2); quickfactor(); return; } - if (!isSmall(p2.q.a) || !isSmall(p2.q.b)) { + if (!isSmall(p2.q.a) || !isSmall(p2.q.b)) { // p2 is EXPO push_symbol(POWER); - push(p1); + push(p1); // p1 is BASE push(p2); list(3); return; @@ -17174,9 +20383,10 @@ return; } y = mpow(x, a); + //mfree(x) p3 = new U(); p3.k = NUM; - if (p2.q.a.isNegative()) { + if (p2.q.a.isNegative()) { // p2 is EXPO p3.q.a = bigInt(1); p3.q.b = y; } else { @@ -17186,36 +20396,74 @@ return push(p3); }; + //----------------------------------------------------------------------------- + + // Normalize the angle of unit imaginary, i.e. (-1) ^ N + + // Input: N on stack (must be rational, not float) + + // Output: Result on stack + + // Note: + + // n = q * d + r + + // Example: + // n d q r + + // (-1)^(8/3) -> (-1)^(2/3) 8 3 2 2 + // (-1)^(7/3) -> (-1)^(1/3) 7 3 2 1 + // (-1)^(5/3) -> -(-1)^(2/3) 5 3 1 2 + // (-1)^(4/3) -> -(-1)^(1/3) 4 3 1 1 + // (-1)^(2/3) -> (-1)^(2/3) 2 3 0 2 + // (-1)^(1/3) -> (-1)^(1/3) 1 3 0 1 + + // (-1)^(-1/3) -> -(-1)^(2/3) -1 3 -1 2 + // (-1)^(-2/3) -> -(-1)^(1/3) -2 3 -1 1 + // (-1)^(-4/3) -> (-1)^(2/3) -4 3 -2 2 + // (-1)^(-5/3) -> (-1)^(1/3) -5 3 -2 1 + // (-1)^(-7/3) -> -(-1)^(2/3) -7 3 -3 2 + // (-1)^(-8/3) -> -(-1)^(1/3) -8 3 -3 1 + + //----------------------------------------------------------------------------- + + //define A p1 + //define Q p2 + //define R p3 normalize_angle = function() { save(); p1 = pop(); - if (isinteger(p1)) { - if (p1.q.a.isOdd()) { - push_integer(-1); + if (isinteger(p1)) { // p1 is A + if (p1.q.a.isOdd()) { // p1 is A + push_integer(-1); // odd exponent } else { - push_integer(1); + push_integer(1); // even exponent } restore(); return; } + // floor push(p1); bignum_truncate(); p2 = pop(); - if (isnegativenumber(p1)) { - push(p2); + if (isnegativenumber(p1)) { // p1 is A + push(p2); // p2 is Q push_integer(-1); add(); - p2 = pop(); + p2 = pop(); // p2 is Q } + + // remainder (always positive) push(p1); push(p2); subtract(); p3 = pop(); push_symbol(POWER); push_integer(-1); - push(p3); + push(p3); // p3 is R list(3); - if (p2.q.a.isOdd()) { + // negate if quotient is odd + if (p2.q.a.isOdd()) { // p2 is Q negate(); } return restore(); @@ -17225,6 +20473,20 @@ return isSmall(p.q.a); }; + //----------------------------------------------------------------------------- + + // Factor small numerical powers + + // Input: tos-2 Base (positive integer < 2^31 - 1) + + // tos-1 Exponent + + // Output: Expr on stack + + //----------------------------------------------------------------------------- + + //define BASE p1 + //define EXPO p2 quickfactor = function() { var h, i, l1, n, ref2, stackIndex; i = 0; @@ -17243,6 +20505,11 @@ multiply(); quickpower(); } + // stack has n results from factor_number_raw() + + // on top of that are all the expressions from quickpower() + + // multiply the quickpower() results multiply_all(tos - h - n); p1 = pop(); moveTos(h); @@ -17250,6 +20517,7 @@ return restore(); }; + // p1 (BASE) is a prime number so power is simpler quickpower = function() { var expo; expo = 0; @@ -17288,6 +20556,9 @@ return restore(); }; + //if SELFTEST + + // Divide polynomials Eval_quotient = function() { push(cadr(p1)); Eval(); @@ -17303,6 +20574,25 @@ return divpoly(); }; + //----------------------------------------------------------------------------- + + // Divide polynomials + + // Input: tos-3 Dividend + + // tos-2 Divisor + + // tos-1 x + + // Output: tos-1 Quotient + + //----------------------------------------------------------------------------- + + //define DIVIDEND p1 + //define DIVISOR p2 + //define X p3 + //define Q p4 + //define QUOTIENT p5 divpoly = function() { var dividend, divisor, h, i, l1, m, n, ref2, x; h = 0; @@ -17310,6 +20600,7 @@ m = 0; n = 0; x = 0; + //U **dividend, **divisor save(); p3 = pop(); p2 = pop(); @@ -17331,7 +20622,7 @@ push(stack[divisor + n]); divide(); p4 = pop(); - for (i = l1 = 0, ref2 = n; 0 <= ref2 ? l1 <= ref2 : l1 >= ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = n; (0 <= ref2 ? l1 <= ref2 : l1 >= ref2); i = 0 <= ref2 ? ++l1 : --l1) { push(stack[dividend + x + i]); push(stack[divisor + i]); push(p4); @@ -17384,6 +20675,7 @@ printf("rationalize: this is the input expr:\n"); printline(theArgument); } + // get common denominator push(one); multiply_denominators(theArgument); commonDenominator = pop(); @@ -17391,6 +20683,7 @@ printf("rationalize: this is the common denominator:\n"); printline(commonDenominator); } + // multiply each term by common denominator push(zero); eachTerm = cdr(theArgument); while (iscons(eachTerm)) { @@ -17404,11 +20697,13 @@ printf("rationalize: original expr times common denominator:\n"); printline(stack[tos - 1]); } + // collect common factors Condense(); if (DEBUG) { printf("rationalize: after factoring:\n"); printline(stack[tos - 1]); } + // divide by common denominator push(commonDenominator); divide(); if (DEBUG) { @@ -17453,16 +20748,19 @@ } push(p); p = caddr(p); + // like x^(-2) ? if (isnegativenumber(p)) { inverse(); __lcm(); return; } + // like x^(-a) ? if (car(p) === symbol(MULTIPLY) && isnegativenumber(cadr(p))) { inverse(); __lcm(); return; } + // no match return pop(); }; @@ -17472,12 +20770,12 @@ push(theTensor); Eval(); theTensor = pop(); - if (!istensor(theTensor)) { + if (!istensor(theTensor)) { // might be zero push(theTensor); return; } n = theTensor.tensor.nelem; - for (i = l1 = 0, ref2 = n; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = n; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { push(theTensor.tensor.elem[i]); rationalize(); theTensor.tensor.elem[i] = pop(); @@ -17500,18 +20798,6 @@ return restore(); }; - - /* - Returns the real part of complex z - - z real(z) - - ------- - - a + i b a - - exp(i a) cos(a) - */ - Eval_real = function() { push(cadr(p1)); Eval(); @@ -17531,15 +20817,6 @@ return restore(); }; - - /* - Convert complex z to rectangular form - - Input: push z - - Output: Result on stack - */ - DEBUG_RECT = false; Eval_rect = function() { @@ -17559,6 +20836,10 @@ if (DEBUG_RECT) { console.log("any clock forms in : " + input + " ? " + findPossibleClockForm(input)); } + // if we assume real variables, then the + // rect of any symbol is the symbol itself + // (note that 'i' is not a symbol, it's made of (-1)^(1/2)) + // otherwise we have to leave unevalled if (issymbol(p1)) { if (DEBUG_RECT) { console.log(" rect: simple symbol: " + input); @@ -17570,13 +20851,15 @@ push(p1); list(2); } - } else if (!isZeroAtomOrTensor(get_binding(symbol(ASSUME_REAL_VARIABLES))) && !findPossibleExponentialForm(p1) && !findPossibleClockForm(p1) && !(Find(p1, symbol(SIN)) && Find(p1, symbol(COS)) && Find(p1, imaginaryunit))) { + } else if (!isZeroAtomOrTensor(get_binding(symbol(ASSUME_REAL_VARIABLES))) && !findPossibleExponentialForm(p1) && !findPossibleClockForm(p1) && !(Find(p1, symbol(SIN)) && Find(p1, symbol(COS)) && Find(p1, imaginaryunit))) { // no polar form? if (DEBUG_RECT) { console.log(" rect: simple symbol: " + input); } push(p1); + // ib } else if (car(p1) === symbol(MULTIPLY) && isimaginaryunit(cadr(p1)) && !isZeroAtomOrTensor(get_binding(symbol(ASSUME_REAL_VARIABLES)))) { push(p1); + // sum } else if (car(p1) === symbol(ADD)) { if (DEBUG_RECT) { console.log(" rect - " + input + " is a sum "); @@ -17590,6 +20873,9 @@ p1 = cdr(p1); } } else { + // try to get to the rectangular form by doing + // abs(p1) * (cos (theta) + i * sin(theta)) + // where theta is arg(p1) if (DEBUG_RECT) { console.log(" rect - " + input + " is NOT a sum "); } @@ -17631,11 +20917,18 @@ } }; + //define POLY p1 + //define X p2 + //define A p3 + //define B p4 + //define C p5 + //define Y p6 show_power_debug = false; performing_roots = false; Eval_roots = function() { + // A == B -> A - B p2 = cadr(p1); if (car(p2) === symbol(SETQ) || car(p2) === symbol(TESTEQ)) { push(cadr(p2)); @@ -17657,6 +20950,7 @@ push(p2); } } + // 2nd arg, x push(caddr(p1)); Eval(); p2 = pop(); @@ -17677,9 +20971,11 @@ hasImaginaryCoeff = function(k) { var h, i, imaginaryCoefficients, l1, ref2; + //polycoeff = tos imaginaryCoefficients = false; h = tos; for (i = l1 = ref2 = k; l1 > 0; i = l1 += -1) { + //console.log "hasImaginaryCoeff - coeff.:" + stack[tos-i].toString() if (iscomplexnumber(stack[tos - i])) { imaginaryCoefficients = true; break; @@ -17690,6 +20986,10 @@ isSimpleRoot = function(k) { var h, i, isSimpleRootPolynomial, l1, ref2; + //polycoeff = tos + + //tos-n Coefficient of x^0 + //tos-1 Coefficient of x^(n-1) if (k > 2) { isSimpleRootPolynomial = true; h = tos; @@ -17711,25 +21011,36 @@ normalisedCoeff = function() { var divideBy, i, k, l1, m1, miniStack, ref2, ref3; k = coeff(); + //console.log("->" + tos) divideBy = stack[tos - 1]; miniStack = []; - for (i = l1 = 1, ref2 = k; 1 <= ref2 ? l1 <= ref2 : l1 >= ref2; i = 1 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 1, ref2 = k; (1 <= ref2 ? l1 <= ref2 : l1 >= ref2); i = 1 <= ref2 ? ++l1 : --l1) { miniStack.push(pop()); } - for (i = m1 = ref3 = k - 1; ref3 <= 0 ? m1 <= 0 : m1 >= 0; i = ref3 <= 0 ? ++m1 : --m1) { +//console.log(tos) + for (i = m1 = ref3 = k - 1; (ref3 <= 0 ? m1 <= 0 : m1 >= 0); i = ref3 <= 0 ? ++m1 : --m1) { push(miniStack[i]); push(divideBy); divide(); } + //console.log(tos) return k; }; + // takes the polynomial and the + // variable on the stack roots = function() { var h, i, k, l1, lastCoeff, leadingCoeff, n, ref2; h = 0; i = 0; n = 0; save(); + // the simplification of nested radicals uses + // "roots", which in turn uses simplification + // of nested radicals. Usually there is no problem, + // one level of recursion does the job. Beyond that, + // we probably got stuck in a strange case of infinite + // recursion, so bail out and return NIL. if (recursionLevelNestedRadicalsRemoval > 1) { pop(); pop(); @@ -17776,7 +21087,7 @@ p1 = alloc_tensor(n); p1.tensor.ndim = 1; p1.tensor.dim[0] = n; - for (i = l1 = 0, ref2 = n; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = n; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p1.tensor.elem[i] = stack[h + i]; } moveTos(h); @@ -17785,12 +21096,20 @@ return performing_roots = false; }; + // ok to generate these roots take a look at their form + // in the case of even and odd exponents here: + // http://www.wolframalpha.com/input/?i=roots+x%5E14+%2B+1 + // http://www.wolframalpha.com/input/?i=roots+ax%5E14+%2B+b + // http://www.wolframalpha.com/input/?i=roots+x%5E15+%2B+1 + // http://www.wolframalpha.com/input/?i=roots+a*x%5E15+%2B+b getSimpleRoots = function(n, leadingCoeff, lastCoeff) { var aSol, commonPart, l1, m1, ref2, ref3, rootsOfOne; if (DEBUG) { console.log("getSimpleRoots"); } save(); + //tos-n Coefficient of x^0 + //tos-1 Coefficient of x^(n-1) n = n - 1; push(lastCoeff); push_rational(1, n); @@ -17813,7 +21132,7 @@ negate(); } } else { - for (rootsOfOne = m1 = 1, ref3 = n; 1 <= ref3 ? m1 <= ref3 : m1 >= ref3; rootsOfOne = 1 <= ref3 ? ++m1 : --m1) { + for (rootsOfOne = m1 = 1, ref3 = n; (1 <= ref3 ? m1 <= ref3 : m1 >= ref3); rootsOfOne = 1 <= ref3 ? ++m1 : --m1) { push(commonPart); push_integer(-1); push_rational(rootsOfOne, n); @@ -17830,8 +21149,8 @@ roots2 = function() { var k; save(); - p2 = pop(); - p1 = pop(); + p2 = pop(); // the polynomial variable + p1 = pop(); // the polynomial push(p1); push(p2); push(p1); @@ -17848,6 +21167,8 @@ } if (car(p1) === symbol(MULTIPLY)) { p1 = cdr(p1); + // scan through all the factors + // and find the roots of each of them while (iscons(p1)) { push(car(p1)); push(p2); @@ -17881,10 +21202,29 @@ return restore(); }; + //----------------------------------------------------------------------------- + + // Input: stack[tos - 2] polynomial + + // stack[tos - 1] dependent symbol + + // Output: stack roots on stack + + // (input args are popped first) + + //----------------------------------------------------------------------------- + + // note that for many quadratic, cubic and quartic polynomials we don't + // actually end up using the quadratic/cubic/quartic formulas in here, + // since there is a chance we factored the polynomial and in so + // doing we found some solutions and lowered the degree. mini_solve = function(n) { var C_CHECKED_AS_NOT_ZERO, Q_CHECKED_AS_NOT_ZERO, R_18_a_b_c_d, R_27_a2_d, R_2_b3, R_3_a, R_3_a_C, R_3_a_c, R_4_DELTA03, R_6_a, R_6_a_C, R_C, R_C_over_3a, R_C_simplified_toCheckIfZero, R_DELTA0, R_DELTA0_simplified_toCheckIfZero, R_DELTA0_toBeCheckedIfZero, R_DELTA1, R_Q, R_Q_simplified_toCheckIfZero, R_S, R_S_simplified_toCheckIfZero, R_a2, R_a2_d, R_a2_d2, R_a3, R_a_b_c, R_a_b_c_d, R_a_c, R_b2, R_b2_c2, R_b3, R_b3_d, R_c2, R_c3, R_d2, R_determinant, R_determinant_simplified_toCheckIfZero, R_e2, R_e3, R_m, R_m27_a2_d2, R_m4_a_c3, R_m4_b3_d, R_m9_a_b_c, R_m_b_over_3a, R_minus_4S2_minus_2p, R_minus_b_over_4a, R_p, R_principalCubicRoot, R_q, R_q_over_S, R_r, S_CHECKED_AS_NOT_ZERO, ThreePPlus2M, TwoQOversqrtPPlus2M, biquadraticSolutions, choiceOfRadicalInQSoSIsNotZero, coeff2, coeff3, coeff4, depressedSolutions, eachSolution, flipSignOFQSoCIsNotZero, flipSignOFRadicalSoQIsNotZero, i_sqrt3, l1, len, len1, len2, m1, n1, one_minus_i_sqrt3, one_plus_i_sqrt3, ref2, ref3, ref4, resolventCubicSolutions, root_solution, sqrtPPlus2M, toBeCheckedIFZero; + //console.log "mini_solve >>>>>>>>>>>>>>>>>>>>>>>> tos:" + tos save(); + // AX + B, X = -B/A if (n === 2) { + //console.log "mini_solve >>>>>>>>> 1st degree" p3 = pop(); p4 = pop(); push(p4); @@ -17894,45 +21234,71 @@ restore(); return; } + // AX^2 + BX + C, X = (-B +/- (B^2 - 4AC)^(1/2)) / (2A) if (n === 3) { - p3 = pop(); - p4 = pop(); - p5 = pop(); + //console.log "mini_solve >>>>>>>>> 2nd degree" + p3 = pop(); // A + p4 = pop(); // B + p5 = pop(); // C + + // B^2 push(p4); push_integer(2); power(); + // 4AC push_integer(4); push(p3); multiply(); push(p5); multiply(); + // B^2 - 4AC subtract(); + //(B^2 - 4AC)^(1/2) push_rational(1, 2); power(); + //p6 is (B^2 - 4AC)^(1/2) p6 = pop(); push(p6); push(p4); - subtract(); + subtract(); // -B + (B^2 - 4AC)^(1/2) + + // 1/2A push(p3); push_integer(2); multiply(); divide(); + //simplify() + //rationalize() + // tos - 1 now is 1st root: (-B + (B^2 - 4AC)^(1/2)) / (2A) push(p6); push(p4); add(); + // tos - 1 now is B + (B^2 - 4AC)^(1/2) + // tos - 2: 1st root: (-B + (B^2 - 4AC)^(1/2)) / (2A) negate(); + // tos - 1 now is -B -(B^2 - 4AC)^(1/2) + // tos - 2: 1st root: (-B + (B^2 - 4AC)^(1/2)) / (2A) + + // 1/2A again push(p3); divide(); push_rational(1, 2); multiply(); + //simplify() + //rationalize() + // tos - 1: 2nd root: (-B - (B^2 - 4AC)^(1/2)) / (2A) + // tos - 2: 1st root: (-B + (B^2 - 4AC)^(1/2)) / (2A) restore(); return; } + //if (n == 4) if (n === 4 || n === 5) { - p3 = pop(); - p4 = pop(); - p5 = pop(); - p6 = pop(); + p3 = pop(); // A + p4 = pop(); // B + p5 = pop(); // C + p6 = pop(); // D + + // C - only related calculations push(p5); push(p5); multiply(); @@ -17941,6 +21307,7 @@ push(p5); multiply(); R_c3 = pop(); + // B - only related calculations push(p4); push(p4); multiply(); @@ -17961,6 +21328,7 @@ push_integer(2); multiply(); R_2_b3 = pop(); + // A - only related calculations push(p3); push(p3); multiply(); @@ -17993,6 +21361,7 @@ push_integer(2); multiply(); R_6_a = pop(); + // mixed calculations push(p3); push(p5); multiply(); @@ -18041,6 +21410,10 @@ if (DEBUG) { console.log(">>>>>>>>>>>>>>>> actually using cubic formula <<<<<<<<<<<<<<< "); } + //console.log ">>>> A:" + p3.toString() + //console.log ">>>> B:" + p4.toString() + //console.log ">>>> C:" + p5.toString() + //console.log ">>>> D:" + p6.toString() if (DEBUG) { console.log("cubic: D0: " + R_DELTA0.toString()); } @@ -18057,6 +21430,10 @@ if (DEBUG) { console.log("cubic: D0 as float: " + R_DELTA0_toBeCheckedIfZero.toString()); } + //if isZeroAtomOrTensor(R_DELTA0_toBeCheckedIfZero) + // console.log " *********************************** D0 IS ZERO" + + // DETERMINANT push(R_18_a_b_c_d); push(R_m4_b3_d); push(R_b2_c2); @@ -18072,6 +21449,7 @@ if (DEBUG) { console.log("cubic: DETERMINANT: " + R_determinant.toString()); } + // R_DELTA1 push(R_2_b3); push(R_m9_a_b_c); push(R_27_a2_d); @@ -18081,6 +21459,7 @@ if (DEBUG) { console.log("cubic: D1: " + R_DELTA1.toString()); } + // R_Q push(R_DELTA1); push_integer(2); power(); @@ -18095,7 +21474,7 @@ if (DEBUG) { console.log(" cubic: DETERMINANT IS ZERO and delta0 is zero"); } - push(R_m_b_over_3a); + push(R_m_b_over_3a); // just same solution three times restore(); return; } else { @@ -18114,13 +21493,16 @@ push(R_DELTA0); push_integer(2); multiply(); - divide(); + divide(); // first solution root_solution = pop(); + push(root_solution); // pushing two of them on the stack push(root_solution); - push(root_solution); + // second solution here + // 4abc push(R_a_b_c); push_integer(4); multiply(); + // -9a*a*d push(p3); push(p3); push(p6); @@ -18129,13 +21511,17 @@ multiply(); multiply(); negate(); + // -9*b^3 push(R_b3); negate(); + // sum the three terms add(); add(); + // denominator is a*delta0 push(p3); push(R_DELTA0); multiply(); + // build the fraction divide(); restore(); return; @@ -18144,6 +21530,7 @@ C_CHECKED_AS_NOT_ZERO = false; flipSignOFQSoCIsNotZero = false; while (!C_CHECKED_AS_NOT_ZERO) { + // R_C push(R_Q); if (flipSignOFQSoCIsNotZero) { negate(); @@ -18183,6 +21570,7 @@ push_integer(2); multiply(); R_6_a_C = pop(); + // imaginary parts calculations push(imaginaryunit); push_integer(3); push_rational(1, 2); @@ -18201,52 +21589,60 @@ push(R_3_a); divide(); R_C_over_3a = pop(); - push(R_m_b_over_3a); + // first solution + push(R_m_b_over_3a); // first term push(R_C_over_3a); - negate(); + negate(); // second term push(R_DELTA0); push(R_3_a_C); divide(); - negate(); + negate(); // third term + // now add the three terms together add(); add(); simplify(); - push(R_m_b_over_3a); + // second solution + push(R_m_b_over_3a); // first term push(R_C_over_3a); push(one_plus_i_sqrt3); multiply(); push_integer(2); - divide(); + divide(); // second term push(one_minus_i_sqrt3); push(R_DELTA0); multiply(); push(R_6_a_C); - divide(); + divide(); // third term + // now add the three terms together add(); add(); simplify(); - push(R_m_b_over_3a); + // third solution + push(R_m_b_over_3a); // first term push(R_C_over_3a); push(one_minus_i_sqrt3); multiply(); push_integer(2); - divide(); + divide(); // second term push(one_plus_i_sqrt3); push(R_DELTA0); multiply(); push(R_6_a_C); - divide(); + divide(); // third term + // now add the three terms together add(); add(); simplify(); restore(); return; } + // See http://www.sscc.edu/home/jdavidso/Math/Catalog/Polynomials/Fourth/Fourth.html + // for a description of general shapes and properties of fourth degree polynomials if (n === 5) { if (DEBUG) { console.log(">>>>>>>>>>>>>>>> actually using quartic formula <<<<<<<<<<<<<<< "); } - p7 = pop(); + p7 = pop(); // E if (isZeroAtomOrTensor(p4) && isZeroAtomOrTensor(p6) && !isZeroAtomOrTensor(p5) && !isZeroAtomOrTensor(p7)) { if (DEBUG) { console.log("biquadratic case"); @@ -18281,10 +21677,12 @@ restore(); return; } + // D - only related calculations push(p6); push(p6); multiply(); R_d2 = pop(); + // E - only related calculations push(p7); push(p7); multiply(); @@ -18293,42 +21691,43 @@ push(p7); multiply(); R_e3 = pop(); + // DETERMINANT push_integer(256); push(R_a3); push(R_e3); multiply(); - multiply(); + multiply(); // first term 256 a^3 e^3 push_integer(-192); push(R_a2_d); push(R_e2); push(p4); multiply(); multiply(); - multiply(); + multiply(); // second term -192 a^3 b d e^2 push_integer(-128); push(R_a2); push(R_c2); push(R_e2); multiply(); multiply(); - multiply(); + multiply(); // third term -128 a^2 c^2 e^2 push_integer(144); push(R_a2_d2); push(p5); push(p7); multiply(); multiply(); - multiply(); + multiply(); // fourth term 144 a^2 c d^2 e push(R_m27_a2_d2); push(R_d2); - multiply(); + multiply(); // fifth term -27 a^2 d^4 push_integer(144); push(R_a_b_c); push(p4); push(R_e2); multiply(); multiply(); - multiply(); + multiply(); // sixth term 144 a b^2 c e^2 push_integer(-6); push(p3); push(R_b2); @@ -18337,60 +21736,63 @@ multiply(); multiply(); multiply(); - multiply(); + multiply(); // seventh term -6 a b^2 d^2 e push_integer(-80); push(R_a_b_c_d); push(p5); push(p7); multiply(); multiply(); - multiply(); + multiply(); // eigth term -80 a b c^2 d e push_integer(18); push(R_a_b_c_d); push(R_d2); multiply(); - multiply(); + multiply(); // ninth term 18 a b c d^3 push_integer(16); push(R_a_c); push(R_c3); push(p7); multiply(); multiply(); - multiply(); + multiply(); // tenth term 16 a c^4 e push_integer(-4); push(R_a_c); push(R_c2); push(R_d2); multiply(); multiply(); - multiply(); + multiply(); // eleventh term -4 a c^3 d^2 push_integer(-27); push(R_b3); push(p4); push(R_e2); multiply(); multiply(); - multiply(); + multiply(); // twelveth term -27 b^4 e^2 push_integer(18); push(R_b3_d); push(p5); push(p7); multiply(); multiply(); - multiply(); + multiply(); // thirteenth term 18 b^3 c d e push(R_m4_b3_d); push(R_d2); - multiply(); + multiply(); // fourteenth term -4 b^3 d^3 push_integer(-4); push(R_b2_c2); push(p5); push(p7); multiply(); multiply(); - multiply(); + multiply(); // fifteenth term -4 b^2 c^3 e push(R_b2_c2); push(R_d2); - multiply(); + multiply(); // sixteenth term b^2 c^2 d^2 + + // add together the sixteen terms by doing + // fifteen adds add(); add(); add(); @@ -18410,23 +21812,27 @@ if (DEBUG) { console.log("R_determinant: " + R_determinant.toString()); } - push(R_c2); + // DELTA0 + push(R_c2); // term one of DELTA0 push_integer(-3); push(p4); push(p6); multiply(); - multiply(); + multiply(); // term two of DELTA0 push_integer(12); push(p3); push(p7); multiply(); - multiply(); + multiply(); // term three of DELTA0 + + // add the three terms together add(); add(); R_DELTA0 = pop(); if (DEBUG) { console.log("R_DELTA0: " + R_DELTA0.toString()); } + // DELTA1 push_integer(2); push(R_c3); multiply(); @@ -18452,6 +21858,7 @@ push(p7); multiply(); multiply(); + // add the five terms together add(); add(); add(); @@ -18460,6 +21867,7 @@ if (DEBUG) { console.log("R_DELTA1: " + R_DELTA1.toString()); } + // p push_integer(8); push(R_a_c); multiply(); @@ -18475,6 +21883,7 @@ if (DEBUG) { console.log("p: " + R_p.toString()); } + // q push(R_b3); push_integer(-4); push(R_a_b_c); @@ -18549,6 +21958,7 @@ if (DEBUG) { console.log("q for depressed quartic: " + R_q.toString()); } + // convert to depressed quartic push(p4); push_integer(4); power(); @@ -18645,17 +22055,16 @@ R_p = p5; R_q = p6; R_r = p7; - /* * Descartes' solution * https://en.wikipedia.org/wiki/Quartic_function#Descartes.27_solution * finding the "u" in the depressed equation - + push_integer(2) push(R_p) multiply() coeff2 = pop() - + push_integer(-4) push(R_p) push_integer(2) @@ -18664,43 +22073,43 @@ push(R_r) multiply() coeff3 = pop() - + push(R_q) push_integer(2) power() negate() coeff4 = pop() - + * now build the polynomial push(symbol(SECRETX)) push_integer(3) power() - + push(coeff2) push(symbol(SECRETX)) push_integer(2) power() multiply() - + push(coeff3) push(symbol(SECRETX)) multiply() - + push(coeff4) - + add() add() add() - + console.log("Descarte's resolventCubic: " + stack[tos-1].toString()) push(symbol(SECRETX)) - + roots() - + resolventCubicSolutions = pop() console.log("Descarte's resolventCubic solutions: " + resolventCubicSolutions) console.log("tos: " + tos) - + R_u = null #R_u = resolventCubicSolutions.tensor.elem[1] for eachSolution in resolventCubicSolutions.tensor.elem @@ -18710,20 +22119,20 @@ multiply() push(R_p) add() - + absValFloat() toBeCheckedIFZero = pop() console.log("abs value is: " + eachSolution) if !isZeroAtomOrTensor(toBeCheckedIFZero) R_u = eachSolution break - + console.log("chosen solution: " + R_u) - + push(R_u) negate() R_s = pop() - + push(R_p) push(R_u) push_integer(2) @@ -18736,7 +22145,7 @@ push_integer(2) divide() R_t = pop() - + push(R_p) push(R_u) push_integer(2) @@ -18749,44 +22158,47 @@ push_integer(2) divide() R_v = pop() - + * factoring the quartic into two quadratics: - + * now build the polynomial push(symbol(SECRETX)) push_integer(2) power() - + push(R_s) push(symbol(SECRETX)) multiply() - + push(R_t) - + add() add() - + console.log("factored quartic 1: " + stack[tos-1].toString()) - + push(symbol(SECRETX)) push_integer(2) power() - + push(R_u) push(symbol(SECRETX)) multiply() - + push(R_v) - + add() add() - + console.log("factored quartic 2: " + stack[tos-1].toString()) pop() - + restore() return */ + // Ferrari's solution + // https://en.wikipedia.org/wiki/Quartic_function#Ferrari.27s_solution + // finding the "m" in the depressed equation push_rational(5, 2); push(R_p); multiply(); @@ -18843,6 +22255,7 @@ } R_m = null; ref4 = resolventCubicSolutions.tensor.elem; + //R_m = resolventCubicSolutions.tensor.elem[1] for (n1 = 0, len2 = ref4.length; n1 < len2; n1++) { eachSolution = ref4[n1]; if (DEBUG) { @@ -18890,6 +22303,7 @@ multiply(); add(); ThreePPlus2M = pop(); + // solution1 push(sqrtPPlus2M); push(ThreePPlus2M); push(TwoQOversqrtPPlus2M); @@ -18901,6 +22315,7 @@ add(); push_integer(2); divide(); + // solution2 push(sqrtPPlus2M); push(ThreePPlus2M); push(TwoQOversqrtPPlus2M); @@ -18912,6 +22327,7 @@ subtract(); push_integer(2); divide(); + // solution3 push(sqrtPPlus2M); negate(); push(ThreePPlus2M); @@ -18924,6 +22340,7 @@ add(); push_integer(2); divide(); + // solution4 push(sqrtPPlus2M); negate(); push(ThreePPlus2M); @@ -18939,6 +22356,7 @@ restore(); return; } + // Q --------------------------- push(R_determinant); simplify(); absValFloat(); @@ -18953,27 +22371,37 @@ Q_CHECKED_AS_NOT_ZERO = false; flipSignOFRadicalSoQIsNotZero = false; while (!Q_CHECKED_AS_NOT_ZERO) { + // D1 under the outer radical push(R_DELTA1); + // D1^2 under the inner radical push(R_DELTA1); push_integer(2); power(); + // 4*D0^3 under the inner radical push_integer(-4); push(R_DELTA0); push_integer(3); power(); multiply(); + // addition under the inner radical add(); + // the second radical push_rational(1, 2); power(); if (flipSignOFRadicalSoQIsNotZero) { negate(); } + // the addition under the outer radical add(); + // content of outer radical divided by two push_integer(2); divide(); if (DEBUG) { console.log("content of cubic root: " + stack[tos - 1].toString()); } + // outer radical calculation: cubic root + // now we actually have to find all the roots + // because we have to pick the one that makes S != 0 push_rational(1, 3); power(); simplify(); @@ -19051,6 +22479,7 @@ console.log("tos: " + tos); } } + // S push_rational(-2, 3); push(R_p); multiply(); @@ -19059,6 +22488,9 @@ push(R_Q); divide(); add(); + //rationalize() + //console.log("rationalised: " + stack[tos-1].toString()) + //simplify() push(R_3_a); divide(); add(); @@ -19072,6 +22504,7 @@ if (DEBUG) { console.log("S " + R_S.toString()); } + // now check if S is zero push(R_S); simplify(); absValFloat(); @@ -19091,6 +22524,7 @@ console.log("tos: " + tos); } } + // ---------------------------- if (DEBUG) { console.log("tos: " + tos); } @@ -19118,7 +22552,8 @@ if (DEBUG) { console.log("tos before putting together the 4 solutions: " + tos); } - push(R_minus_b_over_4a); + // first solution + push(R_minus_b_over_4a); // first term push(R_S); subtract(); push(R_minus_4S2_minus_2p); @@ -19130,7 +22565,8 @@ divide(); add(); simplify(); - push(R_minus_b_over_4a); + // second solution + push(R_minus_b_over_4a); // first term push(R_S); subtract(); push(R_minus_4S2_minus_2p); @@ -19142,7 +22578,8 @@ divide(); subtract(); simplify(); - push(R_minus_b_over_4a); + // third solution + push(R_minus_b_over_4a); // first term push(R_S); add(); push(R_minus_4S2_minus_2p); @@ -19154,7 +22591,8 @@ divide(); add(); simplify(); - push(R_minus_b_over_4a); + // fourth solution + push(R_minus_b_over_4a); // first term push(R_S); add(); push(R_minus_4S2_minus_2p); @@ -19211,6 +22649,49 @@ return push_integer(Math.round(p1.d)); }; + // This scanner uses the recursive descent method. + + // The char pointers token_str and scan_str are pointers to the input string as + // in the following example. + + // | g | a | m | m | a | | a | l | p | h | a | + // ^ ^ + // token_str scan_str + + // The char pointer token_buf points to a malloc buffer. + + // | g | a | m | m | a | \0 | + // ^ + // token_buf + + // In the sequence of method invocations for scanning, + // first we do the calls for scanning the operands + // of the operators of least precedence. + // So, since precedence in maths goes something like + // (form high to low) exponents, mult/div, plus/minus + // so we scan first for terms, then factors, then powers. + // That's the general idea, but of course we also have to deal + // with things like parens, non-commutative + // dot (or inner) product, assignments and tests, + // function calls etc. + // Note that a^1/2 is, correctly, a/2, not, incorrectly, sqrt(a), + // see comment in related test in power.coffee for more about this. + + // Notes: + + // Formerly add() and multiply() were used to construct expressions but + // this preevaluation caused problems. + + // For example, suppose A has the floating point value inf. + + // Before, the expression A/A resulted in 1 because the scanner would + // divide the symbols. + + // After removing add() and multiply(), A/A results in nan which is the + // correct result. + + // The functions negate() and inverse() are used but they do not cause + // problems with preevaluation of symbols. T_INTEGER = 1001; T_DOUBLE = 1002; @@ -19263,12 +22744,23 @@ assignmentFound = null; + // Returns number of chars scanned and expr on stack. + + // Returns zero when nothing left to scan. + + // takes a string scanned = ""; scan = function(s) { if (DEBUG) { console.log("#### scanning " + s); } + //if s=="y=x" + // debugger + //if s=="y" + // debugger + //if s=="i=sqrt(-1)" + // debugger lastFoundSymbol = null; symbolsRightOfAssignment = []; symbolsLeftOfAssignment = []; @@ -19295,6 +22787,7 @@ return token_str - input_str; }; + // takes a string scan_meta = function(s) { scanned = s; meta_mode = 1; @@ -19329,21 +22822,30 @@ get_next_token(); push_symbol(SETQ); swap(); + // if it's a := then add a quote if (assignmentIsOfQuotedType) { push_symbol(QUOTE); } scan_relation(); + // if it's a := then you have to list + // together the quote and its argument if (assignmentIsOfQuotedType) { list(2); } list(3); isSymbolLeftOfAssignment = true; if (codeGen) { + // in case of re-assignment, the symbol on the + // left will also be in the set of the symbols + // on the right. In that case just remove it from + // the symbols on the right. indexOfSymbolLeftOfAssignment = symbolsRightOfAssignment.indexOf(symbolLeftOfAssignment); if (indexOfSymbolLeftOfAssignment !== -1) { symbolsRightOfAssignment.splice(indexOfSymbolLeftOfAssignment, 1); symbolsHavingReassignments.push(symbolLeftOfAssignment); } + + // print out the immediate dependencies if (DEBUG) { console.log("locally, " + symbolLeftOfAssignment + " depends on: "); for (l1 = 0, len = symbolsRightOfAssignment.length; l1 < len; l1++) { @@ -19351,10 +22853,16 @@ console.log(" " + i); } } + // ok add the local dependencies to the existing + // dependencies of this left-value symbol + + // create the exiting dependencies list if it doesn't exist if (symbolsDependencies[symbolLeftOfAssignment] == null) { symbolsDependencies[symbolLeftOfAssignment] = []; } existingDependencies = symbolsDependencies[symbolLeftOfAssignment]; +// copy over the new dependencies to the existing +// dependencies avoiding repetitions for (m1 = 0, len1 = symbolsRightOfAssignment.length; m1 < len1; m1++) { i = symbolsRightOfAssignment[m1]; if (existingDependencies.indexOf(i) === -1) { @@ -19459,8 +22967,8 @@ case T_INTEGER: case T_DOUBLE: case T_STRING: - if (newline_flag) { - scan_str = token_str; + if (newline_flag) { // implicit mul can't cross line + scan_str = token_str; // better error display return 0; } else { return 1; @@ -19475,6 +22983,7 @@ } }; + // calculate away consecutive constants multiply_consecutive_constants = function(tos, h) { if (tos > h + 1 && isNumericAtom(stack[tos - 2]) && isNumericAtom(stack[tos - 1])) { return multiply(); @@ -19493,6 +23002,12 @@ get_next_token(); scan_factor(); } else if (token === '/') { + // in case of 1/... then + // we scanned the 1, we get rid + // of it because otherwise it becomes + // an extra factor that wasn't there and + // things like + // 1/(2*a) become 1*(1/(2*a)) simplify_1_in_products(tos, h); get_next_token(); scan_factor(); @@ -19532,6 +23047,7 @@ }; scan_index = function(h) { + //console.log "[ as index" get_next_token(); push_symbol(INDEX); swap(); @@ -19550,6 +23066,7 @@ scan_factor = function() { var firstFactorIsNumber, h; h = tos; + //console.log "scan_factor token: " + token firstFactorIsNumber = false; if (token === '(') { scan_subexpr(); @@ -19558,6 +23075,8 @@ } else if (token === T_FUNCTION) { scan_function_call_with_function_name(); } else if (token === '[') { + //console.log "[ as tensor" + //debugger scan_tensor(); } else if (token === T_INTEGER) { firstFactorIsNumber = true; @@ -19572,10 +23091,21 @@ } else { scan_error("syntax error"); } + // after the main initial part of the factor that + // we just scanned above, + // we can get an arbitrary about of appendages + // of the form ...[...](...)... + // If the main part is not a number, then these are all, respectively, + // - index references (as opposed to tensor definition) and + // - function calls without an explicit function name + // (instead of subexpressions or parameters of function + // definitions or function calls with an explicit function + // name), respectively while (token === '[' || token === '(' && newline_flag === 0 && !firstFactorIsNumber) { if (token === '[') { scan_index(h); } else if (token === '(') { + //console.log "( as function call without function name " scan_function_call_without_function_name(); } } @@ -19601,7 +23131,7 @@ console.log("... adding symbol: " + theSymbol + " to the set of the symbols right of assignment"); } prefixVar = ""; - for (i = l1 = 1, ref2 = functionInvokationsScanningStack.length; 1 <= ref2 ? l1 < ref2 : l1 > ref2; i = 1 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 1, ref2 = functionInvokationsScanningStack.length; (1 <= ref2 ? l1 < ref2 : l1 > ref2); i = 1 <= ref2 ? ++l1 : --l1) { if (functionInvokationsScanningStack[i] !== "") { prefixVar += functionInvokationsScanningStack[i] + "_" + i + "_"; } @@ -19618,7 +23148,7 @@ console.log("... adding symbol: " + theSymbol + " to the set of the symbols left of assignment"); } prefixVar = ""; - for (i = l1 = 1, ref2 = functionInvokationsScanningStack.length; 1 <= ref2 ? l1 < ref2 : l1 > ref2; i = 1 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 1, ref2 = functionInvokationsScanningStack.length; (1 <= ref2 ? l1 < ref2 : l1 > ref2); i = 1 <= ref2 ? ++l1 : --l1) { if (functionInvokationsScanningStack[i] !== "") { prefixVar += functionInvokationsScanningStack[i] + "_" + i + "_"; } @@ -19649,6 +23179,7 @@ } else { push(usr_symbol(token_buf)); } + //console.log "found symbol: " + token_buf if (scanningParameters.length === 0) { if (DEBUG) { console.log("out of scanning parameters, processing " + token_buf); @@ -19684,7 +23215,7 @@ if (DEBUG) { console.log("-- scan_function_call_with_function_name start"); } - n = 1; + n = 1; // the parameter number as we scan parameters p = new U(); p = usr_symbol(token_buf); push(p); @@ -19696,38 +23227,43 @@ if (!isSymbolLeftOfAssignment) { addSymbolRightOfAssignment(token_buf); } - get_next_token(); - get_next_token(); + get_next_token(); // open parens + get_next_token(); // 1st parameter scanningParameters.push(true); if (token !== ')') { scan_stmt(); n++; while (token === ',') { get_next_token(); + // roots' disappearing variable, if there, is the second one if (n === 2 && functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("roots") !== -1) { symbolsRightOfAssignment = symbolsRightOfAssignment.filter(function(x) { return !(new RegExp("roots_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x); }); skipRootVariableToBeSolved = true; } + // sums' disappearing variable, is alsways the second one if (n === 2 && functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("sum") !== -1) { symbolsRightOfAssignment = symbolsRightOfAssignment.filter(function(x) { return !(new RegExp("sum_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x); }); skipRootVariableToBeSolved = true; } + // product's disappearing variable, is alsways the second one if (n === 2 && functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("product") !== -1) { symbolsRightOfAssignment = symbolsRightOfAssignment.filter(function(x) { return !(new RegExp("product_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x); }); skipRootVariableToBeSolved = true; } + // for's disappearing variable, is alsways the second one if (n === 2 && functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("for") !== -1) { symbolsRightOfAssignment = symbolsRightOfAssignment.filter(function(x) { return !(new RegExp("for_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x); }); skipRootVariableToBeSolved = true; } + // defint's disappearing variables can be in positions 2,5,8... if (functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("defint") !== -1 && (n === 2 || (n > 2 && ((n - 2) % 3 === 0)))) { symbolsRightOfAssignment = symbolsRightOfAssignment.filter(function(x) { return !(new RegExp("defint_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x); @@ -19738,6 +23274,8 @@ skipRootVariableToBeSolved = false; n++; } + // todo refactor this, there are two copies + // this catches the case where the "roots" variable is not specified if (n === 2 && functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("roots") !== -1) { symbolsRightOfAssignment = symbolsRightOfAssignment.filter(function(x) { return !(new RegExp("roots_" + (functionInvokationsScanningStack.length - 1) + "_" + "x")).test(x); @@ -19745,7 +23283,7 @@ } } scanningParameters.pop(); - for (i = l1 = 0, ref2 = symbolsRightOfAssignment.length; 0 <= ref2 ? l1 <= ref2 : l1 >= ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = symbolsRightOfAssignment.length; (0 <= ref2 ? l1 <= ref2 : l1 >= ref2); i = 0 <= ref2 ? ++l1 : --l1) { if (symbolsRightOfAssignment[i] != null) { if (functionName === "roots") { symbolsRightOfAssignment[i] = symbolsRightOfAssignment[i].replace(new RegExp("roots_" + (functionInvokationsScanningStack.length - 1) + "_"), ""); @@ -19785,11 +23323,15 @@ if (DEBUG) { console.log("-- scan_function_call_without_function_name start"); } + // the function will have to be looked up + // at runtime (i.e. we need to evaulate something to find it + // e.g. it might be inside a tensor, so we'd need to evaluate + // a tensor element access in that case) push_symbol(EVAL); swap(); list(2); - n = 1; - get_next_token(); + n = 1; // the parameter number as we scan parameters + get_next_token(); // left paren scanningParameters.push(true); if (token !== ')') { scan_stmt(); @@ -19811,6 +23353,7 @@ } }; + // scan subexpression scan_subexpr = function() { var n; n = 0; @@ -19832,6 +23375,7 @@ scan_error("[ expected"); } get_next_token(); + //console.log "scanning the next statement" scan_stmt(); n = 1; while (token === ',') { @@ -19839,6 +23383,7 @@ scan_stmt(); n++; } + //console.log "building tensor with elements number: " + n build_tensor(n); if (token !== ']') { scan_error("] expected"); @@ -19848,6 +23393,7 @@ scan_error = function(errmsg) { errorMessage = ""; + // try not to put question mark on orphan line while (input_str !== scan_str) { if ((scanned[input_str] === '\n' || scanned[input_str] === '\r') && input_str + 1 === scan_str) { break; @@ -19862,14 +23408,23 @@ return stop(errmsg); }; + // There are n expressions on the stack, possibly tensors. + + // This function assembles the stack expressions into a single tensor. + + // For example, at the top level of the expression ((a,b),(c,d)), the vectors + // (a,b) and (c,d) would be on the stack. + + // takes an integer build_tensor = function(n) { var i, l1, ref2; + // int i, j, k, ndim, nelem i = 0; save(); p2 = alloc_tensor(n); p2.tensor.ndim = 1; p2.tensor.dim[0] = n; - for (i = l1 = 0, ref2 = n; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = n; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p2.tensor.elem[i] = stack[tos - n + i]; } check_tensor_dimensions(p2); @@ -19892,7 +23447,10 @@ } }; + //if token == ')' + // debugger get_token = function() { + // skip spaces while (isspace(scanned[scan_str])) { if (scanned[scan_str] === '\n' || scanned[scan_str] === '\r') { token = T_NEWLINE; @@ -19902,10 +23460,12 @@ scan_str++; } token_str = scan_str; + // end of string? if (scan_str === scanned.length) { token = ""; return; } + // number? if (isdigit(scanned[scan_str]) || scanned[scan_str] === '.') { while (isdigit(scanned[scan_str])) { scan_str++; @@ -19928,6 +23488,7 @@ update_token_buf(token_str, scan_str); return; } + // symbol? if (isalpha(scanned[scan_str])) { while (isalnumorunderscore(scanned[scan_str])) { scan_str++; @@ -19940,9 +23501,11 @@ update_token_buf(token_str, scan_str); return; } + // string ? if (scanned[scan_str] === '"') { scan_str++; while (scanned[scan_str] !== '"') { + //if (scan_str == scanned.length || scanned[scan_str] == '\n' || scanned[scan_str] == '\r') if (scan_str === scanned.length - 1) { scan_str++; scan_error("runaway string"); @@ -19955,6 +23518,7 @@ update_token_buf(token_str + 1, scan_str - 1); return; } + // comment? if (scanned[scan_str] === '#' || scanned[scan_str] === '-' && scanned[scan_str + 1] === '-') { while (scanned[scan_str] && scanned[scan_str] !== '\n' && scanned[scan_str] !== '\r') { scan_str++; @@ -19965,16 +23529,23 @@ token = T_NEWLINE; return; } + // quote-assignment if (scanned[scan_str] === ':' && scanned[scan_str + 1] === '=') { scan_str += 2; token = T_QUOTASSIGN; return; } + // relational operator? if (scanned[scan_str] === '=' && scanned[scan_str + 1] === '=') { scan_str += 2; token = T_EQ; return; } + // != operator. It's a little odd because + // "!" is not a "not", which would make things consistent. + // (it's used for factorial). + // An alternative would be to use "<>" but it's not used + // a lot in other languages... if (scanned[scan_str] === '!' && scanned[scan_str + 1] === '=') { scan_str += 2; token = T_NEQ; @@ -19990,15 +23561,24 @@ token = T_GTEQ; return; } + // single char token return token = scanned[scan_str++]; }; + // both strings update_token_buf = function(a, b) { return token_buf = scanned.substring(a, b); }; $.scan = scan; + //----------------------------------------------------------------------------- + + // Author : philippe.billet@noos.fr + + // sgn sign function + + //----------------------------------------------------------------------------- Eval_sgn = function() { push(cadr(p1)); Eval(); @@ -20011,6 +23591,7 @@ return restore(); }; + //define X p1 yysgn = function() { p1 = pop(); if (isdouble(p1)) { @@ -20059,7 +23640,6 @@ multiply(); return; } - /* push_integer(2) push(p1) @@ -20067,12 +23647,13 @@ multiply() push_integer(-1) add() - */ + */ push_symbol(SGN); push(p1); return list(2); }; + // shape of tensor Eval_shape = function() { push(cadr(p1)); Eval(); @@ -20086,10 +23667,11 @@ t = 0; ai = []; an = []; - for (i = l1 = 0, ref2 = MAXDIM; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = MAXDIM; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { ai[i] = 0; an[i] = 0; } + //U **a, **b save(); p1 = pop(); if (!istensor(p1)) { @@ -20104,7 +23686,7 @@ p2 = alloc_tensor(ndim); p2.tensor.ndim = 1; p2.tensor.dim[0] = ndim; - for (i = m1 = 0, ref3 = ndim; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = ndim; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { push_integer(p1.tensor.dim[i]); p2.tensor.elem[i] = pop(); } @@ -20112,40 +23694,41 @@ return restore(); }; - /* Simplify factorials - + The following script - + F(n,k) = k binomial(n,k) (F(n,k) + F(n,k-1)) / F(n+1,k) - + generates - + k! n! n! (1 - k + n)! k! n! -------------------- + -------------------- - ---------------------- (-1 + k)! (1 + n)! (1 + n)! (-k + n)! k (-1 + k)! (1 + n)! - + Simplify each term to get - + k 1 - k + n 1 ------- + ----------- - ------- 1 + n 1 + n 1 + n - + Then simplify the sum to get - + n ------- 1 + n - */ + */ + // simplify factorials term-by-term Eval_simfac = function() { push(cadr(p1)); Eval(); return simfac(); }; + //if 1 simfac = function() { var h; h = 0; @@ -20167,7 +23750,7 @@ return restore(); }; - + //else /* void simfac(void) @@ -20198,26 +23781,28 @@ } restore() } - + #endif */ - simfac_term = function() { var doNothing, h; h = 0; save(); p1 = pop(); + // if not a product of factors then done if (car(p1) !== symbol(MULTIPLY)) { push(p1); restore(); return; } + // push all factors h = tos; p1 = cdr(p1); while (p1 !== symbol(NIL)) { push(car(p1)); p1 = cdr(p1); } + // keep trying until no more to do while (yysimfac(h)) { doNothing = 1; } @@ -20225,17 +23810,19 @@ return restore(); }; + // try all pairs of factors yysimfac = function(h) { var i, j, l1, m1, ref2, ref3, ref4, ref5; i = 0; j = 0; - for (i = l1 = ref2 = h, ref3 = tos; ref2 <= ref3 ? l1 < ref3 : l1 > ref3; i = ref2 <= ref3 ? ++l1 : --l1) { + for (i = l1 = ref2 = h, ref3 = tos; (ref2 <= ref3 ? l1 < ref3 : l1 > ref3); i = ref2 <= ref3 ? ++l1 : --l1) { p1 = stack[i]; - for (j = m1 = ref4 = h, ref5 = tos; ref4 <= ref5 ? m1 < ref5 : m1 > ref5; j = ref4 <= ref5 ? ++m1 : --m1) { + for (j = m1 = ref4 = h, ref5 = tos; (ref4 <= ref5 ? m1 < ref5 : m1 > ref5); j = ref4 <= ref5 ? ++m1 : --m1) { if (i === j) { continue; } p2 = stack[j]; + // n! / n -> (n - 1)! if (car(p1) === symbol(FACTORIAL) && car(p2) === symbol(POWER) && isminusone(caddr(p2)) && equal(cadr(p1), cadr(p2))) { push(cadr(p1)); push(one); @@ -20245,6 +23832,7 @@ stack[j] = one; return 1; } + // n / n! -> 1 / (n - 1)! if (car(p2) === symbol(POWER) && isminusone(caddr(p2)) && caadr(p2) === symbol(FACTORIAL) && equal(p1, cadadr(p2))) { push(p1); push_integer(-1); @@ -20255,6 +23843,7 @@ stack[j] = one; return 1; } + // (n + 1) n! -> (n + 1)! if (car(p2) === symbol(FACTORIAL)) { push(p1); push(cadr(p2)); @@ -20268,6 +23857,7 @@ return 1; } } + // 1 / ((n + 1) n!) -> 1 / (n + 1)! if (car(p1) === symbol(POWER) && isminusone(caddr(p1)) && car(p2) === symbol(POWER) && isminusone(caddr(p2)) && caadr(p2) === symbol(FACTORIAL)) { push(cadr(p1)); push(cadr(cadr(p2))); @@ -20282,6 +23872,9 @@ return 1; } } + // (n + 1)! / n! -> n + 1 + + // n! / (n + 1)! -> 1 / (n + 1) if (car(p1) === symbol(FACTORIAL) && car(p2) === symbol(POWER) && isminusone(caddr(p2)) && caadr(p2) === symbol(FACTORIAL)) { push(cadr(p1)); push(cadr(cadr(p2))); @@ -20333,6 +23926,14 @@ runUserDefinedSimplifications = function() { var atLeastOneSuccessInRouldOfRulesApplications, eachConsecutiveRuleApplication, eachSimplification, l1, len, len1, m1, numberOfRulesApplications, originalexpanding, success; + // ----------------------- + // unfortunately for the time being user + // specified simplifications are only + // run in things which don't contain + // integrals. + // Doesn't work yet, could be because of + // some clobbering as "transform" is called + // recursively? if (userSimplificationsInListForm.length !== 0 && !Find(cadr(p1), symbol(INTEGRAL))) { originalexpanding = expanding; expanding = false; @@ -20394,10 +23995,16 @@ } }; + // ------------------------ simplifyForCodeGeneration = function() { save(); runUserDefinedSimplifications(); codeGen = true; + // in "codeGen" mode we completely + // eval and simplify the function bodies + // because we really want to resolve all + // the variables indirections and apply + // all the simplifications we can. simplify_main(); codeGen = false; return restore(); @@ -20412,12 +24019,20 @@ simplify_main = function() { var args, fbody; p1 = pop(); + // when we do code generation, we proceed to + // fully evaluate and simplify the body of + // a function, so we resolve all variables + // indirections and we simplify everything + // we can given the current assignments. if (codeGen && car(p1) === symbol(FUNCTION)) { fbody = cadr(p1); push(fbody); + // let's simplify the body so we give it a + // compact form eval(); simplify(); p3 = pop(); + // replace the evaled body args = caddr(p1); push_symbol(FUNCTION); push(p3); @@ -20452,6 +24067,12 @@ f9(); simplify_polarRect(); if (do_simplify_nested_radicals) { + // if there is some de-nesting then + // re-run a simplification because + // the shape of the expression might + // have changed significantly. + // e.g. simplify(14^(1/2) - (16 - 4*7^(1/2))^(1/2)) + // needs some more semplification after the de-nesting. if (simplify_nested_radicals()) { if (DEBUG) { console.log("de-nesting successful into: " + p1.toString()); @@ -20471,21 +24092,22 @@ i = 0; p2 = alloc_tensor(p1.tensor.nelem); p2.tensor.ndim = p1.tensor.ndim; - for (i = l1 = 0, ref2 = p1.tensor.ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p1.tensor.ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p2.tensor.dim[i] = p1.tensor.dim[i]; } - for (i = m1 = 0, ref3 = p1.tensor.nelem; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = p1.tensor.nelem; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { push(p1.tensor.elem[i]); simplify(); p2.tensor.elem[i] = pop(); } check_tensor_dimensions(p2); if (isZeroAtomOrTensor(p2)) { - p2 = zero; + p2 = zero; // null tensor becomes scalar zero } return push(p2); }; + // try rationalizing f1 = function() { if (car(p1) !== symbol(ADD)) { return; @@ -20498,6 +24120,7 @@ } }; + // try condensing f2 = function() { if (car(p1) !== symbol(ADD)) { return; @@ -20510,6 +24133,7 @@ } }; + // this simplifies forms like (A-B) / (B-A) f3 = function() { push(p1); rationalize(); @@ -20528,6 +24152,7 @@ carp1 = car(p1); miao = cdr(p1); if (carp1 === symbol(MULTIPLY) || isinnerordot(p1)) { + // both operands a transpose? if ((car(car(cdr(p1))) === symbol(TRANSPOSE)) && (car(car(cdr(cdr(p1)))) === symbol(TRANSPOSE))) { if (DEBUG) { console.log("maybe collecting a transpose " + p1); @@ -20560,6 +24185,7 @@ } }; + // try expanding denominators f4 = function() { if (isZeroAtomOrTensor(p1)) { return; @@ -20576,6 +24202,7 @@ } }; + // simplifies trig forms simplify_trig = function() { save(); p1 = pop(); @@ -20606,6 +24233,7 @@ } }; + // if it's a sum then try to simplify each term f9 = function() { var oldp1, oldp2; if (car(p1) !== symbol(ADD)) { @@ -20655,6 +24283,8 @@ push(polyVar); factor(); theGCD = pop(); + // if there are no common factors then + // bail if (isone(theGCD)) { return; } @@ -20662,6 +24292,7 @@ push(polyVar); factor(); push(theGCD); + //divide() inverse(); multiply_noexpand(); simplify(); @@ -20670,11 +24301,13 @@ push(polyVar); factor(); push(theGCD); + //divide() inverse(); multiply_noexpand(); simplify(); sasa = stack[tos - 1].toString(); divide(); + //simplify() Condense(); sasa = stack[tos - 1].toString(); p2 = pop(); @@ -20683,7 +24316,11 @@ } }; + // things like 6*(cos(2/9*pi)+i*sin(2/9*pi)) + // where we have sin and cos, those might start to + // look better in clock form i.e. 6*(-1)^(2/9) simplify_rectToClock = function() { + //debugger if (Find(p1, symbol(SIN)) === 0 && Find(p1, symbol(COS)) === 0) { return; } @@ -20719,18 +24356,23 @@ return; } if (equal(car(p1), symbol(POWER)) && isminusone(cadr(p1))) { + // base we just said is minus 1 push(one); negate(); + // exponent push(caddr(p1)); polarRectAMinusOneBase(); power(); + // try to simplify it using polar and rect polar(); rect(); } else if (iscons(p1)) { h = tos; while (iscons(p1)) { + //console.log("recursing on: " + car(p1).toString()) push(car(p1)); polarRectAMinusOneBase(); + //console.log("...transformed into: " + stack[tos-1].toString()) p1 = cdr(p1); } list(tos - h); @@ -20758,17 +24400,30 @@ } push(p1); somethingSimplified = take_care_of_nested_radicals(); + // in this paragraph we check whether we can collect + // common factors without complicating the expression + // in particular we want to avoid + // collecting radicals like in this case where + // we collect sqrt(2): + // 2-2^(1/2) into 2^(1/2)*(-1+2^(1/2)) + // but we do like to collect other non-radicals e.g. + // 17/2+3/2*5^(1/2) into 1/2*(17+3*5^(1/2)) + // so what we do is we count the powers and we check + // which version has the least number of them. simplificationWithoutCondense = stack[tos - 1]; prev_expanding = expanding; expanding = 0; yycondense(); expanding = prev_expanding; simplificationWithCondense = pop(); + //console.log("occurrences of powers in " + simplificationWithoutCondense + " :" + countOccurrencesOfSymbol(symbol(POWER),simplificationWithoutCondense)) + //console.log("occurrences of powers in " + simplificationWithCondense + " :" + countOccurrencesOfSymbol(symbol(POWER),simplificationWithCondense)) if (countOccurrencesOfSymbol(symbol(POWER), simplificationWithoutCondense) < countOccurrencesOfSymbol(symbol(POWER), simplificationWithCondense)) { push(simplificationWithoutCondense); } else { push(simplificationWithCondense); } + // we got out result, wrap up p1 = pop(); return somethingSimplified; }; @@ -20783,10 +24438,13 @@ } save(); p1 = pop(); + //console.log("take_care_of_nested_radicals p1: " + p1.toString()) if (equal(car(p1), symbol(POWER))) { + //console.log("ok it's a power ") base = cadr(p1); exponent = caddr(p1); if (!isminusone(exponent) && equal(car(base), symbol(ADD)) && isfraction(exponent) && (equalq(exponent, 1, 3) || equalq(exponent, 1, 2))) { + //console.log("ok there is a radix with a term inside") firstTerm = cadr(base); push(firstTerm); take_care_of_nested_radicals(); @@ -20795,24 +24453,31 @@ push(secondTerm); take_care_of_nested_radicals(); pop(); + //console.log("possible double radical term1: " + firstTerm) + //console.log("possible double radical term2: " + secondTerm) numberOfTerms = 0; countingTerms = base; while (cdr(countingTerms) !== symbol(NIL)) { numberOfTerms++; countingTerms = cdr(countingTerms); } + //console.log("number of terms: " + numberOfTerms) if (numberOfTerms > 2) { + //console.log("too many terms under outer radix ") push(p1); restore(); return false; } + // list here all the factors commonInnerExponent = null; commonBases = []; termsThatAreNotPowers = []; if (car(secondTerm) === symbol(MULTIPLY)) { + // product of factors secondTermFactor = cdr(secondTerm); if (iscons(secondTermFactor)) { while (iscons(secondTermFactor)) { + //console.log("second term factor BIS: " + car(secondTermFactor).toString()) potentialPower = car(secondTermFactor); if (car(potentialPower) === symbol(POWER)) { innerbase = cadr(potentialPower); @@ -20823,6 +24488,7 @@ commonBases.push(innerbase); } else { if (equal(innerexponent, commonInnerExponent)) { + //console.log("common base: " + innerbase.toString()) commonBases.push(innerbase); } else { @@ -20830,6 +24496,8 @@ } } } else { + //console.log("no common bases here ") + //console.log("this one is a power base: " + innerbase + " , exponent: " + innerexponent) termsThatAreNotPowers.push(potentialPower); } secondTermFactor = cdr(secondTermFactor); @@ -20839,6 +24507,7 @@ innerbase = cadr(secondTerm); innerexponent = caddr(secondTerm); if ((commonInnerExponent == null) && equalq(innerexponent, 1, 2)) { + //console.log("tackling double radical 2: " + p1.toString()) commonInnerExponent = innerexponent; commonBases.push(innerbase); } @@ -20849,27 +24518,33 @@ return false; } A = firstTerm; + //console.log("A: " + A.toString()) push_integer(1); for (l1 = 0, len = commonBases.length; l1 < len; l1++) { i = commonBases[l1]; push(i); multiply(); } + //console.log("basis with common exponent: " + i.toString()) C = pop(); + //console.log("C: " + C.toString()) push_integer(1); for (m1 = 0, len1 = termsThatAreNotPowers.length; m1 < len1; m1++) { i = termsThatAreNotPowers[m1]; push(i); multiply(); } + //console.log("terms that are not powers: " + i.toString()) B = pop(); + //console.log("B: " + B.toString()) if (equalq(exponent, 1, 3)) { push(A); negate(); push(C); multiply(); push(B); - divide(); + divide(); // 4th coeff + //console.log("constant coeff " + stack[tos-1].toString()) checkSize = pop(); push(checkSize); real(); @@ -20882,7 +24557,8 @@ push(checkSize); push_integer(3); push(C); - multiply(); + multiply(); // 3rd coeff + //console.log("next coeff " + stack[tos-1].toString()) checkSize = pop(); push(checkSize); real(); @@ -20900,7 +24576,7 @@ push(A); multiply(); push(B); - divide(); + divide(); // 2nd coeff checkSize = pop(); push(checkSize); real(); @@ -20913,11 +24589,13 @@ return false; } push(checkSize); + //console.log("next coeff " + stack[tos-1].toString()) push(symbol(SECRETX)); push_integer(2); power(); multiply(); - push_integer(1); + push_integer(1); // 1st coeff + //console.log("next coeff " + stack[tos-1].toString()) push(symbol(SECRETX)); push_integer(3); power(); @@ -20926,7 +24604,7 @@ add(); add(); } else if (equalq(exponent, 1, 2)) { - push(C); + push(C); // 3th coeff checkSize = pop(); push(checkSize); real(); @@ -20937,11 +24615,12 @@ return false; } push(checkSize); + //console.log("constant coeff " + stack[tos-1].toString()) push_integer(-2); push(A); multiply(); push(B); - divide(); + divide(); // 2nd coeff checkSize = pop(); push(checkSize); real(); @@ -20953,9 +24632,11 @@ return false; } push(checkSize); + //console.log("next coeff " + stack[tos-1].toString()) push(symbol(SECRETX)); multiply(); - push_integer(1); + push_integer(1); // 1st coeff + //console.log("next coeff " + stack[tos-1].toString()) push(symbol(SECRETX)); push_integer(2); power(); @@ -20963,8 +24644,10 @@ add(); add(); } + //console.log("whole polynomial: " + stack[tos-1].toString()) push(symbol(SECRETX)); recursionLevelNestedRadicalsRemoval++; + //console.log("invoking roots at recursion level: " + recursionLevelNestedRadicalsRemoval) roots(); recursionLevelNestedRadicalsRemoval--; if (equal(stack[tos - 1], symbol(NIL))) { @@ -20976,6 +24659,9 @@ restore(); return false; } + //console.log("all solutions: " + stack[tos-1].toString()) + + // exclude the solutions with radicals possibleSolutions = []; ref2 = stack[tos - 1].tensor.elem; for (n1 = 0, len2 = ref2.length; n1 < len2; n1++) { @@ -20984,7 +24670,9 @@ possibleSolutions.push(eachSolution); } } - pop(); + pop(); // popping the tensor with the solutions + + //console.log("possible solutions: " + possibleSolutions.toString()) if (possibleSolutions.length === 0) { push(p1); restore(); @@ -20992,6 +24680,7 @@ } possibleRationalSolutions = []; realOfpossibleRationalSolutions = []; +//console.log("checking the one with maximum real part ") for (o1 = 0, len3 = possibleSolutions.length; o1 < len3; o1++) { i = possibleSolutions[o1]; push(i); @@ -21002,7 +24691,7 @@ } whichRationalSolution = realOfpossibleRationalSolutions.indexOf(Math.max.apply(Math, realOfpossibleRationalSolutions)); SOLUTION = possibleRationalSolutions[whichRationalSolution]; - + //console.log("picked solution: " + SOLUTION) /* #possibleNewExpressions = [] #realOfPossibleNewExpressions = [] @@ -21010,7 +24699,7 @@ lowercase_b = null for SOLUTION in possibleSolutions console.log("testing solution: " + SOLUTION.toString()) - + debugger if equalq(exponent,1,3) push(A) @@ -21039,9 +24728,9 @@ push_rational(1,2) power() console.log("b is: " + stack[tos-1].toString()) - + lowercase_b = pop() - + if !Find(lowercase_b, symbol(POWER)) break */ @@ -21057,6 +24746,7 @@ multiply(); add(); divide(); + //console.log("argument of cubic root: " + stack[tos-1].toString()) push_rational(1, 3); power(); } else if (equalq(exponent, 1, 2)) { @@ -21067,9 +24757,11 @@ push(C); add(); divide(); + //console.log("argument of cubic root: " + stack[tos-1].toString()) push_rational(1, 2); power(); } + //console.log("b is: " + stack[tos-1].toString()) lowercase_b = pop(); if (lowercase_b == null) { push(p1); @@ -21080,6 +24772,7 @@ push(SOLUTION); multiply(); if (equalq(exponent, 1, 3)) { + //console.log("a is: " + stack[tos-1].toString()) lowercase_a = pop(); push(lowercase_b); push(C); @@ -21090,6 +24783,7 @@ add(); simplify(); } else if (equalq(exponent, 1, 2)) { + //console.log("a could be: " + stack[tos-1].toString()) lowercase_a = pop(); push(lowercase_b); push(C); @@ -21100,13 +24794,16 @@ add(); simplify(); possibleNewExpression = pop(); + //console.log("verifying if " + possibleNewExpression + " is positive") push(possibleNewExpression); real(); yyfloat(); possibleNewExpressionValue = pop(); if (!isnegativenumber(possibleNewExpressionValue)) { + //console.log("... it is positive") push(possibleNewExpression); } else { + //console.log("... it is NOT positive") push(lowercase_b); negate(); lowercase_b = pop(); @@ -21123,7 +24820,21 @@ simplify(); } } + // possibleNewExpression is now at top of stack + + //console.log("potential new expression: " + stack[tos-1].toString()) p1 = pop(); + //newExpression = pop() + //debugger + //push(newExpression) + //real() + //yyfloat() + //possibleNewExpressions.push(newExpression) + //realOfPossibleNewExpressions.push(pop().d) + + //whichExpression = realOfPossibleNewExpressions.indexOf(Math.max.apply(Math, realOfPossibleNewExpressions)) + //p1 = possibleNewExpressions[whichExpression] + //console.log("final new expression: " + p1.toString()) push(p1); restore(); return true; @@ -21136,8 +24847,10 @@ h = tos; anyRadicalSimplificationWorked = false; while (iscons(p1)) { + //console.log("recursing on: " + car(p1).toString()) push(car(p1)); anyRadicalSimplificationWorked = anyRadicalSimplificationWorked || take_care_of_nested_radicals(); + //console.log("...transformed into: " + stack[tos-1].toString()) p1 = cdr(p1); } list(tos - h); @@ -21151,16 +24864,22 @@ throw new Error("control flow should never reach here"); }; + // Sine function of numerical and symbolic arguments Eval_sin = function() { + //console.log "sin ---- " push(cadr(p1)); Eval(); return sine(); }; + //console.log "sin end ---- " sine = function() { + //console.log "sine ---- " save(); p1 = pop(); if (car(p1) === symbol(ADD)) { + // sin of a sum can be further decomposed into + //sin(alpha+beta) = sin(alpha)*cos(beta)+sin(beta)*cos(alpha) sine_of_angle_sum(); } else { sine_of_angle(); @@ -21168,11 +24887,21 @@ return restore(); }; + //console.log "sine end ---- " + + // Use angle sum formula for special angles. + + //define A p3 + //define B p4 + + // decompose sum sin(alpha+beta) into + // sin(alpha)*cos(beta)+sin(beta)*cos(alpha) sine_of_angle_sum = function() { + //console.log "sin of angle sum ---- " p2 = cdr(p1); while (iscons(p2)) { p4 = car(p2); - if (isnpi(p4)) { + if (isnpi(p4)) { // p4 is B push(p1); push(p4); subtract(); @@ -21190,11 +24919,13 @@ add(); return; } + //console.log "sin of angle sum end ---- " p2 = cdr(p2); } return sine_of_angle(); }; + //console.log "sin of angle sum end ---- " sine_of_angle = function() { var d, n; if (car(p1) === symbol(ARCSIN)) { @@ -21209,6 +24940,7 @@ push_double(d); return; } + // sine function is antisymmetric, sin(-x) = -sin(x) if (isnegative(p1)) { push(p1); negate(); @@ -21216,6 +24948,9 @@ negate(); return; } + // sin(arctan(x)) = x / sqrt(1 + x^2) + + // see p. 173 of the CRC Handbook of Mathematical Sciences if (car(p1) === symbol(ARCTAN)) { push(cadr(p1)); push_integer(1); @@ -21228,6 +24963,15 @@ multiply(); return; } + // multiply by 180/pi to go from radians to degrees. + // we go from radians to degrees because it's much + // easier to calculate symbolic results of most (not all) "classic" + // angles (e.g. 30,45,60...) if we calculate the degrees + // and the we do a switch on that. + // Alternatively, we could look at the fraction of pi + // (e.g. 60 degrees is 1/3 pi) but that's more + // convoluted as we'd need to look at both numerator and + // denominator. push(p1); push_integer(180); multiply(); @@ -21238,12 +24982,17 @@ } divide(); n = pop_integer(); + // most "good" (i.e. compact) trigonometric results + // happen for a round number of degrees. There are some exceptions + // though, e.g. 22.5 degrees, which we don't capture here. if (n < 0 || isNaN(n)) { push(symbol(SIN)); push(p1); list(2); return; } + // values of some famous angles. Many more here: + // https://en.wikipedia.org/wiki/Trigonometric_constants_expressed_in_real_radicals switch (n % 360) { case 0: case 180: @@ -21293,6 +25042,9 @@ } }; + // exp(x) - exp(-x) + // sinh(x) = ---------------- + // 2 Eval_sinh = function() { push(cadr(p1)); Eval(); @@ -21325,23 +25077,10 @@ push(zero); return; } - push_symbol(SINH); - push(p1); - return list(2); - }; - - - /* - Substitute new expr for old expr in expr. - - Input: push expr - - push old expr - - push new expr - - Output: Result on stack - */ + push_symbol(SINH); + push(p1); + return list(2); + }; subst = function() { var i, l1, m1, ref2, ref3; @@ -21357,10 +25096,10 @@ if (istensor(p1)) { p4 = alloc_tensor(p1.tensor.nelem); p4.tensor.ndim = p1.tensor.ndim; - for (i = l1 = 0, ref2 = p1.tensor.ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p1.tensor.ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p4.tensor.dim[i] = p1.tensor.dim[i]; } - for (i = m1 = 0, ref3 = p1.tensor.nelem; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = p1.tensor.nelem; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { push(p1.tensor.elem[i]); push(p2); push(p3); @@ -21387,16 +25126,27 @@ return restore(); }; + // 'sum' function + + //define A p3 + //define B p4 + //define I p5 + //define X p6 + + // leaves the sum at the top of the stack Eval_sum = function() { var body, i, indexVariable, j, k, l1, ref2, ref3; i = 0; j = 0; k = 0; + // 1st arg body = cadr(p1); + // 2nd arg (index) indexVariable = caddr(p1); if (!issymbol(indexVariable)) { stop("sum: 2nd arg?"); } + // 3rd arg (lower limit) push(cadddr(p1)); Eval(); j = pop_integer(); @@ -21404,6 +25154,7 @@ push(p1); return; } + // 4th arg (upper limit) push(caddddr(p1)); Eval(); k = pop_integer(); @@ -21411,9 +25162,11 @@ push(p1); return; } + // remember contents of the index + // variable so we can put it back after the loop p4 = get_binding(indexVariable); push_integer(0); - for (i = l1 = ref2 = j, ref3 = k; ref2 <= ref3 ? l1 <= ref3 : l1 >= ref3; i = ref2 <= ref3 ? ++l1 : --l1) { + for (i = l1 = ref2 = j, ref3 = k; (ref2 <= ref3 ? l1 <= ref3 : l1 >= ref3); i = ref2 <= ref3 ? ++l1 : --l1) { push_integer(i); p5 = pop(); set_binding(indexVariable, p5); @@ -21421,9 +25174,11 @@ Eval(); add(); } + // put back the index variable to original content return set_binding(indexVariable, p4); }; + // Tangent function of numerical and symbolic arguments Eval_tan = function() { push(cadr(p1)); Eval(); @@ -21453,6 +25208,7 @@ push_double(d); return; } + // tan function is antisymmetric, tan(-x) = -tan(x) if (isnegative(p1)) { push(p1); negate(); @@ -21460,6 +25216,15 @@ negate(); return; } + // multiply by 180/pi to go from radians to degrees. + // we go from radians to degrees because it's much + // easier to calculate symbolic results of most (not all) "classic" + // angles (e.g. 30,45,60...) if we calculate the degrees + // and the we do a switch on that. + // Alternatively, we could look at the fraction of pi + // (e.g. 60 degrees is 1/3 pi) but that's more + // convoluted as we'd need to look at both numerator and + // denominator. push(p1); push_integer(180); multiply(); @@ -21470,6 +25235,9 @@ } divide(); n = pop_integer(); + // most "good" (i.e. compact) trigonometric results + // happen for a round number of degrees. There are some exceptions + // though, e.g. 22.5 degrees, which we don't capture here. if (n < 0 || isNaN(n)) { push(symbol(TAN)); push(p1); @@ -21518,6 +25286,9 @@ } }; + // exp(2 x) - 1 + // tanh(x) = -------------- + // exp(2 x) + 1 Eval_tanh = function() { var d; d = 0.0; @@ -21545,21 +25316,12 @@ return list(2); }; - - /* - Taylor expansion of a function - - push(F) - push(X) - push(N) - push(A) - taylor() - */ - Eval_taylor = function() { + // 1st arg p1 = cdr(p1); push(car(p1)); Eval(); + // 2nd arg p1 = cdr(p1); push(car(p1)); Eval(); @@ -21569,27 +25331,34 @@ } else { push(p2); } + // 3rd arg p1 = cdr(p1); push(car(p1)); Eval(); p2 = pop(); if (p2 === symbol(NIL)) { - push_integer(24); + push_integer(24); // default number of terms } else { push(p2); } + // 4th arg p1 = cdr(p1); push(car(p1)); Eval(); p2 = pop(); if (p2 === symbol(NIL)) { - push_integer(0); + push_integer(0); // default expansion point } else { push(p2); } return taylor(); }; + //define F p1 + //define X p2 + //define N p3 + //define A p4 + //define C p5 taylor = function() { var i, k, l1, ref2; i = 0; @@ -21618,7 +25387,7 @@ Eval(); push_integer(1); p5 = pop(); - for (i = l1 = 1, ref2 = k; 1 <= ref2 ? l1 <= ref2 : l1 >= ref2; i = 1 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 1, ref2 = k; (1 <= ref2 ? l1 <= ref2 : l1 >= ref2); i = 1 <= ref2 ? ++l1 : --l1) { push(p1); push(p2); derivative(); @@ -21647,33 +25416,33 @@ return restore(); }; - + //(docs are generated from top-level comments, keep an eye on the formatting!) /* tensor ===================================================================== - + Tags ---- scripting, JS, internal, treenode, general concept - + General description ------------------- Tensors are a strange in-between of matrices and "computer" rectangular data structures. - + Tensors, unlike matrices, and like rectangular data structures, can have an arbitrary number of dimensions (rank), although a tensor with rank zero is just a scalar. - + Tensors, like matrices and unlike many computer rectangular data structures, must be "contiguous" i.e. have no empty spaces within its size, and "uniform", i.e. each element must have the same shape and hence the same rank. - + Also tensors have necessarily to make a distinction between row vectors, column vectors (which have a rank of 2) and uni-dimensional vectors (rank 1). They look very similar but they are fundamentally different. - + Tensors are 1-indexed, as per general math notation, and like Fortran, Lua, Mathematica, SASL, MATLAB, Julia, Erlang and APL. - + Tensors with elements that are also tensors get promoted to a higher rank , this is so we can represent and get the rank of a matrix correctly. Example: @@ -21689,88 +25458,129 @@ Implication of it all is that you can't put arbitrary tensors inside tensors (like you would do to represent block matrices) Rather, all tensors inside tensors must have same shape (and hence, rank) - + Limitations ----------- n.a. - + Implementation info ------------------- Tensors are implemented... - */ + */ + // Called from the "eval" module to evaluate tensor elements. + // p1 points to the tensor operand. Eval_tensor = function() { var a, b, i, l1, m1, ndim, nelem, ref2, ref3; i = 0; ndim = 0; nelem = 0; + //U **a, **b + + //--------------------------------------------------------------------- + + // create a new tensor for the result + + //--------------------------------------------------------------------- check_tensor_dimensions(p1); nelem = p1.tensor.nelem; ndim = p1.tensor.ndim; p2 = alloc_tensor(nelem); p2.tensor.ndim = ndim; - for (i = l1 = 0, ref2 = ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p2.tensor.dim[i] = p1.tensor.dim[i]; } + //--------------------------------------------------------------------- + + // b = Eval(a) + + //--------------------------------------------------------------------- a = p1.tensor.elem; b = p2.tensor.elem; check_tensor_dimensions(p2); - for (i = m1 = 0, ref3 = nelem; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = nelem; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { + //console.log "push/pop: pushing element a of " + i push(a[i]); Eval(); + //console.log "push/pop: popping into element b of " + i b[i] = pop(); } check_tensor_dimensions(p1); check_tensor_dimensions(p2); + //--------------------------------------------------------------------- + + // push the result + + //--------------------------------------------------------------------- push(p2); return promote_tensor(); }; + //----------------------------------------------------------------------------- + + // Add tensors + + // Input: Operands on stack + + // Output: Result on stack + + //----------------------------------------------------------------------------- tensor_plus_tensor = function() { var a, b, c, i, l1, m1, n1, ndim, nelem, ref2, ref3, ref4; i = 0; ndim = 0; nelem = 0; + //U **a, **b, **c save(); p2 = pop(); p1 = pop(); + // are the dimension lists equal? ndim = p1.tensor.ndim; if (ndim !== p2.tensor.ndim) { push(symbol(NIL)); restore(); return; } - for (i = l1 = 0, ref2 = ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { if (p1.tensor.dim[i] !== p2.tensor.dim[i]) { push(symbol(NIL)); restore(); return; } } + // create a new tensor for the result nelem = p1.tensor.nelem; p3 = alloc_tensor(nelem); p3.tensor.ndim = ndim; - for (i = m1 = 0, ref3 = ndim; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = ndim; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { p3.tensor.dim[i] = p1.tensor.dim[i]; } + // c = a + b a = p1.tensor.elem; b = p2.tensor.elem; c = p3.tensor.elem; - for (i = n1 = 0, ref4 = nelem; 0 <= ref4 ? n1 < ref4 : n1 > ref4; i = 0 <= ref4 ? ++n1 : --n1) { + for (i = n1 = 0, ref4 = nelem; (0 <= ref4 ? n1 < ref4 : n1 > ref4); i = 0 <= ref4 ? ++n1 : --n1) { push(a[i]); push(b[i]); add(); c[i] = pop(); } + // push the result push(p3); return restore(); }; + //----------------------------------------------------------------------------- + + // careful not to reorder factors + + //----------------------------------------------------------------------------- tensor_times_scalar = function() { var a, b, i, l1, m1, ndim, nelem, ref2, ref3; i = 0; ndim = 0; nelem = 0; + //U **a, **b save(); p2 = pop(); p1 = pop(); @@ -21778,12 +25588,12 @@ nelem = p1.tensor.nelem; p3 = alloc_tensor(nelem); p3.tensor.ndim = ndim; - for (i = l1 = 0, ref2 = ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p3.tensor.dim[i] = p1.tensor.dim[i]; } a = p1.tensor.elem; b = p3.tensor.elem; - for (i = m1 = 0, ref3 = nelem; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = nelem; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { push(a[i]); push(p2); multiply(); @@ -21798,6 +25608,7 @@ i = 0; ndim = 0; nelem = 0; + //U **a, **b save(); p2 = pop(); p1 = pop(); @@ -21805,12 +25616,12 @@ nelem = p2.tensor.nelem; p3 = alloc_tensor(nelem); p3.tensor.ndim = ndim; - for (i = l1 = 0, ref2 = ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p3.tensor.dim[i] = p2.tensor.dim[i]; } a = p2.tensor.elem; b = p3.tensor.elem; - for (i = m1 = 0, ref3 = nelem; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = nelem; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { push(p1); push(a[i]); multiply(); @@ -21835,12 +25646,18 @@ } }; + //----------------------------------------------------------------------------- + + // gradient of tensor + + //----------------------------------------------------------------------------- d_tensor_tensor = function() { var a, b, c, i, j, l1, m1, n1, ndim, nelem, ref2, ref3, ref4; i = 0; j = 0; ndim = 0; nelem = 0; + //U **a, **b, **c ndim = p1.tensor.ndim; nelem = p1.tensor.nelem; if (ndim + 1 >= MAXDIM) { @@ -21852,15 +25669,15 @@ } p3 = alloc_tensor(nelem * p2.tensor.nelem); p3.tensor.ndim = ndim + 1; - for (i = l1 = 0, ref2 = ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p3.tensor.dim[i] = p1.tensor.dim[i]; } p3.tensor.dim[ndim] = p2.tensor.dim[0]; a = p1.tensor.elem; b = p2.tensor.elem; c = p3.tensor.elem; - for (i = m1 = 0, ref3 = nelem; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { - for (j = n1 = 0, ref4 = p2.tensor.nelem; 0 <= ref4 ? n1 < ref4 : n1 > ref4; j = 0 <= ref4 ? ++n1 : --n1) { + for (i = m1 = 0, ref3 = nelem; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { + for (j = n1 = 0, ref4 = p2.tensor.nelem; (0 <= ref4 ? n1 < ref4 : n1 > ref4); j = 0 <= ref4 ? ++n1 : --n1) { push(a[i]); push(b[j]); derivative(); @@ -21870,14 +25687,20 @@ return push(p3); }; + //----------------------------------------------------------------------------- + + // gradient of scalar + + //----------------------------------------------------------------------------- d_scalar_tensor = function() { var a, b, i, l1, ref2; + //U **a, **b p3 = alloc_tensor(p2.tensor.nelem); p3.tensor.ndim = 1; p3.tensor.dim[0] = p2.tensor.dim[0]; a = p2.tensor.elem; b = p3.tensor.elem; - for (i = l1 = 0, ref2 = p2.tensor.nelem; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p2.tensor.nelem; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { push(p1); push(a[i]); derivative(); @@ -21886,17 +25709,23 @@ return push(p3); }; + //----------------------------------------------------------------------------- + + // Derivative of tensor + + //----------------------------------------------------------------------------- d_tensor_scalar = function() { var a, b, i, l1, m1, ref2, ref3; i = 0; + //U **a, **b p3 = alloc_tensor(p1.tensor.nelem); p3.tensor.ndim = p1.tensor.ndim; - for (i = l1 = 0, ref2 = p1.tensor.ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p1.tensor.ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p3.tensor.dim[i] = p1.tensor.dim[i]; } a = p1.tensor.elem; b = p3.tensor.elem; - for (i = m1 = 0, ref3 = p1.tensor.nelem; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = p1.tensor.nelem; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { push(a[i]); push(p2); derivative(); @@ -21914,7 +25743,7 @@ if (p1.tensor.ndim > p2.tensor.ndim) { return 1; } - for (i = l1 = 0, ref2 = p1.tensor.ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p1.tensor.ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { if (p1.tensor.dim[i] < p2.tensor.dim[i]) { return -1; } @@ -21922,7 +25751,7 @@ return 1; } } - for (i = m1 = 0, ref3 = p1.tensor.nelem; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = p1.tensor.nelem; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { if (equal(p1.tensor.elem[i], p2.tensor.elem[i])) { continue; } @@ -21935,11 +25764,23 @@ return 0; }; + //----------------------------------------------------------------------------- + + // Raise a tensor to a power + + // Input: p1 tensor + + // p2 exponent + + // Output: Result on stack + + //----------------------------------------------------------------------------- power_tensor = function() { var i, k, l1, m1, n, ref2, ref3, results; i = 0; k = 0; n = 0; + // first and last dims must be equal k = p1.tensor.ndim - 1; if (p1.tensor.dim[0] !== p1.tensor.dim[k]) { push_symbol(POWER); @@ -21966,7 +25807,7 @@ p1.tensor.ndim = 2; p1.tensor.dim[0] = n; p1.tensor.dim[1] = n; - for (i = l1 = 0, ref2 = n; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = n; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p1.tensor.elem[n * i + i] = one; } check_tensor_dimensions(p1); @@ -21981,7 +25822,7 @@ } push(p1); results = []; - for (i = m1 = 1, ref3 = n; 1 <= ref3 ? m1 < ref3 : m1 > ref3; i = 1 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 1, ref3 = n; (1 <= ref3 ? m1 < ref3 : m1 > ref3); i = 1 <= ref3 ? ++m1 : --m1) { push(p1); inner(); if (isZeroAtomOrTensor(stack[tos - 1])) { @@ -22000,10 +25841,10 @@ p1 = pop(); p2 = alloc_tensor(p1.tensor.nelem); p2.tensor.ndim = p1.tensor.ndim; - for (i = l1 = 0, ref2 = p1.tensor.ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p1.tensor.ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p2.tensor.dim[i] = p1.tensor.dim[i]; } - for (i = m1 = 0, ref3 = p1.tensor.nelem; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = p1.tensor.nelem; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { p2.tensor.elem[i] = p1.tensor.elem[i]; } check_tensor_dimensions(p1); @@ -22012,6 +25853,7 @@ return restore(); }; + // Tensors with elements that are also tensors get promoted to a higher rank. promote_tensor = function() { var i, j, k, l1, m1, n1, ndim, nelem, o1, q1, ref2, ref3, ref4, ref5, ref6; i = 0; @@ -22027,7 +25869,7 @@ return; } p2 = p1.tensor.elem[0]; - for (i = l1 = 1, ref2 = p1.tensor.nelem; 1 <= ref2 ? l1 < ref2 : l1 > ref2; i = 1 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 1, ref2 = p1.tensor.nelem; (1 <= ref2 ? l1 < ref2 : l1 > ref2); i = 1 <= ref2 ? ++l1 : --l1) { if (!compatible(p2, p1.tensor.elem[i])) { stop("Cannot promote tensor due to inconsistent tensor components."); } @@ -22044,16 +25886,16 @@ nelem = p1.tensor.nelem * p2.tensor.nelem; p3 = alloc_tensor(nelem); p3.tensor.ndim = ndim; - for (i = m1 = 0, ref3 = p1.tensor.ndim; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = p1.tensor.ndim; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { p3.tensor.dim[i] = p1.tensor.dim[i]; } - for (j = n1 = 0, ref4 = p2.tensor.ndim; 0 <= ref4 ? n1 < ref4 : n1 > ref4; j = 0 <= ref4 ? ++n1 : --n1) { + for (j = n1 = 0, ref4 = p2.tensor.ndim; (0 <= ref4 ? n1 < ref4 : n1 > ref4); j = 0 <= ref4 ? ++n1 : --n1) { p3.tensor.dim[i + j] = p2.tensor.dim[j]; } k = 0; - for (i = o1 = 0, ref5 = p1.tensor.nelem; 0 <= ref5 ? o1 < ref5 : o1 > ref5; i = 0 <= ref5 ? ++o1 : --o1) { + for (i = o1 = 0, ref5 = p1.tensor.nelem; (0 <= ref5 ? o1 < ref5 : o1 > ref5); i = 0 <= ref5 ? ++o1 : --o1) { p2 = p1.tensor.elem[i]; - for (j = q1 = 0, ref6 = p2.tensor.nelem; 0 <= ref6 ? q1 < ref6 : q1 > ref6; j = 0 <= ref6 ? ++q1 : --q1) { + for (j = q1 = 0, ref6 = p2.tensor.nelem; (0 <= ref6 ? q1 < ref6 : q1 > ref6); j = 0 <= ref6 ? ++q1 : --q1) { p3.tensor.elem[k++] = p2.tensor.elem[j]; } } @@ -22074,7 +25916,7 @@ if (p.tensor.ndim !== q.tensor.ndim) { return 0; } - for (i = l1 = 0, ref2 = p.tensor.ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p.tensor.ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { if (p.tensor.dim[i] !== q.tensor.dim[i]) { return 0; } @@ -22082,11 +25924,17 @@ return 1; }; + // If the number of args is odd then the last arg is the default result. + // Works like a switch statement. Could also be used for piecewise + // functions? TODO should probably be called "switch"? Eval_test = function() { var checkResult, orig; orig = p1; p1 = cdr(p1); while (iscons(p1)) { + // odd number of parameters means that the + // last argument becomes the default case + // i.e. the one without a test. if (cdr(p1) === symbol(NIL)) { push(car(p1)); Eval(); @@ -22094,21 +25942,37 @@ } checkResult = isZeroLikeOrNonZeroLikeOrUndetermined(car(p1)); if (checkResult == null) { + // we couldn't determine the result + // of a test. This means we can't conclude + // anything about the result of the + // overall test, so we must bail + // with the unevalled test push(orig); return; } else if (checkResult) { + // test succesful, we found out output push(cadr(p1)); Eval(); return; } else { + // test unsuccessful, continue to the + // next pair of test,value p1 = cddr(p1); } } + // no test matched and there was no + // catch-all case, so we return zero. return push_integer(0); }; + // we test A==B by first subtracting and checking if we symbolically + // get zero. If not, we evaluate to float and check if we get a zero. + // If we get another NUMBER then we know they are different. + // If we get something else, then we don't know and we return the + // unaveluated test, which is the same as saying "maybe". Eval_testeq = function() { var checkResult, orig, subtractionResult; + // first try without simplifyng both sides orig = p1; push(cadr(p1)); Eval(); @@ -22116,6 +25980,13 @@ Eval(); subtract(); subtractionResult = pop(); + // OK so we are doing something tricky here + // we are using isZeroLikeOrNonZeroLikeOrUndetermined to check if the result + // is zero or not zero or unknown. + // isZeroLikeOrNonZeroLikeOrUndetermined has some routines + // to determine the zero-ness/non-zero-ness or + // undeterminate-ness of things so we use + // that here and down below. checkResult = isZeroLikeOrNonZeroLikeOrUndetermined(subtractionResult); if (checkResult) { push_integer(0); @@ -22124,6 +25995,9 @@ push_integer(1); return; } + // we didn't get a simple numeric result but + // let's try again after doing + // a simplification on both sides push(cadr(p1)); Eval(); simplify(); @@ -22140,9 +26014,13 @@ push_integer(1); return; } + // if we didn't get to a number then we + // don't know whether the quantities are + // different so do nothing return push(orig); }; + // Relational operators expect a numeric result for operand difference. Eval_testge = function() { var comparison, orig; orig = p1; @@ -22203,52 +26081,74 @@ } }; + // not definition Eval_not = function() { var checkResult, wholeAndExpression; wholeAndExpression = p1; checkResult = isZeroLikeOrNonZeroLikeOrUndetermined(cadr(p1)); if (checkResult == null) { + // inconclusive test on predicate return push(wholeAndExpression); } else if (checkResult) { + // true -> false return push_integer(0); } else { + // false -> true return push_integer(1); } }; - /* and ===================================================================== - + Tags ---- scripting, JS, internal, treenode, general concept - + Parameters ---------- a,b,... - + General description ------------------- Logical-and of predicate expressions. - */ + */ + // and definition Eval_and = function() { var andPredicates, checkResult, somePredicateUnknown, wholeAndExpression; wholeAndExpression = p1; andPredicates = cdr(wholeAndExpression); somePredicateUnknown = false; while (iscons(andPredicates)) { + // eval each predicate checkResult = isZeroLikeOrNonZeroLikeOrUndetermined(car(andPredicates)); if (checkResult == null) { + // here we have stuff that is not reconducible to any + // numeric value (or tensor with numeric values) e.g. + // 'a+b', so it just means that we just don't know the + // truth value of this particular predicate. + // We'll track the fact that we found an unknown + // predicate and we continue with the other predicates. + // (note that in case some subsequent predicate will be false, + // it won't matter that we found some unknowns and + // the whole test will be immediately zero). somePredicateUnknown = true; andPredicates = cdr(andPredicates); } else if (checkResult) { + // found a true, move on to the next predicate andPredicates = cdr(andPredicates); } else if (!checkResult) { + // found a false, enough to falsify everything and return push_integer(0); return; } } + // We checked all the predicates and none of them + // was false. So they were all either true or unknown. + // Now, if even just one was unknown, we'll have to call this + // test as inconclusive and return the whole test expression. + // If all the predicates were known, then we can conclude + // that the test returns true. if (somePredicateUnknown) { return push(wholeAndExpression); } else { @@ -22256,23 +26156,42 @@ } }; + // or definition Eval_or = function() { var checkResult, orPredicates, somePredicateUnknown, wholeOrExpression; wholeOrExpression = p1; orPredicates = cdr(wholeOrExpression); somePredicateUnknown = false; while (iscons(orPredicates)) { + // eval each predicate checkResult = isZeroLikeOrNonZeroLikeOrUndetermined(car(orPredicates)); if (checkResult == null) { + // here we have stuff that is not reconducible to any + // numeric value (or tensor with numeric values) e.g. + // 'a+b', so it just means that we just don't know the + // truth value of this particular predicate. + // We'll track the fact that we found an unknown + // predicate and we continue with the other predicates. + // (note that in case some subsequent predicate will be false, + // it won't matter that we found some unknowns and + // the whole test will be immediately zero). somePredicateUnknown = true; orPredicates = cdr(orPredicates); } else if (checkResult) { + // found a true, enough to return true push_integer(1); return; } else if (!checkResult) { + // found a false, move on to the next predicate orPredicates = cdr(orPredicates); } } + // We checked all the predicates and none of them + // was true. So they were all either false or unknown. + // Now, if even just one was unknown, we'll have to call this + // test as inconclusive and return the whole test expression. + // If all the predicates were known, then we can conclude + // that the test returns false. if (somePredicateUnknown) { return push(wholeOrExpression); } else { @@ -22280,6 +26199,12 @@ } }; + // use subtract for cases like A < A + 1 + + // TODO you could be smarter here and + // simplify both sides only in the case + // of "relational operator: cannot determine..." + // a bit like we do in Eval_testeq cmp_args = function() { var t; t = 0; @@ -22291,13 +26216,16 @@ simplify(); subtract(); p1 = pop(); + // try floating point if necessary if (p1.k !== NUM && p1.k !== DOUBLE) { push(p1); yyfloat(); Eval(); p1 = pop(); } + //console.log "comparison: " + p1.toString() if (isZeroAtomOrTensor(p1)) { + //console.log "comparison isZero " return 0; } switch (p1.k) { @@ -22309,6 +26237,7 @@ } break; case DOUBLE: + //console.log "comparison p1.d: " + p1.d if (p1.d < 0.0) { t = -1; } else { @@ -22316,52 +26245,60 @@ } break; default: + //console.log "comparison is null" t = null; } return t; }; - /* Transform an expression using a pattern. The pattern can come from the integrals table or the user-defined patterns. - + The expression and free variable are on the stack. - + The argument s is a null terminated list of transform rules. - + For example, see the itab (integrals table) - + Internally, the following symbols are used: - + F input expression - + X free variable, i.e. F of X - + A template expression - + B result expression - + C list of conditional expressions - + Puts the final expression on top of stack (whether it's transformed or not) and returns true is successful, false if not. - */ + */ + // p1 and p2 are tmps + + //define F p3 + //define X p4 + //define A p5 + //define B p6 + //define C p7 transform = function(s, generalTransform) { var bookmarkTosToPrintDecomps, eachTransformEntry, i, l1, len, len1, m1, n1, numberOfDecomps, ref2, restTerm, secondTerm, success, theTransform, transform_h, transformationSuccessful, transformedTerms; transform_h = 0; save(); p1 = null; - p4 = pop(); - p3 = pop(); + p4 = pop(); // X i.e. free variable + p3 = pop(); // F i.e. input expression if (DEBUG) { console.log(" !!!!!!!!! transform on: " + p3); } saveMetaBindings(); set_binding(symbol(METAX), p4); + // put constants in F(X) on the stack transform_h = tos; push_integer(1); push(p3); @@ -22373,7 +26310,7 @@ numberOfDecomps = tos - bookmarkTosToPrintDecomps; if (DEBUG) { console.log(" " + numberOfDecomps + " decomposed elements ====== "); - for (i = l1 = 0, ref2 = numberOfDecomps; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = numberOfDecomps; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { console.log(" decomposition element " + i + ": " + stack[tos - 1 - i]); } } @@ -22388,12 +26325,18 @@ console.log("scanning table entry " + theTransform); } push(theTransform); + // replacements of meta variables. Note that we don't + // use scan_meta because the pattern is not a string + // that we have to parse, it's a tree already. + // replace a_ with METAA in the passed transformation push(symbol(SYMBOL_A_UNDERSCORE)); push(symbol(METAA)); subst(); + // replace b_ with METAB in the passed transformation push(symbol(SYMBOL_B_UNDERSCORE)); push(symbol(METAB)); subst(); + // replace x_ with METAX in the passed transformation push(symbol(SYMBOL_X_UNDERSCORE)); push(symbol(METAX)); subst(); @@ -22404,7 +26347,6 @@ } p6 = cadr(p1); p7 = cddr(p1); - /* p5 = p1.tensor.elem[0] p6 = p1.tensor.elem[1] @@ -22412,10 +26354,15 @@ push p1.tensor.elem[i] list(p1.tensor.elem.length - 2) p7 = pop() - */ + */ if (f_equals_a(transform_h, generalTransform)) { + // successful transformation, + // transformed result is in p6 transformationSuccessful = true; } else { + // the match failed but perhaps we can match + // something lower down in the tree, so + // let's recurse the tree if (DEBUG) { console.log("p3 at this point: " + p3); } @@ -22439,6 +26386,8 @@ if (DEBUG) { console.log("testing: " + secondTerm); } + //if (secondTerm+"") == "eig(A x,transpose(A x))()" + // debugger if (DEBUG) { console.log("about to try to simplify other term: " + secondTerm); } @@ -22449,13 +26398,15 @@ console.log("tried to simplify other term: " + secondTerm + " ...successful?: " + success + " ...transformed: " + transformedTerms[transformedTerms.length - 1]); } } + // recreate the tree we were passed, + // but with all the terms being transformed if (transformedTerms.length !== 0) { for (m1 = 0, len = transformedTerms.length; m1 < len; m1++) { i = transformedTerms[m1]; push(i); } list(transformedTerms.length); - p6 = pop(); + p6 = pop(); // "integrals" mode } } } @@ -22474,7 +26425,6 @@ p5 = cadr(p1); p6 = caddr(p1); p7 = cdddr(p1); - /* p5 = p1.tensor.elem[0] p6 = p1.tensor.elem[1] @@ -22482,8 +26432,10 @@ push p1.tensor.elem[i] list(p1.tensor.elem.length - 2) p7 = pop() - */ + */ if (f_equals_a(transform_h, generalTransform)) { + // there is a successful transformation, + // transformed result is in p6 transformationSuccessful = true; break; } @@ -22492,12 +26444,17 @@ } moveTos(transform_h); if (transformationSuccessful) { + //console.log "transformation successful" + // a transformation was successful push(p6); Eval(); p1 = pop(); + //console.log "...into: " + p1 transformationSuccessful = true; } else { + // transformations failed if (generalTransform) { + // result = original expression p1 = p3; } else { p1 = symbol(NIL); @@ -22521,20 +26478,22 @@ return set_binding(symbol(METAA), pop()); }; + // search for a METAA and METAB such that F = A f_equals_a = function(h, generalTransform) { var fea_i, fea_j, l1, m1, originalexpanding, ref2, ref3, ref4, ref5; fea_i = 0; fea_j = 0; - for (fea_i = l1 = ref2 = h, ref3 = tos; ref2 <= ref3 ? l1 < ref3 : l1 > ref3; fea_i = ref2 <= ref3 ? ++l1 : --l1) { + for (fea_i = l1 = ref2 = h, ref3 = tos; (ref2 <= ref3 ? l1 < ref3 : l1 > ref3); fea_i = ref2 <= ref3 ? ++l1 : --l1) { set_binding(symbol(METAA), stack[fea_i]); if (DEBUG) { console.log(" binding METAA to " + get_binding(symbol(METAA))); } - for (fea_j = m1 = ref4 = h, ref5 = tos; ref4 <= ref5 ? m1 < ref5 : m1 > ref5; fea_j = ref4 <= ref5 ? ++m1 : --m1) { + for (fea_j = m1 = ref4 = h, ref5 = tos; (ref4 <= ref5 ? m1 < ref5 : m1 > ref5); fea_j = ref4 <= ref5 ? ++m1 : --m1) { set_binding(symbol(METAB), stack[fea_j]); if (DEBUG) { console.log(" binding METAB to " + get_binding(symbol(METAB))); } + // now test all the conditions (it's an and between them) p1 = p7; while (iscons(p1)) { push(car(p1)); @@ -22546,6 +26505,8 @@ p1 = cdr(p1); } if (iscons(p1)) { + // conditions are not met, + // skip to the next binding of metas continue; } push(p3); @@ -22573,16 +26534,19 @@ console.log("binding METAX to " + get_binding(symbol(METAX))); console.log("comparing " + p3 + " to: " + p5); } - return 1; + return 1; // yes } } } - return 0; + return 0; // no }; + // Transpose tensor indices Eval_transpose = function() { push(cadr(p1)); Eval(); + // add default params if they + // have not been passed if (cddr(p1) === symbol(NIL)) { push_integer(1); push_integer(2); @@ -22607,19 +26571,25 @@ t = 0; ai = []; an = []; - for (i = l1 = 0, ref2 = MAXDIM; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = MAXDIM; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { ai[i] = 0; an[i] = 0; } + //U **a, **b save(); - p3 = pop(); - p2 = pop(); - p1 = pop(); + // by default p3 is 2 and p2 is 1 + p3 = pop(); // index to be transposed + p2 = pop(); // other index to be transposed + p1 = pop(); // what needs to be transposed + + // a transposition just goes away when + // applied to a scalar if (isNumericAtom(p1)) { push(p1); restore(); return; } + // transposition goes away for identity matrix if ((isplusone(p2) && isplustwo(p3)) || (isplusone(p3) && isplustwo(p2))) { if (isidentitymatrix(p1)) { push(p1); @@ -22627,6 +26597,9 @@ return; } } + // a transposition just goes away when + // applied to another transposition with + // the same columns to be switched if (istranspose(p1)) { innerTranspSwitch1 = car(cdr(cdr(p1))); innerTranspSwitch2 = car(cdr(cdr(cdr(p1)))); @@ -22636,11 +26609,15 @@ return; } } + // if operand is a sum then distribute + // (if we are in expanding mode) if (expanding && isadd(p1)) { p1 = cdr(p1); push(zero); while (iscons(p1)) { push(car(p1)); + // add the dimensions to switch but only if + // they are not the default ones. push(p2); push(p3); transpose(); @@ -22650,11 +26627,15 @@ restore(); return; } + // if operand is a multiplication then distribute + // (if we are in expanding mode) if (expanding && ismultiply(p1)) { p1 = cdr(p1); push(one); while (iscons(p1)) { push(car(p1)); + // add the dimensions to switch but only if + // they are not the default ones. push(p2); push(p3); transpose(); @@ -22664,6 +26645,12 @@ restore(); return; } + // distribute the transpose of a dot + // if in expanding mode + // note that the distribution happens + // in reverse as per tranpose rules. + // The dot operator is not + // commutative, so, it matters. if (expanding && isinnerordot(p1)) { p1 = cdr(p1); accumulator = []; @@ -22671,7 +26658,7 @@ accumulator.push([car(p1), p2, p3]); p1 = cdr(p1); } - for (eachEntry = m1 = ref3 = accumulator.length - 1; ref3 <= 0 ? m1 <= 0 : m1 >= 0; eachEntry = ref3 <= 0 ? ++m1 : --m1) { + for (eachEntry = m1 = ref3 = accumulator.length - 1; (ref3 <= 0 ? m1 <= 0 : m1 >= 0); eachEntry = ref3 <= 0 ? ++m1 : --m1) { push(accumulator[eachEntry][0]); push(accumulator[eachEntry][1]); push(accumulator[eachEntry][2]); @@ -22685,6 +26672,7 @@ } if (!istensor(p1)) { if (!isZeroAtomOrTensor(p1)) { + //stop("transpose: tensor expected, 1st arg is not a tensor") push_symbol(TRANSPOSE); push(p1); if ((!isplusone(p2) || !isplustwo(p3)) && (!isplusone(p3) || !isplustwo(p2))) { @@ -22703,6 +26691,14 @@ } ndim = p1.tensor.ndim; nelem = p1.tensor.nelem; + // is it a vector? + // so here it's something curious - note how vectors are + // not really special two-dimensional matrices, but rather + // 1-dimension objects (like tensors can be). So since + // they have one dimension, transposition has no effect. + // (as opposed as if they were special two-dimensional + // matrices) + // see also Ran Pan, Tensor Transpose and Its Properties. CoRR abs/1411.1503 (2014) if (ndim === 1) { push(p1); restore(); @@ -22719,18 +26715,21 @@ m--; p2 = alloc_tensor(nelem); p2.tensor.ndim = ndim; - for (i = n1 = 0, ref4 = ndim; 0 <= ref4 ? n1 < ref4 : n1 > ref4; i = 0 <= ref4 ? ++n1 : --n1) { + for (i = n1 = 0, ref4 = ndim; (0 <= ref4 ? n1 < ref4 : n1 > ref4); i = 0 <= ref4 ? ++n1 : --n1) { p2.tensor.dim[i] = p1.tensor.dim[i]; } p2.tensor.dim[l] = p1.tensor.dim[m]; p2.tensor.dim[m] = p1.tensor.dim[l]; a = p1.tensor.elem; b = p2.tensor.elem; - for (i = o1 = 0, ref5 = ndim; 0 <= ref5 ? o1 < ref5 : o1 > ref5; i = 0 <= ref5 ? ++o1 : --o1) { +// init tensor index + for (i = o1 = 0, ref5 = ndim; (0 <= ref5 ? o1 < ref5 : o1 > ref5); i = 0 <= ref5 ? ++o1 : --o1) { ai[i] = 0; an[i] = p1.tensor.dim[i]; } - for (i = q1 = 0, ref6 = nelem; 0 <= ref6 ? q1 < ref6 : q1 > ref6; i = 0 <= ref6 ? ++q1 : --q1) { +// copy components from a to b + for (i = q1 = 0, ref6 = nelem; (0 <= ref6 ? q1 < ref6 : q1 > ref6); i = 0 <= ref6 ? ++q1 : --q1) { + // swap indices l and m t = ai[l]; ai[l] = ai[m]; ai[m] = t; @@ -22738,9 +26737,10 @@ an[l] = an[m]; an[m] = t; k = 0; - for (j = r1 = 0, ref7 = ndim; 0 <= ref7 ? r1 < ref7 : r1 > ref7; j = 0 <= ref7 ? ++r1 : --r1) { + for (j = r1 = 0, ref7 = ndim; (0 <= ref7 ? r1 < ref7 : r1 > ref7); j = 0 <= ref7 ? ++r1 : --r1) { k = (k * an[j]) + ai[j]; } + // swap indices back t = ai[l]; ai[l] = ai[m]; ai[m] = t; @@ -22748,7 +26748,17 @@ an[l] = an[m]; an[m] = t; b[k] = a[i]; - for (j = s1 = ref8 = ndim - 1; ref8 <= 0 ? s1 <= 0 : s1 >= 0; j = ref8 <= 0 ? ++s1 : --s1) { +// increment tensor index + + // Suppose the tensor dimensions are 2 and 3. +// Then the tensor index ai increments as follows: +// 00 -> 01 +// 01 -> 02 +// 02 -> 10 +// 10 -> 11 +// 11 -> 12 +// 12 -> 00 + for (j = s1 = ref8 = ndim - 1; (ref8 <= 0 ? s1 <= 0 : s1 >= 0); j = ref8 <= 0 ? ++s1 : --s1) { if (++ai[j] < an[j]) { break; } @@ -22759,24 +26769,37 @@ return restore(); }; + // Evaluate a user defined function + //define F p3 # F is the function body + //define A p4 # A is the formal argument list + //define B p5 # B is the calling argument list + //define S p6 # S is the argument substitution list + + // we got here because there was a function invocation and + // it's not been parsed (and consequently tagged) as any + // system function. + // So we are dealing with another function. + // The function could be actually defined, or not yet, + // so we'll deal with both cases. /* d ===================================================================== - + Tags ---- scripting, JS, internal, treenode, general concept - + Parameters ---------- f,x - + General description ------------------- Returns the partial derivative of f with respect to x. x can be a vector e.g. [x,y]. - */ + */ Eval_user_function = function() { var bodyAndFormalArguments, h; + // Use "derivative" instead of "d" if there is no user function "d" if (DEBUG) { console.log("Eval_user_function evaluating: " + car(p1)); } @@ -22784,8 +26807,18 @@ Eval_derivative(); return; } + // normally car(p1) is a symbol with the function name + // but it could be something that has to be + // evaluated to get to the function definition instead + // (e.g. the function is an element of an array) + // so we do an eval to sort it all out. push(car(p1)); Eval(); + // we expect to find either the body and + // formula arguments, OR, if the function + // has not been defined yet, then the + // function will just contain its own name, as + // all undefined variables do. bodyAndFormalArguments = pop(); if (isNumericAtom(bodyAndFormalArguments)) { stop("expected function invocation, found multiplication instead. Use '*' symbol explicitly for multiplication."); @@ -22794,10 +26827,14 @@ } else if (isstr(bodyAndFormalArguments)) { stop("expected function, found string instead."); } - p3 = car(cdr(bodyAndFormalArguments)); + p3 = car(cdr(bodyAndFormalArguments)); // p3 is function body F + // p4 is the formal argument list + // that is also contained here in the FUNCTION node p4 = car(cdr(cdr(bodyAndFormalArguments))); p5 = cdr(p1); - if ((car(bodyAndFormalArguments) !== symbol(FUNCTION)) || (bodyAndFormalArguments === car(p1))) { + // next check is whether evaluation did nothing, so the function is undefined + if ((car(bodyAndFormalArguments) !== symbol(FUNCTION)) || (bodyAndFormalArguments === car(p1))) { // p3 is F + // leave everything as it was and return h = tos; push(bodyAndFormalArguments); p1 = p5; @@ -22809,29 +26846,39 @@ list(tos - h); return; } + // Create the argument substitution list p6(S) p1 = p4; p2 = p5; h = tos; while (iscons(p1) && iscons(p2)) { push(car(p1)); push(car(p2)); + // why explicitly Eval the parameters when + // the body of the function is + // evalled anyways? Commenting it out. All tests pass... + //Eval() p1 = cdr(p1); p2 = cdr(p2); } list(tos - h); p6 = pop(); push(p3); - if (iscons(p6)) { + if (iscons(p6)) { // p6 is S push(p6); rewrite_args(); } + //console.log "rewritten body: " + stack[tos-1] return Eval(); }; + // Rewrite by expanding symbols that contain args rewrite_args = function() { var h, n; n = 0; save(); + // subst. list which is a list + // where each consecutive pair + // is what needs to be substituted and with what p2 = pop(); p1 = pop(); if (istensor(p1)) { @@ -22842,12 +26889,19 @@ if (iscons(p1)) { h = tos; if (car(p1) === car(p2)) { + // rewrite a function in + // the body with the one + // passed from the paramaters push_symbol(EVAL); push(car(cdr(p2))); list(2); } else { + // if there is no match + // then no substitution necessary push(car(p1)); } + // continue recursively to + // rewrite the rest of the body p1 = cdr(p1); while (iscons(p1)) { push(car(p1)); @@ -22864,6 +26918,11 @@ restore(); return 0; } + // Here we are in a symbol case + // so we need to substitute + + // Check if there is a direct match + // of symbols right away p3 = p2; while (iscons(p3)) { if (p1 === car(p3)) { @@ -22873,6 +26932,8 @@ } p3 = cddr(p3); } + // Get the symbol's content, if _that_ + // matches then do the substitution p3 = get_binding(p1); push(p3); if (p1 !== p3) { @@ -22880,7 +26941,7 @@ n = rewrite_args(); if (n === 0) { pop(); - push(p1); + push(p1); // restore if not rewritten with arg } } restore(); @@ -22894,7 +26955,7 @@ push(p1); copy_tensor(); p1 = pop(); - for (i = l1 = 0, ref2 = p1.tensor.nelem; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p1.tensor.nelem; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { push(p1.tensor.elem[i]); push(p2); n += rewrite_args(); @@ -22911,7 +26972,7 @@ k = []; m = 0; n = 0; - for (i = l1 = 0, ref2 = MAXDIM; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = MAXDIM; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { k[i] = 0; } m = 1; @@ -22922,6 +26983,8 @@ Eval(); i = pop_integer(); if (i < 1 || isNaN(i)) { + // if the input is nonsensical + // just return 0 push(zero); return; } @@ -22935,47 +26998,12 @@ } p1 = alloc_tensor(m); p1.tensor.ndim = n; - for (i = m1 = 0, ref3 = n; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = n; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { p1.tensor.dim[i] = k[i]; } return push(p1); }; - - /* - // up to 100 blocks of 100,000 atoms - - #define M 100 - #define N 100000 - - U *mem[M] - int mcount - - U *free_list - int free_count - - U * - alloc(void) - { - U *p - if (free_count == 0) { - if (mcount == 0) - alloc_mem() - else { - gc() - if (free_count < N * mcount / 2) - alloc_mem() - } - if (free_count == 0) - stop("atom space exhausted") - } - p = free_list - free_list = free_list->u.cons.cdr - free_count-- - return p - } - */ - allocatedId = 0; alloc_tensor = function(nelem) { @@ -22985,35 +27013,36 @@ p.k = TENSOR; p.tensor = new tensor(); p.tensor.nelem = nelem; - for (i = l1 = 0, ref2 = nelem; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = nelem; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p.tensor.elem[i] = zero; } p.tensor.allocatedId = allocatedId; + //if allocatedId == 9 + // debugger allocatedId++; check_tensor_dimensions(p); return p; }; - /* // garbage collector - + void gc(void) { int i, j U *p - + // tag everything - + for (i = 0; i < mcount; i++) { p = mem[i] for (j = 0; j < N; j++) p[j].tag = 1 } - + // untag what's used - + untag(p0) untag(p1) untag(p2) @@ -23024,26 +27053,26 @@ untag(p7) untag(p8) untag(p9) - + untag(one) untag(zero) untag(imaginaryunit) - + for (i = 0; i < NSYM; i++) { untag(binding[i]) untag(arglist[i]) } - + for (i = 0; i < tos; i++) untag(stack[i]) - + for (i = (int) (frame - stack); i < TOS; i++) untag(stack[i]) - + // collect everything that's still tagged - + free_count = 0 - + for (i = 0; i < mcount; i++) { p = mem[i] for (j = 0; j < N; j++) { @@ -23069,12 +27098,12 @@ } } } - + void untag(U *p) { int i - + if (iscons(p)) { do { if (p->tag == 0) @@ -23086,7 +27115,7 @@ untag(p) return } - + if (p->tag) { p->tag = 0 if (istensor(p)) { @@ -23095,9 +27124,9 @@ } } } - + // get memory for 100,000 atoms - + void alloc_mem(void) { @@ -23117,23 +27146,23 @@ free_list = p free_count += N } - + void print_mem_info(void) { char buf[100] - + sprintf(buf, "%d blocks (%d bytes/block)\n", N * mcount, (int) sizeof (U)) printstr(buf) - + sprintf(buf, "%d free\n", free_count) printstr(buf) - + sprintf(buf, "%d used\n", N * mcount - free_count) printstr(buf) } - */ - + */ + // returns 1 if expr p contains expr q, otherweise returns 0 Find = function(p, q) { var i, l1, ref2; i = 0; @@ -23141,7 +27170,7 @@ return 1; } if (istensor(p)) { - for (i = l1 = 0, ref2 = p.tensor.nelem; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p.tensor.nelem; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { if (Find(p.tensor.elem[i], q)) { return 1; } @@ -23157,6 +27186,8 @@ return 0; }; + // find stuff like (-1)^(something (but disregard + // imaginary units which are in the form (-1)^(1/2)) findPossibleClockForm = function(p) { var i, l1, ref2; i = 0; @@ -23165,14 +27196,16 @@ } if (car(p) === symbol(POWER) && !isinteger(caddr(p1))) { if (Find(cadr(p), imaginaryunit)) { + //console.log "found i^fraction " + p return 1; } } if (car(p) === symbol(POWER) && equaln(cadr(p), -1) && !isinteger(caddr(p1))) { + //console.log "found -1^fraction in " + p return 1; } if (istensor(p)) { - for (i = l1 = 0, ref2 = p.tensor.nelem; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p.tensor.nelem; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { if (findPossibleClockForm(p.tensor.elem[i])) { return 1; } @@ -23188,6 +27221,7 @@ return 0; }; + // find stuff like (e)^(i something) findPossibleExponentialForm = function(p) { var i, l1, ref2; i = 0; @@ -23195,7 +27229,7 @@ return Find(caddr(p), imaginaryunit); } if (istensor(p)) { - for (i = l1 = 0, ref2 = p.tensor.nelem; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p.tensor.nelem; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { if (findPossibleExponentialForm(p.tensor.elem[i])) { return 1; } @@ -23215,15 +27249,18 @@ init = function() { var i, l1, ref2; + //debugger + //console.log "DOING AN INIT ========================================================================" i = 0; flag = 0; reset_after_error(); chainOfUserSymbolsNotFunctionsBeingEvaluated = []; - if (flag) { + if (flag) { // already initted return; } flag = 1; - for (i = l1 = 0, ref2 = NSYM; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { +// total clearout of symbol table + for (i = l1 = 0, ref2 = NSYM; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { symtab[i] = new U(); symtab[i].k = SYM; binding[i] = symtab[i]; @@ -23232,39 +27269,35 @@ return defn(); }; - - /* cross ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept, script_defined - - Parameters - ---------- - u,v - - General description - ------------------- - Returns the cross product of vectors u and v. - */ - - - /* curl ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept, script_defined - - Parameters - ---------- - u - - General description - ------------------- - Returns the curl of vector u. - */ - - defn_str = ["version=\"" + version + "\"", "e=exp(1)", "i=sqrt(-1)", "autoexpand=1", "assumeRealVariables=1", "trange=[-pi,pi]", "xrange=[-10,10]", "yrange=[-10,10]", "last=0", "trace=0", "forceFixedPrintout=1", "maxFixedPrintoutDigits=6", "printLeaveEAlone=1", "printLeaveXAlone=0", "cross(u,v)=[u[2]*v[3]-u[3]*v[2],u[3]*v[1]-u[1]*v[3],u[1]*v[2]-u[2]*v[1]]", "curl(v)=[d(v[3],y)-d(v[2],z),d(v[1],z)-d(v[3],x),d(v[2],x)-d(v[1],y)]", "div(v)=d(v[1],x)+d(v[2],y)+d(v[3],z)", "ln(x)=log(x)"]; + defn_str = [ + "version=\"" + version + "\"", + "e=exp(1)", + "i=sqrt(-1)", + "autoexpand=1", + "assumeRealVariables=1", + "trange=[-pi,pi]", + "xrange=[-10,10]", + "yrange=[-10,10]", + "last=0", + "trace=0", + "forceFixedPrintout=1", + "maxFixedPrintoutDigits=20", + "printLeaveEAlone=1", + "printLeaveXAlone=0", + // cross definition + "cross(u,v)=[u[2]*v[3]-u[3]*v[2],u[3]*v[1]-u[1]*v[3],u[1]*v[2]-u[2]*v[1]]", + // curl definition + "curl(v)=[d(v[3],y)-d(v[2],z),d(v[1],z)-d(v[3],x),d(v[2],x)-d(v[1],y)]", + // div definition + "div(v)=d(v[1],x)+d(v[2],y)+d(v[3],z)", + // Note that we use the mathematics / Javascript / Mathematica + // convention that "log" is indeed the natural logarithm. + + // In engineering, biology, astronomy, "log" can stand instead + // for the "common" logarithm i.e. base 10. Also note that Google + // calculations use log for the common logarithm. + "ln(x)=log(x)" + ]; defn = function() { var definitionOfInterest, defn_i, l1, originalCodeGen, ref2; @@ -23354,6 +27387,7 @@ std_symbol("isinteger", ISINTEGER); std_symbol("isprime", ISPRIME); std_symbol("laguerre", LAGUERRE); + // std_symbol("laplace", LAPLACE) std_symbol("lcm", LCM); std_symbol("leading", LEADING); std_symbol("legendre", LEGENDRE); @@ -23428,8 +27462,8 @@ std_symbol("trace", TRACE); std_symbol("forceFixedPrintout", FORCE_FIXED_PRINTOUT); std_symbol("maxFixedPrintoutDigits", MAX_FIXED_PRINTOUT_DIGITS); - std_symbol("~", YYE); - std_symbol("$DRAWX", DRAWX); + std_symbol("~", YYE); // tilde so sort puts it after other symbols + std_symbol("$DRAWX", DRAWX); // special purpose internal symbols std_symbol("$METAA", METAA); std_symbol("$METAB", METAB); std_symbol("$METAX", METAX); @@ -23460,9 +27494,11 @@ std_symbol("$C5", C5); std_symbol("$C6", C6); defineSomeHandyConstants(); + // don't add all these functions to the + // symbolsDependencies, clone the original originalCodeGen = codeGen; codeGen = false; - for (defn_i = l1 = 0, ref2 = defn_str.length; 0 <= ref2 ? l1 < ref2 : l1 > ref2; defn_i = 0 <= ref2 ? ++l1 : --l1) { + for (defn_i = l1 = 0, ref2 = defn_str.length; (0 <= ref2 ? l1 < ref2 : l1 > ref2); defn_i = 0 <= ref2 ? ++l1 : --l1) { definitionOfInterest = defn_str[defn_i]; scan(definitionOfInterest); if (DEBUG) { @@ -23473,16 +27509,18 @@ Eval(); pop(); } + // restore the symbol dependencies as they were before. return codeGen = originalCodeGen; }; defineSomeHandyConstants = function() { push_integer(0); - zero = pop(); + zero = pop(); // must be untagged in gc push_integer(1); - one = pop(); + one = pop(); // must be untagged in gc push_double(1.0); one_as_double = pop(); + // i is the square root of -1 i.e. -1 ^ 1/2 push_symbol(POWER); if (DEBUG) { console.log(print_list(stack[tos - 1])); @@ -23499,13 +27537,23 @@ if (DEBUG) { console.log(print_list(stack[tos - 1])); } - return imaginaryunit = pop(); + return imaginaryunit = pop(); // must be untagged in gc }; + // Bignum compare + + // returns + + // -1 a < b + + // 0 a = b + + // 1 a > b mcmp = function(a, b) { return a.compare(b); }; + // a is a bigint, n is a normal int mcmpint = function(a, n) { var b, t; b = bigInt(n); @@ -23525,30 +27573,51 @@ doubleToReasonableString = function(d) { var maxFixedPrintoutDigits, stringRepresentation; + // when generating code, print out + // the standard JS Number printout if (codeGen) { return "" + d; } if (isZeroAtomOrTensor(get_binding(symbol(FORCE_FIXED_PRINTOUT)))) { stringRepresentation = "" + d; + // manipulate the string so that it can be parsed by + // Algebrite (something like 1.23e-123 wouldn't cut it because + // that would be parsed as 1.23*e - 123) if (printMode === PRINTMODE_LATEX) { + // 1.0\mathrm{e}{-10} looks much better than the plain 1.0e-10 if (/\d*\.\d*e.*/gm.test(stringRepresentation)) { stringRepresentation = stringRepresentation.replace(/e(.*)/gm, "\\mathrm{e}{$1}"); } else { + // if there is no dot in the mantissa, add it so we see it's + // a double and not a perfect number + // e.g. 1e-10 becomes 1.0\mathrm{e}{-10} stringRepresentation = stringRepresentation.replace(/(\d+)e(.*)/gm, "$1.0\\mathrm{e}{$2}"); } } else { if (/\d*\.\d*e.*/gm.test(stringRepresentation)) { stringRepresentation = stringRepresentation.replace(/e(.*)/gm, "*10^($1)"); } else { + // if there is no dot in the mantissa, add it so we see it's + // a double and not a perfect number + // e.g. 1e-10 becomes 1.0e-10 stringRepresentation = stringRepresentation.replace(/(\d+)e(.*)/gm, "$1.0*10^($2)"); } } } else { push(get_binding(symbol(MAX_FIXED_PRINTOUT_DIGITS))); maxFixedPrintoutDigits = pop_integer(); + //console.log "maxFixedPrintoutDigits: " + maxFixedPrintoutDigits + //console.log "type: " + typeof(maxFixedPrintoutDigits) + //console.log "toFixed: " + d.toFixed(maxFixedPrintoutDigits) stringRepresentation = "" + d.toFixed(maxFixedPrintoutDigits); + // remove any trailing zeroes after the dot + // see https://stackoverflow.com/questions/26299160/using-regex-how-do-i-remove-the-trailing-zeros-from-a-decimal-number stringRepresentation = stringRepresentation.replace(/(\.\d*?[1-9])0+$/gm, "$1"); + // in case there are only zeroes after the dot, removes the dot too stringRepresentation = stringRepresentation.replace(/\.0+$/gm, ""); + // we actually want to give a hint to user that + // it's a double, so add a trailing ".0" if there + // is no decimal point if (stringRepresentation.indexOf(".") === -1) { stringRepresentation += ".0"; } @@ -23559,8 +27628,10 @@ return stringRepresentation; }; + // does nothing clear_term = function() {}; + // s is a string here anyways isspace = function(s) { if (s == null) { return false; @@ -23579,6 +27650,7 @@ if (str == null) { return false; } + //Check for non-alphabetic characters and space return str.search(/[^A-Za-z]/) === -1; }; @@ -23586,6 +27658,7 @@ if (str == null) { return false; } + //Check for non-alphabetic characters and space return str.search(/[^A-Za-z_]/) === -1; }; @@ -23617,6 +27690,10 @@ return n; }; + // this probably works out to be + // more general than just counting symbols, it can + // probably count instances of anything you pass as + // first argument but didn't try it. countOccurrencesOfSymbol = function(needle, p) { var n; n = 0; @@ -23633,11 +27710,13 @@ return n; }; + // returns the total number of elements + // in an expression countsize = function(p) { var i, l1, n, ref2; n = 0; if (istensor(p)) { - for (i = l1 = 0, ref2 = p.tensor.nelem; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p.tensor.nelem; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { n += p.tensor.elem[i]; } } else if (iscons(p)) { @@ -23651,16 +27730,55 @@ return n; }; + //jmp_buf stop_return, draw_stop_return + + // s is a string here stop = function(s) { var message; + //if (draw_flag == 2) + // longjmp(draw_stop_return, 1) + //else errorMessage += "Stop: "; errorMessage += s; + //debugger message = errorMessage; errorMessage = ''; moveTos(0); throw new Error(message); }; + + //longjmp(stop_return, 1) + + // Figuring out dependencies is key to automatically + // generating a method signature when generating JS code + // from algebrite scripts. + // This is important because the user can keep using normal Algebrite + // scripting without special notations. + // Basically the process consists of figuring out + // the "ground variables" that are needed to compute each variable. + // Now there are two ways of doing this: + // * at parse time + // * after running the scripts + // Doing it at parse time means that we can't track simplifications + // canceling-out some variables for example. But on the other side + // it's very quick and the user can somehow see what the signature is + // going to look like (assuming tha code is rather simple), or anyways + // is going to easily make sense of the generated signature. + // Doing it after execution on the other hand would allow us to see + // if some variable cancel-out. But if variables cancel out then + // they might do so according to some run-time behaviour that the user + // might struggle to keep track of. + // So the effort for the user to make sense of the signature in the first case + // is similar to the effort of keeping tab of types in a typed language. + // While in the second case the effort is similar to running the + // code and simplifications in her head. + + // If we just want to compute the dependencies, we don't need to do + // anything costly, we don't "run" the code and we don't simplify + // the code. Just finding the plain dependencies + // TODO change the name of this function, as it doesn't just find the + // dependencies. It also runs it and generates the JS code. findDependenciesInScript = function(stringToBeParsed, dontGenerateCode) { var allReturnedLatexStrings, allReturnedPlainStrings, bodyForReadableSummaryOfGeneratedCode, cyclesDescriptions, deQuotedDep, dependencyInfo, eachDependency, error, generatedBody, generatedCode, i, indexOfEachReplacement, indexOfPartRemainingToBeParsed, inited, key, l1, len, len1, len2, len3, len4, len5, len6, len7, m1, n, n1, newUserSymbol, o1, origPrintMode, originalUserSymbol, parameters, q1, r1, readableSummaryOfGeneratedCode, recursedDependencies, ref2, replacementsFrom, replacementsTo, s1, scriptEvaluation, stringToBeRun, t1, testableString, timeStartFromAlgebra, toBePrinted, u1, userVariablesMentioned, value, variablesWithCycles; if (DEBUG) { @@ -23677,11 +27795,17 @@ allReturnedPlainStrings = ""; allReturnedLatexStrings = ""; n = 0; + // we are going to store the dependencies _of the block as a whole_ + // so all affected variables in the whole block are lumped + // together, and same for the variable that affect those, we + // lump them all together. dependencyInfo = { affectsVariables: [], affectedBy: [] }; stringToBeRun = stringToBeParsed; + // parse the input. This collects the + // dependency information while (1) { try { errorMessage = ""; @@ -23701,6 +27825,7 @@ console.log(error); } errorMessage = error + ""; + //debugger reset_after_error(); break; } @@ -23710,6 +27835,8 @@ indexOfPartRemainingToBeParsed += n; } testableString = ""; + // print out all local dependencies as collected by this + // parsing pass if (DEBUG) { console.log("all local dependencies ----------------"); } @@ -23734,6 +27861,7 @@ testableString += "; "; } testableString += ". "; + // print out the symbols with re-assignments: if (DEBUG) { console.log("Symbols with reassignments ----------------"); } @@ -23746,6 +27874,7 @@ } } testableString += ". "; + // print out the symbols that appear in expressions without assignments if (DEBUG) { console.log("Symbols in expressions without assignments ----------------"); } @@ -23758,11 +27887,14 @@ } } testableString += ". "; + // ALL Algebrite code is affected by any pattern changing dependencyInfo.affectedBy.push("PATTERN_DEPENDENCY"); if (patternHasBeenFound) { dependencyInfo.affectsVariables.push("PATTERN_DEPENDENCY"); testableString += " - PATTERN_DEPENDENCY inserted - "; } + // print out all global dependencies as collected by this + // parsing pass if (DEBUG) { console.log("All dependencies recursively ----------------"); } @@ -23783,6 +27915,7 @@ console.log(error); } errorMessage = error + ""; + //debugger init(); } if (errorMessage === "") { @@ -23821,7 +27954,30 @@ if (DEBUG) { console.log(" code generation:" + key + " is: " + get_binding(usr_symbol(key)).toString()); } + // we really want to make an extra effort + // to generate simplified code, so + // run a "simplify" on the content of each + // variable that we are generating code for. + // Note that the variable + // will still point to un-simplified structures, + // we only simplify the generated code. push(get_binding(usr_symbol(key))); + // Since we go and simplify all variables we meet, + // we have to replace each variable passed as a parameter + // with something entirely new, so that there is no chance + // that it might evoke previous values in the external scope + // as in this case: + // a = 2 + // f(a) = a+1+b + // we don't want 'a' in the body of f to be simplified to 2 + // There are two cases: 1) the variable actually was already in + // the symbol table, in which case there is going to be this new + // one prepended with AVOID_BINDING_TO_EXTERNAL_SCOPE_VALUE, and + // we'll have to remove up this variable later. + // OR 2) the variable wasn't already in the symbol table, in which + // case we directly create this one, which means that we'll have + // to rename it later to the correct name without the prepended + // part. replacementsFrom = []; replacementsTo = []; for (s1 = 0, len6 = recursedDependencies.length; s1 < len6; s1++) { @@ -23848,9 +28004,11 @@ console.log(error); } errorMessage = error + ""; + //debugger init(); } - for (indexOfEachReplacement = t1 = 0, ref2 = replacementsFrom.length; 0 <= ref2 ? t1 < ref2 : t1 > ref2; indexOfEachReplacement = 0 <= ref2 ? ++t1 : --t1) { + for (indexOfEachReplacement = t1 = 0, ref2 = replacementsFrom.length; (0 <= ref2 ? t1 < ref2 : t1 > ref2); indexOfEachReplacement = 0 <= ref2 ? ++t1 : --t1) { + //console.log "replacing back " + replacementsTo[indexOfEachReplacement] + " into: " + replacementsFrom[indexOfEachReplacement] push(replacementsTo[indexOfEachReplacement]); push(replacementsFrom[indexOfEachReplacement]); subst(); @@ -23858,6 +28016,9 @@ clearRenamedVariablesToAvoidBindingToExternalScope(); if (errorMessage === "") { toBePrinted = pop(); + // we have to get all the variables used on the right side + // here. I.e. to print the arguments it's better to look at the + // actual method body after simplification. userVariablesMentioned = []; collectUserSymbols(toBePrinted, userVariablesMentioned); allReturnedPlainStrings = ""; @@ -23873,7 +28034,6 @@ generatedCode += "// " + key + " is part of a cyclic dependency, no code generated."; readableSummaryOfGeneratedCode += "#" + key + " is part of a cyclic dependency, no code generated."; } else { - /* * using this paragraph instead of the following one * creates methods signatures that @@ -23894,9 +28054,13 @@ if recursedDependencies.indexOf(i.substring(1)) == -1 parameters += i.substring(1) + ", " */ + // remove all native functions from the + // parameters as well. userVariablesMentioned = userVariablesMentioned.filter(function(x) { return predefinedSymbolsInGlobalScope_doNotTrackInDependencies.indexOf(x + "") === -1; }); + // remove the variable that are not in the dependency list + // i.e. only allow the variables that are in the dependency list userVariablesMentioned = userVariablesMentioned.filter(function(x) { return recursedDependencies.indexOf(x + "") !== -1 || recursedDependencies.indexOf("\'" + x + "") !== -1; }); @@ -23908,6 +28072,7 @@ parameters += i.printname + ", "; } } + // eliminate the last ", " for printout clarity parameters = parameters.replace(/, $/gm, ""); parameters += ")"; generatedCode += key + " = function " + parameters + " { return ( " + generatedBody + " ); }"; @@ -23926,8 +28091,10 @@ } } } + // eliminate the last new line generatedCode = generatedCode.replace(/\n$/gm, ""); readableSummaryOfGeneratedCode = readableSummaryOfGeneratedCode.replace(/\n$/gm, ""); + // cleanup symbolsDependencies = {}; symbolsHavingReassignments = []; patternHasBeenFound = false; @@ -23944,6 +28111,7 @@ recursiveDependencies = function(variableToBeChecked, arrayWhereDependenciesWillBeAdded, variablesAlreadyFleshedOut, variablesWithCycles, chainBeingChecked, cyclesDescriptions) { var cyclesDescription, i, k, l1, len, len1, m1, ref2; variablesAlreadyFleshedOut.push(variableToBeChecked); + // recursive dependencies can only be descended if the variable is not bound to a parameter if (symbolsDependencies[chainBeingChecked[chainBeingChecked.length - 1]] != null) { if (symbolsDependencies[chainBeingChecked[chainBeingChecked.length - 1]].indexOf("'" + variableToBeChecked) !== -1) { if (DEBUG) { @@ -23957,14 +28125,22 @@ } chainBeingChecked.push(variableToBeChecked); if (symbolsDependencies[variableToBeChecked] == null) { + // end case: the passed variable has no dependencies + // so there is nothing else to do if (arrayWhereDependenciesWillBeAdded.indexOf(variableToBeChecked) === -1) { arrayWhereDependenciesWillBeAdded.push(variableToBeChecked); } return arrayWhereDependenciesWillBeAdded; } else { ref2 = symbolsDependencies[variableToBeChecked]; + // recursion case: we have to dig deeper for (l1 = 0, len = ref2.length; l1 < len; l1++) { i = ref2[l1]; + // check that there is no recursion in dependencies + // we do that by keeping a list of variables that + // have already been "fleshed-out". If we encounter + // any of those "fleshed-out" variables while + // fleshing out, then there is a cycle if (chainBeingChecked.indexOf(i) !== -1) { if (DEBUG) { console.log(" found cycle:"); @@ -23985,18 +28161,26 @@ } cyclesDescription += " ... then " + i + " again"; cyclesDescriptions.push(cyclesDescription); + //if DEBUG then console.log " --> cycle through " + i + // we want to flesh-out i but it's already been + // fleshed-out, just add it to the variables + // with cycles and move on + // todo refactor this, there are two copies of these two lines if (variablesWithCycles.indexOf(i) === -1) { variablesWithCycles.push(i); } } else { + // flesh-out i recursively recursiveDependencies(i, arrayWhereDependenciesWillBeAdded, variablesAlreadyFleshedOut, variablesWithCycles, chainBeingChecked, cyclesDescriptions); chainBeingChecked.pop(); } } + //variablesAlreadyFleshedOut.pop() return arrayWhereDependenciesWillBeAdded; } }; + // parses and runs one statement/expression at a time inited = false; latexErrorSign = "\\rlap{\\large\\color{red}\\bigtriangleup}{\\ \\ \\tiny\\color{red}!}"; @@ -24010,9 +28194,13 @@ theErrorMessage = theErrorMessage.replace("->", "} \\rightarrow \\text{"); theErrorMessage = theErrorMessage.replace("?", "}\\enspace " + latexErrorSign + " \\enspace \\text{"); theErrorMessage = "$$\\text{" + theErrorMessage.replace(/\n/g, "") + "}$$"; + //console.log "theErrorMessage: " + theErrorMessage return theErrorMessage; }; + // there are around a dozen different unicodes that + // represent some sort of middle dot, let's catch the most + // common and turn them into what we can process normaliseDots = function(stringToNormalise) { stringToNormalise = stringToNormalise.replace(new RegExp(String.fromCharCode(8901), 'g'), String.fromCharCode(dotprod_unicode)); stringToNormalise = stringToNormalise.replace(new RegExp(String.fromCharCode(8226), 'g'), String.fromCharCode(dotprod_unicode)); @@ -24024,13 +28212,12 @@ TIMING_DEBUGS = false; - run = function(stringToBeRun, generateLatex) { + run = function(stringToBeRun, generateLatex = false) { var allReturnedLatexStrings, allReturnedPlainStrings, collectedLatexResult, collectedPlainResult, error, errorWhileExecution, i, indexOfPartRemainingToBeParsed, n, stringToBeReturned, theErrorMessage, timeStart, timingDebugWrite; - if (generateLatex == null) { - generateLatex = false; - } timeStart = new Date().getTime(); + //stringToBeRun = stringToBeRun + "\n" stringToBeRun = normaliseDots(stringToBeRun); + //console.log "run running: " + stringToBeRun if (stringToBeRun === "selftest") { selftest(); return; @@ -24046,6 +28233,8 @@ allReturnedLatexStrings = ""; while (1) { try { + // while we can keep scanning commands out of the + // passed input AND we can execute them... errorMessage = ""; check_stack(); n = scan(stringToBeRun.substring(indexOfPartRemainingToBeParsed)); @@ -24056,8 +28245,10 @@ if (PRINTOUTRESULT) { console.log(error); } + //debugger allReturnedPlainStrings += error.message; if (generateLatex) { + //debugger theErrorMessage = turnErrorMessageToLatex(error.message); allReturnedLatexStrings += theErrorMessage; } @@ -24067,12 +28258,24 @@ if (n === 0) { break; } + // if debug mode then print the source text + + //if (equaln(get_binding(symbol(TRACE)), 1)) { + // for (i = 0 i < n i++) + // if (s[i] != '\r') + // printchar(s[i]) + // if (s[n - 1] != '\n') # n is not zero, see above + // printchar('\n') + //} indexOfPartRemainingToBeParsed += n; push(p1); + //debugger errorWhileExecution = false; try { stringsEmittedByUserPrintouts = ""; top_level_eval(); + //console.log "emitted string after top_level_eval(): >" + stringsEmittedByUserPrintouts + "<" + //console.log "allReturnedPlainStrings string after top_level_eval(): >" + allReturnedPlainStrings + "<" p2 = pop(); check_stack(); if (isstr(p2)) { @@ -24083,14 +28286,20 @@ console.log("\n"); } } + // if the return value is nil there isn't much point + // in adding "nil" to the printout if (p2 === symbol(NIL)) { + //collectedPlainResult = stringsEmittedByUserPrintouts collectedPlainResult = stringsEmittedByUserPrintouts; if (generateLatex) { collectedLatexResult = "$$" + stringsEmittedByUserPrintouts + "$$"; } } else { + //console.log "emitted string before collectPlainStringFromReturnValue: >" + stringsEmittedByUserPrintouts + "<" + //console.log "allReturnedPlainStrings string before collectPlainStringFromReturnValue: >" + allReturnedPlainStrings + "<" collectedPlainResult = print_expr(p2); collectedPlainResult += "\n"; + //console.log "collectedPlainResult: >" + collectedPlainResult + "<" if (generateLatex) { collectedLatexResult = "$$" + collectLatexStringFromReturnValue(p2) + "$$"; if (DEBUG) { @@ -24110,6 +28319,7 @@ console.log(collectedPlainResult); } } + //alert collectedPlainResult if (PRINTOUTRESULT) { if (DEBUG) { console.log("display:"); @@ -24188,6 +28398,9 @@ } }; + // cannot reference symbols yet + + // returns nil on stack if no result to print top_level_eval = function() { var evalledArgument, originalArgument, shouldAutoexpand; if (DEBUG) { @@ -24203,16 +28416,24 @@ originalArgument = top(); Eval(); evalledArgument = top(); + // "draw", "for" and "setq" return "nil", there is no result to print if (evalledArgument === symbol(NIL)) { return; } + // update "last" to contain the last result set_binding(symbol(LAST), evalledArgument); if (!isZeroAtomOrTensor(get_binding(symbol(BAKE)))) { bake(); evalledArgument = top(); } + // If user asked explicitly asked to evaluate "i" or "j" and + // they represent the imaginary unit (-1)^(1/2), then + // show (-1)^(1/2). if ((originalArgument === symbol(SYMBOL_I) || originalArgument === symbol(SYMBOL_J)) && isimaginaryunit(evalledArgument)) { + // In all other cases, replace all instances of (-1)^(1/2) in the result + // with the symbol "i" or "j" depending on which one + // represents the imaginary unit } else if (isimaginaryunit(get_binding(symbol(SYMBOL_J)))) { push(imaginaryunit); push_symbol(SYMBOL_J); @@ -24230,7 +28451,14 @@ } }; + // this is called when the whole notebook is re-run + // so we get the chance of clearing the whole state from + // scratch. + // In practice, the state we need to clear that persists + // across blocks are only the patterns, so + // just eject those. clearAlgebraEnvironment = function() { + //console.log "CLEARING clearAlgebraEnvironment =============================================================" return do_clearall(); }; @@ -24239,15 +28467,21 @@ if (DEBUG) { console.log("computeDependenciesFromAlgebra!!!"); } + // return findDependenciesInScript(codeFromAlgebraBlock, true)[6] + + // TODO this part below is duplicated from computeResultsAndJavaScriptFromAlgebra + // ...should refactor. originalcodeFromAlgebraBlock = codeFromAlgebraBlock; keepState = true; called_from_Algebra_block = true; + //console.log "codeFromAlgebraBlock: " + codeFromAlgebraBlock codeFromAlgebraBlock = normaliseDots(codeFromAlgebraBlock); if (!keepState) { userSimplificationsInListForm = []; userSimplificationsInProgramForm = ""; for (l1 = 0, len = userSimplificationsInListForm.length; l1 < len; l1++) { i = userSimplificationsInListForm[l1]; + //console.log "silentpattern(" + car(i) + ","+cdr(i)+")" userSimplificationsInProgramForm += "silentpattern(" + car(i) + "," + car(cdr(i)) + "," + car(cdr(cdr(i))) + ")\n"; } do_clearall(); @@ -24269,7 +28503,7 @@ }; computeResultsAndJavaScriptFromAlgebra = function(codeFromAlgebraBlock) { - var code, dependencyInfo, i, keepState, l1, latexResult, len, len1, m1, originalcodeFromAlgebraBlock, readableSummaryOfCode, ref2, result, stringToBeRun, testableStringIsIgnoredHere, timeStartFromAlgebra, userSimplificationsInProgramForm; + var code, dependencyInfo, i, keepState, l1, latexResult, len, len1, m1, originalcodeFromAlgebraBlock, readableSummaryOfCode, result, stringToBeRun, testableStringIsIgnoredHere, timeStartFromAlgebra, userSimplificationsInProgramForm; originalcodeFromAlgebraBlock = codeFromAlgebraBlock; keepState = true; called_from_Algebra_block = true; @@ -24277,6 +28511,11 @@ if (TIMING_DEBUGS) { console.log(" --------- computeResultsAndJavaScriptFromAlgebra input: " + codeFromAlgebraBlock + " at: " + (new Date())); } + // we start "clean" each time: + // clear all the symbols and then re-define + // the "starting" symbols. + + //console.log "codeFromAlgebraBlock: " + codeFromAlgebraBlock codeFromAlgebraBlock = normaliseDots(codeFromAlgebraBlock); stringToBeRun = codeFromAlgebraBlock; if (DEBUG) { @@ -24292,6 +28531,7 @@ userSimplificationsInProgramForm = ""; for (m1 = 0, len1 = userSimplificationsInListForm.length; m1 < len1; m1++) { i = userSimplificationsInListForm[m1]; + //console.log "silentpattern(" + car(i) + ","+cdr(i)+")" userSimplificationsInProgramForm += "silentpattern(" + car(i) + "," + car(cdr(i)) + "," + car(cdr(cdr(i))) + ")\n"; } do_clearall(); @@ -24300,7 +28540,8 @@ console.log("codeFromAlgebraBlock including patterns: " + codeFromAlgebraBlock); } } - ref2 = findDependenciesInScript(codeFromAlgebraBlock), testableStringIsIgnoredHere = ref2[0], result = ref2[1], code = ref2[2], readableSummaryOfCode = ref2[3], latexResult = ref2[4], errorMessage = ref2[5], dependencyInfo = ref2[6]; + //debugger + [testableStringIsIgnoredHere, result, code, readableSummaryOfCode, latexResult, errorMessage, dependencyInfo] = findDependenciesInScript(codeFromAlgebraBlock); called_from_Algebra_block = false; if (readableSummaryOfCode !== "" || errorMessage !== "") { result += "\n" + readableSummaryOfCode; @@ -24314,15 +28555,24 @@ } latexResult = latexResult.replace(/\n/g, "\n\n"); } + // remove empty results altogether from latex output, which happens + // for example for assignments to variables or + // functions definitions latexResult = latexResult.replace(/\n*/, ""); latexResult = latexResult.replace(/\$\$\$\$\n*/g, ""); code = code.replace(/Math\./g, ""); code = code.replace(/\n/g, "\n\n"); + //console.log "code: " + code + //console.log "result: " + result + //console.log "latexResult: " + latexResult if (TIMING_DEBUGS) { console.log("computeResultsAndJavaScriptFromAlgebra time (total time from notebook and back) for: " + stringToBeRun + " : " + ((new Date().getTime()) - timeStartFromAlgebra) + "ms"); } return { + //code: "// no code generated yet\n//try again later" + //code: "console.log('some passed code is run'); window.something = 1;" code: code, + // TODO temporarily pass latex in place of standard result too result: latexResult, latexResult: latexResult, dependencyInfo: dependencyInfo @@ -24339,8 +28589,28 @@ (typeof exports !== "undefined" && exports !== null ? exports : this).clearAlgebraEnvironment = clearAlgebraEnvironment; + // _______ + // | | <- stack + // | | + // |_______| + // | | <- stack + tos + // | | + // | | + // |_______| + // | | <- frame + // |_______| + // <- stack + TOS + + // The stack grows from low memory towards high memory. This is so that + // multiple expressions can be pushed on the stack and then accessed as an + // array. + + // The frame area holds local variables and grows from high memory towards + // low memory. The frame area makes local variables visible to the garbage + // collector. tos = 0; + // p is a U nil_symbols = 0; push = function(p) { @@ -24350,23 +28620,35 @@ if (p.isZero != null) { debugger; } + //console.log "pushing " + //console.log print_list(p) if (p === symbol(NIL)) { nil_symbols++; if (DEBUG) { console.log("pushing symbol(NIL) #" + nil_symbols); } } + //if nil_symbols == 111 + // debugger if (tos >= frame) { stop("stack overflow"); } return stack[tos++] = p; }; + // returns a U moveTos = function(stackPos) { if (tos <= stackPos) { + // we are moving the stack pointer + // "up" the stack (as if we were doing a push) tos = stackPos; return; } + // we are moving the stack pointer + // "down" the stack i.e. as if we were + // doing a pop, we can zero- + // out all the elements that we pass + // so we can reclaim the memory while (tos > stackPos) { stack[tos] = null; tos--; @@ -24379,6 +28661,8 @@ pop = function() { var elementToBeReturned; + //popsNum++ + //console.log "pop #" + popsNum if (tos === 0) { debugger; stop("stack underflow"); @@ -24387,10 +28671,17 @@ debugger; } elementToBeReturned = stack[--tos]; + + // give a chance to the garbage + // collection to reclaim space + // This is JS-specific, it would + // actually make the C garbage + // collector useless. stack[tos] = null; return elementToBeReturned; }; + // n is an integer push_frame = function(n) { var i, l1, ref2, results; i = 0; @@ -24400,12 +28691,13 @@ stop("frame overflow, circular reference?"); } results = []; - for (i = l1 = 0, ref2 = n; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = n; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { results.push(stack[frame + i] = symbol(NIL)); } return results; }; + // n is an integer pop_frame = function(n) { frame += n; if (frame > TOS) { @@ -24448,16 +28740,21 @@ return frame += 10; }; + // Local U * is OK here because there is no functional path to the garbage collector. swap = function() { var p, q; + //U *p, *q + // p and q are both Us p = pop(); q = pop(); push(p); return push(q); }; + // Local U * is OK here because there is no functional path to the garbage collector. dupl = function() { var p; + //U *p p = pop(); push(p); return push(p); @@ -24475,6 +28772,9 @@ $.pop = pop; + // The symbol table is a simple array of struct U. + + // put symbol at index n Eval_symbolsinfo = function() { var symbolsinfoToBePrinted; symbolsinfoToBePrinted = symbolsinfo(); @@ -24488,7 +28788,7 @@ symbolsinfo = function() { var bindingi, i, l1, ref2, ref3, symbolsinfoToBePrinted, symtabi; symbolsinfoToBePrinted = ""; - for (i = l1 = ref2 = NIL + 1, ref3 = symtab.length; ref2 <= ref3 ? l1 < ref3 : l1 > ref3; i = ref2 <= ref3 ? ++l1 : --l1) { + for (i = l1 = ref2 = NIL + 1, ref3 = symtab.length; (ref2 <= ref3 ? l1 < ref3 : l1 > ref3); i = ref2 <= ref3 ? ++l1 : --l1) { if (symtab[i].printname === "") { if (isSymbolReclaimable[i] === false) { break; @@ -24503,6 +28803,11 @@ return symbolsinfoToBePrinted; }; + // s is a string, n is an int + // TODO: elsewhere when we create a symbol we + // rather prefer to create a new entry. Here we just + // reuse the existing one. If that can never be a problem + // then explain why, otherwise do create a new entry. std_symbol = function(s, n, latexPrint) { var p; p = symtab[n]; @@ -24517,14 +28822,52 @@ } }; + // symbol lookup, or symbol creation if symbol doesn't exist yet + // this happens often from the scanner. When the scanner sees something + // like myVar = 2, it create a tree (SETQ ("myVar" symbol as created/looked up here (2))) + // user-defined functions also have a usr symbol. + + // Note that some symbols like, say, "abs", + // are picked up by the scanner directly as keywords, + // so they are not looked up via this. + // So in fact you could redefine abs to be abs(x) = x + // but still abs would be picked up by the scanner as a particular + // node type and calls to abs() will be always to the "native" abs + + // Also note that some symbols such as "zero" are (strangely) not picked up by + // the scanner as special nodes, rather they are identified as keywords + // (e.g. not redefinable) at time of symbol lookup (in Eval_sym) and + // evalled, where eval has a case for ZERO. + + // Also note that there are a number of symbols, such as a,b,c,x,y,z,... + // that are actually created by std_symbols. + // They are not special node types (like abs), they are normal symbols + // that are looked up, but the advantage is that since they are often + // used internally by algebrite, we create the symbol in advance and + // we can reference the symbol entry in a clean way + // (e.g. symbol(SYMBOL_X)) rather than + // by looking up a string. + + // s is a string usr_symbol = function(s) { var i, l1, ref2; + //console.log "usr_symbol of " + s + //if s == "aaa" + // debugger + + // find either the existing symbol, or if we + // reach an empty symbol (printname == "") then + // re-use that location. i = 0; - for (i = l1 = 0, ref2 = NSYM; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = NSYM; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { if (s === symtab[i].printname) { + // found the symbol return symtab[i]; } if (symtab[i].printname === "") { + // found an entry in the symbol table + // with no printname, exit the loop + // and re-use this location break; } } @@ -24534,11 +28877,19 @@ symtab[i] = new U(); symtab[i].k = SYM; symtab[i].printname = s; + // say that we just created the symbol + // then, binding[the new symbol entry] + // by default points to the symbol. + // So the value of an unassigned symbol will + // be just its name. binding[i] = symtab[i]; isSymbolReclaimable[i] = false; return symtab[i]; }; + // get the symbol's printname + + // p is a U get_printname = function(p) { if (p.k !== SYM) { stop("symbol error"); @@ -24546,13 +28897,20 @@ return p.printname; }; + // p and q are both U + // there are two Us at play here. One belongs to the + // symtab array and is the variable name. + // The other one is the U with the content, and that + // one will go in the corresponding "binding" array entry. set_binding = function(p, q) { var indexFound; if (p.k !== SYM) { stop("symbol error"); } + //console.log "setting binding of " + p.toString() + " to: " + q.toString() + //if p.toString() == "aaa" + // debugger indexFound = symtab.indexOf(p); - /* if indexFound == -1 debugger @@ -24561,7 +28919,7 @@ indexFound = i console.log "remedied an index not found!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" break - */ + */ if (symtab.indexOf(p, indexFound + 1) !== -1) { console.log("ops, more than one element!"); debugger; @@ -24573,13 +28931,16 @@ return binding[indexFound] = q; }; + // p is a U get_binding = function(p) { var indexFound; if (p.k !== SYM) { stop("symbol error"); } + //console.log "getting binding of " + p.toString() + //if p.toString() == "aaa" + // debugger indexFound = symtab.indexOf(p); - /* if indexFound == -1 debugger @@ -24588,7 +28949,7 @@ indexFound = i console.log "remedied an index not found!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" break - */ + */ if (symtab.indexOf(p, indexFound + 1) !== -1) { console.log("ops, more than one element!"); debugger; @@ -24596,21 +28957,31 @@ if (DEBUG) { console.log("lookup >> get_binding lookup " + indexFound); } + //if indexFound == 139 + // debugger + //if indexFound == 137 + // debugger return binding[indexFound]; }; + // the concept of user symbol is a little fuzzy + // beucase mathematics is full of symbols that actually + // have a special meaning, e.g. e,i,I in some cases j... is_usr_symbol = function(p) { var theSymnum; if (p.k !== SYM) { return false; } theSymnum = symnum(p); + // see "defs" file for the naming of the symbols if (theSymnum > PI && theSymnum !== SYMBOL_I && theSymnum !== SYMBOL_IDENTITY_MATRIX) { return true; } return false; }; + // get symbol's number from ptr + // p is U lookupsTotal = 0; symnum = function(p) { @@ -24627,17 +28998,29 @@ if (DEBUG) { console.log("lookup >> symnum lookup " + indexFound + " lookup # " + lookupsTotal); } + //if lookupsTotal == 21 + // debugger + //if indexFound == 79 + // debugger return indexFound; }; + // push indexed symbol + + // k is an int push_symbol = function(k) { return push(symtab[k]); }; clear_symbols = function() { var i, l1, ref2, ref3, results; +// we can clear just what's assignable. +// everything before NIL is not assignable, +// so there is no need to clear it. results = []; - for (i = l1 = ref2 = NIL + 1, ref3 = NSYM; ref2 <= ref3 ? l1 < ref3 : l1 > ref3; i = ref2 <= ref3 ? ++l1 : --l1) { + for (i = l1 = ref2 = NIL + 1, ref3 = NSYM; (ref2 <= ref3 ? l1 < ref3 : l1 > ref3); i = ref2 <= ref3 ? ++l1 : --l1) { + // stop at the first empty + // entry that is not reclaimable if (symtab[i].printname === "") { if (isSymbolReclaimable[i] === false) { break; @@ -24653,11 +29036,12 @@ return results; }; - collectUserSymbols = function(p, accumulator) { + //symtab[i].printname = "" + //binding[i] = symtab[i] + + // collect all the variables in a tree + collectUserSymbols = function(p, accumulator = []) { var i, l1, ref2; - if (accumulator == null) { - accumulator = []; - } if (is_usr_symbol(p)) { if (accumulator.indexOf(p) === -1) { accumulator.push(p); @@ -24665,7 +29049,7 @@ } } if (istensor(p)) { - for (i = l1 = 0, ref2 = p.tensor.nelem; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p.tensor.nelem; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { collectUserSymbols(p.tensor.elem[i], accumulator); } return; @@ -24696,6 +29080,7 @@ parse_internal = function(argu) { if (typeof argu === 'string') { return scan(argu); + // now its in the stack } else if (typeof argu === 'number') { if (argu % 1 === 0) { return push_integer(argu); @@ -24703,6 +29088,7 @@ return push_double(argu); } } else if (argu instanceof U) { + // hey look its a U return push(argu); } else { console.warn('unknown argument type', argu); @@ -24724,9 +29110,11 @@ return data; }; - exec = function() { - var argu, argus, error, fn, l1, len, name, result; - name = arguments[0], argus = 2 <= arguments.length ? slice.call(arguments, 1) : []; + // exec handles the running ia JS of all the algebrite + // functions. The function name is passed in "name" and + // the corresponding function is pushed at the top of the stack + exec = function(name, ...argus) { + var argu, error, fn, l1, len, result; fn = get_binding(usr_symbol(name)); check_stack(); push(fn); @@ -24770,20 +29158,27 @@ frozenContents = []; frozenPatterns = []; frozenHash = ""; - for (i = l1 = 0, ref2 = symtab.length; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = symtab.length; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { + //if symtab[i].printname == "" + // if isSymbolReclaimable[i] == false + // break + // else + // continue if (isSymbolReclaimable[i] === false) { frozenSymbols.push(symtab[i]); frozenContents.push(binding[i]); } } + // just clone them frozenPatterns = userSimplificationsInListForm.slice(0); return [frozenSymbols, frozenContents, frozenPatterns, zero, one, imaginaryunit, getStateHash()]; }; unfreeze = function(frozen) { var frozenContents, frozenPatterns, frozenSymbols, i, l1, ref2; - frozenSymbols = frozen[0], frozenContents = frozen[1], frozenPatterns = frozen[2], zero = frozen[3], one = frozen[4], imaginaryunit = frozen[5]; - for (i = l1 = 0, ref2 = frozenSymbols.length; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + [frozenSymbols, frozenContents, frozenPatterns, zero, one, imaginaryunit] = frozen; +//clear_symbols() + for (i = l1 = 0, ref2 = frozenSymbols.length; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { symtab[i] = frozenSymbols[i]; binding[i] = frozenContents[i]; } @@ -24803,7 +29198,7 @@ getStateHash = function() { var bindingi, frozenHash, i, l1, len, m1, ref2, ref3, symtabi; frozenHash = ""; - for (i = l1 = ref2 = NIL + 1, ref3 = symtab.length; ref2 <= ref3 ? l1 < ref3 : l1 > ref3; i = ref2 <= ref3 ? ++l1 : --l1) { + for (i = l1 = ref2 = NIL + 1, ref3 = symtab.length; (ref2 <= ref3 ? l1 < ref3 : l1 > ref3); i = ref2 <= ref3 ? ++l1 : --l1) { if (symtab[i].printname === "") { if (isSymbolReclaimable[i] === false) { break; diff --git a/dist/algebrite.js b/dist/algebrite.js index 407473e8..f1eb7c64 100644 --- a/dist/algebrite.js +++ b/dist/algebrite.js @@ -1,21 +1,924 @@ -// Generated by CoffeeScript 1.12.7 +// Generated by CoffeeScript 2.5.1 (function() { + + /* arg ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + z + + General description + ------------------- + Returns the angle of complex z. + + */ + /* + Argument (angle) of complex z + + z arg(z) + - ------ + + a 0 + + -a -pi See note 3 below + + (-1)^a a pi + + exp(a + i b) b + + a b arg(a) + arg(b) + + a + i b arctan(b/a) + + Result by quadrant + + z arg(z) + - ------ + + 1 + i 1/4 pi + + 1 - i -1/4 pi + + -1 + i 3/4 pi + + -1 - i -3/4 pi + + Notes + + 1. Handles mixed polar and rectangular forms, e.g. 1 + exp(i pi/3) + + 2. Symbols in z are assumed to be positive and real. + + 3. Negative direction adds -pi to angle. + + Example: z = (-1)^(1/3), abs(z) = 1/3 pi, abs(-z) = -2/3 pi + + 4. jean-francois.debroux reports that when z=(a+i*b)/(c+i*d) then + + arg(numerator(z)) - arg(denominator(z)) + + must be used to get the correct answer. Now the operation is + automatic. + */ + /* + Returns the coefficient of the imaginary part of complex z + + z imag(z) + - ------- + + a + i b b + + exp(i a) sin(a) + */ + /* Power function + + Input: push Base + + push Exponent + + Output: Result on stack + */ + /* + Convert complex z to rectangular form + + Input: push z + + Output: Result on stack + */ + /* det ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + m + + General description + ------------------- + Returns the determinant of matrix m. + Uses Gaussian elimination for numerical matrices. + + Example: + + det(((1,2),(3,4))) + > -2 + + */ + /* adj ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + m + + General description + ------------------- + Returns the adjunct of matrix m. The inverse of m is equal to adj(m) divided by det(m). + + */ + /* + Guesses a rational for each float in the passed expression + */ + /* arccos ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + Returns the inverse cosine of x. + + */ + /* arccosh ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + Returns the inverse hyperbolic cosine of x. + + */ + /* arcsin ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + Returns the inverse sine of x. + + */ + /* arcsinh ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + Returns the inverse hyperbolic sine of x. + + */ + /* arctan ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + Returns the inverse tangent of x. + + */ + /* arctanh ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + Returns the inverse hyperbolic tangent of x. + + */ + /* besselj ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x,n + + General description + ------------------- + + Returns a solution to the Bessel differential equation (Bessel function of first kind). + + Recurrence relation: + + besselj(x,n) = (2/x) (n-1) besselj(x,n-1) - besselj(x,n-2) + + besselj(x,1/2) = sqrt(2/pi/x) sin(x) + + besselj(x,-1/2) = sqrt(2/pi/x) cos(x) + + For negative n, reorder the recurrence relation as: + + besselj(x,n-2) = (2/x) (n-1) besselj(x,n-1) - besselj(x,n) + + Substitute n+2 for n to obtain + + besselj(x,n) = (2/x) (n+1) besselj(x,n+1) - besselj(x,n+2) + + Examples: + + besselj(x,3/2) = (1/x) besselj(x,1/2) - besselj(x,-1/2) + + besselj(x,-3/2) = -(1/x) besselj(x,-1/2) - besselj(x,1/2) + + */ + /* bessely ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x,n + + General description + ------------------- + + Bessel function of second kind. + + */ + /* ceiling ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + + Returns the smallest integer not less than x. + + */ + /* check ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + p + + General description + ------------------- + Returns whether the predicate p is true/false or unknown: + 0 if false, 1 if true or remains unevaluated if unknown. + Note that if "check" is passed an assignment, it turns it into a test, + i.e. check(a = b) is turned into check(a==b) + so "a" is not assigned anything. + Like in many programming languages, "check" also gives truthyness/falsyness + for numeric values. In which case, "true" is returned for non-zero values. + Potential improvements: "check" can't evaluate strings yet. + + */ + /* choose ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + n,k + + General description + ------------------- + + Returns the number of combinations of n items taken k at a time. + + For example, the number of five card hands is choose(52,5) + + ``` + n! + choose(n,k) = ------------- + k! (n - k)! + ``` + */ + /* circexp ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + + Returns expression x with circular and hyperbolic functions converted to exponential forms. Sometimes this will simplify an expression. + + */ + /* clear ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + + Completely wipes a variable from the environment (while doing x = quote(x) just unassigns it). + + */ + /* clearall ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + General description + ------------------- + + Completely wipes all variables from the environment. + + */ + /* cofactor ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + m,i,j + + General description + ------------------- + Cofactor of a matrix component. + Let c be the cofactor matrix of matrix m, i.e. tranpose(c) = adj(m). + This function returns c[i,j]. + + */ + /* conj ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + z + + General description + ------------------- + Returns the complex conjugate of z. + + */ + /* contract ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + a,i,j + + General description + ------------------- + Contract across tensor indices i.e. returns "a" summed over indices i and j. + If i and j are omitted then 1 and 2 are used. + contract(m) is equivalent to the trace of matrix m. + + */ + /* cosh ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + Returns the hyperbolic cosine of x + + ``` + exp(x) + exp(-x) + cosh(x) = ---------------- + 2 + ``` + + */ + /* deg ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + p,x + + General description + ------------------- + Returns the degree of polynomial p(x). + + */ + /* denominator ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + x + + General description + ------------------- + Returns the denominator of expression x. + + */ + /* dim ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + m,n + + General description + ------------------- + Returns the cardinality of the nth index of tensor "m". + + */ + /* do ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + a,b,... + + General description + ------------------- + Evaluates each argument from left to right. Returns the result of the last argument. + + */ + /* eigenval ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + m + + General description + ------------------- + Compute eigenvalues of m. See "eigen" for more info. + + */ + /* eigenvec ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + m + + General description + ------------------- + Compute eigenvectors of m. See "eigen" for more info. + + */ + /* erf ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Authors + ------- + philippe.billet@noos.fr + + Parameters + ---------- + x + + General description + ------------------- + Error function erf(x). + erf(-x)=erf(x) + + */ + /* + Remove terms that involve a given symbol or expression. For example... + + filter(x^2 + x + 1, x) => 1 + + filter(x^2 + x + 1, x^2) => x + 1 + */ + /* dot ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept + + Parameters + ---------- + a,b,... + + General description + ------------------- + + The inner (or dot) operator gives products of vectors, + matrices, and tensors. + + Note that for Algebrite, the elements of a vector/matrix + can only be scalars. This allows for example to flesh out + matrix multiplication using the usual multiplication. + So for example block-representations are not allowed. + + There is an aweful lot of confusion between sw packages on + what dot and inner do. + + First off, the "dot" operator is different from the + mathematical notion of dot product, which can be + slightly confusing. + + The mathematical notion of dot product is here: + http://mathworld.wolfram.com/DotProduct.html + + However, "dot" does that and a bunch of other things, + i.e. in Algebrite + dot/inner does what the dot of Mathematica does, i.e.: + + scalar product of vectors: + + inner((a, b, c), (x, y, z)) + > a x + b y + c z + + products of matrices and vectors: + + inner(((a, b), (c,d)), (x, y)) + > (a x + b y,c x + d y) + + inner((x, y), ((a, b), (c,d))) + > (a x + c y,b x + d y) + + inner((x, y), ((a, b), (c,d)), (r, s)) + > a r x + b s x + c r y + d s y + + matrix product: + + inner(((a,b),(c,d)),((r,s),(t,u))) + > ((a r + b t,a s + b u),(c r + d t,c s + d u)) + + the "dot/inner" operator is associative and + distributive but not commutative. + + In Mathematica, Inner is a generalisation of Dot where + the user can specify the multiplication and the addition + operators. + But here in Algebrite they do the same thing. + + https://reference.wolfram.com/language/ref/Dot.html + https://reference.wolfram.com/language/ref/Inner.html + + http://uk.mathworks.com/help/matlab/ref/dot.html + http://uk.mathworks.com/help/matlab/ref/mtimes.html + + */ + /* + Laguerre function + + Example + + laguerre(x,3) + + Result + + 1 3 3 2 + - --- x + --- x - 3 x + 1 + 6 2 + + The computation uses the following recurrence relation. + + L(x,0,k) = 1 + + L(x,1,k) = -x + k + 1 + + n*L(x,n,k) = (2*(n-1)+1-x+k)*L(x,n-1,k) - (n-1+k)*L(x,n-2,k) + + In the "for" loop i = n-1 so the recurrence relation becomes + + (i+1)*L(x,n,k) = (2*i+1-x+k)*L(x,n-1,k) - (i+k)*L(x,n-2,k) + */ + /* + Return the leading coefficient of a polynomial. + + Example + + leading(5x^2+x+1,x) + + Result + + 5 + + The result is undefined if P is not a polynomial. + */ + /* + Legendre function + + Example + + legendre(x,3,0) + + Result + + 5 3 3 + --- x - --- x + 2 2 + + The computation uses the following recurrence relation. + + P(x,0) = 1 + + P(x,1) = x + + n*P(x,n) = (2*(n-1)+1)*x*P(x,n-1) - (n-1)*P(x,n-2) + + In the "for" loop we have i = n-1 so the recurrence relation becomes + + (i+1)*P(x,n) = (2*i+1)*x*P(x,n-1) - i*P(x,n-2) + + For m > 0 + + P(x,n,m) = (-1)^m * (1-x^2)^(m/2) * d^m/dx^m P(x,n) + */ + /* + Convert complex z to polar form + + Input: push z + + Output: Result on stack + + polar(z) = abs(z) * exp(i * arg(z)) + */ + /* + Returns the real part of complex z + + z real(z) + - ------- + + a + i b a + + exp(i a) cos(a) + */ + /* + Taylor expansion of a function + + push(F) + push(X) + push(N) + push(A) + taylor() + */ + /* + // up to 100 blocks of 100,000 atoms + + #define M 100 + #define N 100000 + + U *mem[M] + int mcount + + U *free_list + int free_count + + U * + alloc(void) + { + U *p + if (free_count == 0) { + if (mcount == 0) + alloc_mem() + else { + gc() + if (free_count < N * mcount / 2) + alloc_mem() + } + if (free_count == 0) + stop("atom space exhausted") + } + p = free_list + free_list = free_list->u.cons.cdr + free_count-- + return p + } + */ + /* + Compare adjacent terms in s[] and combine if possible. + + Returns the number of terms remaining in s[]. + + n number of terms in s[] initially + */ + /* cross ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept, script_defined + + Parameters + ---------- + u,v + + General description + ------------------- + Returns the cross product of vectors u and v. + + */ + /* curl ===================================================================== + + Tags + ---- + scripting, JS, internal, treenode, general concept, script_defined + + Parameters + ---------- + u + + General description + ------------------- + Returns the curl of vector u. + + */ + /* + Clear all patterns + */ + /* + if 0 + + * left brace + + for (i = 0; i < h; i++) { + if (yindex == YMAX) + break + chartab[yindex].c = '|' + chartab[yindex].x = x - 2 + chartab[yindex].y = y + i + yindex++ + } + + * right brace + + emit_x++ + + for (i = 0; i < h; i++) { + if (yindex == YMAX) + break + chartab[yindex].c = '|' + chartab[yindex].x = emit_x + chartab[yindex].y = y + i + yindex++ + } + + emit_x++ + + endif + */ + /* + For example... + + push(F) + push(X) + filter() + F = pop() + */ + /* + Symbolic addition + + Terms in a sum are combined if they are identical modulo rational + coefficients. + + For example, A + 2A becomes 3A. + + However, the sum A + sqrt(2) A is not modified. + + Combining terms can lead to second-order effects. + + For example, consider the case of + + 1/sqrt(2) A + 3/sqrt(2) A + sqrt(2) A + + The first two terms are combined to yield 2 sqrt(2) A. + + This result can now be combined with the third term to yield + + 3 sqrt(2) A + */ + /* + Table of integrals + + The symbol f is just a dummy symbol for creating a list f(A,B,C,C,...) where + + A is the template expression + + B is the result expression + + C is an optional list of conditional expressions + */ + /* + Partition a term + + Input stack: + + term (factor or product of factors) + + free variable + + Output stack: + + constant expression + + variable expression + */ + /* + Substitute new expr for old expr in expr. + + Input: push expr + + push old expr + + push new expr + + Output: Result on stack + */ var $, ABS, ADD, ADJ, AND, APPROXRATIO, ARCCOS, ARCCOSH, ARCSIN, ARCSINH, ARCTAN, ARCTANH, ARG, ASSUME_REAL_VARIABLES, ATOMIZE, AUTOEXPAND, BAKE, BESSELJ, BESSELY, BINDING, BINOMIAL, BINOM_check_args, BUF, C1, C2, C3, C4, C5, C6, CEILING, CHECK, CHOOSE, CIRCEXP, CLEAR, CLEARALL, CLEARPATTERNS, CLOCK, COEFF, COFACTOR, CONDENSE, CONJ, CONS, CONTRACT, COS, COSH, Condense, DEBUG, DEBUG_ABS, DEBUG_ARG, DEBUG_CLOCKFORM, DEBUG_IMAG, DEBUG_IS, DEBUG_POWER, DEBUG_RECT, DECOMP, DEFINT, DEGREE, DENOMINATOR, DERIVATIVE, DET, DET_check_arg, DIM, DIRAC, DIVISORS, DO, DOT, DOUBLE, DRAW, DRAWX, DSOLVE, E, EIGEN, EIGENVAL, EIGENVEC, EIG_N, EIG_check_arg, EIG_yydd, EIG_yyqq, ERF, ERFC, EVAL, EXP, EXPAND, EXPCOS, EXPSIN, Eval, Eval_Eval, Eval_abs, Eval_add, Eval_adj, Eval_and, Eval_approxratio, Eval_arccos, Eval_arccosh, Eval_arcsin, Eval_arcsinh, Eval_arctan, Eval_arctanh, Eval_arg, Eval_besselj, Eval_bessely, Eval_binding, Eval_binomial, Eval_ceiling, Eval_check, Eval_choose, Eval_circexp, Eval_clear, Eval_clearall, Eval_clearpatterns, Eval_clock, Eval_coeff, Eval_cofactor, Eval_condense, Eval_conj, Eval_cons, Eval_contract, Eval_cos, Eval_cosh, Eval_decomp, Eval_defint, Eval_degree, Eval_denominator, Eval_derivative, Eval_det, Eval_dim, Eval_dirac, Eval_divisors, Eval_do, Eval_dsolve, Eval_eigen, Eval_eigenval, Eval_eigenvec, Eval_erf, Eval_erfc, Eval_exp, Eval_expand, Eval_expcos, Eval_expsin, Eval_factor, Eval_factorial, Eval_factorpoly, Eval_filter, Eval_float, Eval_floor, Eval_for, Eval_function_reference, Eval_gamma, Eval_gcd, Eval_hermite, Eval_hilbert, Eval_imag, Eval_index, Eval_inner, Eval_integral, Eval_inv, Eval_invg, Eval_isinteger, Eval_isprime, Eval_laguerre, Eval_lcm, Eval_leading, Eval_legendre, Eval_log, Eval_lookup, Eval_mod, Eval_multiply, Eval_noexpand, Eval_not, Eval_nroots, Eval_number, Eval_numerator, Eval_operator, Eval_or, Eval_outer, Eval_pattern, Eval_patternsinfo, Eval_polar, Eval_power, Eval_predicate, Eval_prime, Eval_print, Eval_print2dascii, Eval_printcomputer, Eval_printhuman, Eval_printlatex, Eval_printlist, Eval_product, Eval_quote, Eval_quotient, Eval_rank, Eval_rationalize, Eval_real, Eval_rect, Eval_roots, Eval_round, Eval_setq, Eval_sgn, Eval_shape, Eval_silentpattern, Eval_simfac, Eval_simplify, Eval_sin, Eval_sinh, Eval_sqrt, Eval_stop, Eval_subst, Eval_sum, Eval_sym, Eval_symbolsinfo, Eval_tan, Eval_tanh, Eval_taylor, Eval_tensor, Eval_test, Eval_testeq, Eval_testge, Eval_testgt, Eval_testle, Eval_testlt, Eval_transpose, Eval_unit, Eval_user_function, Eval_zero, Evalpoly, FACTOR, FACTORIAL, FACTORPOLY, FILTER, FLOATF, FLOOR, FOR, FORCE_FIXED_PRINTOUT, FUNCTION, Find, GAMMA, GCD, HERMITE, HILBERT, IMAG, INDEX, INNER, INTEGRAL, INV, INVG, INV_check_arg, INV_decomp, ISINTEGER, ISPRIME, LAGUERRE, LAST, LAST_2DASCII_PRINT, LAST_FULL_PRINT, LAST_LATEX_PRINT, LAST_LIST_PRINT, LAST_PLAIN_PRINT, LAST_PRINT, LCM, LEADING, LEGENDRE, LOG, LOOKUP, M, MAXDIM, MAXPRIMETAB, MAX_CONSECUTIVE_APPLICATIONS_OF_ALL_RULES, MAX_CONSECUTIVE_APPLICATIONS_OF_SINGLE_RULE, MAX_FIXED_PRINTOUT_DIGITS, MAX_PROGRAM_SIZE, MEQUAL, METAA, METAB, METAX, MLENGTH, MOD, MSIGN, MULTIPLY, MZERO, N, NIL, NOT, NROOTS, NROOTS_ABS, NROOTS_DELTA, NROOTS_EPSILON, NROOTS_RANDOM, NROOTS_YMAX, NROOTS_divpoly, NSYM, NUM, NUMBER, NUMERATOR, OPERATOR, OR, OUTER, PATTERN, PATTERNSINFO, PI, POLAR, POWER, PRIME, PRINT, PRINT2DASCII, PRINTFULL, PRINTLATEX, PRINTLIST, PRINTMODE_2DASCII, PRINTMODE_COMPUTER, PRINTMODE_HUMAN, PRINTMODE_LATEX, PRINTMODE_LIST, PRINTOUTRESULT, PRINTPLAIN, PRINT_LEAVE_E_ALONE, PRINT_LEAVE_X_ALONE, PRODUCT, QUOTE, QUOTIENT, RANK, RATIONALIZE, REAL, ROOTS, ROUND, SECRETX, SELFTEST, SETQ, SGN, SHAPE, SILENTPATTERN, SIMPLIFY, SIN, SINH, SPACE_BETWEEN_COLUMNS, SPACE_BETWEEN_ROWS, SQRT, STOP, STR, SUBST, SUM, SYM, SYMBOLSINFO, SYMBOL_A, SYMBOL_A_UNDERSCORE, SYMBOL_B, SYMBOL_B_UNDERSCORE, SYMBOL_C, SYMBOL_D, SYMBOL_I, SYMBOL_IDENTITY_MATRIX, SYMBOL_J, SYMBOL_N, SYMBOL_R, SYMBOL_S, SYMBOL_T, SYMBOL_X, SYMBOL_X_UNDERSCORE, SYMBOL_Y, SYMBOL_Z, TAN, TANH, TAYLOR, TENSOR, TEST, TESTEQ, TESTGE, TESTGT, TESTLE, TESTLT, TIMING_DEBUGS, TOS, TRACE, TRANSPOSE, T_DOUBLE, T_EQ, T_FUNCTION, T_GTEQ, T_INTEGER, T_LTEQ, T_NEQ, T_NEWLINE, T_QUOTASSIGN, T_STRING, T_SYMBOL, U, UNIT, USR_SYMBOLS, VERSION, YMAX, YYE, YYRECT, ZERO, __emit_char, __emit_str, __factor_add, __factorial, __is_negative, __is_radical_number, __lcm, __legendre, __legendre2, __legendre3, __normalize_radical_factors, __rationalize_tensor, _print, abs, absValFloat, absval, absval_tensor, add, addSymbolLeftOfAssignment, addSymbolRightOfAssignment, add_all, add_factor_to_accumulator, add_numbers, add_terms, addf, adj, alloc_tensor, allocatedId, any_denominators, approxAll, approxLogs, approxLogsOfRationals, approxOneRatioOnly, approxRadicals, approxRadicalsOfRationals, approxRationalsOfLogs, approxRationalsOfPowersOfE, approxRationalsOfPowersOfPI, approxRationalsOfRadicals, approxSineOfRationalMultiplesOfPI, approxSineOfRationals, approxTrigonometric, approx_just_an_integer, approx_logarithmsOfRationals, approx_nothingUseful, approx_radicalOfRatio, approx_ratioOfRadical, approx_rationalOfE, approx_rationalOfPi, approx_rationalsOfLogarithms, approx_sine_of_pi_times_rational, approx_sine_of_rational, approxratioRecursive, arccos, arccosh, arcsin, arcsinh, arctan, arctanh, areunivarpolysfactoredorexpandedform, arg, arglist, assignmentFound, avoidCalculatingPowersIntoArctans, bake, bake_poly, bake_poly_term, besselj, bessely, bigInt, bignum_factorial, bignum_float, bignum_power_number, bignum_scan_float, bignum_scan_integer, bignum_truncate, binding, binomial, buffer, build_tensor, caaddr, caadr, caar, cadaddr, cadadr, cadar, caddaddr, caddadr, caddar, caddddr, cadddr, caddr, cadr, called_from_Algebra_block, car, cdaddr, cdadr, cdar, cddaddr, cddar, cdddaddr, cddddr, cdddr, cddr, cdr, ceiling, chainOfUserSymbolsNotFunctionsBeingEvaluated, charTabIndex, chartab, checkFloatHasWorkedOutCompletely, check_esc_flag, check_stack, check_tensor_dimensions, choose, choose_check_args, circexp, clearAlgebraEnvironment, clearRenamedVariablesToAvoidBindingToExternalScope, clear_symbols, clear_term, clearall, clockform, cmpGlyphs, cmp_args, cmp_expr, cmp_terms, cmp_terms_count, codeGen, coeff, cofactor, collectLatexStringFromReturnValue, collectUserSymbols, combine_factors, combine_gammas, combine_terms, compareState, compare_numbers, compare_rationals, compare_tensors, compatible, computeDependenciesFromAlgebra, computeResultsAndJavaScriptFromAlgebra, compute_fa, conjugate, cons, consCount, contract, convert_bignum_to_double, convert_rational_to_double, copy_tensor, cosine, cosine_of_angle, cosine_of_angle_sum, count, countOccurrencesOfSymbol, count_denominators, counter, countsize, d_scalar_scalar, d_scalar_scalar_1, d_scalar_tensor, d_tensor_scalar, d_tensor_tensor, dabs, darccos, darccosh, darcsin, darcsinh, darctan, darctanh, dbesselj0, dbesseljn, dbessely0, dbesselyn, dcos, dcosh, dd, decomp, decomp_product, decomp_sum, defineSomeHandyConstants, define_user_function, defn, defn_str, degree, denominator, derf, derfc, derivative, derivative_of_integral, det, determinant, detg, dfunction, dhermite, dirac, display, display_flag, displaychar, divide, divide_numbers, divisors, divisors_onstack, divpoly, dlog, do_clearPatterns, do_clearall, do_simplify_nested_radicals, dontCreateNewRadicalsInDenominatorWhenEvalingMultiplication, dotprod_unicode, doubleToReasonableString, dpow, dpower, dproduct, draw_flag, draw_stop_return, dsgn, dsin, dsinh, dsum, dtan, dtanh, dupl, eigen, elelmIndex, elem, emit_denominator, emit_denominators, emit_expr, emit_factor, emit_factorial_function, emit_flat_tensor, emit_fraction, emit_function, emit_index_function, emit_multiply, emit_number, emit_numerators, emit_numerical_fraction, emit_power, emit_string, emit_subexpr, emit_symbol, emit_tensor, emit_tensor_inner, emit_term, emit_top_expr, emit_unsigned_expr, emit_x, equal, equaln, equalq, erfc, errorMessage, esc_flag, evaluatingAsFloats, evaluatingPolar, exec, expand, expand_get_A, expand_get_AF, expand_get_B, expand_get_C, expand_get_CF, expand_tensor, expanding, expcos, exponential, expr_level, expsin, f1, f10, f2, f3, f4, f5, f9, f_equals_a, factor, factor_a, factor_again, factor_b, factor_number, factor_small_number, factor_term, factorial, factorpoly, factors, factpoly_expo, fill_buf, filter, filter_main, filter_sum, filter_tensor, findDependenciesInScript, findPossibleClockForm, findPossibleExponentialForm, findroot, fixup_fraction, fixup_power, flag, floatToRatioRoutine, fmt_index, fmt_level, fmt_x, frame, freeze, functionInvokationsScanningStack, gamma, gamma_of_sum, gammaf, gcd, gcd_main, gcd_numbers, gcd_polys, gcd_powers_with_same_base, gcd_product_product, gcd_product_sum, gcd_sum, gcd_sum_product, gcd_sum_sum, gen, getSimpleRoots, getStateHash, get_binding, get_factor_from_complex_root, get_factor_from_real_root, get_innerprod_factors, get_next_token, get_printname, get_size, get_token, getdisplaystr, glyph, gp, guess, hasImaginaryCoeff, hasNegativeRationalExponent, hash_addition, hash_function, hash_multiplication, hash_power, hashcode_values, hashed_itab, hermite, hilbert, i1, imag, imaginaryunit, index_function, init, initNRoots, inited, inner, inner_f, input_str, integral, integral_of_form, integral_of_product, integral_of_sum, inv, inverse, invert_number, invg, isNumberOneOverSomething, isNumericAtom, isNumericAtomOrTensor, isSimpleRoot, isSmall, isSymbolLeftOfAssignment, isSymbolReclaimable, isZeroAtom, isZeroAtomOrTensor, isZeroLikeOrNonZeroLikeOrUndetermined, isZeroTensor, is_denominator, is_factor, is_small_integer, is_square_matrix, is_usr_symbol, isadd, isalnumorunderscore, isalpha, isalphaOrUnderscore, iscomplexnumber, iscomplexnumberdouble, iscons, isdenominator, isdigit, isdouble, iseveninteger, isfactor, isfactorial, isfloating, isfraction, isidentitymatrix, isimaginarynumber, isimaginarynumberdouble, isimaginaryunit, isinnerordot, isinteger, isintegerfactor, isintegerorintegerfloat, isinv, iskeyword, isminusone, isminusoneoversqrttwo, isminusoneovertwo, ismultiply, isnegative, isnegativenumber, isnegativeterm, isnonnegativeinteger, isnpi, isone, isoneover, isoneoversqrttwo, isoneovertwo, isplusone, isplustwo, ispolyexpandedform, ispolyexpandedform_expr, ispolyexpandedform_factor, ispolyexpandedform_term, ispolyfactoredorexpandedform, ispolyfactoredorexpandedform_factor, ispolyfactoredorexpandedform_power, isposint, ispositivenumber, ispower, isquarterturn, isrational, isspace, isstr, issymbol, issymbolic, istensor, istranspose, isunderscore, isunivarpolyfactoredorexpandedform, itab, italu_hashcode, j1, laguerre, laguerre2, lastFoundSymbol, latexErrorSign, lcm, leading, legendre, length, lessp, level, list, listLength, logarithm, logbuf, lookupsTotal, lu_decomp, madd, makePositive, makeSignSameAs, make_hashed_itab, mask, mcmp, mcmpint, mdiv, mdivrem, meta_mode, mgcd, mini_solve, mint, mmod, mmul, mod, monic, move, moveTos, mp_clr_bit, mp_denominator, mp_numerator, mp_set_bit, mpow, mprime, mroot, mshiftright, msub, mtotal, multinomial_sum, multiply, multiply_all, multiply_all_noexpand, multiply_consecutive_constants, multiply_denominators, multiply_denominators_factor, multiply_denominators_term, multiply_noexpand, multiply_numbers, n_factor_number, negate, negate_expand, negate_noexpand, negate_number, new_string, newline_flag, nil_symbols, normaliseDots, normalisedCoeff, normalize_angle, nroots_a, nroots_b, nroots_c, nroots_df, nroots_dx, nroots_fa, nroots_fb, nroots_x, nroots_y, nterms, nthCadr, numerator, numericRootOfPolynomial, o, one, oneElement, one_as_double, out_buf, out_count, out_of_memory, outer, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, parse, parse_internal, parse_p1, parse_p2, parse_time_simplifications, partition, patternHasBeenFound, patternsinfo, peek, performing_roots, polar, polarRectAMinusOneBase, polycoeff, polyform, pop, pop_double, pop_frame, pop_integer, power, power_str, power_sum, power_tensor, predefinedSymbolsInGlobalScope_doNotTrackInDependencies, prime, primetab, print2dascii, printMode, print_ABS_latex, print_ARCCOS_codegen, print_ARCSIN_codegen, print_ARCTAN_codegen, print_BINOMIAL_latex, print_COS_codegen, print_DEFINT_latex, print_DOT_codegen, print_DOT_latex, print_DO_codegen, print_FOR_codegen, print_INV_codegen, print_INV_latex, print_PRODUCT_codegen, print_PRODUCT_latex, print_SETQ_codegen, print_SIN_codegen, print_SQRT_latex, print_SUM_codegen, print_SUM_latex, print_TAN_codegen, print_TESTEQ_latex, print_TESTGE_latex, print_TESTGT_latex, print_TESTLE_latex, print_TESTLT_latex, print_TEST_codegen, print_TEST_latex, print_TRANSPOSE_codegen, print_TRANSPOSE_latex, print_UNIT_codegen, print_a_over_b, print_base, print_base_of_denom, print_char, print_denom, print_double, print_expo_of_denom, print_exponent, print_expr, print_factor, print_factorial_function, print_glyphs, print_index_function, print_list, print_multiply_sign, print_number, print_power, print_str, print_subexpr, print_tensor, print_tensor_inner, print_tensor_inner_latex, print_tensor_latex, print_term, printchar, printchar_nowrap, printline, program_buf, promote_tensor, push, pushTryNotToDuplicate, push_cars, push_double, push_factor, push_frame, push_identity_matrix, push_integer, push_rational, push_symbol, push_term_factors, push_terms, push_zero_matrix, qadd, qdiv, qmul, qpow, qpowf, quickfactor, quickpower, rational, rationalize, rationalize_coefficients, real, reciprocate, rect, recursionLevelNestedRadicalsRemoval, recursiveDependencies, ref, ref1, rememberPrint, remove_negative_exponents, reset_after_error, restore, restoreMetaBindings, rewrite_args, rewrite_args_tensor, roots, roots2, roots3, run, runUserDefinedSimplifications, save, saveMetaBindings, scalar_times_tensor, scan, scan_error, scan_expression, scan_factor, scan_function_call_with_function_name, scan_function_call_without_function_name, scan_index, scan_meta, scan_power, scan_relation, scan_stmt, scan_str, scan_string, scan_subexpr, scan_symbol, scan_tensor, scan_term, scanned, scanningParameters, setM, setSignTo, set_binding, set_component, setq_indexed, sfac_product, sfac_product_f, sgn, shape, show_power_debug, sign, sign_of_term, simfac, simfac_term, simpleComplexityMeasure, simplify, simplifyForCodeGeneration, simplify_1_in_products, simplify_main, simplify_nested_radicals, simplify_polar, simplify_polarRect, simplify_rational_expressions, simplify_rectToClock, simplify_tensor, simplify_trig, simplifyfactorials, sine, sine_of_angle, sine_of_angle_sum, skipRootVariableToBeSolved, sort_stack, square, ssqrt, stack, stackAddsCount, std_symbol, step, step2, stop, strcmp, stringsEmittedByUserPrintouts, subf, subst, subtract, subtract_numbers, swap, symbol, symbolsDependencies, symbolsHavingReassignments, symbolsInExpressionsWithoutAssignments, symbolsLeftOfAssignment, symbolsRightOfAssignment, symbolsinfo, symnum, symtab, take_care_of_nested_radicals, tangent, taylor, tensor, tensor_plus_tensor, tensor_times_scalar, testApprox, test_flag, text_metric, theRandom, token, token_buf, token_str, top, top_level_eval, tos, transform, transpose, transpose_unicode, trigmode, trivial_divide, try_kth_prime, turnErrorMessageToLatex, ucmp, unfreeze, unique, unique_f, update_token_buf, userSimplificationsInListForm, userSimplificationsInStringForm, usr_symbol, verbosing, version, will_be_displayed_as_fraction, ybinomial, ycosh, ydirac, yerf, yerfc, yfloor, yindex, yround, ysinh, yyarg, yybesselj, yybessely, yyceiling, yycondense, yycontract, yycosh, yydegree, yydetg, yydivpoly, yyerf, yyerfc, yyexpand, yyfactorpoly, yyfloat, yyfloor, yyhermite, yyhermite2, yyinvg, yylcm, yylog, yymultiply, yyouter, yypower, yyrationalize, yyround, yysgn, yysimfac, yysinh, yytangent, zero, zzfloat, - hasProp = {}.hasOwnProperty, - slice = [].slice; + hasProp = {}.hasOwnProperty; bigInt = require('big-integer'); + // also change the version in the package.json file version = "1.3.1"; SELFTEST = 1; + // size of the symbol table NSYM = 1000; DEBUG = false; PRINTOUTRESULT = false; + // printing-related constants PRINTMODE_LATEX = "PRINTMODE_LATEX"; PRINTMODE_2DASCII = "PRINTMODE_2DASCII"; @@ -26,6 +929,8 @@ PRINTMODE_LIST = "PRINTMODE_LIST"; + // when the user uses the generic "print" statement + // this setting kicks-in. printMode = PRINTMODE_COMPUTER; dontCreateNewRadicalsInDenominatorWhenEvalingMultiplication = true; @@ -37,18 +942,54 @@ avoidCalculatingPowersIntoArctans = true; rational = (function() { - function rational() {} - - rational.prototype.a = null; - - rational.prototype.b = null; + // Symbolic expressions are built by connecting U structs. + + // For example, (a b + c) is built like this: + + // _______ _______ _______ + // |CONS |--->|CONS |----------------------------->|CONS | + // | | | | | | + // |_______| |_______| |_______| + // | | | + // ___v___ ___v___ _______ _______ ___v___ + // |ADD | |CONS |--->|CONS |--->|CONS | |SYM c | + // | | | | | | | | | | + // |_______| |_______| |_______| |_______| |_______| + // | | | + // ___v___ ___v___ ___v___ + // |MUL | |SYM a | |SYM b | + // | | | | | | + // |_______| |_______| |_______| + class rational {}; + + rational.prototype.a = null; // a bigInteger + + rational.prototype.b = null; // a bigInteger return rational; - })(); + }).call(this); U = (function() { - U.prototype.cons = null; + class U { + toString() { + return print_expr(this); + } + + toLatexString() { + return collectLatexStringFromReturnValue(this); + } + + constructor() { + this.cons = {}; + this.cons.car = null; + this.cons.cdr = null; + this.q = new rational(); + } + + }; + + U.prototype.cons = null; // will have a car and cdr U.prototype.printname = ""; @@ -56,35 +997,22 @@ U.prototype.tensor = null; - U.prototype.q = null; + // rational number a over b + U.prototype.q = null; // will point to a rational - U.prototype.d = 0.0; + U.prototype.d = 0.0; // a double U.prototype.k = 0; U.prototype.tag = 0; - U.prototype.toString = function() { - return print_expr(this); - }; - - U.prototype.toLatexString = function() { - return collectLatexStringFromReturnValue(this); - }; - - function U() { - this.cons = {}; - this.cons.car = null; - this.cons.cdr = null; - this.q = new rational(); - } - return U; - })(); + }).call(this); errorMessage = ""; + // the following enum is for struct U, member k CONS = 0; NUM = 1; @@ -97,6 +1025,9 @@ SYM = 5; + // the following enum is for indexing the symbol table + + // standard functions first, then nil, then everything else counter = 0; ABS = counter++; @@ -251,6 +1182,7 @@ LAGUERRE = counter++; + // LAPLACE = counter++ LCM = counter++; LEADING = counter++; @@ -371,7 +1303,9 @@ ZERO = counter++; - NIL = counter++; + // ALL THE SYMBOLS ABOVE NIL ARE KEYWORDS, + // WHICH MEANS THAT USER CANNOT REDEFINE THEM + NIL = counter++; // nil goes here, after standard functions LAST = counter++; @@ -401,7 +1335,7 @@ YYE = counter++; - DRAWX = counter++; + DRAWX = counter++; // special purpose internal symbols METAA = counter++; @@ -461,10 +1395,13 @@ C6 = counter++; - USR_SYMBOLS = counter++; + USR_SYMBOLS = counter++; // this must be last E = YYE; + // TOS cannot be arbitrarily large because the OS seg faults on deep recursion. + // For example, a circular evaluation like x=x+1 can cause a seg fault. + // At this setting (100,000) the evaluation stack overruns before seg fault. TOS = 100000; BUF = 10000; @@ -477,8 +1414,12 @@ MAX_CONSECUTIVE_APPLICATIONS_OF_SINGLE_RULE = 10; + //define _USE_MATH_DEFINES // for MS C++ MAXDIM = 24; + // needed for the mechanism to + // find all dependencies between variables + // in a script symbolsDependencies = {}; symbolsHavingReassignments = []; @@ -489,41 +1430,52 @@ predefinedSymbolsInGlobalScope_doNotTrackInDependencies = ["rationalize", "abs", "e", "i", "pi", "sin", "ceiling", "cos", "roots", "integral", "derivative", "defint", "sqrt", "eig", "cov", "deig", "dcov", "float", "floor", "product", "root", "round", "sum", "test", "unit"]; + // you can do some little simplifications + // at parse time, such as calculating away + // immediately simple operations on + // constants, removing 1s from products + // etc. parse_time_simplifications = true; chainOfUserSymbolsNotFunctionsBeingEvaluated = []; stringsEmittedByUserPrintouts = ""; + // flag use to potentially switch on/off some quirks "deep" + // in the code due to call from Algebra block. + // Currently not used. called_from_Algebra_block = false; tensor = (function() { - tensor.prototype.ndim = 0; + class tensor { + constructor() { + this.dim = (function() { + var o, ref, results; + results = []; + for (o = 0, ref = MAXDIM; (0 <= ref ? o <= ref : o >= ref); 0 <= ref ? o++ : o--) { + results.push(0); + } + return results; + })(); + this.elem = []; + } - tensor.prototype.dim = null; + }; - tensor.prototype.nelem = 0; + tensor.prototype.ndim = 0; // number of dimensions - tensor.prototype.elem = null; + tensor.prototype.dim = null; // dimension length, for each dimension - function tensor() { - this.dim = (function() { - var o, ref, results; - results = []; - for (o = 0, ref = MAXDIM; 0 <= ref ? o <= ref : o >= ref; 0 <= ref ? o++ : o--) { - results.push(0); - } - return results; - })(); - this.elem = []; - } + tensor.prototype.nelem = 0; // total number of elements + + tensor.prototype.elem = null; // an array containing all the data return tensor; - })(); + }).call(this); display = (function() { - function display() {} + class display {}; display.prototype.h = 0; @@ -531,14 +1483,14 @@ display.prototype.n = 0; - display.prototype.a = []; + display.prototype.a = []; // will contain an array of c,x,y (color,x,y) return display; - })(); + }).call(this); text_metric = (function() { - function text_metric() {} + class text_metric {}; text_metric.prototype.ascent = 0; @@ -548,9 +1500,9 @@ return text_metric; - })(); + }).call(this); - tos = 0; + tos = 0; // top of stack expanding = 0; @@ -601,45 +1553,48 @@ program_buf = ""; + // will contain the variable names symtab = []; + // will contain the contents of the variable + // in the corresponding position in symtab array binding = []; isSymbolReclaimable = []; - arglist = []; + arglist = []; // will contain U - stack = []; + stack = []; // will contain *U frame = 0; - p0 = null; + p0 = null; // will contain U - p1 = null; + p1 = null; // will contain U - p2 = null; + p2 = null; // will contain U - p3 = null; + p3 = null; // will contain U - p4 = null; + p4 = null; // will contain U - p5 = null; + p5 = null; // will contain U - p6 = null; + p6 = null; // will contain U - p7 = null; + p7 = null; // will contain U - p8 = null; + p8 = null; // will contain U - p9 = null; + p9 = null; // will contain U - zero = null; + zero = null; // will contain U - one = null; + one = null; // will contain U one_as_double = null; - imaginaryunit = null; + imaginaryunit = null; // will contain U out_buf = ""; @@ -649,7 +1604,7 @@ codeGen = false; - draw_stop_return = null; + draw_stop_return = null; // extern jmp_buf ????? userSimplificationsInListForm = []; @@ -691,18 +1646,22 @@ } }; + // because of recursion, we consider a scalar to be + // a tensor, so a numeric scalar will return true isNumericAtomOrTensor = function(p) { var a, i, n, o, ref; if (isNumericAtom(p) || p === symbol(SYMBOL_IDENTITY_MATRIX)) { return 1; } if (!istensor(p) && !isNumericAtom(p)) { + //console.log "p not an atom nor a tensor: " + p return 0; } n = p.tensor.nelem; a = p.tensor.elem; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { if (!isNumericAtomOrTensor(a[i])) { + //console.log "non-numeric element: " + a[i] return 0; } } @@ -821,6 +1780,7 @@ return car(cdr(cdr(car(cdr(cdr(p)))))); }; + // not used yet listLength = function(p) { var startCount; startCount = -1; @@ -831,6 +1791,7 @@ return startCount; }; + // not used yet nthCadr = function(p, n) { var startCount; startCount = 0; @@ -869,6 +1830,9 @@ return car(p) === symbol(INV); }; + // TODO this is a bit of a shallow check, we should + // check when we are passed an actual tensor and possibly + // cache the test result. isidentitymatrix = function(p) { return p === symbol(SYMBOL_IDENTITY_MATRIX); }; @@ -997,53 +1961,52 @@ $.SYM = SYM; - - /* abs ===================================================================== + //(docs are generated from top-level comments, keep an eye on the formatting!) + /* abs ===================================================================== + Tags ---- scripting, JS, internal, treenode, general concept - + Parameters ---------- x - + General description ------------------- Returns the absolute value of a real number, the magnitude of a complex number, or the vector length. - */ - + */ /* Absolute value of a number,or magnitude of complex z, or norm of a vector - + z abs(z) - ------ - + a a - + -a a - + (-1)^a 1 - + exp(a + i b) exp(a) - + a b abs(a) abs(b) - + a + i b sqrt(a^2 + b^2) - + Notes - + 1. Handles mixed polar and rectangular forms, e.g. 1 + exp(i pi/3) - + 2. jean-francois.debroux reports that when z=(a+i*b)/(c+i*d) then - + abs(numerator(z)) / abs(denominator(z)) - + must be used to get the correct answer. Now the operation is automatic. - */ - + */ DEBUG_ABS = false; Eval_abs = function() { @@ -1059,6 +2022,13 @@ return zzfloat(); }; + // zzfloat of an abs doesn't necessarily result in a double + // , for example if there are variables. But + // in many of the tests there should be indeed + // a float, these two lines come handy to highlight + // when that doesn't happen for those tests. + //if !isdouble(stack[tos-1]) + // stop("absValFloat should return a double and instead got: " + stack[tos-1]) abs = function() { var theArgument; theArgument = top(); @@ -1099,6 +2069,7 @@ if (DEBUG_ABS) { console.log("ABS of " + p1); } + // handle all the "number" cases first ----------------------------------------- if (isZeroAtomOrTensor(p1)) { if (DEBUG_ABS) { console.log(" abs: " + p1 + " just zero"); @@ -1141,6 +2112,17 @@ restore(); return; } + // ??? should there be a shortcut case here for the imaginary unit? + + // now handle decomposition cases ---------------------------------------------- + + // we catch the "add", "power", "multiply" cases first, + // before falling back to the + // negative/positive cases because there are some + // simplification thay we might be able to do. + // Note that for this routine to give a correct result, this + // must be a sum where a complex number appears. + // If we apply this to "a+b", we get an incorrect result. if (car(p1) === symbol(ADD) && (findPossibleClockForm(p1) || findPossibleExponentialForm(p1) || Find(p1, imaginaryunit))) { if (DEBUG_ABS) { console.log(" abs: " + p1 + " is a sum"); @@ -1148,8 +2130,9 @@ if (DEBUG_ABS) { console.log("abs of a sum"); } + // sum push(p1); - rect(); + rect(); // convert polar terms, if any p1 = pop(); push(p1); real(); @@ -1173,6 +2156,7 @@ if (DEBUG_ABS) { console.log(" abs: " + p1 + " is -1 to any power"); } + // -1 to any power if (evaluatingAsFloats) { if (DEBUG_ABS) { console.log(" abs: numeric, so result is 1.0"); @@ -1190,6 +2174,7 @@ restore(); return; } + // abs(a^b) is equal to abs(a)^b IF b is positive if (car(p1) === symbol(POWER) && ispositivenumber(caddr(p1))) { if (DEBUG_ABS) { console.log(" abs: " + p1 + " is something to the power of a positive number"); @@ -1204,10 +2189,12 @@ restore(); return; } + // abs(e^something) if (car(p1) === symbol(POWER) && cadr(p1) === symbol(E)) { if (DEBUG_ABS) { console.log(" abs: " + p1 + " is an exponential"); } + // exponential push(caddr(p1)); real(); exponential(); @@ -1221,6 +2208,7 @@ if (DEBUG_ABS) { console.log(" abs: " + p1 + " is a product"); } + // product anyFactorsYet = false; p1 = cdr(p1); while (iscons(p1)) { @@ -1242,6 +2230,7 @@ if (DEBUG_ABS) { console.log(" abs: " + p1 + " is abs of a abs"); } + // abs of a abs push_symbol(ABS); push(cadr(p1)); list(2); @@ -1251,7 +2240,6 @@ restore(); return; } - /* * Evaluation via zzfloat() * ...while this is in theory a powerful mechanism, I've commented it @@ -1259,7 +2247,7 @@ * Evaling via zzfloat() is in principle more problematic because it could * require further evaluations which could end up in further "abs" which * would end up in infinite loops. Better not use it if not necessary. - + * we look directly at the float evaluation of the argument * to see if we end up with a number, which would mean that there * is no imaginary component and we can just return the input @@ -1267,14 +2255,14 @@ push p1 zzfloat() floatEvaluation = pop() - + if (isnegativenumber(floatEvaluation)) if DEBUG_ABS then console.log " abs: " + p1 + " just a negative" push(p1) negate() restore() return - + if (ispositivenumber(floatEvaluation)) if DEBUG_ABS then console.log " abs: " + p1 + " just a positive" push(p1) @@ -1304,6 +2292,7 @@ return restore(); }; + // also called the "norm" of a vector absval_tensor = function() { if (p1.tensor.ndim !== 1) { stop("abs(tensor) with tensor rank > 1"); @@ -1318,30 +2307,6 @@ return Eval(); }; - - /* - Symbolic addition - - Terms in a sum are combined if they are identical modulo rational - coefficients. - - For example, A + 2A becomes 3A. - - However, the sum A + sqrt(2) A is not modified. - - Combining terms can lead to second-order effects. - - For example, consider the case of - - 1/sqrt(2) A + 3/sqrt(2) A + sqrt(2) A - - The first two terms are combined to yield 2 sqrt(2) A. - - This result can now be combined with the third term to yield - - 3 sqrt(2) A - */ - flag = 0; Eval_add = function() { @@ -1358,6 +2323,7 @@ return add_terms(tos - h); }; + // Add n terms, returns one expression on the stack. stackAddsCount = 0; add_terms = function(n) { @@ -1366,11 +2332,14 @@ i = 0; h = tos - n; s = h; + // ensure no infinite loop, use "for" if (DEBUG) { console.log("stack before adding terms #" + stackAddsCount); } + //if stackAddsCount == 137 + // debugger if (DEBUG) { - for (i = o = 0, ref = tos; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = tos; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { console.log(print_list(stack[i])); } } @@ -1379,6 +2348,7 @@ break; } flag = 0; + //qsort(s, n, sizeof (U *), cmp_terms) subsetOfStack = stack.slice(h, h + n); subsetOfStack.sort(cmp_terms); stack = stack.slice(0, h).concat(subsetOfStack).concat(stack.slice(h + n)); @@ -1408,41 +2378,54 @@ if (DEBUG) { console.log("stack after adding terms #" + stackAddsCount); } + //if stackAddsCount == 5 + // debugger if (DEBUG) { results = []; - for (i = j1 = 0, ref1 = tos; 0 <= ref1 ? j1 < ref1 : j1 > ref1; i = 0 <= ref1 ? ++j1 : --j1) { + for (i = j1 = 0, ref1 = tos; (0 <= ref1 ? j1 < ref1 : j1 > ref1); i = 0 <= ref1 ? ++j1 : --j1) { results.push(console.log(print_list(stack[i]))); } return results; } }; + // Compare terms for order, clobbers p1 and p2. cmp_terms_count = 0; cmp_terms = function(p1, p2) { var i, o, ref, t; cmp_terms_count++; + //if cmp_terms_count == 52 + // debugger i = 0; + // numbers can be combined if (isNumericAtom(p1) && isNumericAtom(p2)) { flag = 1; + //if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns 0" return 0; } + // congruent tensors can be combined if (istensor(p1) && istensor(p2)) { if (p1.tensor.ndim < p2.tensor.ndim) { + //if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns -1" return -1; } if (p1.tensor.ndim > p2.tensor.ndim) { + //if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns 1" return 1; } - for (i = o = 0, ref = p1.tensor.ndim; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = p1.tensor.ndim; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { if (p1.tensor.dim[i] < p2.tensor.dim[i]) { + //if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns -1" return -1; } if (p1.tensor.dim[i] > p2.tensor.dim[i]) { + //if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns 1" return 1; } } flag = 1; + //if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns 0" return 0; } if (car(p1) === symbol(MULTIPLY)) { @@ -1463,24 +2446,25 @@ } } } - t = cmp_expr(p1, p2); - if (t === 0) { - flag = 1; - } - return t; - }; - - - /* - Compare adjacent terms in s[] and combine if possible. - - Returns the number of terms remaining in s[]. - - n number of terms in s[] initially - */ + t = cmp_expr(p1, p2); + if (t === 0) { + flag = 1; + } + //if DEBUG then console.log "cmp_terms #" + cmp_terms_count + " returns " + t + return t; + }; combine_terms = function(s, n) { var i, i1, j, j1, l1, m1, o, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, t; + //debugger + + // I had to turn the coffeescript for loop into + // a more mundane while loop because the i + // variable was changed from within the body, + // which is something that is not supposed to + // happen in the coffeescript 'vector' form. + // Also this means I had to add a 'i++' jus before + // the end of the body and before the "continue"s i = 0; while (i < (n - 1)) { check_esc_flag(); @@ -1493,7 +2477,7 @@ p1 = pop(); if (p1 !== symbol(NIL)) { stack[s + i] = p1; - for (j = o = ref = i + 1, ref1 = n - 1; ref <= ref1 ? o < ref1 : o > ref1; j = ref <= ref1 ? ++o : --o) { + for (j = o = ref = i + 1, ref1 = n - 1; (ref <= ref1 ? o < ref1 : o > ref1); j = ref <= ref1 ? ++o : --o) { stack[s + j] = stack[s + j + 1]; } n--; @@ -1512,13 +2496,13 @@ add_numbers(); p1 = pop(); if (isZeroAtomOrTensor(p1)) { - for (j = i1 = ref2 = i, ref3 = n - 2; ref2 <= ref3 ? i1 < ref3 : i1 > ref3; j = ref2 <= ref3 ? ++i1 : --i1) { + for (j = i1 = ref2 = i, ref3 = n - 2; (ref2 <= ref3 ? i1 < ref3 : i1 > ref3); j = ref2 <= ref3 ? ++i1 : --i1) { stack[s + j] = stack[s + j + 2]; } n -= 2; } else { stack[s + i] = p1; - for (j = j1 = ref4 = i + 1, ref5 = n - 1; ref4 <= ref5 ? j1 < ref5 : j1 > ref5; j = ref4 <= ref5 ? ++j1 : --j1) { + for (j = j1 = ref4 = i + 1, ref5 = n - 1; (ref4 <= ref5 ? j1 < ref5 : j1 > ref5); j = ref4 <= ref5 ? ++j1 : --j1) { stack[s + j] = stack[s + j + 1]; } n--; @@ -1570,7 +2554,7 @@ add_numbers(); p1 = pop(); if (isZeroAtomOrTensor(p1)) { - for (j = l1 = ref6 = i, ref7 = n - 2; ref6 <= ref7 ? l1 < ref7 : l1 > ref7; j = ref6 <= ref7 ? ++l1 : --l1) { + for (j = l1 = ref6 = i, ref7 = n - 2; (ref6 <= ref7 ? l1 < ref7 : l1 > ref7); j = ref6 <= ref7 ? ++l1 : --l1) { stack[s + j] = stack[s + j + 2]; } n -= 2; @@ -1588,11 +2572,12 @@ } multiply(); stack[s + i] = pop(); - for (j = m1 = ref8 = i + 1, ref9 = n - 1; ref8 <= ref9 ? m1 < ref9 : m1 > ref9; j = ref8 <= ref9 ? ++m1 : --m1) { + for (j = m1 = ref8 = i + 1, ref9 = n - 1; (ref8 <= ref9 ? m1 < ref9 : m1 > ref9); j = ref8 <= ref9 ? ++m1 : --m1) { stack[s + j] = stack[s + j + 1]; } n--; i--; + // this i++ is to match the while i++; } return n; @@ -1613,6 +2598,7 @@ } }; + // add two expressions add = function() { var h; save(); @@ -1631,7 +2617,7 @@ save(); s = tos - k; h = tos; - for (i = o = 0, ref = k; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = k; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push_terms(stack[s + i]); } add_terms(tos - h); @@ -1646,22 +2632,6 @@ return add(); }; - - /* adj ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - m - - General description - ------------------- - Returns the adjunct of matrix m. The inverse of m is equal to adj(m) divided by det(m). - */ - Eval_adj = function() { push(cadr(p1)); Eval(); @@ -1685,21 +2655,16 @@ p2.tensor.ndim = 2; p2.tensor.dim[0] = n; p2.tensor.dim[1] = n; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { - for (j = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { + for (j = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); j = 0 <= ref1 ? ++i1 : --i1) { cofactor(p1, n, i, j); - p2.tensor.elem[n * j + i] = pop(); + p2.tensor.elem[n * j + i] = pop(); // transpose } } push(p2); return restore(); }; - - /* - Guesses a rational for each float in the passed expression - */ - Eval_approxratio = function() { var theArgument; theArgument = cadr(p1); @@ -1715,10 +2680,10 @@ if (istensor(p1)) { p4 = alloc_tensor(p1.tensor.nelem); p4.tensor.ndim = p1.tensor.ndim; - for (i = o = 0, ref = p1.tensor.ndim; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = p1.tensor.ndim; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p4.tensor.dim[i] = p1.tensor.dim[i]; } - for (i = i1 = 0, ref1 = p1.tensor.nelem; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = p1.tensor.nelem; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { push(p1.tensor.elem[i]); approxratioRecursive(); p4.tensor.elem[i] = pop(); @@ -1757,11 +2722,22 @@ } return; } + // we didn't manage, just leave unexpressed push_symbol(APPROXRATIO); push(theArgument); return list(2); }; + // original routine by John Kennedy, see + // https://web.archive.org/web/20111027100847/http://homepage.smc.edu/kennedy_john/DEC2FRAC.PDF + // courtesy of Michael Borcherds + // who ported this to JavaScript under MIT licence + // also see + // https://github.com/geogebra/geogebra/blob/master/common/src/main/java/org/geogebra/common/kernel/algos/AlgoFractionText.java + // potential other ways to do this: + // https://rosettacode.org/wiki/Convert_decimal_number_to_rational + // http://www.homeschoolmath.net/teaching/rational_numbers.php + // http://stackoverflow.com/questions/95727/how-to-convert-floats-to-human-readable-fractions floatToRatioRoutine = function(decimal, AccuracyFactor) { var DecimalSign, FractionDenominator, FractionNumerator, PreviousDenominator, ScratchValue, Z, ret; FractionNumerator = void 0; @@ -1774,14 +2750,17 @@ if (isNaN(decimal)) { return ret; } + // return 0/0 if (decimal === 2e308) { ret[0] = 1; ret[1] = 0; + // 1/0 return ret; } if (decimal === -2e308) { ret[0] = -1; ret[1] = 0; + // -1/0 return ret; } if (decimal < 0.0) { @@ -1791,6 +2770,7 @@ } decimal = Math.abs(decimal); if (Math.abs(decimal - Math.floor(decimal)) < AccuracyFactor) { + // handles exact integers including 0 FractionNumerator = decimal * DecimalSign; FractionDenominator = 1.0; ret[0] = FractionNumerator; @@ -1798,6 +2778,7 @@ return ret; } if (decimal < 1.0e-19) { + // X = 0 already taken care of FractionNumerator = DecimalSign; FractionDenominator = 9999999999999999999.0; ret[0] = FractionNumerator; @@ -1820,6 +2801,7 @@ FractionDenominator = FractionDenominator * Math.floor(Z) + PreviousDenominator; PreviousDenominator = ScratchValue; FractionNumerator = Math.floor(decimal * FractionDenominator + 0.5); + // Rounding Function if (!(Math.abs(decimal - (FractionNumerator / FractionDenominator)) > AccuracyFactor && Z !== Math.floor(Z))) { break; } @@ -1860,27 +2842,34 @@ return ["" + Math.floor(theFloat), approx_just_an_integer, Math.floor(theFloat), 1, 2]; } console.log("precision: " + precision); + // simple radicals. bestResultSoFar = null; minimumComplexity = Number.MAX_VALUE; ref = [2, 3, 5, 6, 7, 8, 10]; for (o = 0, len = ref.length; o < len; o++) { i = ref[o]; for (j = i1 = 1; i1 <= 10; j = ++i1) { + //console.log "i,j: " + i + "," + j hypothesis = Math.sqrt(i) / j; + //console.log "hypothesis: " + hypothesis if (Math.abs(hypothesis) > 1e-10) { ratio = theFloat / hypothesis; likelyMultiplier = Math.round(ratio); + //console.log "ratio: " + ratio error = Math.abs(1 - ratio / likelyMultiplier); } else { ratio = 1; likelyMultiplier = 1; error = Math.abs(theFloat - hypothesis); } + //console.log "error: " + error if (error < 2 * precision) { complexity = simpleComplexityMeasure(likelyMultiplier, i, j); if (complexity < minimumComplexity) { + //console.log "MINIMUM MULTIPLIER SO FAR" minimumComplexity = complexity; result = likelyMultiplier + " * sqrt( " + i + " ) / " + j; + //console.log result + " error: " + error bestResultSoFar = [result, approx_ratioOfRadical, likelyMultiplier, i, j]; } } @@ -1899,29 +2888,39 @@ return ["" + Math.floor(theFloat), approx_just_an_integer, Math.floor(theFloat), 1, 2]; } console.log("precision: " + precision); + // simple radicals. bestResultSoFar = null; minimumComplexity = Number.MAX_VALUE; ref = [1, 2, 3, 5, 6, 7, 8, 10]; + // this one catches things like Math.sqrt(3/4), but + // things like Math.sqrt(1/2) are caught by the paragraph + // above (and in a better form) for (o = 0, len = ref.length; o < len; o++) { i = ref[o]; ref1 = [1, 2, 3, 5, 6, 7, 8, 10]; for (i1 = 0, len1 = ref1.length; i1 < len1; i1++) { j = ref1[i1]; + //console.log "i,j: " + i + "," + j hypothesis = Math.sqrt(i / j); + //console.log "hypothesis: " + hypothesis if (Math.abs(hypothesis) > 1e-10) { ratio = theFloat / hypothesis; likelyMultiplier = Math.round(ratio); + //console.log "ratio: " + ratio error = Math.abs(1 - ratio / likelyMultiplier); } else { ratio = 1; likelyMultiplier = 1; error = Math.abs(theFloat - hypothesis); } + //console.log "error: " + error if (error < 2 * precision) { complexity = simpleComplexityMeasure(likelyMultiplier, i, j); if (complexity < minimumComplexity) { + //console.log "MINIMUM MULTIPLIER SO FAR" minimumComplexity = complexity; result = likelyMultiplier + " * (sqrt( " + i + " / " + j + " )"; + //console.log result + " error: " + error bestResultSoFar = [result, approx_radicalOfRatio, likelyMultiplier, i, j]; } } @@ -1940,6 +2939,11 @@ return ["" + Math.floor(theFloat), approx_just_an_integer, Math.floor(theFloat), 1, 2]; } console.log("precision: " + precision); + // simple radicals. + + // we always prefer a rational of a radical of an integer + // to a radical of a rational. Radicals of rationals generate + // radicals at the denominator which we'd rather avoid approxRationalsOfRadicalsResult = approxRationalsOfRadicals(theFloat); if (approxRationalsOfRadicalsResult != null) { return approxRationalsOfRadicalsResult; @@ -1961,6 +2965,8 @@ return ["" + Math.floor(theFloat), approx_just_an_integer, Math.floor(theFloat), 1, 2]; } console.log("precision: " + precision); + // we always prefer a rational of a log to a log of + // a rational approxRationalsOfLogsResult = approxRationalsOfLogs(theFloat); if (approxRationalsOfLogsResult != null) { return approxRationalsOfLogsResult; @@ -1984,26 +2990,41 @@ console.log("precision: " + precision); bestResultSoFar = null; minimumComplexity = Number.MAX_VALUE; +// simple rationals of logs for (i = o = 2; o <= 5; i = ++o) { for (j = i1 = 1; i1 <= 5; j = ++i1) { + //console.log "i,j: " + i + "," + j hypothesis = Math.log(i) / j; + //console.log "hypothesis: " + hypothesis if (Math.abs(hypothesis) > 1e-10) { ratio = theFloat / hypothesis; likelyMultiplier = Math.round(ratio); + //console.log "ratio: " + ratio error = Math.abs(1 - ratio / likelyMultiplier); } else { ratio = 1; likelyMultiplier = 1; error = Math.abs(theFloat - hypothesis); } + //console.log "error: " + error + + // it does happen that due to roundings + // a "higher multiple" is picked, which is obviously + // unintended. + // E.g. 1 * log(1 / 3 ) doesn't match log( 3 ) BUT + // it matches -5 * log( 3 ) / 5 + // so we avoid any case where the multiplier is a multiple + // of the divisor. if (likelyMultiplier !== 1 && Math.abs(Math.floor(likelyMultiplier / j)) === Math.abs(likelyMultiplier / j)) { continue; } if (error < 2.2 * precision) { complexity = simpleComplexityMeasure(likelyMultiplier, i, j); if (complexity < minimumComplexity) { + //console.log "MINIMUM MULTIPLIER SO FAR" minimumComplexity = complexity; result = likelyMultiplier + " * log( " + i + " ) / " + j; + //console.log result + " error: " + error bestResultSoFar = [result, approx_rationalsOfLogarithms, likelyMultiplier, i, j]; } } @@ -2024,23 +3045,30 @@ console.log("precision: " + precision); bestResultSoFar = null; minimumComplexity = Number.MAX_VALUE; +// simple logs of rationals for (i = o = 1; o <= 5; i = ++o) { for (j = i1 = 1; i1 <= 5; j = ++i1) { + //console.log "i,j: " + i + "," + j hypothesis = Math.log(i / j); + //console.log "hypothesis: " + hypothesis if (Math.abs(hypothesis) > 1e-10) { ratio = theFloat / hypothesis; likelyMultiplier = Math.round(ratio); + //console.log "ratio: " + ratio error = Math.abs(1 - ratio / likelyMultiplier); } else { ratio = 1; likelyMultiplier = 1; error = Math.abs(theFloat - hypothesis); } + //console.log "error: " + error if (error < 1.96 * precision) { complexity = simpleComplexityMeasure(likelyMultiplier, i, j); if (complexity < minimumComplexity) { + //console.log "MINIMUM MULTIPLIER SO FAR" minimumComplexity = complexity; result = likelyMultiplier + " * log( " + i + " / " + j + " )"; + //console.log result + " error: " + error bestResultSoFar = [result, approx_logarithmsOfRationals, likelyMultiplier, i, j]; } } @@ -2061,23 +3089,30 @@ console.log("precision: " + precision); bestResultSoFar = null; minimumComplexity = Number.MAX_VALUE; +// simple rationals of a few powers of e for (i = o = 1; o <= 2; i = ++o) { for (j = i1 = 1; i1 <= 12; j = ++i1) { + //console.log "i,j: " + i + "," + j hypothesis = Math.pow(Math.E, i) / j; + //console.log "hypothesis: " + hypothesis if (Math.abs(hypothesis) > 1e-10) { ratio = theFloat / hypothesis; likelyMultiplier = Math.round(ratio); + //console.log "ratio: " + ratio error = Math.abs(1 - ratio / likelyMultiplier); } else { ratio = 1; likelyMultiplier = 1; error = Math.abs(theFloat - hypothesis); } + //console.log "error: " + error if (error < 2 * precision) { complexity = simpleComplexityMeasure(likelyMultiplier, i, j); if (complexity < minimumComplexity) { + //console.log "MINIMUM MULTIPLIER SO FAR" minimumComplexity = complexity; result = likelyMultiplier + " * (e ^ " + i + " ) / " + j; + //console.log result + " error: " + error bestResultSoFar = [result, approx_rationalOfE, likelyMultiplier, i, j]; } } @@ -2097,29 +3132,45 @@ } console.log("precision: " + precision); bestResultSoFar = null; + // here we do somethng a little special: since + // the powers of pi can get quite big, there might + // be multiple hypothesis where more of the + // magnitude is shifted to the multiplier, and some + // where more of the magnitude is shifted towards the + // exponent of pi. So we prefer the hypotheses with the + // lower multiplier since it's likely to insert more + // information. minimumComplexity = Number.MAX_VALUE; +// simple rationals of a few powers of PI for (i = o = 1; o <= 5; i = ++o) { for (j = i1 = 1; i1 <= 12; j = ++i1) { + //console.log "i,j: " + i + "," + j hypothesis = Math.pow(Math.PI, i) / j; + //console.log "hypothesis: " + hypothesis if (Math.abs(hypothesis) > 1e-10) { ratio = theFloat / hypothesis; likelyMultiplier = Math.round(ratio); + //console.log "ratio: " + ratio error = Math.abs(1 - ratio / likelyMultiplier); } else { ratio = 1; likelyMultiplier = 1; error = Math.abs(theFloat - hypothesis); } + //console.log "error: " + error if (error < 2 * precision) { complexity = simpleComplexityMeasure(likelyMultiplier, i, j); if (complexity < minimumComplexity) { + //console.log "MINIMUM MULTIPLIER SO FAR" minimumComplexity = complexity; result = likelyMultiplier + " * (pi ^ " + i + " ) / " + j + " )"; + //console.log result + " error: " + error bestResultSoFar = [result, approx_rationalOfPi, likelyMultiplier, i, j]; } } } } + //console.log "approxRationalsOfPowersOfPI returning: " + bestResultSoFar return bestResultSoFar; }; @@ -2133,6 +3184,7 @@ return ["" + Math.floor(theFloat), approx_just_an_integer, Math.floor(theFloat), 1, 2]; } console.log("precision: " + precision); + // we always prefer a sin of a rational without the PI approxSineOfRationalsResult = approxSineOfRationals(theFloat); if (approxSineOfRationalsResult != null) { return approxSineOfRationalsResult; @@ -2156,24 +3208,35 @@ console.log("precision: " + precision); bestResultSoFar = null; minimumComplexity = Number.MAX_VALUE; +// we only check very simple rationals because they begin to get tricky +// quickly, also they collide often with the "rational of pi" hypothesis. +// For example sin(11) is veeery close to 1 (-0.99999020655) +// (see: http://mathworld.wolfram.com/AlmostInteger.html ) +// we stop at rationals that mention up to 10 for (i = o = 1; o <= 4; i = ++o) { for (j = i1 = 1; i1 <= 4; j = ++i1) { + //console.log "i,j: " + i + "," + j fraction = i / j; hypothesis = Math.sin(fraction); + //console.log "hypothesis: " + hypothesis if (Math.abs(hypothesis) > 1e-10) { ratio = theFloat / hypothesis; likelyMultiplier = Math.round(ratio); + //console.log "ratio: " + ratio error = Math.abs(1 - ratio / likelyMultiplier); } else { ratio = 1; likelyMultiplier = 1; error = Math.abs(theFloat - hypothesis); } + //console.log "error: " + error if (error < 2 * precision) { complexity = simpleComplexityMeasure(likelyMultiplier, i, j); if (complexity < minimumComplexity) { + //console.log "MINIMUM MULTIPLIER SO FAR" minimumComplexity = complexity; result = likelyMultiplier + " * sin( " + i + "/" + j + " )"; + //console.log result + " error: " + error bestResultSoFar = [result, approx_sine_of_rational, likelyMultiplier, i, j]; } } @@ -2194,24 +3257,32 @@ console.log("precision: " + precision); bestResultSoFar = null; minimumComplexity = Number.MAX_VALUE; +// check rational multiples of pi for (i = o = 1; o <= 13; i = ++o) { for (j = i1 = 1; i1 <= 13; j = ++i1) { + //console.log "i,j: " + i + "," + j fraction = i / j; hypothesis = Math.sin(Math.PI * fraction); + //console.log "hypothesis: " + hypothesis if (Math.abs(hypothesis) > 1e-10) { ratio = theFloat / hypothesis; likelyMultiplier = Math.round(ratio); + //console.log "ratio: " + ratio error = Math.abs(1 - ratio / likelyMultiplier); } else { ratio = 1; likelyMultiplier = 1; error = Math.abs(theFloat - hypothesis); } + //console.log "error: " + error + // magic number 23 comes from the case sin(pi/10) if (error < 23 * precision) { complexity = simpleComplexityMeasure(likelyMultiplier, i, j); if (complexity < minimumComplexity) { + //console.log "MINIMUM MULTIPLIER SO FAR" minimumComplexity = complexity; result = likelyMultiplier + " * sin( " + i + "/" + j + " * pi )"; + //console.log result + " error: " + error bestResultSoFar = [result, approx_sine_of_pi_times_rational, likelyMultiplier, i, j]; } } @@ -2316,10 +3387,17 @@ var theSum; theSum = null; if (aResult instanceof Array) { + // we want PI and E to somewhat increase the + // complexity of the expression, so basically they count + // more than any integer lower than 3, i.e. we consider + // 1,2,3 to be more fundamental than PI or E. switch (aResult[1]) { case approx_sine_of_pi_times_rational: theSum = 4; break; + // exponents of PI and E need to be penalised as well + // otherwise they come to explain any big number + // so we count them just as much as the multiplier case approx_rationalOfPi: theSum = Math.pow(4, Math.abs(aResult[3])) * Math.abs(aResult[2]); break; @@ -2333,6 +3411,8 @@ } else { theSum += Math.abs(aResult) * (Math.abs(b) + Math.abs(c)); } + + // heavily discount unit constants if (aResult[2] === 1) { theSum -= 1; } else { @@ -2363,7 +3443,7 @@ for (i1 = 0, len1 = ref1.length; i1 < len1; i1++) { j = ref1[i1]; if (i === j) { - continue; + continue; // this is just 1 } console.log("testapproxRadicals testing: " + "1 * sqrt( " + i + " ) / " + j); fraction = i / j; @@ -2382,7 +3462,7 @@ for (l1 = 0, len3 = ref3.length; l1 < len3; l1++) { j = ref3[l1]; if (i === j) { - continue; + continue; // this is just 1 } console.log("testapproxRadicals testing with 4 digits: " + "1 * sqrt( " + i + " ) / " + j); fraction = i / j; @@ -2402,7 +3482,7 @@ for (n1 = 0, len5 = ref5.length; n1 < len5; n1++) { j = ref5[n1]; if (i === j) { - continue; + continue; // this is just 1 } console.log("testapproxRadicals testing: " + "1 * sqrt( " + i + " / " + j + " )"); fraction = i / j; @@ -2549,6 +3629,7 @@ } } } +// 5 digits create no problem for (i = v2 = 1; v2 <= 4; i = ++v2) { for (j = x2 = 1; x2 <= 4; j = ++x2) { console.log("testApproxAll testing with 5 digits: " + "1 * sin( " + i + "/" + j + " )"); @@ -2567,6 +3648,7 @@ } } } +// 4 digits create two collisions for (i = z2 = 1; z2 <= 4; i = ++z2) { for (j = i3 = 1; i3 <= 4; j = ++i3) { console.log("testApproxAll testing with 4 digits: " + "1 * sin( " + i + "/" + j + " )"); @@ -2637,6 +3719,8 @@ if (approxAll(value)[0] !== "1 * sqrt( 2 ) / 1") { console.log("fail testApproxAll: 1.41"); } + // if we narrow down to a particular family then we can get + // an OK guess even with few digits, expecially for really "famous" numbers value = 1.4; if (approxRadicals(value)[0] !== "1 * sqrt( 2 ) / 1") { console.log("fail approxRadicals: 1.4"); @@ -2729,10 +3813,12 @@ if (approxAll(value)[0] !== "1 * sin( 1/5 * pi )") { console.log("fail testApproxAll: Math.sqrt(10 - 2*Math.sqrt(5))/4"); } + // this has a radical form but it's too long to write value = Math.sin(Math.PI / 7); if (approxAll(value)[0] !== "1 * sin( 1/7 * pi )") { console.log("fail testApproxAll: Math.sin(Math.PI/7)"); } + // this has a radical form but it's too long to write value = Math.sin(Math.PI / 9); if (approxAll(value)[0] !== "1 * sin( 1/9 * pi )") { console.log("fail testApproxAll: Math.sin(Math.PI/9)"); @@ -2746,6 +3832,9 @@ console.log("approxTrigonometric testing: " + "1 * sin( " + i + "/" + j + " * pi )"); fraction = i / j; value = Math.sin(Math.PI * fraction); + // we specifically search for sines of rational multiples of PI + // because too many of them would be picked up as simple + // rationals. returned = approxTrigonometric(value); returnedFraction = returned[3] / returned[4]; returnedValue = returned[2] * Math.sin(Math.PI * returnedFraction); @@ -2756,6 +3845,10 @@ } for (i = l3 = 1; l3 <= 13; i = ++l3) { for (j = m3 = 1; m3 <= 13; j = ++m3) { + // with four digits, there are two collisions with the + // "simple fraction" argument hypotesis, which we prefer since + // it's a simpler expression, so let's skip those + // two tests if (i === 5 && j === 11 || i === 6 && j === 11) { continue; } @@ -2763,6 +3856,9 @@ fraction = i / j; originalValue = Math.sin(Math.PI * fraction); value = originalValue.toFixed(4); + // we specifically search for sines of rational multiples of PI + // because too many of them would be picked up as simple + // rationals. returned = approxTrigonometric(value); returnedFraction = returned[3] / returned[4]; returnedValue = returned[2] * Math.sin(Math.PI * returnedFraction); @@ -2783,22 +3879,6 @@ $.testApprox = testApprox; - - /* arccos ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - Returns the inverse cosine of x. - */ - Eval_arccos = function() { push(cadr(p1)); Eval(); @@ -2826,6 +3906,8 @@ restore(); return; } + // if p1 == 1/sqrt(2) then return 1/4*pi (45 degrees) + // second if catches the other way of saying it, sqrt(2)/2 if ((isoneoversqrttwo(p1)) || (car(p1) === symbol(MULTIPLY) && equalq(car(cdr(p1)), 1, 2) && car(car(cdr(cdr(p1)))) === symbol(POWER) && equaln(car(cdr(car(cdr(cdr(p1))))), 2) && equalq(car(cdr(cdr(car(cdr(cdr(p1)))))), 1, 2))) { if (evaluatingAsFloats) { push_double(Math.PI / 4.0); @@ -2837,6 +3919,8 @@ restore(); return; } + // if p1 == -1/sqrt(2) then return 3/4*pi (135 degrees) + // second if catches the other way of saying it, -sqrt(2)/2 if ((isminusoneoversqrttwo(p1)) || (car(p1) === symbol(MULTIPLY) && equalq(car(cdr(p1)), -1, 2) && car(car(cdr(cdr(p1)))) === symbol(POWER) && equaln(car(cdr(car(cdr(cdr(p1))))), 2) && equalq(car(cdr(cdr(car(cdr(cdr(p1)))))), 1, 2))) { if (evaluatingAsFloats) { push_double(Math.PI * 3.0 / 4.0); @@ -2909,22 +3993,6 @@ return restore(); }; - - /* arccosh ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - Returns the inverse hyperbolic cosine of x. - */ - Eval_arccosh = function() { push(cadr(p1)); Eval(); @@ -2962,22 +4030,6 @@ return restore(); }; - - /* arcsin ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - Returns the inverse sine of x. - */ - Eval_arcsin = function() { push(cadr(p1)); Eval(); @@ -3005,6 +4057,8 @@ restore(); return; } + // if p1 == 1/sqrt(2) then return 1/4*pi (45 degrees) + // second if catches the other way of saying it, sqrt(2)/2 if ((isoneoversqrttwo(p1)) || (car(p1) === symbol(MULTIPLY) && equalq(car(cdr(p1)), 1, 2) && car(car(cdr(cdr(p1)))) === symbol(POWER) && equaln(car(cdr(car(cdr(cdr(p1))))), 2) && equalq(car(cdr(cdr(car(cdr(cdr(p1)))))), 1, 2))) { push_rational(1, 4); push_symbol(PI); @@ -3012,6 +4066,8 @@ restore(); return; } + // if p1 == -1/sqrt(2) then return -1/4*pi (-45 degrees) + // second if catches the other way of saying it, -sqrt(2)/2 if ((isminusoneoversqrttwo(p1)) || (car(p1) === symbol(MULTIPLY) && equalq(car(cdr(p1)), -1, 2) && car(car(cdr(cdr(p1)))) === symbol(POWER) && equaln(car(cdr(car(cdr(cdr(p1))))), 2) && equalq(car(cdr(cdr(car(cdr(cdr(p1)))))), 1, 2))) { if (evaluatingAsFloats) { push_double(-Math.PI / 4.0); @@ -3086,22 +4142,6 @@ return restore(); }; - - /* arcsinh ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - Returns the inverse hyperbolic sine of x. - */ - Eval_arcsinh = function() { push(cadr(p1)); Eval(); @@ -3136,22 +4176,6 @@ return restore(); }; - - /* arctan ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - Returns the inverse tangent of x. - */ - Eval_arctan = function() { push(cadr(p1)); Eval(); @@ -3191,6 +4215,7 @@ restore(); return; } + // arctan(sin(a) / cos(a)) ? if (Find(p1, symbol(SIN)) && Find(p1, symbol(COS))) { push(p1); numerator(); @@ -3204,6 +4229,8 @@ return; } } + // arctan(1/sqrt(3)) -> pi/6 + // second if catches the other way of saying it, sqrt(3)/3 if ((car(p1) === symbol(POWER) && equaln(cadr(p1), 3) && equalq(caddr(p1), -1, 2)) || (car(p1) === symbol(MULTIPLY) && equalq(car(cdr(p1)), 1, 3) && car(car(cdr(cdr(p1)))) === symbol(POWER) && equaln(car(cdr(car(cdr(cdr(p1))))), 3) && equalq(car(cdr(cdr(car(cdr(cdr(p1)))))), 1, 2))) { push_rational(1, 6); if (evaluatingAsFloats) { @@ -3215,6 +4242,7 @@ restore(); return; } + // arctan(1) -> pi/4 if (equaln(p1, 1)) { push_rational(1, 4); if (evaluatingAsFloats) { @@ -3226,6 +4254,7 @@ restore(); return; } + // arctan(sqrt(3)) -> pi/3 if (car(p1) === symbol(POWER) && equaln(cadr(p1), 3) && equalq(caddr(p1), 1, 2)) { push_rational(1, 3); if (evaluatingAsFloats) { @@ -3243,22 +4272,6 @@ return restore(); }; - - /* arctanh ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - Returns the inverse hyperbolic tangent of x. - */ - Eval_arctanh = function() { push(cadr(p1)); Eval(); @@ -3296,72 +4309,6 @@ return restore(); }; - - /* arg ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - z - - General description - ------------------- - Returns the angle of complex z. - */ - - - /* - Argument (angle) of complex z - - z arg(z) - - ------ - - a 0 - - -a -pi See note 3 below - - (-1)^a a pi - - exp(a + i b) b - - a b arg(a) + arg(b) - - a + i b arctan(b/a) - - Result by quadrant - - z arg(z) - - ------ - - 1 + i 1/4 pi - - 1 - i -1/4 pi - - -1 + i 3/4 pi - - -1 - i -3/4 pi - - Notes - - 1. Handles mixed polar and rectangular forms, e.g. 1 + exp(i pi/3) - - 2. Symbols in z are assumed to be positive and real. - - 3. Negative direction adds -pi to angle. - - Example: z = (-1)^(1/3), abs(z) = 1/3 pi, abs(-z) = -2/3 pi - - 4. jean-francois.debroux reports that when z=(a+i*b)/(c+i*d) then - - arg(numerator(z)) - arg(denominator(z)) - - must be used to get the correct answer. Now the operation is - automatic. - */ - DEBUG_ARG = false; Eval_arg = function() { @@ -3383,9 +4330,12 @@ return restore(); }; + //define RE p2 + //define IM p3 yyarg = function() { save(); p1 = pop(); + // case of plain number if (ispositivenumber(p1) || p1 === symbol(PI)) { if (isdouble(p1) || evaluatingAsFloats) { push_double(0); @@ -3399,11 +4349,16 @@ push(symbol(PI)); } negate(); + // you'd think that something like + // arg(a) is always 0 when a is real but no, + // arg(a) is pi when a is negative so we have + // to leave unexpressed } else if (issymbol(p1)) { push_symbol(ARG); push(p1); list(2); } else if (car(p1) === symbol(POWER) && equaln(cadr(p1), -1)) { + // -1 to a power if (evaluatingAsFloats) { push_double(Math.PI); } else { @@ -3412,8 +4367,11 @@ push(caddr(p1)); multiply(); } else if (car(p1) === symbol(POWER) && cadr(p1) === symbol(E)) { + // exponential push(caddr(p1)); imag(); + // arg(a^(1/2)) is always equal to 1/2 * arg(a) + // this can obviously be made more generic TODO } else if (car(p1) === symbol(POWER) && isoneovertwo(caddr(p1))) { if (DEBUG_ARG) { console.log("arg of a sqrt: " + p1); @@ -3429,6 +4387,7 @@ push(caddr(p1)); multiply(); } else if (car(p1) === symbol(MULTIPLY)) { + // product of factors push_integer(0); p1 = cdr(p1); while (iscons(p1)) { @@ -3438,6 +4397,7 @@ p1 = cdr(p1); } } else if (car(p1) === symbol(ADD)) { + // sum of terms push(p1); rect(); p1 = pop(); @@ -3468,16 +4428,19 @@ push_symbol(PI); } if (isnegative(p3)) { - subtract(); + subtract(); // quadrant 1 -> 3 } else { - add(); + add(); // quadrant 4 -> 2 } } } } else { if (!isZeroAtomOrTensor(get_binding(symbol(ASSUME_REAL_VARIABLES)))) { + // if we assume all passed values are real push_integer(0); } else { + // if we don't assume all passed values are real, all + // we con do is to leave unexpressed push_symbol(ARG); push(p1); list(2); @@ -3486,6 +4449,7 @@ return restore(); }; + // pretty print bake = function() { var h, s, t, x, y, z; h = 0; @@ -3517,6 +4481,10 @@ } else if (s === 0 && t === 0 && x === 0 && y === 0 && z === 1) { p2 = symbol(SYMBOL_Z); bake_poly(); + // don't bake the contents of some constructs such as "for" + // because we don't want to evaluate the body of + // such constructs "statically", i.e. without fully running + // the loops. } else if ((iscons(p1)) && car(p1) !== symbol(FOR)) { h = tos; push(car(p1)); @@ -3565,6 +4533,7 @@ i = 0; k = 0; n = 0; + //U **a a = tos; push(p1); push(p2); @@ -3586,6 +4555,9 @@ return push(p1); }; + // p1 points to coefficient of p2 ^ k + + // k is an int bake_poly_term = function(k) { var h, n; h = 0; @@ -3593,6 +4565,7 @@ if (isZeroAtomOrTensor(p1)) { return; } + // constant term? if (k === 0) { if (car(p1) === symbol(ADD)) { p1 = cdr(p1); @@ -3606,6 +4579,7 @@ return; } h = tos; + // coefficient if (car(p1) === symbol(MULTIPLY)) { p1 = cdr(p1); while (iscons(p1)) { @@ -3615,6 +4589,7 @@ } else if (!equaln(p1, 1)) { push(p1); } + // x ^ k if (k === 1) { push(p2); } else { @@ -3632,45 +4607,6 @@ } }; - - /* besselj ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x,n - - General description - ------------------- - - Returns a solution to the Bessel differential equation (Bessel function of first kind). - - Recurrence relation: - - besselj(x,n) = (2/x) (n-1) besselj(x,n-1) - besselj(x,n-2) - - besselj(x,1/2) = sqrt(2/pi/x) sin(x) - - besselj(x,-1/2) = sqrt(2/pi/x) cos(x) - - For negative n, reorder the recurrence relation as: - - besselj(x,n-2) = (2/x) (n-1) besselj(x,n-1) - besselj(x,n) - - Substitute n+2 for n to obtain - - besselj(x,n) = (2/x) (n+1) besselj(x,n+1) - besselj(x,n+2) - - Examples: - - besselj(x,3/2) = (1/x) besselj(x,1/2) - besselj(x,-1/2) - - besselj(x,-3/2) = -(1/x) besselj(x,-1/2) - besselj(x,1/2) - */ - Eval_besselj = function() { push(cadr(p1)); Eval(); @@ -3685,6 +4621,9 @@ return restore(); }; + //define X p1 + //define N p2 + //define SGN p3 yybesselj = function() { var d, n; d = 0.0; @@ -3693,20 +4632,25 @@ p1 = pop(); push(p2); n = pop_integer(); + // numerical result if (isdouble(p1) && !isNaN(n)) { d = jn(n, p1.d); push_double(d); return; } + // bessej(0,0) = 1 if (isZeroAtomOrTensor(p1) && isZeroAtomOrTensor(p2)) { push_integer(1); return; } + // besselj(0,n) = 0 if (isZeroAtomOrTensor(p1) && !isNaN(n)) { push_integer(0); return; } + // half arguments if (p2.k === NUM && MEQUAL(p2.q.b, 2)) { + // n = 1/2 if (MEQUAL(p2.q.a, 1)) { if (evaluatingAsFloats) { push_double(2.0 / Math.PI); @@ -3724,6 +4668,7 @@ multiply(); return; } + // n = -1/2 if (MEQUAL(p2.q.a, -1)) { if (evaluatingAsFloats) { push_double(2.0 / Math.PI); @@ -3741,6 +4686,7 @@ multiply(); return; } + // besselj(x,n) = (2/x) (n-sgn(n)) besselj(x,n-sgn(n)) - besselj(x,n-2*sgn(n)) push_integer(MSIGN(p2.q.a)); p3 = pop(); push_integer(2); @@ -3766,6 +4712,7 @@ subtract(); return; } + //if 0 # test cases needed if (isnegativeterm(p1)) { push(p1); negate(); @@ -3796,28 +4743,12 @@ multiply(); return; } + //endif push(symbol(BESSELJ)); push(p1); push(p2); - return list(3); - }; - - - /* bessely ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x,n - - General description - ------------------- - - Bessel function of second kind. - */ + return list(3); + }; Eval_bessely = function() { push(cadr(p1)); @@ -3833,6 +4764,8 @@ return restore(); }; + //define X p1 + //define N p2 yybessely = function() { var d, n; d = 0.0; @@ -3864,6 +4797,9 @@ list(3); }; + //double convert_rational_to_double(U *) + //double convert_bignum_to_double(unsigned int *) + //int ge(unsigned int *, unsigned int *, int) mint = function(a) { return bigInt(a); }; @@ -3872,12 +4808,14 @@ return a.geq(Number.MIN_SAFE_INTEGER) && a.leq(Number.MAX_SAFE_INTEGER); }; + // b is +1 or -1, a is a bigint setSignTo = function(a, b) { if (a.isPositive()) { if (b < 0) { return a.multiply(bigInt(-1)); } } else { + // a is negative if (b > 0) { return a.multiply(bigInt(-1)); } @@ -3891,6 +4829,7 @@ return a.multiply(bigInt(-1)); } } else { + // a is negative if (b.isPositive()) { return a.multiply(bigInt(-1)); } @@ -3905,12 +4844,12 @@ return a; }; - + // n is an int /* mtotal = 0 MP_MIN_SIZE = 2 MP_MAX_FREE = 1000 - + mnew = (n) -> if (n < MP_MIN_SIZE) n = MP_MIN_SIZE @@ -3919,16 +4858,16 @@ else p = [] #(unsigned int *) malloc((n + 3) * sizeof (int)) #if (p == 0) - * stop("malloc failure") + * stop("malloc failure") p[0] = n mtotal += n return p[3] */ - - + // p is the index of array of ints + // !!! array wasn't passed here /* free_stack = [] - + mfree = (array, p) -> p -= 3 mtotal -= array[p] @@ -3936,44 +4875,44 @@ free_stack[mfreecount++] = p else free(p) - */ - + */ + // convert int to bignum + // n is an int /* mint = (n) -> p = mnew(1) if (n < 0) - * !!! this is FU - * MSIGN(p) = -1 + * !!! this is FU + * MSIGN(p) = -1 fu = true else - * !!! this is FU + * !!! this is FU #MSIGN(p) = 1 fu = true - * !!! this is FU + * !!! this is FU #MLENGTH(p) = 1 p[0] = Math.abs(n) return p */ + // copy bignum - + // a is an array of ints /* mcopy = (a) -> #unsigned int *b - + b = mnew(MLENGTH(a)) - - * !!! fu + + * !!! fu #MSIGN(b) = MSIGN(a) #MLENGTH(b) = MLENGTH(a) - + for i in [0...MLENGTH(a)] b[i] = a[i] - + return b */ - - /* * * ge not invoked from anywhere - is you need ge @@ -3994,11 +4933,11 @@ else return 0 */ - add_numbers = function() { var a, b, theResult; a = 1.0; b = 1.0; + //if DEBUG then console.log("add_numbers adding numbers: " + print_list(stack[tos - 1]) + " and " + print_list(stack[tos - 2])) if (isrational(stack[tos - 1]) && isrational(stack[tos - 2])) { qadd(); return; @@ -4101,6 +5040,7 @@ invert_number = function() { var a, b; + //unsigned int *a, *b save(); p1 = pop(); if (isZeroAtomOrTensor(p1)) { @@ -4123,15 +5063,18 @@ return restore(); }; + // a and b are Us compare_rationals = function(a, b) { var ab, ba, t; t = 0; + //unsigned int *ab, *ba ab = mmul(a.q.a, b.q.b); ba = mmul(a.q.b, b.q.a); t = mcmp(ab, ba); return t; }; + // a and b are Us compare_numbers = function(a, b) { var x, y; x = 0.0; @@ -4185,6 +5128,7 @@ bignum_truncate = function() { var a; + //unsigned int *a save(); p1 = pop(); a = mdiv(p1.q.a, p1.q.b); @@ -4228,13 +5172,16 @@ return restore(); }; + // expo is an integer bignum_power_number = function(expo) { var a, b, t; + //unsigned int *a, *b, *t save(); p1 = pop(); a = mpow(p1.q.a, Math.abs(expo)); b = mpow(p1.q.b, Math.abs(expo)); if (expo < 0) { + // swap a and b t = a; a = b; b = t; @@ -4249,10 +5196,12 @@ return restore(); }; + // p an array of ints convert_bignum_to_double = function(p) { return p.toJSNumber(); }; + // p is a U convert_rational_to_double = function(p) { var quotientAndRemainder, result; if (p.q == null) { @@ -4263,6 +5212,7 @@ return result; }; + // n an integer push_integer = function(n) { if (DEBUG) { console.log("pushing integer " + n); @@ -4276,6 +5226,7 @@ return restore(); }; + // d a double push_double = function(d) { save(); p1 = new U(); @@ -4285,8 +5236,8 @@ return restore(); }; + // a,b parts of a rational push_rational = function(a, b) { - /* save() p1 = new U() @@ -4331,6 +5282,7 @@ return n; }; + // p is a U, flag is an int print_double = function(p, flag) { var accumulator, buf; accumulator = ""; @@ -4343,14 +5295,18 @@ return accumulator; }; + // s is a string bignum_scan_integer = function(s) { var a, scounter, sign_; + //unsigned int *a + //char sign save(); scounter = 0; sign_ = s[scounter]; if (sign_ === '+' || sign_ === '-') { scounter++; } + // !!!! some mess in here, added an argument a = bigInt(s.substring(scounter)); p1 = new U(); p1.k = NUM; @@ -4363,10 +5319,18 @@ return restore(); }; + // s a string bignum_scan_float = function(s) { return push_double(parseFloat(s)); }; + // gives the capability of printing the unsigned + // value. This is handy because printing of the sign + // might be taken care of "upstream" + // e.g. when printing a base elevated to a negative exponent + // prints the inverse of the base powered to the unsigned + // exponent. + // p is a U print_number = function(p, signed) { var aAsString, accumulator, buf, denominatorString; accumulator = ""; @@ -4411,6 +5375,8 @@ save(); p2 = pop(); p1 = pop(); + // if (!isinteger(p1) || !isinteger(p2)) + // stop("integer args expected for gcd") p3 = new U(); p3.k = NUM; p3.q.a = mgcd(p1.q.a, p2.q.a); @@ -4446,6 +5412,9 @@ return push_double(d); }; + //static unsigned int *__factorial(int) + + // n is an int bignum_factorial = function(n) { save(); p1 = new U(); @@ -4456,9 +5425,11 @@ return restore(); }; + // n is an int __factorial = function(n) { var a, b, i, o, ref, t; i = 0; + //unsigned int *a, *b, *t if (n === 0 || n === 1) { a = bigInt(1); return a; @@ -4466,7 +5437,7 @@ a = bigInt(2); b = bigInt(0); if (3 <= n) { - for (i = o = 3, ref = n; 3 <= ref ? o <= ref : o >= ref; i = 3 <= ref ? ++o : --o) { + for (i = o = 3, ref = n; (3 <= ref ? o <= ref : o >= ref); i = 3 <= ref ? ++o : --o) { b = bigInt(i); t = mmul(a, b); a = t; @@ -4477,22 +5448,36 @@ mask = [0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000, 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000, 0x00800000, 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000]; + // unsigned int *x, unsigned int k mp_set_bit = function(x, k) { console.log("not implemented yet"); debugger; return x[k / 32] |= mask[k % 32]; }; + // unsigned int *x, unsigned int k mp_clr_bit = function(x, k) { console.log("not implemented yet"); debugger; return x[k / 32] &= ~mask[k % 32]; }; + // unsigned int *a mshiftright = function(a) { return a = a.shiftRight(); }; + // Binomial coefficient + + // Input: tos-2 n + + // tos-1 k + + // Output: Binomial coefficient on stack + + // binomial(n, k) = n! / k! / (n - k)! + + // The binomial coefficient vanishes for k < 0 or k > n. (A=B, p. 19) Eval_binomial = function() { push(cadr(p1)); Eval(); @@ -4507,6 +5492,8 @@ return restore(); }; + //define N p1 + //define K p2 ybinomial = function() { p2 = pop(); p1 = pop(); @@ -4538,23 +5525,6 @@ } }; - - /* ceiling ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - - Returns the smallest integer not less than x. - */ - Eval_ceiling = function() { push(cadr(p1)); Eval(); @@ -4599,31 +5569,6 @@ } }; - - /* choose ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - n,k - - General description - ------------------- - - Returns the number of combinations of n items taken k at a time. - - For example, the number of five card hands is choose(52,5) - - ``` - n! - choose(n,k) = ------------- - k! (n - k)! - ``` - */ - Eval_choose = function() { push(cadr(p1)); Eval(); @@ -4632,6 +5577,10 @@ return choose(); }; + // Result vanishes for k < 0 or k > n. (A=B, p. 19) + + //define N p1 + //define K p2 choose = function() { save(); p2 = pop(); @@ -4666,27 +5615,11 @@ } }; - - /* circexp ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - - Returns expression x with circular and hyperbolic functions converted to exponential forms. Sometimes this will simplify an expression. - */ - Eval_circexp = function() { push(cadr(p1)); Eval(); circexp(); + // normalize return Eval(); }; @@ -4791,7 +5724,7 @@ push(p1); copy_tensor(); p1 = pop(); - for (i = o = 0, ref = p1.tensor.nelem; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = p1.tensor.nelem; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push(p1.tensor.elem[i]); circexp(); p1.tensor.elem[i] = pop(); @@ -4804,20 +5737,6 @@ return restore(); }; - - /* clearall ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - - General description - ------------------- - - Completely wipes all variables from the environment. - */ - Eval_clearall = function() { do_clearall(); return push(symbol(NIL)); @@ -4833,15 +5752,19 @@ return codeGen = false; }; + // clearall from application GUI code clearall = function() { return run("clearall"); }; + // this transformation is done in run.coffee, see there + // for more info. clearRenamedVariablesToAvoidBindingToExternalScope = function() { var i, o, ref, results; results = []; - for (i = o = 0, ref = symtab.length; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = symtab.length; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { if (symtab[i].printname.indexOf("AVOID_BINDING_TO_EXTERNAL_SCOPE_VALUE") !== -1) { + // just clear it symtab[i].k = SYM; symtab[i].printname = ""; binding[i] = symtab[i]; @@ -4853,31 +5776,18 @@ return results; }; - - /* clear ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - - Completely wipes a variable from the environment (while doing x = quote(x) just unassigns it). - */ - Eval_clear = function() { var indexFound, variableToBeCleared; p2 = cdr(p1); while (iscons(p2)) { variableToBeCleared = car(p2); + //console.log variableToBeCleared + "" if (variableToBeCleared.k !== SYM) { stop("symbol error"); } + //console.log "getting binding of " + p.toString() + //if p.toString() == "aaa" + // debugger indexFound = symtab.indexOf(variableToBeCleared); symtab[indexFound].k = SYM; symtab[indexFound].printname = ""; @@ -4888,19 +5798,26 @@ return push(symbol(NIL)); }; - /* Convert complex z to clock form - + Input: push z - + Output: Result on stack - + clock(z) = abs(z) * (-1) ^ (arg(z) / pi) - - For example, clock(exp(i pi/3)) gives the result (-1)^(1/3) - */ + For example, clock(exp(i pi/3)) gives the result (-1)^(1/3) + */ + // P.S. I couldn't find independent definition/aknowledgment + // of the naming "clock form" anywhere on the web, seems like a + // naming specific to eigenmath. + // Clock form is another way to express a complex number, and + // it has three advantages + // 1) it's uniform with how for example + // i is expressed i.e. (-1)^(1/2) + // 2) it's very compact + // 3) it's a straighforward notation for roots of 1 and -1 DEBUG_CLOCKFORM = false; Eval_clock = function() { @@ -4911,12 +5828,17 @@ clockform = function() { save(); + //if 1 p1 = pop(); push(p1); abs(); if (DEBUG_CLOCKFORM) { console.log("clockform: abs of " + p1 + " : " + stack[tos - 1]); } + // pushing the expression (-1)^... but note + // that we can't use "power", as "power" evaluates + // clock forms into rectangular form (see "-1 ^ rational" + // section in power) push_symbol(POWER); push_integer(-1); push(p1); @@ -4941,7 +5863,7 @@ if (DEBUG_CLOCKFORM) { console.log("clockform: multiply : " + stack[tos - 1]); } - + //else /* p1 = pop() push(p1) @@ -4953,26 +5875,29 @@ multiply() power() multiply() - */ + */ + //endif return restore(); }; - /* coeff ===================================================================== - + Tags ---- scripting, JS, internal, treenode, general concept - + Parameters ---------- p,x,n - + General description ------------------- Returns the coefficient of x^n in polynomial p. The x argument can be omitted for polynomials in x. - */ + */ + //define P p1 + //define X p2 + //define N p3 Eval_coeff = function() { push(cadr(p1)); Eval(); @@ -4983,9 +5908,9 @@ p3 = pop(); p2 = pop(); p1 = pop(); - if (p3 === symbol(NIL)) { + if (p3 === symbol(NIL)) { // p3 is N # only 2 args? p3 = p2; - p2 = symbol(SYMBOL_X); + p2 = symbol(SYMBOL_X); // p2 is X } push(p1); push(p2); @@ -4996,6 +5921,21 @@ return filter(); }; + //----------------------------------------------------------------------------- + + // Put polynomial coefficients on the stack + + // Input: tos-2 p(x) (the polynomial) + + // tos-1 x (the variable) + + // Output: Returns number of coefficients on stack + + // tos-n Coefficient of x^0 + + // tos-1 Coefficient of x^(n-1) + + //----------------------------------------------------------------------------- coeff = function() { var h, n, prev_expanding; save(); @@ -5025,28 +5965,11 @@ expanding = 1; divide(); expanding = prev_expanding; + //console.log("just divided: " + stack[tos-1].toString()) p1 = pop(); } }; - - /* cofactor ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - m,i,j - - General description - ------------------- - Cofactor of a matrix component. - Let c be the cofactor matrix of matrix m, i.e. tranpose(c) = adj(m). - This function returns c[i,j]. - */ - Eval_cofactor = function() { var doNothing, i, j, n; i = 0; @@ -5080,8 +6003,8 @@ var i, i1, j, o, ref, ref1; i = 0; j = 0; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { - for (j = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { + for (j = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); j = 0 <= ref1 ? ++i1 : --i1) { if (i !== row && j !== col) { push(p.tensor.elem[n * i + j]); } @@ -5093,6 +6016,7 @@ } }; + // Condense an expression by factoring common terms. Eval_condense = function() { push(cadr(p1)); Eval(); @@ -5110,19 +6034,26 @@ }; yycondense = function() { + //expanding = 0 p1 = pop(); if (car(p1) !== symbol(ADD)) { push(p1); return; } + // get gcd of all terms p3 = cdr(p1); push(car(p3)); p3 = cdr(p3); while (iscons(p3)) { push(car(p3)); + //console.log "calculating gcd between: " + stack[tos - 1] + " and " + stack[tos - 2] gcd(); + //console.log "partial gcd: " + stack[tos - 1] p3 = cdr(p3); } + //console.log "condense: this is the gcd of all the terms: " + stack[tos - 1] + + // divide each term by gcd inverse(); p2 = pop(); push(zero); @@ -5130,37 +6061,27 @@ while (iscons(p3)) { push(p2); push(car(p3)); + //multiply() multiply_noexpand(); add(); p3 = cdr(p3); } + // We multiplied above w/o expanding so some factors cancelled. + + // Now we expand which normalizes the result and, in some cases, + // simplifies it too (see test case H). yyexpand(); + // multiply result by gcd push(p2); return divide(); }; - - /* conj ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - z - - General description - ------------------- - Returns the complex conjugate of z. - */ - Eval_conj = function() { push(cadr(p1)); Eval(); p1 = pop(); push(p1); - if (!Find(p1, imaginaryunit)) { + if (!Find(p1, imaginaryunit)) { // example: (-1)^(1/3) polar(); conjugate(); return clockform(); @@ -5169,6 +6090,8 @@ } }; + // careful is you pass this one an expression with + // i (instead of (-1)^(1/2)) then this doesn't work! conjugate = function() { push(imaginaryunit); push(imaginaryunit); @@ -5177,6 +6100,7 @@ return Eval(); }; + // Cons two things on the stack. consCount = 0; cons = function() { @@ -5185,6 +6109,9 @@ if (DEBUG) { console.log("cons tos: " + tos + " # " + consCount); } + //if consCount == 444 + // debugger + // auto var ok, no opportunity for garbage collection after p = alloc() p = new U(); p.k = CONS; p.cons.cdr = pop(); @@ -5193,34 +6120,15 @@ console.log("something wrong p == its cdr"); } p.cons.car = pop(); - /* console.log "cons new cdr.k = " + p.cons.cdr.k + "\nor more in detail:" console.log print_list p.cons.cdr console.log "cons new car.k = " + p.cons.car.k + "\nor more in detail:" console.log print_list p.cons.car - */ + */ return push(p); }; - - /* contract ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - a,i,j - - General description - ------------------- - Contract across tensor indices i.e. returns "a" summed over indices i and j. - If i and j are omitted then 1 and 2 are used. - contract(m) is equivalent to the trace of matrix m. - */ - Eval_contract = function() { push(cadr(p1)); Eval(); @@ -5276,40 +6184,48 @@ l--; m--; n = p1.tensor.dim[l]; + // nelem is the number of elements in "b" nelem = 1; - for (i = o = 0, ref = ndim; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = ndim; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { if (i !== l && i !== m) { nelem *= p1.tensor.dim[i]; } } + //console.log "nelem:" + nelem p2 = alloc_tensor(nelem); + //console.log "p2:" + p2 p2.tensor.ndim = ndim - 2; j = 0; - for (i = i1 = 0, ref1 = ndim; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = ndim; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { if (i !== l && i !== m) { p2.tensor.dim[j++] = p1.tensor.dim[i]; } } a = p1.tensor.elem; b = p2.tensor.elem; - for (i = j1 = 0, ref2 = ndim; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { +//console.log "a: " + a +//console.log "b: " + b + for (i = j1 = 0, ref2 = ndim; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { ai[i] = 0; an[i] = p1.tensor.dim[i]; } - for (i = l1 = 0, ref3 = nelem; 0 <= ref3 ? l1 < ref3 : l1 > ref3; i = 0 <= ref3 ? ++l1 : --l1) { + for (i = l1 = 0, ref3 = nelem; (0 <= ref3 ? l1 < ref3 : l1 > ref3); i = 0 <= ref3 ? ++l1 : --l1) { push(zero); - for (j = m1 = 0, ref4 = n; 0 <= ref4 ? m1 < ref4 : m1 > ref4; j = 0 <= ref4 ? ++m1 : --m1) { + for (j = m1 = 0, ref4 = n; (0 <= ref4 ? m1 < ref4 : m1 > ref4); j = 0 <= ref4 ? ++m1 : --m1) { ai[l] = j; ai[m] = j; h = 0; - for (k = n1 = 0, ref5 = ndim; 0 <= ref5 ? n1 < ref5 : n1 > ref5; k = 0 <= ref5 ? ++n1 : --n1) { + for (k = n1 = 0, ref5 = ndim; (0 <= ref5 ? n1 < ref5 : n1 > ref5); k = 0 <= ref5 ? ++n1 : --n1) { h = (h * an[k]) + ai[k]; } push(a[h]); + //console.log "a[h]: " + a[h] add(); } + //console.log "tos: " + stack[tos-1] b[i] = pop(); - for (j = o1 = ref6 = ndim - 1; ref6 <= 0 ? o1 <= 0 : o1 >= 0; j = ref6 <= 0 ? ++o1 : --o1) { +//console.log "b[i]: " + b[i] + for (j = o1 = ref6 = ndim - 1; (ref6 <= 0 ? o1 <= 0 : o1 >= 0); j = ref6 <= 0 ? ++o1 : --o1) { if (j === l || j === m) { continue; } @@ -5326,22 +6242,22 @@ } }; - + //console.log "returning: " + stack[tos-1] /* cos ===================================================================== - + Tags ---- scripting, JS, internal, treenode, general concept - + Parameters ---------- x - + General description ------------------- Returns the cosine of x. - */ + */ Eval_cos = function() { push(cadr(p1)); Eval(); @@ -5359,11 +6275,15 @@ return restore(); }; + // Use angle sum formula for special angles. + + //define A p3 + //define B p4 cosine_of_angle_sum = function() { p2 = cdr(p1); while (iscons(p2)) { p4 = car(p2); - if (isnpi(p4)) { + if (isnpi(p4)) { // p4 is B push(p1); push(p4); subtract(); @@ -5400,11 +6320,15 @@ push_double(d); return; } + // cosine function is symmetric, cos(-x) = cos(x) if (isnegative(p1)) { push(p1); negate(); p1 = pop(); } + // cos(arctan(x)) = 1 / sqrt(1 + x^2) + + // see p. 173 of the CRC Handbook of Mathematical Sciences if (car(p1) === symbol(ARCTAN)) { push_integer(1); push(cadr(p1)); @@ -5415,6 +6339,15 @@ power(); return; } + // multiply by 180/pi to go from radians to degrees. + // we go from radians to degrees because it's much + // easier to calculate symbolic results of most (not all) "classic" + // angles (e.g. 30,45,60...) if we calculate the degrees + // and the we do a switch on that. + // Alternatively, we could look at the fraction of pi + // (e.g. 60 degrees is 1/3 pi) but that's more + // convoluted as we'd need to look at both numerator and + // denominator. push(p1); push_integer(180); multiply(); @@ -5425,6 +6358,9 @@ } divide(); n = pop_integer(); + // most "good" (i.e. compact) trigonometric results + // happen for a round number of degrees. There are some exceptions + // though, e.g. 22.5 degrees, which we don't capture here. if (n < 0 || isNaN(n)) { push(symbol(COS)); push(p1); @@ -5480,28 +6416,6 @@ } }; - - /* cosh ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - Returns the hyperbolic cosine of x - - ``` - exp(x) + exp(-x) - cosh(x) = ---------------- - 2 - ``` - */ - Eval_cosh = function() { push(cadr(p1)); Eval(); @@ -5539,6 +6453,19 @@ return list(2); }; + // this function extract parts subtrees from a tree. + // It is used in two + // places that have to do with pattern matching. + // One is for integrals, where an expression or its + // subparts are matched against cases in an + // integrals table. + // Another one is for applyging tranformation patterns + // defined via PATTERN, again patterns are applied to + // either the whole expression or any of its parts. + + // unclear to me at the moment + // why this is exposed as something that can + // be evalled. Never called. Eval_decomp = function() { var h; save(); @@ -5575,6 +6502,7 @@ return push(toBePushed); }; + // returns constant expressions on the stack decomp = function(generalTransform) { save(); p2 = pop(); @@ -5582,6 +6510,7 @@ if (DEBUG) { console.log("DECOMPOSING " + p1); } + // is the entire expression constant? if (generalTransform) { if (!iscons(p1)) { if (DEBUG) { @@ -5597,20 +6526,25 @@ console.log(" entire expression is constant"); } pushTryNotToDuplicate(p1); + //push(p1); # may need later for pushing both +a, -a + //negate() restore(); return; } } + // sum? if (isadd(p1)) { decomp_sum(generalTransform); restore(); return; } + // product? if (ismultiply(p1)) { decomp_product(generalTransform); restore(); return; } + // naive decomp if not sum or product if (DEBUG) { console.log(" naive decomp"); } @@ -5619,6 +6553,10 @@ console.log("startig p3: " + p3); } while (iscons(p3)) { + // for a general transformations, + // we want to match any part of the tree so + // we need to push the subtree as well + // as recurse to its parts if (generalTransform) { push(car(p3)); } @@ -5645,6 +6583,7 @@ console.log(" decomposing the sum "); } h = 0; + // decomp terms involving x p3 = cdr(p1); while (iscons(p3)) { if (Find(car(p3), p2) || generalTransform) { @@ -5654,6 +6593,7 @@ } p3 = cdr(p3); } + // add together all constant terms h = tos; p3 = cdr(p1); while (iscons(p3)) { @@ -5667,7 +6607,7 @@ p3 = pop(); pushTryNotToDuplicate(p3); push(p3); - return negate(); + return negate(); // need both +a, -a for some integrals } }; @@ -5677,6 +6617,7 @@ console.log(" decomposing the product "); } h = 0; + // decomp factors involving x p3 = cdr(p1); while (iscons(p3)) { if (Find(car(p3), p2) || generalTransform) { @@ -5686,6 +6627,7 @@ } p3 = cdr(p3); } + // multiply together all constant factors h = tos; p3 = cdr(p1); while (iscons(p3)) { @@ -5699,18 +6641,70 @@ } }; + //p3 = pop(); # may need later for pushing both +a, -a + //push(p3) + //push(p3) + //negate() + // Store a function definition + + // Example: + + // f(x,y)=x^y + + // For this definition, p1 points to the following structure. + + // p1 + // | + // ___v__ ______ ______ + // |CONS |->|CONS |--------------------->|CONS | + // |______| |______| |______| + // | | | + // ___v__ ___v__ ______ ______ ___v__ ______ ______ + // |SETQ | |CONS |->|CONS |->|CONS | |CONS |->|CONS |->|CONS | + // |______| |______| |______| |______| |______| |______| |______| + // | | | | | | + // ___v__ ___v__ ___v__ ___v__ ___v__ ___v__ + // |SYM f | |SYM x | |SYM y | |POWER | |SYM x | |SYM y | + // |______| |______| |______| |______| |______| |______| + + // the result (in f) is a FUNCTION node + // that contains both the body and the argument list. + + // We have + + // caadr(p1) points to the function name i.e. f + // cdadr(p1) points to the arguments i.e. the list (x y) + // caddr(p1) points to the function body i.e. (power x y) + + //define F p3 # F points to the function name + //define A p4 # A points to the argument list + //define B p5 # B points to the function body define_user_function = function() { p3 = caadr(p1); p4 = cdadr(p1); p5 = caddr(p1); - if (!issymbol(p3)) { + if (!issymbol(p3)) { // p3 is F stop("function name?"); } - if (car(p5) === symbol(EVAL)) { + // evaluate function body (maybe) + if (car(p5) === symbol(EVAL)) { // p5 is B push(cadr(p5)); Eval(); - p5 = pop(); + p5 = pop(); // p5 is B } + + // note how, unless explicitly forced by an eval, + // (handled by the if just above) + // we don't eval/simplify + // the body. + // Why? because it's the easiest way + // to solve scope problems i.e. + // x = 0 + // f(x) = x + 1 + // f(4) # would reply 1 + // which would need to otherwise + // be solved by some scope device + // somehow push_symbol(FUNCTION); push(p5); push(p4); @@ -5724,17 +6718,16 @@ return push(p1); }; - /* defint ===================================================================== - + Tags ---- scripting, JS, internal, treenode, general concept - + Parameters ---------- f,x,a,b[,y,c,d...] - + General description ------------------- Returns the definite integral of f with respect to x evaluated from "a" to b. @@ -5742,62 +6735,69 @@ integrals"), for example a double integral (which can represent for example a volume under a surface), or a triple integral, etc. For example, defint(f,x,a,b,y,c,d). - */ + */ + //define F p2 + //define X p3 + //define A p4 + //define B p5 Eval_defint = function() { push(cadr(p1)); Eval(); - p2 = pop(); + p2 = pop(); // p2 is F p1 = cddr(p1); + // defint can handle multiple + // integrals, so we loop over the + // multiple integrals here while (iscons(p1)) { push(car(p1)); p1 = cdr(p1); Eval(); - p3 = pop(); + p3 = pop(); // p3 is X push(car(p1)); p1 = cdr(p1); Eval(); - p4 = pop(); + p4 = pop(); // p4 is A push(car(p1)); p1 = cdr(p1); Eval(); - p5 = pop(); + p5 = pop(); // p5 is B + + // obtain the primitive of F against the + // specified variable X + // note that the primitive changes over + // the calculation of the multiple + // integrals. push(p2); push(p3); integral(); - p2 = pop(); + p2 = pop(); // contains the antiderivative of F + + // evaluate the integral in A push(p2); push(p3); push(p5); subst(); Eval(); + // evaluate the integral in B push(p2); push(p3); push(p4); subst(); Eval(); + // integral between B and A is the + // subtraction. Note that this could + // be a number but also a function. + // and we might have to integrate this + // number/function again doing the while + // loop again if this is a multiple + // integral. subtract(); p2 = pop(); } return push(p2); }; - - /* deg ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - p,x - - General description - ------------------- - Returns the degree of polynomial p(x). - */ - Eval_degree = function() { push(cadr(p1)); Eval(); @@ -5812,6 +6812,24 @@ return degree(); }; + //----------------------------------------------------------------------------- + + // Find the degree of a polynomial + + // Input: tos-2 p(x) + + // tos-1 x + + // Output: Result on stack + + // Note: Finds the largest numerical power of x. Does not check for + // weirdness in p(x). + + //----------------------------------------------------------------------------- + + //define POLY p1 + //define X p2 + //define DEGREE p3 degree = function() { save(); p2 = pop(); @@ -5843,22 +6861,6 @@ } }; - - /* denominator ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - x - - General description - ------------------- - Returns the denominator of expression x. - */ - Eval_denominator = function() { push(cadr(p1)); Eval(); @@ -5869,6 +6871,7 @@ var h, theArgument; h = 0; theArgument = pop(); + //console.trace "denominator of: " + theArgument if (car(theArgument) === symbol(ADD)) { push(theArgument); rationalize(); @@ -5894,12 +6897,27 @@ } }; + // derivative + + //define F p3 + //define X p4 + //define N p5 Eval_derivative = function() { var doNothing, i, i1, n, o, ref, ref1; + // evaluate 1st arg to get function F i = 0; p1 = cdr(p1); push(car(p1)); Eval(); + // evaluate 2nd arg and then... + + // example result of 2nd arg what to do + + // d(f) nil guess X, N = nil + // d(f,2) 2 guess X, N = 2 + // d(f,x) x X = x, N = nil + // d(f,x,2) x X = x, N = 2 + // d(f,x,y) x X = x, N = y p1 = cdr(p1); push(car(p1)); Eval(); @@ -5920,7 +6938,8 @@ p4 = pop(); p3 = pop(); while (1) { - if (isNumericAtom(p5)) { + // p5 (N) might be a symbol instead of a number + if (isNumericAtom(p5)) { // p5 is N push(p5); n = pop_integer(); if (isNaN(n)) { @@ -5931,47 +6950,58 @@ } push(p3); if (n >= 0) { - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push(p4); derivative(); } } else { n = -n; - for (i = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { push(p4); integral(); } } p3 = pop(); - if (p5 === symbol(NIL)) { + if (p5 === symbol(NIL)) { // p5 is N break; } - if (isNumericAtom(p5)) { + // otherwise... + + // N arg1 what to do + + // number nil break + // number number N = arg1, continue + // number symbol X = arg1, N = arg2, continue + + // symbol nil X = N, N = nil, continue + // symbol number X = N, N = arg1, continue + // symbol symbol X = N, N = arg1, continue + if (isNumericAtom(p5)) { // p5 is N p1 = cdr(p1); push(car(p1)); Eval(); p5 = pop(); - if (p5 === symbol(NIL)) { - break; + if (p5 === symbol(NIL)) { // p5 is N + break; // arglist exhausted } - if (isNumericAtom(p5)) { - doNothing = 1; + if (isNumericAtom(p5)) { // p5 is N + doNothing = 1; // N = arg1 } else { p4 = p5; p1 = cdr(p1); push(car(p1)); Eval(); - p5 = pop(); + p5 = pop(); // p5 is N # N = arg2 } } else { p4 = p5; p1 = cdr(p1); push(car(p1)); Eval(); - p5 = pop(); + p5 = pop(); // p5 is N # N = arg1 } } - return push(p3); + return push(p3); // p3 is F # final result }; derivative = function() { @@ -6001,6 +7031,8 @@ if (issymbol(p2)) { return d_scalar_scalar_1(); } else { + // Example: d(sin(cos(x)),cos(x)) + // Replace cos(x) <- X, find derivative, then do X <- cos(x) push(p1); push(p2); push(symbol(SECRETX)); @@ -6009,11 +7041,12 @@ derivative(); push(symbol(SECRETX)); push(p2); - return subst(); + return subst(); // cos(X) -> cos(cos(x)) } }; d_scalar_scalar_1 = function() { + // d(x,x)? if (equal(p1, p2)) { push(one); return; @@ -6152,9 +7185,9 @@ j = 0; n = 0; n = length(p1) - 1; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p3 = cdr(p1); - for (j = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (j = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); j = 0 <= ref1 ? ++i1 : --i1) { push(car(p3)); if (i === j) { push(p2); @@ -6167,6 +7200,22 @@ return add_all(n); }; + //----------------------------------------------------------------------------- + + // v + // y = u + + // log y = v log u + + // 1 dy v du dv + // - -- = - -- + (log u) -- + // y dx u dx dx + + // dy v v du dv + // -- = u (- -- + (log u) --) + // dx u dx dx + + //----------------------------------------------------------------------------- dpower = function() { push(caddr(p1)); push(cadr(p1)); @@ -6194,12 +7243,25 @@ return divide(); }; + // derivative of derivative + + // example: d(d(f(x,y),y),x) + + // p1 = d(f(x,y),y) + + // p2 = x + + // cadr(p1) = f(x,y) + + // caddr(p1) = y dd = function() { + // d(f(x,y),x) push(cadr(p1)); push(p2); derivative(); p3 = pop(); if (car(p3) === symbol(DERIVATIVE)) { + // sort dx terms push_symbol(DERIVATIVE); push_symbol(DERIVATIVE); push(cadr(p3)); @@ -6220,6 +7282,7 @@ } }; + // derivative of a generic function dfunction = function() { p3 = cdr(p1); if (p3 === symbol(NIL) || Find(p3, p2)) { @@ -6291,6 +7354,11 @@ return negate(); }; + // Without simplify With simplify + + // d(arctan(y/x),x) -y/(x^2*(y^2/x^2+1)) -y/(x^2+y^2) + + // d(arctan(y/x),y) 1/(x*(y^2/x^2+1)) x/(x^2+y^2) darctan = function() { push(cadr(p1)); push(p2); @@ -6529,28 +7597,6 @@ return push(cadr(p1)); }; - - /* det ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - m - - General description - ------------------- - Returns the determinant of matrix m. - Uses Gaussian elimination for numerical matrices. - - Example: - - det(((1,2),(3,4))) - > -2 - */ - DET_check_arg = function() { if (!istensor(p1)) { return 0; @@ -6567,6 +7613,7 @@ var a, i, i1, n, o, ref, ref1; i = 0; n = 0; + //U **a save(); p1 = pop(); if (DET_check_arg() === 0) { @@ -6578,7 +7625,7 @@ } n = p1.tensor.nelem; a = p1.tensor.elem; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { if (!isNumericAtom(a[i])) { break; } @@ -6586,7 +7633,7 @@ if (i === n) { yydetg(); } else { - for (i = i1 = 0, ref1 = p1.tensor.nelem; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = p1.tensor.nelem; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { push(p1.tensor.elem[i]); } determinant(p1.tensor.dim[0]); @@ -6594,6 +7641,7 @@ return restore(); }; + // determinant of n * n matrix elements on the stack determinant = function(n) { var a, breakFromOutherWhile, h, i, i1, j, k, o, q, ref, ref1, s, sign_, t; h = 0; @@ -6605,8 +7653,13 @@ sign_ = 0; t = 0; a = []; + //int *a, *c, *d h = tos - n * n; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { +//a = (int *) malloc(3 * n * sizeof (int)) + + //if (a == NULL) +// out_of_memory() + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { a[i] = i; a[i + n] = 0; a[i + n + n] = 1; @@ -6619,12 +7672,13 @@ } else { push_integer(-1); } - for (i = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { k = n * a[i] + i; push(stack[h + k]); - multiply(); + multiply(); // FIXME -- problem here } add(); + // next permutation (Knuth's algorithm P) j = n - 1; s = 0; breakFromOutherWhile = false; @@ -6660,6 +7714,20 @@ return moveTos(h + 1); }; + //----------------------------------------------------------------------------- + + // Input: Matrix on stack + + // Output: Determinant on stack + + // Note: + + // Uses Gaussian elimination which is faster for numerical matrices. + + // Gaussian Elimination works by walking down the diagonal and clearing + // out the columns below it. + + //----------------------------------------------------------------------------- detg = function() { save(); p1 = pop(); @@ -6679,7 +7747,7 @@ i = 0; n = 0; n = p1.tensor.dim[0]; - for (i = o = 0, ref = n * n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n * n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push(p1.tensor.elem[i]); } lu_decomp(n); @@ -6687,6 +7755,17 @@ return push(p1); }; + //----------------------------------------------------------------------------- + + // Input: n * n matrix elements on stack + + // Output: p1 determinant + + // p2 mangled + + // upper diagonal matrix on stack + + //----------------------------------------------------------------------------- M = function(h, n, i, j) { return stack[h + n * i + j]; }; @@ -6703,9 +7782,11 @@ j = 0; h = tos - n * n; p1 = one; - for (d = o = 0, ref = n - 1; 0 <= ref ? o < ref : o > ref; d = 0 <= ref ? ++o : --o) { + for (d = o = 0, ref = n - 1; (0 <= ref ? o < ref : o > ref); d = 0 <= ref ? ++o : --o) { + // diagonal element zero? if (equal(M(h, n, d, d), zero)) { - for (i = i1 = ref1 = d + 1, ref2 = n; ref1 <= ref2 ? i1 < ref2 : i1 > ref2; i = ref1 <= ref2 ? ++i1 : --i1) { +// find a new row + for (i = i1 = ref1 = d + 1, ref2 = n; (ref1 <= ref2 ? i1 < ref2 : i1 > ref2); i = ref1 <= ref2 ? ++i1 : --i1) { if (!equal(M(h, n, i, d), zero)) { break; } @@ -6714,27 +7795,33 @@ p1 = zero; break; } - for (j = j1 = ref3 = d, ref4 = n; ref3 <= ref4 ? j1 < ref4 : j1 > ref4; j = ref3 <= ref4 ? ++j1 : --j1) { +// exchange rows + for (j = j1 = ref3 = d, ref4 = n; (ref3 <= ref4 ? j1 < ref4 : j1 > ref4); j = ref3 <= ref4 ? ++j1 : --j1) { p2 = M(h, n, d, j); setM(h, n, d, j, M(h, n, i, j)); setM(h, n, i, j, p2); } + // negate det push(p1); negate(); p1 = pop(); } + // update det push(p1); push(M(h, n, d, d)); multiply(); p1 = pop(); - for (i = l1 = ref5 = d + 1, ref6 = n; ref5 <= ref6 ? l1 < ref6 : l1 > ref6; i = ref5 <= ref6 ? ++l1 : --l1) { +// update lower diagonal matrix + for (i = l1 = ref5 = d + 1, ref6 = n; (ref5 <= ref6 ? l1 < ref6 : l1 > ref6); i = ref5 <= ref6 ? ++l1 : --l1) { + // multiplier push(M(h, n, i, d)); push(M(h, n, d, d)); divide(); negate(); p2 = pop(); + // update one row setM(h, n, i, d, zero); - for (j = m1 = ref7 = d + 1, ref8 = n; ref7 <= ref8 ? m1 < ref8 : m1 > ref8; j = ref7 <= ref8 ? ++m1 : --m1) { + for (j = m1 = ref7 = d + 1, ref8 = n; (ref7 <= ref8 ? m1 < ref8 : m1 > ref8); j = ref7 <= ref8 ? ++m1 : --m1) { push(M(h, n, d, j)); push(p2); multiply(); @@ -6744,12 +7831,21 @@ } } } + // last diagonal element push(p1); push(M(h, n, n - 1, n - 1)); multiply(); return p1 = pop(); }; + //----------------------------------------------------------------------------- + + // Author : philippe.billet@noos.fr + + // Dirac function dirac(x) + // dirac(-x)=dirac(x) + // dirac(b-a)=dirac(a-b) + //----------------------------------------------------------------------------- Eval_dirac = function() { push(cadr(p1)); Eval(); @@ -6762,6 +7858,7 @@ return restore(); }; + //define p1 p1 ydirac = function() { p1 = pop(); if (isdouble(p1)) { @@ -6805,6 +7902,15 @@ return list(2); }; + //----------------------------------------------------------------------------- + + // Generate all divisors of a term + + // Input: Term on stack (factor * factor * ...) + + // Output: Divisors on stack + + //----------------------------------------------------------------------------- divisors = function() { var h, i, n, o, ref, subsetOfStack; i = 0; @@ -6814,13 +7920,14 @@ h = tos - 1; divisors_onstack(); n = tos - h; + //qsort(stack + h, n, sizeof (U *), __cmp) subsetOfStack = stack.slice(h, h + n); subsetOfStack.sort(cmp_expr); stack = stack.slice(0, h).concat(subsetOfStack).concat(stack.slice(h + n)); p1 = alloc_tensor(n); p1.tensor.ndim = 1; p1.tensor.dim[0] = n; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p1.tensor.elem[i] = stack[h + i]; } moveTos(h); @@ -6837,12 +7944,17 @@ save(); p1 = pop(); h = tos; + // push all of the term's factors if (isNumericAtom(p1)) { push(p1); factor_small_number(); } else if (car(p1) === symbol(ADD)) { push(p1); __factor_add(); + //printf(">>>\n") + //for (i = h; i < tos; i++) + //print(stdout, stack[i]) + //printf("<<<\n") } else if (car(p1) === symbol(MULTIPLY)) { p1 = cdr(p1); if (isNumericAtom(car(p1))) { @@ -6869,16 +7981,44 @@ push(one); } k = tos; + // contruct divisors by recursive descent push(one); gen(h, k); + // move n = tos - k; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { stack[h + i] = stack[k + i]; } moveTos(h + n); return restore(); }; + //----------------------------------------------------------------------------- + + // Generate divisors + + // Input: Base-exponent pairs on stack + + // h first pair + + // k just past last pair + + // Output: Divisors on stack + + // For example, factor list 2 2 3 1 results in 6 divisors, + + // 1 + // 3 + // 2 + // 6 + // 4 + // 12 + + //----------------------------------------------------------------------------- + + //define ACCUM p1 + //define BASE p2 + //define EXPO p3 gen = function(h, k) { var expo, i, o, ref; expo = 0; @@ -6895,7 +8035,7 @@ push(p3); expo = pop_integer(); if (!isNaN(expo)) { - for (i = o = 0, ref = Math.abs(expo); 0 <= ref ? o <= ref : o >= ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = Math.abs(expo); (0 <= ref ? o <= ref : o >= ref); i = 0 <= ref ? ++o : --o) { push(p1); push(p2); push_integer(sign(expo) * i); @@ -6907,9 +8047,22 @@ return restore(); }; + //----------------------------------------------------------------------------- + + // Factor ADD expression + + // Input: Expression on stack + + // Output: Factors on stack + + // Each factor consists of two expressions, the factor itself followed + // by the exponent. + + //----------------------------------------------------------------------------- __factor_add = function() { save(); p1 = pop(); + // get gcd of all terms p3 = cdr(p1); push(car(p3)); p3 = cdr(p3); @@ -6918,6 +8071,7 @@ gcd(); p3 = cdr(p3); } + // check gcd p2 = pop(); if (isplusone(p2)) { push(p1); @@ -6925,6 +8079,7 @@ restore(); return; } + // push factored gcd if (isNumericAtom(p2)) { push(p2); factor_small_number(); @@ -6947,6 +8102,7 @@ push(p2); push(one); } + // divide each term by gcd push(p2); inverse(); p2 = pop(); @@ -6963,6 +8119,7 @@ return restore(); }; + // power function for double precision floating point dpow = function() { var a, b, base, expo, result, theta; a = 0.0; @@ -6973,9 +8130,11 @@ theta = 0.0; expo = pop_double(); base = pop_double(); + // divide by zero? if (base === 0.0 && expo < 0.0) { stop("divide by zero"); } + // nonnegative base or integer power? if (base >= 0.0 || (expo % 1.0) === 0.0) { result = Math.pow(base, expo); push_double(result); @@ -6983,6 +8142,7 @@ } result = Math.pow(Math.abs(base), expo); theta = Math.PI * expo; + // this ensures the real part is 0.0 instead of a tiny fraction if ((expo % 0.5) === 0.0) { a = 0.0; b = Math.sin(theta); @@ -6997,17 +8157,16 @@ return add(); }; - /* eigen ===================================================================== - + Tags ---- scripting, JS, internal, treenode, general concept - + Parameters ---------- m - + General description ------------------- Compute eigenvalues and eigenvectors. Matrix m must be both numerical and symmetric. @@ -7015,68 +8174,70 @@ The eigenvec function returns a matrix with the eigenvectors arranged as row vectors. The eigen function does not return anything but stores the eigenvalue matrix in D and the eigenvector matrix in Q. - + Input: stack[tos - 1] symmetric matrix - + Output: D diagnonal matrix Q eigenvector matrix - + D and Q have the property that - + A == dot(transpose(Q),D,Q) - + where A is the original matrix. - + The eigenvalues are on the diagonal of D. The eigenvectors are row vectors in Q. - + The eigenvalue relation: - + A X = lambda X - + can be checked as follows: - + lambda = D[1,1] X = Q[1] dot(A,X) - lambda X - + Example 1. Check the relation AX = lambda X where lambda is an eigenvalue and X is the associated eigenvector. - + Enter: - + A = hilbert(3) - + eigen(A) - + lambda = D[1,1] - + X = Q[1] - + dot(A,X) - lambda X - + Result: - + -1.16435e-14 - + -6.46705e-15 - + -4.55191e-15 - + Example 2: Check the relation A = QTDQ. - + Enter: - + A - dot(transpose(Q),D,Q) - + Result: - + 6.27365e-12 -1.58236e-11 1.81902e-11 - + -1.58236e-11 -1.95365e-11 2.56514e-12 - + 1.81902e-11 2.56514e-12 1.32627e-11 - */ + */ + //define D(i, j) yydd[EIG_N * (i) + (j)] + //define Q(i, j) yyqq[EIG_N * (i) + (j)] EIG_N = 0; EIG_yydd = []; @@ -7095,22 +8256,6 @@ return push(symbol(NIL)); }; - - /* eigenval ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - m - - General description - ------------------- - Compute eigenvalues of m. See "eigen" for more info. - */ - Eval_eigenval = function() { if (EIG_check_arg() === 0) { push_symbol(EIGENVAL); @@ -7122,22 +8267,6 @@ return push(p2); }; - - /* eigenvec ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - m - - General description - ------------------- - Compute eigenvectors of m. See "eigen" for more info. - */ - Eval_eigenvec = function() { if (EIG_check_arg() === 0) { push_symbol(EIGENVEC); @@ -7165,15 +8294,15 @@ stop("eigen: argument is not a square matrix"); } EIG_N = p1.tensor.dim[0]; - for (i = o = 0, ref = EIG_N; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { - for (j = i1 = 0, ref1 = EIG_N; 0 <= ref1 ? i1 < ref1 : i1 > ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (i = o = 0, ref = EIG_N; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { + for (j = i1 = 0, ref1 = EIG_N; (0 <= ref1 ? i1 < ref1 : i1 > ref1); j = 0 <= ref1 ? ++i1 : --i1) { if (!isdouble(p1.tensor.elem[EIG_N * i + j])) { stop("eigen: matrix is not numerical"); } } } - for (i = j1 = 0, ref2 = EIG_N - 1; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { - for (j = l1 = ref3 = i + 1, ref4 = EIG_N; ref3 <= ref4 ? l1 < ref4 : l1 > ref4; j = ref3 <= ref4 ? ++l1 : --l1) { + for (i = j1 = 0, ref2 = EIG_N - 1; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { + for (j = l1 = ref3 = i + 1, ref4 = EIG_N; (ref3 <= ref4 ? l1 < ref4 : l1 > ref4); j = ref3 <= ref4 ? ++l1 : --l1) { if (Math.abs(p1.tensor.elem[EIG_N * i + j].d - p1.tensor.elem[EIG_N * j + i].d) > 1e-10) { stop("eigen: matrix is not symmetrical"); } @@ -7182,30 +8311,52 @@ return 1; }; + //----------------------------------------------------------------------------- + + // Input: p1 matrix + + // Output: p2 eigenvalues + + // p3 eigenvectors + + //----------------------------------------------------------------------------- eigen = function(op) { var i, i1, j, j1, l1, m1, n1, o, o1, q1, r1, ref, ref1, ref10, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, results, s1; i = 0; j = 0; - for (i = o = 0, ref = EIG_N * EIG_N; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { +// malloc working vars + + //EIG_yydd = (double *) malloc(n * n * sizeof (double)) + for (i = o = 0, ref = EIG_N * EIG_N; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { EIG_yydd[i] = 0.0; } - for (i = i1 = 0, ref1 = EIG_N * EIG_N; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { +//if (EIG_yydd == NULL) +// stop("malloc failure") + + //EIG_yyqq = (double *) malloc(n * n * sizeof (double)) + for (i = i1 = 0, ref1 = EIG_N * EIG_N; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { EIG_yyqq[i] = 0.0; } - for (i = j1 = 0, ref2 = EIG_N; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { +//if (EIG_yyqq == NULL) +// stop("malloc failure") + + // initialize D + for (i = j1 = 0, ref2 = EIG_N; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { EIG_yydd[EIG_N * i + i] = p1.tensor.elem[EIG_N * i + i].d; - for (j = l1 = ref3 = i + 1, ref4 = EIG_N; ref3 <= ref4 ? l1 < ref4 : l1 > ref4; j = ref3 <= ref4 ? ++l1 : --l1) { + for (j = l1 = ref3 = i + 1, ref4 = EIG_N; (ref3 <= ref4 ? l1 < ref4 : l1 > ref4); j = ref3 <= ref4 ? ++l1 : --l1) { EIG_yydd[EIG_N * i + j] = p1.tensor.elem[EIG_N * i + j].d; EIG_yydd[EIG_N * j + i] = p1.tensor.elem[EIG_N * i + j].d; } } - for (i = m1 = 0, ref5 = EIG_N; 0 <= ref5 ? m1 < ref5 : m1 > ref5; i = 0 <= ref5 ? ++m1 : --m1) { +// initialize Q + for (i = m1 = 0, ref5 = EIG_N; (0 <= ref5 ? m1 < ref5 : m1 > ref5); i = 0 <= ref5 ? ++m1 : --m1) { EIG_yyqq[EIG_N * i + i] = 1.0; - for (j = n1 = ref6 = i + 1, ref7 = EIG_N; ref6 <= ref7 ? n1 < ref7 : n1 > ref7; j = ref6 <= ref7 ? ++n1 : --n1) { + for (j = n1 = ref6 = i + 1, ref7 = EIG_N; (ref6 <= ref7 ? n1 < ref7 : n1 > ref7); j = ref6 <= ref7 ? ++n1 : --n1) { EIG_yyqq[EIG_N * i + j] = 0.0; EIG_yyqq[EIG_N * j + i] = 0.0; } } +// step up to 100 times for (i = o1 = 0; o1 < 100; i = ++o1) { if (step() === 0) { break; @@ -7214,27 +8365,29 @@ if (i === 100) { printstr("\nnote: eigen did not converge\n"); } + // p2 = D if (op === EIGEN || op === EIGENVAL) { push(p1); copy_tensor(); p2 = pop(); - for (i = q1 = 0, ref8 = EIG_N; 0 <= ref8 ? q1 < ref8 : q1 > ref8; i = 0 <= ref8 ? ++q1 : --q1) { - for (j = r1 = 0, ref9 = EIG_N; 0 <= ref9 ? r1 < ref9 : r1 > ref9; j = 0 <= ref9 ? ++r1 : --r1) { + for (i = q1 = 0, ref8 = EIG_N; (0 <= ref8 ? q1 < ref8 : q1 > ref8); i = 0 <= ref8 ? ++q1 : --q1) { + for (j = r1 = 0, ref9 = EIG_N; (0 <= ref9 ? r1 < ref9 : r1 > ref9); j = 0 <= ref9 ? ++r1 : --r1) { push_double(EIG_yydd[EIG_N * i + j]); p2.tensor.elem[EIG_N * i + j] = pop(); } } } + // p3 = Q if (op === EIGEN || op === EIGENVEC) { push(p1); copy_tensor(); p3 = pop(); results = []; - for (i = s1 = 0, ref10 = EIG_N; 0 <= ref10 ? s1 < ref10 : s1 > ref10; i = 0 <= ref10 ? ++s1 : --s1) { + for (i = s1 = 0, ref10 = EIG_N; (0 <= ref10 ? s1 < ref10 : s1 > ref10); i = 0 <= ref10 ? ++s1 : --s1) { results.push((function() { var ref11, results1, t1; results1 = []; - for (j = t1 = 0, ref11 = EIG_N; 0 <= ref11 ? t1 < ref11 : t1 > ref11; j = 0 <= ref11 ? ++t1 : --t1) { + for (j = t1 = 0, ref11 = EIG_N; (0 <= ref11 ? t1 < ref11 : t1 > ref11); j = 0 <= ref11 ? ++t1 : --t1) { push_double(EIG_yyqq[EIG_N * i + j]); results1.push(p3.tensor.elem[EIG_N * i + j] = pop()); } @@ -7245,13 +8398,212 @@ } }; + // free working vars + + //----------------------------------------------------------------------------- + + // Example: p = 1, q = 3 + + // c 0 s 0 + + // 0 1 0 0 + // G = + // -s 0 c 0 + + // 0 0 0 1 + + // The effect of multiplying G times A is... + + // row 1 of A = c (row 1 of A ) + s (row 3 of A ) + // n+1 n n + + // row 3 of A = c (row 3 of A ) - s (row 1 of A ) + // n+1 n n + + // In terms of components the overall effect is... + + // row 1 = c row 1 + s row 3 + + // A[1,1] = c A[1,1] + s A[3,1] + + // A[1,2] = c A[1,2] + s A[3,2] + + // A[1,3] = c A[1,3] + s A[3,3] + + // A[1,4] = c A[1,4] + s A[3,4] + + // row 3 = c row 3 - s row 1 + + // A[3,1] = c A[3,1] - s A[1,1] + + // A[3,2] = c A[3,2] - s A[1,2] + + // A[3,3] = c A[3,3] - s A[1,3] + + // A[3,4] = c A[3,4] - s A[1,4] + + // T + // The effect of multiplying A times G is... + + // col 1 of A = c (col 1 of A ) + s (col 3 of A ) + // n+1 n n + + // col 3 of A = c (col 3 of A ) - s (col 1 of A ) + // n+1 n n + + // In terms of components the overall effect is... + + // col 1 = c col 1 + s col 3 + + // A[1,1] = c A[1,1] + s A[1,3] + + // A[2,1] = c A[2,1] + s A[2,3] + + // A[3,1] = c A[3,1] + s A[3,3] + + // A[4,1] = c A[4,1] + s A[4,3] + + // col 3 = c col 3 - s col 1 + + // A[1,3] = c A[1,3] - s A[1,1] + + // A[2,3] = c A[2,3] - s A[2,1] + + // A[3,3] = c A[3,3] - s A[3,1] + + // A[4,3] = c A[4,3] - s A[4,1] + + // What we want to do is just compute the upper triangle of A since we + // know the lower triangle is identical. + + // In other words, we just want to update components A[i,j] where i < j. + + //----------------------------------------------------------------------------- + + // Example: p = 2, q = 5 + + // p q + + // j=1 j=2 j=3 j=4 j=5 j=6 + + // i=1 . A[1,2] . . A[1,5] . + + // p i=2 A[2,1] A[2,2] A[2,3] A[2,4] A[2,5] A[2,6] + + // i=3 . A[3,2] . . A[3,5] . + + // i=4 . A[4,2] . . A[4,5] . + + // q i=5 A[5,1] A[5,2] A[5,3] A[5,4] A[5,5] A[5,6] + + // i=6 . A[6,2] . . A[6,5] . + + //----------------------------------------------------------------------------- + + // This is what B = GA does: + + // row 2 = c row 2 + s row 5 + + // B[2,1] = c * A[2,1] + s * A[5,1] + // B[2,2] = c * A[2,2] + s * A[5,2] + // B[2,3] = c * A[2,3] + s * A[5,3] + // B[2,4] = c * A[2,4] + s * A[5,4] + // B[2,5] = c * A[2,5] + s * A[5,5] + // B[2,6] = c * A[2,6] + s * A[5,6] + + // row 5 = c row 5 - s row 2 + + // B[5,1] = c * A[5,1] + s * A[2,1] + // B[5,2] = c * A[5,2] + s * A[2,2] + // B[5,3] = c * A[5,3] + s * A[2,3] + // B[5,4] = c * A[5,4] + s * A[2,4] + // B[5,5] = c * A[5,5] + s * A[2,5] + // B[5,6] = c * A[5,6] + s * A[2,6] + + // T + // This is what BG does: + + // col 2 = c col 2 + s col 5 + + // B[1,2] = c * A[1,2] + s * A[1,5] + // B[2,2] = c * A[2,2] + s * A[2,5] + // B[3,2] = c * A[3,2] + s * A[3,5] + // B[4,2] = c * A[4,2] + s * A[4,5] + // B[5,2] = c * A[5,2] + s * A[5,5] + // B[6,2] = c * A[6,2] + s * A[6,5] + + // col 5 = c col 5 - s col 2 + + // B[1,5] = c * A[1,5] - s * A[1,2] + // B[2,5] = c * A[2,5] - s * A[2,2] + // B[3,5] = c * A[3,5] - s * A[3,2] + // B[4,5] = c * A[4,5] - s * A[4,2] + // B[5,5] = c * A[5,5] - s * A[5,2] + // B[6,5] = c * A[6,5] - s * A[6,2] + + //----------------------------------------------------------------------------- + + // Step 1: Just do upper triangle (i < j), B[2,5] = 0 + + // B[1,2] = c * A[1,2] + s * A[1,5] + + // B[2,3] = c * A[2,3] + s * A[5,3] + // B[2,4] = c * A[2,4] + s * A[5,4] + // B[2,6] = c * A[2,6] + s * A[5,6] + + // B[1,5] = c * A[1,5] - s * A[1,2] + // B[3,5] = c * A[3,5] - s * A[3,2] + // B[4,5] = c * A[4,5] - s * A[4,2] + + // B[5,6] = c * A[5,6] + s * A[2,6] + + //----------------------------------------------------------------------------- + + // Step 2: Transpose where i > j since A[i,j] == A[j,i] + + // B[1,2] = c * A[1,2] + s * A[1,5] + + // B[2,3] = c * A[2,3] + s * A[3,5] + // B[2,4] = c * A[2,4] + s * A[4,5] + // B[2,6] = c * A[2,6] + s * A[5,6] + + // B[1,5] = c * A[1,5] - s * A[1,2] + // B[3,5] = c * A[3,5] - s * A[2,3] + // B[4,5] = c * A[4,5] - s * A[2,4] + + // B[5,6] = c * A[5,6] + s * A[2,6] + + //----------------------------------------------------------------------------- + + // Step 3: Same as above except reorder + + // k < p (k = 1) + + // A[1,2] = c * A[1,2] + s * A[1,5] + // A[1,5] = c * A[1,5] - s * A[1,2] + + // p < k < q (k = 3..4) + + // A[2,3] = c * A[2,3] + s * A[3,5] + // A[3,5] = c * A[3,5] - s * A[2,3] + + // A[2,4] = c * A[2,4] + s * A[4,5] + // A[4,5] = c * A[4,5] - s * A[2,4] + + // q < k (k = 6) + + // A[2,6] = c * A[2,6] + s * A[5,6] + // A[5,6] = c * A[5,6] - s * A[2,6] + + //----------------------------------------------------------------------------- step = function() { var count, i, i1, j, o, ref, ref1, ref2; i = 0; j = 0; count = 0; - for (i = o = 0, ref = EIG_N - 1; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { - for (j = i1 = ref1 = i + 1, ref2 = EIG_N; ref1 <= ref2 ? i1 < ref2 : i1 > ref2; j = ref1 <= ref2 ? ++i1 : --i1) { +// for each upper triangle "off-diagonal" component do step2 + for (i = o = 0, ref = EIG_N - 1; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { + for (j = i1 = ref1 = i + 1, ref2 = EIG_N; (ref1 <= ref2 ? i1 < ref2 : i1 > ref2); j = ref1 <= ref2 ? ++i1 : --i1) { if (EIG_yydd[EIG_N * i + j] !== 0.0) { step2(i, j); count++; @@ -7270,6 +8622,9 @@ cc = 0.0; s = 0.0; ss = 0.0; + // compute c and s + + // from Numerical Recipes (except they have a_qq - a_pp) theta = 0.5 * (EIG_yydd[EIG_N * p + p] - EIG_yydd[EIG_N * q + q]) / EIG_yydd[EIG_N * p + q]; t = 1.0 / (Math.abs(theta) + Math.sqrt(theta * theta + 1.0)); if (theta < 0.0) { @@ -7277,19 +8632,28 @@ } c = 1.0 / Math.sqrt(t * t + 1.0); s = t * c; - for (k = o = 0, ref = EIG_N; 0 <= ref ? o < ref : o > ref; k = 0 <= ref ? ++o : --o) { +// D = GD + + // which means "add rows" + for (k = o = 0, ref = EIG_N; (0 <= ref ? o < ref : o > ref); k = 0 <= ref ? ++o : --o) { cc = EIG_yydd[EIG_N * p + k]; ss = EIG_yydd[EIG_N * q + k]; EIG_yydd[EIG_N * p + k] = c * cc + s * ss; EIG_yydd[EIG_N * q + k] = c * ss - s * cc; } - for (k = i1 = 0, ref1 = EIG_N; 0 <= ref1 ? i1 < ref1 : i1 > ref1; k = 0 <= ref1 ? ++i1 : --i1) { +// D = D transpose(G) + + // which means "add columns" + for (k = i1 = 0, ref1 = EIG_N; (0 <= ref1 ? i1 < ref1 : i1 > ref1); k = 0 <= ref1 ? ++i1 : --i1) { cc = EIG_yydd[EIG_N * k + p]; ss = EIG_yydd[EIG_N * k + q]; EIG_yydd[EIG_N * k + p] = c * cc + s * ss; EIG_yydd[EIG_N * k + q] = c * ss - s * cc; } - for (k = j1 = 0, ref2 = EIG_N; 0 <= ref2 ? j1 < ref2 : j1 > ref2; k = 0 <= ref2 ? ++j1 : --j1) { +// Q = GQ + + // which means "add rows" + for (k = j1 = 0, ref2 = EIG_N; (0 <= ref2 ? j1 < ref2 : j1 > ref2); k = 0 <= ref2 ? ++j1 : --j1) { cc = EIG_yyqq[EIG_N * p + k]; ss = EIG_yyqq[EIG_N * q + k]; EIG_yyqq[EIG_N * p + k] = c * cc + s * ss; @@ -7299,27 +8663,6 @@ return EIG_yydd[EIG_N * q + p] = 0.0; }; - - /* erf ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Authors - ------- - philippe.billet@noos.fr - - Parameters - ---------- - x - - General description - ------------------- - Error function erf(x). - erf(-x)=erf(x) - */ - Eval_erf = function() { push(cadr(p1)); Eval(); @@ -7358,6 +8701,15 @@ list(2); }; + //----------------------------------------------------------------------------- + + // Author : philippe.billet@noos.fr + + // erfc(x) + + // GW Added erfc() from Numerical Recipes in C + + //----------------------------------------------------------------------------- Eval_erfc = function() { push(cadr(p1)); Eval(); @@ -7388,6 +8740,7 @@ list(2); }; + // from Numerical Recipes in C erfc = function(x) { var ans, t, z; if (x === 0) { @@ -7406,6 +8759,11 @@ } }; + // Evaluate an expression, for example... + + // push(p1) + // Eval() + // p2 = pop() Eval = function() { var willEvaluateAsFloats; check_esc_flag(); @@ -7450,6 +8808,15 @@ Eval_sym = function() { var cycleString, i, o, positionIfSymbolAlreadyBeingEvaluated, ref, ref1; + // note that function calls are not processed here + // because, since they have an argument (at least an empty one) + // they are actually CONs, which is a branch of the + // switch before the one that calls this function + + // bare keyword? + // If it's a keyword, then we don't look + // at the binding array, because keywords + // are not redefinable. if (iskeyword(p1)) { push(p1); push(symbol(LAST)); @@ -7460,16 +8827,29 @@ push_double(Math.PI); return; } + // Evaluate symbol's binding p2 = get_binding(p1); if (DEBUG) { console.log("looked up: " + p1 + " which contains: " + p2); } push(p2); + // differently from standard Lisp, + // here the evaluation is not + // one-step only, rather it keeps evaluating + // "all the way" until a symbol is + // defined as itself. + // Uncomment these two lines to get Lisp + // behaviour (and break most tests) if (p1 !== p2) { + // detect recursive lookup of symbols, which would otherwise + // cause a stack overflow. + // Note that recursive functions will still work because + // as mentioned at the top, this method doesn't look + // up and evaluate function calls. positionIfSymbolAlreadyBeingEvaluated = chainOfUserSymbolsNotFunctionsBeingEvaluated.indexOf(p1); if (positionIfSymbolAlreadyBeingEvaluated !== -1) { cycleString = ""; - for (i = o = ref = positionIfSymbolAlreadyBeingEvaluated, ref1 = chainOfUserSymbolsNotFunctionsBeingEvaluated.length; ref <= ref1 ? o < ref1 : o > ref1; i = ref <= ref1 ? ++o : --o) { + for (i = o = ref = positionIfSymbolAlreadyBeingEvaluated, ref1 = chainOfUserSymbolsNotFunctionsBeingEvaluated.length; (ref <= ref1 ? o < ref1 : o > ref1); i = ref <= ref1 ? ++o : --o) { cycleString += chainOfUserSymbolsNotFunctionsBeingEvaluated[i].printname + " -> "; } cycleString += p1.printname; @@ -7485,6 +8865,15 @@ Eval_cons = function() { var cons_head; cons_head = car(p1); + // normally the cons_head is a symbol, + // but sometimes in the case of + // functions we don't have a symbol, + // we have to evaluate something to get to the + // symbol. For example if a function is inside + // a tensor, then we need to evaluate an index + // access first to get to the function. + // In those cases, we find an EVAL here, + // so we proceed to EVAL if (car(cons_head) === symbol(EVAL)) { Eval_user_function(); return; @@ -7617,6 +9006,10 @@ return Eval_floor(); case FOR: return Eval_for(); + // this is invoked only when we + // evaluate a function that is NOT being called + // e.g. when f is a function as we do + // g = f case FUNCTION: return Eval_function_reference(); case GAMMA: @@ -7645,6 +9038,7 @@ return Eval_isprime(); case LAGUERRE: return Eval_laguerre(); + // when LAPLACE then Eval_laplace() case LCM: return Eval_lcm(); case LEADING: @@ -7770,35 +9164,16 @@ return push(get_binding(cadr(p1))); }; - - /* check ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - p - - General description - ------------------- - Returns whether the predicate p is true/false or unknown: - 0 if false, 1 if true or remains unevaluated if unknown. - Note that if "check" is passed an assignment, it turns it into a test, - i.e. check(a = b) is turned into check(a==b) - so "a" is not assigned anything. - Like in many programming languages, "check" also gives truthyness/falsyness - for numeric values. In which case, "true" is returned for non-zero values. - Potential improvements: "check" can't evaluate strings yet. - */ - Eval_check = function() { var checkResult; + // check the argument checkResult = isZeroLikeOrNonZeroLikeOrUndetermined(cadr(p1)); if (checkResult == null) { + // returned null: unknown result + // leave the whole check unevalled return push(p1); } else { + // returned 1 or 0 return push_integer(checkResult); } }; @@ -7809,24 +9184,9 @@ return det(); }; - - /* dim ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - m,n - - General description - ------------------- - Returns the cardinality of the nth index of tensor "m". - */ - Eval_dim = function() { var n; + //int n push(cadr(p1)); Eval(); p2 = pop(); @@ -7838,7 +9198,7 @@ n = 1; } if (!istensor(p2)) { - return push_integer(1); + return push_integer(1); // dim of scalar is 1 } else if (n < 1 || n > p2.tensor.ndim) { return push(p1); } else { @@ -7852,22 +9212,6 @@ return divisors(); }; - - /* do ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - a,b,... - - General description - ------------------- - Evaluates each argument from left to right. Returns the result of the last argument. - */ - Eval_do = function() { var results; push(car(p1)); @@ -7892,6 +9236,7 @@ return dsolve(); }; + // for example, Eval(f,x,2) Eval_Eval = function() { push(cadr(p1)); Eval(); @@ -7907,6 +9252,8 @@ return Eval(); }; + // exp evaluation: it replaces itself with + // a POWER(E,something) node and evals that one Eval_exp = function() { push(cadr(p1)); Eval(); @@ -7957,6 +9304,9 @@ var h, orig, theTensor; h = tos; orig = p1; + + // look into the head of the list, + // when evaluated it should be a tensor p1 = cdr(p1); push(car(p1)); Eval(); @@ -7965,15 +9315,22 @@ stop("trying to access a scalar as a tensor"); } if (!istensor(theTensor)) { + // the tensor is not allocated yet, so + // leaving the expression unevalled moveTos(h); push(orig); return; } + // we examined the head of the list which + // was the tensor, now look into + // the indexes p1 = cdr(p1); while (iscons(p1)) { push(car(p1)); Eval(); if (!isintegerorintegerfloat(stack[tos - 1])) { + // index with something other than + // an integer moveTos(h); push(orig); return; @@ -8046,10 +9403,12 @@ return list(tos - h); }; + // quote definition Eval_quote = function() { return push(cadr(p1)); }; + // rank definition Eval_rank = function() { push(cadr(p1)); Eval(); @@ -8061,11 +9420,30 @@ } }; + // Evaluates the right side and assigns the + // result of the evaluation to the left side. + // It's called setq because it stands for "set quoted" from Lisp, + // see: + // http://stackoverflow.com/questions/869529/difference-between-set-setq-and-setf-in-common-lisp + // Note that this also takes case of assigning to a tensor + // element, which is something that setq wouldn't do + // in list, see comments further down below. + + // Example: + // f = x + // // f evaluates to x, so x is assigned to g really + // // rather than actually f being assigned to g + // g = f + // f = y + // g + // > x Eval_setq = function() { + // case of tensor if (caadr(p1) === symbol(INDEX)) { setq_indexed(); return; } + // case of function definition if (iscons(cadr(p1))) { define_user_function(); return; @@ -8077,14 +9455,52 @@ Eval(); p2 = pop(); set_binding(cadr(p1), p2); + // An assignment returns nothing. + // This is unlike most programming languages + // where an assignment does return the + // assigned value. + // TODO Could be changed. return push(symbol(NIL)); }; + // Here "setq" is a misnomer because + // setq wouldn't work in Lisp to set array elements + // since setq stands for "set quoted" and you wouldn't + // quote an array element access. + // You'd rather use setf, which is a macro that can + // assign a value to anything. + // (setf (aref YourArray 2) "blue") + // see + // http://stackoverflow.com/questions/18062016/common-lisp-how-to-set-an-element-in-a-2d-array + //----------------------------------------------------------------------------- + + // Example: a[1] = b + + // p1 *-------*-----------------------* + // | | | + // setq *-------*-------* b + // | | | + // index a 1 + + // cadadr(p1) -> a + + //----------------------------------------------------------------------------- setq_indexed = function() { var h; p4 = cadadr(p1); console.log("p4: " + p4); if (!issymbol(p4)) { + // this is likely to happen when one tries to + // do assignments like these + // 1[2] = 3 + // or + // f(x)[1] = 2 + // or + // [[1,2],[3,4]][5] = 6 + + // In other words, one can only do + // a straight assignment like + // existingMatrix[index] = something stop("indexed assignment: expected a symbol name"); } h = tos; @@ -8121,9 +9537,13 @@ push(cadr(p1)); Eval(); subst(); - return Eval(); + return Eval(); // normalize }; + + // always returns a matrix with rank 2 + // i.e. two dimensions, + // the passed parameter is the size Eval_unit = function() { var i, n, o, ref; i = 0; @@ -8143,7 +9563,7 @@ p1.tensor.ndim = 2; p1.tensor.dim[0] = n; p1.tensor.dim[1] = n; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p1.tensor.elem[n * i + i] = one; } check_tensor_dimensions(p1); @@ -8158,10 +9578,26 @@ return expanding = prev_expanding; }; + // like Eval() except "=" (assignment) is treated + // as "==" (equality test) + // This is because + // * this allows users to be lazy and just + // use "=" instead of "==" as per more common + // mathematical notation + // * in many places we don't expect an assignment + // e.g. we don't expect to test the zero-ness + // of an assignment or the truth value of + // an assignment + // Note that these are questionable assumptions + // as for example in most programming languages one + // can indeed test the value of an assignment (the + // value is just the evaluation of the right side) Eval_predicate = function() { save(); p1 = top(); if (car(p1) === symbol(SETQ)) { + // replace the assignment in the + // head with an equality test pop(); push_symbol(TESTEQ); push(cadr(p1)); @@ -8172,9 +9608,21 @@ return restore(); }; + // Partial fraction expansion + + // Example + + // expand(1/(x^3+x^2),x) + + // 1 1 1 + // ---- - --- + ------- + // 2 x x + 1 + // x Eval_expand = function() { + // 1st arg push(cadr(p1)); Eval(); + // 2nd arg push(caddr(p1)); Eval(); p2 = pop(); @@ -8186,6 +9634,14 @@ return expand(); }; + //define A p2 + //define B p3 + //define C p4 + //define F p5 + //define P p6 + //define Q p7 + //define T p8 + //define X p9 expand = function() { var prev_expanding; save(); @@ -8196,6 +9652,7 @@ restore(); return; } + // if sum of terms then sum over the expansion of each term if (car(p5) === symbol(ADD)) { push_integer(0); p1 = cdr(p5); @@ -8209,43 +9666,56 @@ restore(); return; } + // B = numerator push(p5); numerator(); p3 = pop(); + // A = denominator push(p5); denominator(); p2 = pop(); remove_negative_exponents(); + // Q = quotient push(p3); push(p2); push(p9); + // if the denominator is one then always bail out + // also bail out if the denominator is not one but + // it's not anything recognizable as a polynomial. if (isone(p3) || isone(p2)) { if (!ispolyexpandedform(p2, p9) || isone(p2)) { pop(); pop(); pop(); push(p5); + // p5 is the original input, leave unchanged restore(); return; } } divpoly(); p7 = pop(); + // remainder B = B - A * Q push(p3); push(p2); push(p7); multiply(); subtract(); p3 = pop(); + // if the remainder is zero then we're done if (isZeroAtomOrTensor(p3)) { push(p7); restore(); return; } + // A = factor(A) + + //console.log("expand - to be factored: " + p2) push(p2); push(p9); factorpoly(); p2 = pop(); + //console.log("expand - factored to: " + p2) expand_get_C(); expand_get_B(); expand_get_A(); @@ -8280,7 +9750,7 @@ push(p5); copy_tensor(); p5 = pop(); - for (i = o = 0, ref = p5.tensor.nelem; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = p5.tensor.nelem; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push(p5.tensor.elem[i]); push(p9); expand(); @@ -8300,8 +9770,9 @@ factors(p2); factors(p3); n = tos - h; + // find the smallest exponent j = 0; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p1 = stack[h + i]; if (car(p1) !== symbol(POWER)) { continue; @@ -8322,12 +9793,14 @@ if (j === 0) { return; } + // A = A / X^j push(p2); push(p9); push_integer(-j); power(); multiply(); p2 = pop(); + // B = B / X^j push(p3); push(p9); push_integer(-j); @@ -8336,12 +9809,68 @@ return p3 = pop(); }; + // Returns the expansion coefficient matrix C. + + // Example: + + // B 1 + // --- = ----------- + // A 2 + // x (x + 1) + + // We have + + // B Y1 Y2 Y3 + // --- = ---- + ---- + ------- + // A 2 x x + 1 + // x + + // Our task is to solve for the unknowns Y1, Y2, and Y3. + + // Multiplying both sides by A yields + + // AY1 AY2 AY3 + // B = ----- + ----- + ------- + // 2 x x + 1 + // x + + // Let + + // A A A + // W1 = ---- W2 = --- W3 = ------- + // 2 x x + 1 + // x + + // Then the coefficient matrix C is + + // coeff(W1,x,0) coeff(W2,x,0) coeff(W3,x,0) + + // C = coeff(W1,x,1) coeff(W2,x,1) coeff(W3,x,1) + + // coeff(W1,x,2) coeff(W2,x,2) coeff(W3,x,2) + + // It follows that + + // coeff(B,x,0) Y1 + + // coeff(B,x,1) = C Y2 + + // coeff(B,x,2) = Y3 + + // Hence + + // Y1 coeff(B,x,0) + // -1 + // Y2 = C coeff(B,x,1) + + // Y3 coeff(B,x,2) expand_get_C = function() { var a, h, i, i1, j, n, o, prev_expanding, ref, ref1; h = 0; i = 0; j = 0; n = 0; + //U **a h = tos; if (car(p2) === symbol(MULTIPLY)) { p1 = cdr(p2); @@ -8364,8 +9893,8 @@ p4.tensor.dim[0] = n; p4.tensor.dim[1] = n; a = h; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { - for (j = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { + for (j = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); j = 0 <= ref1 ? ++i1 : --i1) { push(stack[a + j]); push(p9); push_integer(i); @@ -8382,6 +9911,70 @@ return moveTos(tos - n); }; + // The following table shows the push order for simple roots, repeated roots, + // and inrreducible factors. + + // Factor F Push 1st Push 2nd Push 3rd Push 4th + + // A + // x --- + // x + + // 2 A A + // x ---- --- + // 2 x + // x + + // A + // x + 1 ------- + // x + 1 + + // 2 A A + // (x + 1) ---------- ------- + // 2 x + 1 + // (x + 1) + + // 2 A Ax + // x + x + 1 ------------ ------------ + // 2 2 + // x + x + 1 x + x + 1 + + // 2 2 A Ax A Ax + // (x + x + 1) --------------- --------------- ------------ ------------ + // 2 2 2 2 2 2 + // (x + x + 1) (x + x + 1) x + x + 1 x + x + 1 + + // For T = A/F and F = P^N we have + + // Factor F Push 1st Push 2nd Push 3rd Push 4th + + // x T + + // 2 + // x T TP + + // x + 1 T + + // 2 + // (x + 1) T TP + + // 2 + // x + x + 1 T TX + + // 2 2 + // (x + x + 1) T TX TP TPX + + // Hence we want to push in the order + + // T * (P ^ i) * (X ^ j) + + // for all i, j such that + + // i = 0, 1, ..., N - 1 + + // j = 0, 1, ..., deg(P) - 1 + + // where index j runs first. expand_get_CF = function() { var d, i, j, n, o, prev_expanding, ref, results; d = 0; @@ -8408,11 +10001,11 @@ degree(); d = pop_integer(); results = []; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { results.push((function() { var i1, ref1, results1; results1 = []; - for (j = i1 = 0, ref1 = d; 0 <= ref1 ? i1 < ref1 : i1 > ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (j = i1 = 0, ref1 = d; (0 <= ref1 ? i1 < ref1 : i1 > ref1); j = 0 <= ref1 ? ++i1 : --i1) { push(p8); push(p6); push_integer(i); @@ -8435,6 +10028,7 @@ return results; }; + // Returns T = A/F where F is a factor of A. trivial_divide = function() { var h; h = 0; @@ -8444,7 +10038,7 @@ while (iscons(p0)) { if (!equal(car(p0), p5)) { push(car(p0)); - Eval(); + Eval(); // force expansion of (x+1)^2, f.e. } p0 = cdr(p0); } @@ -8455,6 +10049,7 @@ return p8 = pop(); }; + // Returns the expansion coefficient vector B. expand_get_B = function() { var i, n, o, prev_expanding, ref; i = 0; @@ -8466,7 +10061,7 @@ p8 = alloc_tensor(n); p8.tensor.ndim = 1; p8.tensor.dim[0] = n; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push(p3); push(p9); push_integer(i); @@ -8482,6 +10077,7 @@ return p3 = p8; }; + // Returns the expansion fractions in A. expand_get_A = function() { var h, i, n, o, ref; h = 0; @@ -8509,7 +10105,7 @@ p8 = alloc_tensor(n); p8.tensor.ndim = 1; p8.tensor.dim[0] = n; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p8.tensor.elem[i] = stack[h + i]; } moveTos(h); @@ -8535,11 +10131,11 @@ degree(); d = pop_integer(); results = []; - for (i = o = ref = n; ref <= 0 ? o < 0 : o > 0; i = ref <= 0 ? ++o : --o) { + for (i = o = ref = n; (ref <= 0 ? o < 0 : o > 0); i = ref <= 0 ? ++o : --o) { results.push((function() { var i1, ref1, results1; results1 = []; - for (j = i1 = 0, ref1 = d; 0 <= ref1 ? i1 < ref1 : i1 > ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (j = i1 = 0, ref1 = d; (0 <= ref1 ? i1 < ref1 : i1 > ref1); j = 0 <= ref1 ? ++i1 : --i1) { push(p5); push_integer(i); power(); @@ -8555,6 +10151,7 @@ return results; }; + // Do the exponential cosine function. Eval_expcos = function() { push(cadr(p1)); Eval(); @@ -8581,6 +10178,7 @@ return restore(); }; + // Do the exponential sine function. Eval_expsin = function() { push(cadr(p1)); Eval(); @@ -8611,6 +10209,7 @@ return restore(); }; + // factor a polynomial or integer Eval_factor = function() { var results; push(cadr(p1)); @@ -8624,6 +10223,7 @@ push(p2); } factor(); + // more factoring? p1 = cdddr(p1); results = []; while (iscons(p1)) { @@ -8683,7 +10283,7 @@ p1 = pop(); if (isinteger(p1)) { push(p1); - factor_number(); + factor_number(); // see pollard.cpp } else { push(p1); push(p2); @@ -8692,6 +10292,7 @@ return restore(); }; + // for factoring small integers (2^32 or less) factor_small_number = function() { var d, expo, i, n, o, ref; i = 0; @@ -8703,7 +10304,7 @@ if (n < 0) { n = -n; } - for (i = o = 0, ref = MAXPRIMETAB; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = MAXPRIMETAB; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { d = primetab[i]; if (d > n / d) { break; @@ -8743,6 +10344,25 @@ return restore(); }; + // simplification rules for factorials (m < n) + + // (e + 1) * factorial(e) -> factorial(e + 1) + + // factorial(e) / e -> factorial(e - 1) + + // e / factorial(e) -> 1 / factorial(e - 1) + + // factorial(e + n) + // ---------------- -> (e + m + 1)(e + m + 2)...(e + n) + // factorial(e + m) + + // factorial(e + m) 1 + // ---------------- -> -------------------------------- + // factorial(e + n) (e + m + 1)(e + m + 2)...(e + n) + + // this function is not actually used, but + // all these simplifications + // do happen automatically via simplify simplifyfactorials = function() { var x; x = 0; @@ -8787,11 +10407,11 @@ p1 = cdr(p1); n++; } - for (i = o = 0, ref = n - 1; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n - 1; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { if (stack[s + i] === symbol(NIL)) { continue; } - for (j = i1 = ref1 = i + 1, ref2 = n; ref1 <= ref2 ? i1 < ref2 : i1 > ref2; j = ref1 <= ref2 ? ++i1 : --i1) { + for (j = i1 = ref1 = i + 1, ref2 = n; (ref1 <= ref2 ? i1 < ref2 : i1 > ref2); j = ref1 <= ref2 ? ++i1 : --i1) { if (stack[s + j] === symbol(NIL)) { continue; } @@ -8799,7 +10419,7 @@ } } push(one); - for (i = j1 = 0, ref3 = n; 0 <= ref3 ? j1 < ref3 : j1 > ref3; i = 0 <= ref3 ? ++j1 : --j1) { + for (i = j1 = 0, ref3 = n; (0 <= ref3 ? j1 < ref3 : j1 > ref3); i = 0 <= ref3 ? ++j1 : --j1) { if (stack[s + i] === symbol(NIL)) { continue; } @@ -8830,6 +10450,7 @@ p4 = one; } if (isfactorial(p1) && isfactorial(p2)) { + // Determine if the powers cancel. push(p3); push(p4); add(); @@ -8838,6 +10459,9 @@ if (n !== 0) { return; } + // Find the difference between the two factorial args. + + // For example, the difference between (a + 2)! and a! is 2. push(cadr(p1)); push(cadr(p2)); subtract(); @@ -8856,7 +10480,7 @@ p4 = p5; } push(one); - for (i = o = 1, ref = n; 1 <= ref ? o <= ref : o >= ref; i = 1 <= ref ? ++o : --o) { + for (i = o = 1, ref = n; (1 <= ref ? o <= ref : o >= ref); i = 1 <= ref ? ++o : --o) { push(cadr(p2)); push_integer(i); add(); @@ -8869,6 +10493,16 @@ } }; + // Factor a polynomial + + //define POLY p1 + //define X p2 + //define Z p3 + //define A p4 + //define B p5 + //define Q p6 + //define RESULT p7 + //define FACTOR p8 polycoeff = 0; factpoly_expo = 0; @@ -8898,6 +10532,15 @@ return restore(); }; + //----------------------------------------------------------------------------- + + // Input: tos-2 true polynomial + + // tos-1 free variable + + // Output: factored polynomial on stack + + //----------------------------------------------------------------------------- yyfactorpoly = function() { var checkingTheDivision, dividend, foundComplexRoot, foundRealRoot, h, i, i1, j1, l1, o, prev_expanding, previousFactorisation, ref, ref1, ref2, ref3, remainingPoly, whichRootsAreWeFinding; h = 0; @@ -8914,6 +10557,7 @@ push(p2); factpoly_expo = coeff() - 1; rationalize_coefficients(h); + // for univariate polynomials we could do factpoly_expo > 1 whichRootsAreWeFinding = "real"; remainingPoly = null; while (factpoly_expo > 0) { @@ -8923,6 +10567,7 @@ push_integer(0); p5 = pop(); } else { + //console.log("trying to find a " + whichRootsAreWeFinding + " root") if (whichRootsAreWeFinding === "real") { foundRealRoot = get_factor_from_real_root(); } else if (whichRootsAreWeFinding === "complex") { @@ -8934,16 +10579,19 @@ whichRootsAreWeFinding = "complex"; continue; } else { - push(p4); - push(p2); + // build the 1-degree polynomial out of the + // real solution that was just found. + push(p4); // A + push(p2); // x multiply(); - push(p5); + push(p5); // B add(); p8 = pop(); if (DEBUG) { console.log("success\nFACTOR=" + p8); } - + // factor out negative sign (not req'd because p4 > 1) + //if 0 /* if (isnegativeterm(p4)) push(p8) @@ -8952,19 +10600,30 @@ push(p7) negate_noexpand() p7 = pop() - */ + */ + //endif + + // p7 is the part of the polynomial that was factored so far, + // add the newly found factor to it. Note that we are not actually + // multiplying the polynomials fully, we are just leaving them + // expressed as (P1)*(P2), we are not expanding the product. push(p7); push(p8); multiply_noexpand(); p7 = pop(); + // ok now on stack we have the coefficients of the + // remaining part of the polynomial still to factor. + // Divide it by the newly-found factor so that + // the stack then contains the coefficients of the + // polynomial part still left to factor. yydivpoly(); while (factpoly_expo && isZeroAtomOrTensor(stack[polycoeff + factpoly_expo])) { factpoly_expo--; } push(zero); - for (i = o = 0, ref = factpoly_expo; 0 <= ref ? o <= ref : o >= ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = factpoly_expo; (0 <= ref ? o <= ref : o >= ref); i = 0 <= ref ? ++o : --o) { push(stack[polycoeff + i]); - push(p2); + push(p2); // the free variable push_integer(i); power(); multiply(); @@ -8972,23 +10631,32 @@ } remainingPoly = pop(); } + //console.log("real branch remainingPoly: " + remainingPoly) } else if (whichRootsAreWeFinding === "complex") { if (foundComplexRoot === 0) { break; } else { - push(p4); - push(p2); + // build the 2-degree polynomial out of the + // real solution that was just found. + push(p4); // A + push(p2); // x subtract(); - push(p4); + //console.log("first factor: " + stack[tos-1].toString()) + push(p4); // A conjugate(); - push(p2); + push(p2); // x subtract(); + //console.log("second factor: " + stack[tos-1].toString()) multiply(); + //if (factpoly_expo > 0 && isnegativeterm(stack[polycoeff+factpoly_expo])) + // negate() + // negate_noexpand() p8 = pop(); if (DEBUG) { console.log("success\nFACTOR=" + p8); } - + // factor out negative sign (not req'd because p4 > 1) + //if 0 /* if (isnegativeterm(p4)) push(p8) @@ -8997,18 +10665,25 @@ push(p7) negate_noexpand() p7 = pop() - */ + */ + //endif + + // p7 is the part of the polynomial that was factored so far, + // add the newly found factor to it. Note that we are not actually + // multiplying the polynomials fully, we are just leaving them + // expressed as (P1)*(P2), we are not expanding the product. push(p7); previousFactorisation = pop(); + //console.log("previousFactorisation: " + previousFactorisation) push(p7); push(p8); multiply_noexpand(); p7 = pop(); if (remainingPoly == null) { push(zero); - for (i = i1 = 0, ref1 = factpoly_expo; 0 <= ref1 ? i1 <= ref1 : i1 >= ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = factpoly_expo; (0 <= ref1 ? i1 <= ref1 : i1 >= ref1); i = 0 <= ref1 ? ++i1 : --i1) { push(stack[polycoeff + i]); - push(p2); + push(p2); // the free variable push_integer(i); power(); multiply(); @@ -9016,17 +10691,25 @@ } remainingPoly = pop(); } + //console.log("original polynomial (dividend): " + remainingPoly) dividend = remainingPoly; + //push(dividend) + //degree() + //startingDegree = pop() push(dividend); - push(p8); - push(p2); + //console.log("dividing " + stack[tos-1].toString() + " by " + p8) + push(p8); // divisor + push(p2); // X divpoly(); remainingPoly = pop(); push(remainingPoly); - push(p8); + push(p8); // divisor multiply(); checkingTheDivision = pop(); if (!equal(checkingTheDivision, dividend)) { + //push(dividend) + //gcd_sum() + //console.log("gcd top of stack: " + stack[tos-1].toString()) if (DEBUG) { console.log("we found a polynomial based on complex root and its conj but it doesn't divide the poly, quitting"); } @@ -9046,17 +10729,21 @@ restore(); return; } - - /* - if compare_numbers(startingDegree, remainingDegree) - * ok even if we found a complex root that - * together with the conjugate generates a poly in Z, - * that doesn't mean that the division would end up in Z. - * Example: 1+x^2+x^4+x^6 has +i and -i as one of its roots - * so a factor is 1+x^2 ( = (x+i)*(x-i)) - * BUT - */ - for (i = j1 = 0, ref2 = factpoly_expo; 0 <= ref2 ? j1 <= ref2 : j1 >= ref2; i = 0 <= ref2 ? ++j1 : --j1) { +//console.log("result: (still to be factored) " + remainingPoly) + + //push(remainingPoly) +//degree() +//remainingDegree = pop() +/* +if compare_numbers(startingDegree, remainingDegree) + * ok even if we found a complex root that + * together with the conjugate generates a poly in Z, + * that doesn't mean that the division would end up in Z. + * Example: 1+x^2+x^4+x^6 has +i and -i as one of its roots + * so a factor is 1+x^2 ( = (x+i)*(x-i)) + * BUT + */ + for (i = j1 = 0, ref2 = factpoly_expo; (0 <= ref2 ? j1 <= ref2 : j1 >= ref2); i = 0 <= ref2 ? ++j1 : --j1) { pop(); } push(remainingPoly); @@ -9066,10 +10753,13 @@ } } } + //console.log("factpoly_expo: " + factpoly_expo) + + // build the remaining unfactored part of the polynomial push(zero); - for (i = l1 = 0, ref3 = factpoly_expo; 0 <= ref3 ? l1 <= ref3 : l1 >= ref3; i = 0 <= ref3 ? ++l1 : --l1) { + for (i = l1 = 0, ref3 = factpoly_expo; (0 <= ref3 ? l1 <= ref3 : l1 >= ref3); i = 0 <= ref3 ? ++l1 : --l1) { push(stack[polycoeff + i]); - push(p2); + push(p2); // the free variable push_integer(i); power(); multiply(); @@ -9085,9 +10775,16 @@ yycondense(); expanding = prev_expanding; p1 = pop(); + //console.log("new poly with extracted common factor: " + p1) + //debugger + + // factor out negative sign if (factpoly_expo > 0 && isnegativeterm(stack[polycoeff + factpoly_expo])) { push(p1); + //prev_expanding = expanding + //expanding = 1 negate(); + //expanding = prev_expanding p1 = pop(); push(p7); negate_noexpand(); @@ -9108,20 +10805,23 @@ rationalize_coefficients = function(h) { var i, i1, o, ref, ref1, ref2, ref3; i = 0; + // LCM of all polynomial coefficients p7 = one; - for (i = o = ref = h, ref1 = tos; ref <= ref1 ? o < ref1 : o > ref1; i = ref <= ref1 ? ++o : --o) { + for (i = o = ref = h, ref1 = tos; (ref <= ref1 ? o < ref1 : o > ref1); i = ref <= ref1 ? ++o : --o) { push(stack[i]); denominator(); push(p7); lcm(); p7 = pop(); } - for (i = i1 = ref2 = h, ref3 = tos; ref2 <= ref3 ? i1 < ref3 : i1 > ref3; i = ref2 <= ref3 ? ++i1 : --i1) { +// multiply each coefficient by RESULT + for (i = i1 = ref2 = h, ref3 = tos; (ref2 <= ref3 ? i1 < ref3 : i1 > ref3); i = ref2 <= ref3 ? ++i1 : --i1) { push(p7); push(stack[i]); multiply(); stack[i] = pop(); } + // reciprocate RESULT push(p7); reciprocate(); p7 = pop(); @@ -9130,6 +10830,7 @@ } }; + //console.log print_list(p7) get_factor_from_real_root = function() { var a0, an, h, i, i1, j, j1, l1, m1, na0, nan, o, ref, ref1, ref2, ref3, ref4, rootsTries_i, rootsTries_j; i = 0; @@ -9141,7 +10842,7 @@ nan = 0; if (DEBUG) { push(zero); - for (i = o = 0, ref = factpoly_expo; 0 <= ref ? o <= ref : o >= ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = factpoly_expo; (0 <= ref ? o <= ref : o >= ref); i = 0 <= ref ? ++o : --o) { push(stack[polycoeff + i]); push(p2); push_integer(i); @@ -9163,16 +10864,18 @@ na0 = tos - a0; if (DEBUG) { console.log("divisors of base term"); - for (i = i1 = 0, ref1 = na0; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = na0; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { console.log(", " + stack[a0 + i]); } console.log("divisors of leading term"); - for (i = j1 = 0, ref2 = nan; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = nan; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { console.log(", " + stack[an + i]); } } - for (rootsTries_i = l1 = 0, ref3 = nan; 0 <= ref3 ? l1 < ref3 : l1 > ref3; rootsTries_i = 0 <= ref3 ? ++l1 : --l1) { - for (rootsTries_j = m1 = 0, ref4 = na0; 0 <= ref4 ? m1 < ref4 : m1 > ref4; rootsTries_j = 0 <= ref4 ? ++m1 : --m1) { +// try roots + for (rootsTries_i = l1 = 0, ref3 = nan; (0 <= ref3 ? l1 < ref3 : l1 > ref3); rootsTries_i = 0 <= ref3 ? ++l1 : --l1) { + for (rootsTries_j = m1 = 0, ref4 = na0; (0 <= ref4 ? m1 < ref4 : m1 > ref4); rootsTries_j = 0 <= ref4 ? ++m1 : --m1) { + //if DEBUG then console.log "nan: " + nan + " na0: " + na0 + " i: " + rootsTries_i + " j: " + rootsTries_j p4 = stack[an + rootsTries_i]; p5 = stack[a0 + rootsTries_j]; push(p5); @@ -9248,6 +10951,8 @@ } h = tos; an = tos; + // trying -1^(2/3) which generates a polynomial in Z + // generates x^2 + 2x + 1 push_integer(-1); push_rational(2, 3); power(); @@ -9270,6 +10975,9 @@ } return 1; } + // trying 1^(2/3) which generates a polynomial in Z + // http://www.wolframalpha.com/input/?i=(1)%5E(2%2F3) + // generates x^2 - 2x + 1 push_integer(1); push_rational(2, 3); power(); @@ -9292,6 +11000,8 @@ } return 1; } +// trying some simple complex numbers. All of these +// generate polynomials in Z for (rootsTries_i = o = -10; o <= 10; rootsTries_i = ++o) { for (rootsTries_j = i1 = 1; i1 <= 5; rootsTries_j = ++i1) { push_integer(rootsTries_i); @@ -9301,10 +11011,12 @@ add(); rect(); p4 = pop(); + //console.log("complex root finding: trying simple complex combination: " + p4) push(p4); p3 = pop(); push(p3); Evalpoly(); + //console.log("complex root finding result: " + p6) if (isZeroAtomOrTensor(p6)) { moveTos(h); if (DEBUG) { @@ -9321,11 +11033,26 @@ return 0; }; + //----------------------------------------------------------------------------- + + // Divide a polynomial by Ax+B + + // Input: on stack: polycoeff Dividend coefficients + + // factpoly_expo Degree of dividend + + // A (p4) As above + + // B (p5) As above + + // Output: on stack: polycoeff Contains quotient coefficients + + //----------------------------------------------------------------------------- yydivpoly = function() { var i, o, ref; i = 0; p6 = zero; - for (i = o = ref = factpoly_expo; ref <= 0 ? o < 0 : o > 0; i = ref <= 0 ? ++o : --o) { + for (i = o = ref = factpoly_expo; (ref <= 0 ? o < 0 : o > 0); i = ref <= 0 ? ++o : --o) { push(stack[polycoeff + i]); stack[polycoeff + i] = p6; push(p4); @@ -9344,11 +11071,12 @@ } }; + //console.log print_list(p6) Evalpoly = function() { var i, o, ref; i = 0; push(zero); - for (i = o = ref = factpoly_expo; ref <= 0 ? o <= 0 : o >= 0; i = ref <= 0 ? ++o : --o) { + for (i = o = ref = factpoly_expo; (ref <= 0 ? o <= 0 : o >= 0); i = ref <= 0 ? ++o : --o) { push(p3); multiply(); push(stack[polycoeff + i]); @@ -9361,6 +11089,24 @@ return p6 = pop(); }; + // Push expression factors onto the stack. For example... + + // Input + + // 2 + // 3x + 2x + 1 + + // Output on stack + + // [ 3 ] + // [ x^2 ] + // [ 2 ] + // [ x ] + // [ 1 ] + + // but not necessarily in that order. Returns the number of factors. + + // Local U *p is OK here because no functional path to garbage collector. factors = function(p) { var h; h = tos; @@ -9376,29 +11122,21 @@ return tos - h; }; + // Local U *p is OK here because no functional path to garbage collector. push_term_factors = function(p) { var results; if (car(p) === symbol(MULTIPLY)) { p = cdr(p); results = []; while (iscons(p)) { - push(car(p)); - results.push(p = cdr(p)); - } - return results; - } else { - return push(p); - } - }; - - - /* - Remove terms that involve a given symbol or expression. For example... - - filter(x^2 + x + 1, x) => 1 - - filter(x^2 + x + 1, x^2) => x + 1 - */ + push(car(p)); + results.push(p = cdr(p)); + } + return results; + } else { + return push(p); + } + }; Eval_filter = function() { var results; @@ -9416,16 +11154,6 @@ return results; }; - - /* - For example... - - push(F) - push(X) - filter() - F = pop() - */ - filter = function() { save(); p2 = pop(); @@ -9468,10 +11196,10 @@ n = p1.tensor.nelem; p3 = alloc_tensor(n); p3.tensor.ndim = p1.tensor.ndim; - for (i = o = 0, ref = p1.tensor.ndim; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = p1.tensor.ndim; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p3.tensor.dim[i] = p1.tensor.dim[i]; } - for (i = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { push(p1.tensor.elem[i]); push(p2); filter(); @@ -9511,6 +11239,9 @@ zzfloat = function() { save(); evaluatingAsFloats++; + //p1 = pop() + //push(cadr(p1)) + //push(p1) Eval(); yyfloat(); Eval(); @@ -9518,6 +11249,12 @@ return restore(); }; + // zzfloat doesn't necessarily result in a double + // , for example if there are variables. But + // in many of the tests there should be indeed + // a float, this line comes handy to highlight + // when that doesn't happen for those tests. + //checkFloatHasWorkedOutCompletely(stack[tos-1]) yyfloat = function() { var h, i, o, ref; i = 0; @@ -9537,7 +11274,7 @@ push(p1); copy_tensor(); p1 = pop(); - for (i = o = 0, ref = p1.tensor.nelem; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = p1.tensor.nelem; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push(p1.tensor.elem[i]); yyfloat(); p1.tensor.elem[i] = pop(); @@ -9599,21 +11336,25 @@ } }; - + // 'for' function /* x=0 y=2 for(do(x=sqrt(2+x),y=2*y/x),k,1,9) float(y) - + X: k B: 1...9 - + 1st parameter is the body 2nd parameter is the variable to loop with 3rd and 4th are the limits - */ + */ + //define A p3 + //define B p4 + //define I p5 + //define X p6 Eval_for = function() { var i, j, k, loopingVariable, o, ref, ref1; i = 0; @@ -9637,8 +11378,10 @@ push(p1); return; } + // remember contents of the index + // variable so we can put it back after the loop p4 = get_binding(loopingVariable); - for (i = o = ref = j, ref1 = k; ref <= ref1 ? o <= ref1 : o >= ref1; i = ref <= ref1 ? ++o : --o) { + for (i = o = ref = j, ref1 = k; (ref <= ref1 ? o <= ref1 : o >= ref1); i = ref <= ref1 ? ++o : --o) { push_integer(i); p5 = pop(); set_binding(loopingVariable, p5); @@ -9646,10 +11389,19 @@ Eval(); pop(); } + // put back the index variable to original content set_binding(loopingVariable, p4); + // return value return push_symbol(NIL); }; + //----------------------------------------------------------------------------- + + // Author : philippe.billet@noos.fr + + // Gamma function gamma(x) + + //----------------------------------------------------------------------------- Eval_gamma = function() { push(cadr(p1)); Eval(); @@ -9663,6 +11415,7 @@ }; gammaf = function() { + // double d p1 = pop(); if (isrational(p1) && MEQUAL(p1.q.a, 1) && MEQUAL(p1.q.b, 2)) { if (evaluatingAsFloats) { @@ -9686,6 +11439,12 @@ multiply(); return; } + + // if (p1->k == DOUBLE) { + // d = exp(lgamma(p1.d)) + // push_double(d) + // return + // } if (isnegativeterm(p1)) { if (evaluatingAsFloats) { push_double(Math.PI); @@ -9743,6 +11502,10 @@ } }; + // Greatest common denominator + // can also be run on polynomials, however + // it works only on the integers and it works + // by factoring the polynomials (not Euclidean algorithm) Eval_gcd = function() { var results; p1 = cdr(p1); @@ -9825,6 +11588,7 @@ }; gcd_polys = function(polyVar) { + // gcd of factors push(p1); push(polyVar); factorpoly(); @@ -9842,7 +11606,21 @@ if (DEBUG) { console.log("p2:" + p2.toString()); } + // In case one of two polynomials can be factored, + // (and only in that case), then + // we'll need to run gcd_factors on the two polynomials. + // (In case neither of them can be factored there is no gcd). + // However, gcd_factors expects two _products_ , and + // in case _one_ of the polynomials can't be factored it will look + // like a sum instead of a product. + // So, we'll have to make that sum to look like a factor: + // let's just turn it into a product with 1. + + // in case one of the two polys has been factored... if (car(p1) === symbol(MULTIPLY) || car(p2) === symbol(MULTIPLY)) { + // then make sure that if one of them is a single + // factor, we take the sum and wrap it into a + // multiplication by 1 if (car(p1) !== symbol(MULTIPLY)) { push_symbol(MULTIPLY); push(p1); @@ -9887,14 +11665,14 @@ gcd_powers_with_same_base = function() { if (car(p1) === symbol(POWER)) { - p3 = caddr(p1); - p1 = cadr(p1); + p3 = caddr(p1); // exponent + p1 = cadr(p1); // base } else { p3 = one; } if (car(p2) === symbol(POWER)) { - p4 = caddr(p2); - p2 = cadr(p2); + p4 = caddr(p2); // exponent + p2 = cadr(p2); // base } else { p4 = one; } @@ -9902,6 +11680,7 @@ push(one); return; } + // are both exponents numerical? if (isNumericAtom(p3) && isNumericAtom(p4)) { push(p1); if (lessp(p3, p4)) { @@ -9912,12 +11691,14 @@ power(); return; } + // are the exponents multiples of eah other? push(p3); push(p4); divide(); p5 = pop(); if (isNumericAtom(p5)) { push(p1); + // choose the smallest exponent if (car(p3) === symbol(MULTIPLY) && isNumericAtom(cadr(p3))) { p5 = cadr(p3); } else { @@ -9944,6 +11725,7 @@ push(one); return; } + // can't be equal because of test near beginning push(p1); if (isnegativenumber(p5)) { push(p3); @@ -9953,6 +11735,7 @@ return power(); }; + // in this case gcd is used as a composite function, i.e. gcd(gcd(gcd... gcd_sum_sum = function() { if (length(p1) !== length(p2)) { push(one); @@ -10039,6 +11822,7 @@ return results; }; + // Guess which symbol to use for derivative, integral, etc. guess = function() { var p; p = pop(); @@ -10058,12 +11842,30 @@ } }; + //----------------------------------------------------------------------------- + + // Hermite polynomial + + // Input: tos-2 x (can be a symbol or expr) + + // tos-1 n + + // Output: Result on stack + + //----------------------------------------------------------------------------- hermite = function() { save(); yyhermite(); return restore(); }; + // uses the recurrence relation H(x,n+1)=2*x*H(x,n)-2*n*H(x,n-1) + + //define X p1 + //define N p2 + //define Y p3 + //define Y1 p4 + //define Y0 p5 yyhermite = function() { var n; n = 0; @@ -10099,7 +11901,7 @@ push_integer(0); p4 = pop(); results = []; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p5 = p4; p4 = pop(); push(p1); @@ -10115,6 +11917,25 @@ return results; }; + //----------------------------------------------------------------------------- + + // Create a Hilbert matrix + + // Input: Dimension on stack + + // Output: Hilbert matrix on stack + + // Example: + + // > hilbert(5) + // ((1,1/2,1/3,1/4),(1/2,1/3,1/4,1/5),(1/3,1/4,1/5,1/6),(1/4,1/5,1/6,1/7)) + + //----------------------------------------------------------------------------- + + //define A p1 + //define N p2 + + //define AELEM(i, j) A->u.tensor->elem[i * n + j] hilbert = function() { var i, i1, j, n, o, ref, ref1; i = 0; @@ -10133,8 +11954,8 @@ } push_zero_matrix(n, n); p1 = pop(); - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { - for (j = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { + for (j = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); j = 0 <= ref1 ? ++i1 : --i1) { push_integer(i + j + 1); inverse(); p1.tensor.elem[i * n + j] = pop(); @@ -10144,18 +11965,6 @@ return restore(); }; - - /* - Returns the coefficient of the imaginary part of complex z - - z imag(z) - - ------- - - a + i b b - - exp(i a) sin(a) - */ - DEBUG_IMAG = false; Eval_imag = function() { @@ -10191,6 +12000,10 @@ return restore(); }; + // n is the total number of things on the stack. The first thing on the stack + // is the object to be indexed, followed by the indices themselves. + + // called by Eval_index index_function = function(n) { var i, i1, j1, k, l1, m, m1, ndim, nelem, o, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, s, t; i = 0; @@ -10208,7 +12021,7 @@ stop("too many indices for tensor"); } k = 0; - for (i = o = 0, ref = m; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = m; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push(stack[s + i + 1]); t = pop_integer(); if (t < 1 || t > p1.tensor.dim[i]) { @@ -10222,19 +12035,19 @@ restore(); return; } - for (i = i1 = ref1 = m, ref2 = ndim; ref1 <= ref2 ? i1 < ref2 : i1 > ref2; i = ref1 <= ref2 ? ++i1 : --i1) { + for (i = i1 = ref1 = m, ref2 = ndim; (ref1 <= ref2 ? i1 < ref2 : i1 > ref2); i = ref1 <= ref2 ? ++i1 : --i1) { k = k * p1.tensor.dim[i] + 0; } nelem = 1; - for (i = j1 = ref3 = m, ref4 = ndim; ref3 <= ref4 ? j1 < ref4 : j1 > ref4; i = ref3 <= ref4 ? ++j1 : --j1) { + for (i = j1 = ref3 = m, ref4 = ndim; (ref3 <= ref4 ? j1 < ref4 : j1 > ref4); i = ref3 <= ref4 ? ++j1 : --j1) { nelem *= p1.tensor.dim[i]; } p2 = alloc_tensor(nelem); p2.tensor.ndim = ndim - m; - for (i = l1 = ref5 = m, ref6 = ndim; ref5 <= ref6 ? l1 < ref6 : l1 > ref6; i = ref5 <= ref6 ? ++l1 : --l1) { + for (i = l1 = ref5 = m, ref6 = ndim; (ref5 <= ref6 ? l1 < ref6 : l1 > ref6); i = ref5 <= ref6 ? ++l1 : --l1) { p2.tensor.dim[i - m] = p1.tensor.dim[i]; } - for (i = m1 = 0, ref7 = nelem; 0 <= ref7 ? m1 < ref7 : m1 > ref7; i = 0 <= ref7 ? ++m1 : --m1) { + for (i = m1 = 0, ref7 = nelem; (0 <= ref7 ? m1 < ref7 : m1 > ref7); i = 0 <= ref7 ? ++m1 : --m1) { p2.tensor.elem[i] = p1.tensor.elem[k + i]; } check_tensor_dimensions(p1); @@ -10244,6 +12057,29 @@ return restore(); }; + //----------------------------------------------------------------------------- + + // Input: n Number of args on stack + + // tos-n Right-hand value + + // tos-n+1 Left-hand value + + // tos-n+2 First index + + // . + // . + // . + + // tos-1 Last index + + // Output: Result on stack + + //----------------------------------------------------------------------------- + + //define LVALUE p1 + //define RVALUE p2 + //define TMP p3 set_component = function(n) { var i, i1, j1, k, l1, m, m1, n1, ndim, o, ref, ref1, ref2, ref3, ref4, ref5, ref6, s, t; i = 0; @@ -10258,7 +12094,7 @@ s = tos - n; p2 = stack[s]; p1 = stack[s + 1]; - if (!istensor(p1)) { + if (!istensor(p1)) { // p1 is LVALUE stop("error in indexed assign: assigning to something that is not a tensor"); } ndim = p1.tensor.ndim; @@ -10267,7 +12103,7 @@ stop("error in indexed assign"); } k = 0; - for (i = o = 0, ref = m; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = m; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push(stack[s + i + 2]); t = pop_integer(); if (t < 1 || t > p1.tensor.dim[i]) { @@ -10275,22 +12111,23 @@ } k = k * p1.tensor.dim[i] + t - 1; } - for (i = i1 = ref1 = m, ref2 = ndim; ref1 <= ref2 ? i1 < ref2 : i1 > ref2; i = ref1 <= ref2 ? ++i1 : --i1) { + for (i = i1 = ref1 = m, ref2 = ndim; (ref1 <= ref2 ? i1 < ref2 : i1 > ref2); i = ref1 <= ref2 ? ++i1 : --i1) { k = k * p1.tensor.dim[i] + 0; } + // copy p3 = alloc_tensor(p1.tensor.nelem); p3.tensor.ndim = p1.tensor.ndim; - for (i = j1 = 0, ref3 = p1.tensor.ndim; 0 <= ref3 ? j1 < ref3 : j1 > ref3; i = 0 <= ref3 ? ++j1 : --j1) { + for (i = j1 = 0, ref3 = p1.tensor.ndim; (0 <= ref3 ? j1 < ref3 : j1 > ref3); i = 0 <= ref3 ? ++j1 : --j1) { p3.tensor.dim[i] = p1.tensor.dim[i]; } - for (i = l1 = 0, ref4 = p1.tensor.nelem; 0 <= ref4 ? l1 < ref4 : l1 > ref4; i = 0 <= ref4 ? ++l1 : --l1) { + for (i = l1 = 0, ref4 = p1.tensor.nelem; (0 <= ref4 ? l1 < ref4 : l1 > ref4); i = 0 <= ref4 ? ++l1 : --l1) { p3.tensor.elem[i] = p1.tensor.elem[i]; } check_tensor_dimensions(p1); check_tensor_dimensions(p3); p1 = p3; if (ndim === m) { - if (istensor(p2)) { + if (istensor(p2)) { // p2 is RVALUE stop("error in indexed assign"); } p1.tensor.elem[k] = p2; @@ -10300,18 +12137,21 @@ restore(); return; } - if (!istensor(p2)) { + if (!istensor(p2)) { // p2 is RVALUE stop("error in indexed assign"); } - if (ndim - m !== p2.tensor.ndim) { + if (ndim - m !== p2.tensor.ndim) { // p2 is RVALUE stop("error in indexed assign"); } - for (i = m1 = 0, ref5 = p2.tensor.ndim; 0 <= ref5 ? m1 < ref5 : m1 > ref5; i = 0 <= ref5 ? ++m1 : --m1) { +// p2 is RVALUE + for (i = m1 = 0, ref5 = p2.tensor.ndim; (0 <= ref5 ? m1 < ref5 : m1 > ref5); i = 0 <= ref5 ? ++m1 : --m1) { if (p1.tensor.dim[m + i] !== p2.tensor.dim[i]) { stop("error in indexed assign"); } } - for (i = n1 = 0, ref6 = p2.tensor.nelem; 0 <= ref6 ? n1 < ref6 : n1 > ref6; i = 0 <= ref6 ? ++n1 : --n1) { +// p2 is RVALUE +// copy rvalue + for (i = n1 = 0, ref6 = p2.tensor.nelem; (0 <= ref6 ? n1 < ref6 : n1 > ref6); i = 0 <= ref6 ? ++n1 : --n1) { p1.tensor.elem[k + i] = p2.tensor.elem[i]; } check_tensor_dimensions(p1); @@ -10321,80 +12161,17 @@ return restore(); }; - - /* dot ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept - - Parameters - ---------- - a,b,... - - General description - ------------------- - - The inner (or dot) operator gives products of vectors, - matrices, and tensors. - - Note that for Algebrite, the elements of a vector/matrix - can only be scalars. This allows for example to flesh out - matrix multiplication using the usual multiplication. - So for example block-representations are not allowed. - - There is an aweful lot of confusion between sw packages on - what dot and inner do. - - First off, the "dot" operator is different from the - mathematical notion of dot product, which can be - slightly confusing. - - The mathematical notion of dot product is here: - http://mathworld.wolfram.com/DotProduct.html - - However, "dot" does that and a bunch of other things, - i.e. in Algebrite - dot/inner does what the dot of Mathematica does, i.e.: - - scalar product of vectors: - - inner((a, b, c), (x, y, z)) - > a x + b y + c z - - products of matrices and vectors: - - inner(((a, b), (c,d)), (x, y)) - > (a x + b y,c x + d y) - - inner((x, y), ((a, b), (c,d))) - > (a x + c y,b x + d y) - - inner((x, y), ((a, b), (c,d)), (r, s)) - > a r x + b s x + c r y + d s y - - matrix product: - - inner(((a,b),(c,d)),((r,s),(t,u))) - > ((a r + b t,a s + b u),(c r + d t,c s + d u)) - - the "dot/inner" operator is associative and - distributive but not commutative. - - In Mathematica, Inner is a generalisation of Dot where - the user can specify the multiplication and the addition - operators. - But here in Algebrite they do the same thing. - - https://reference.wolfram.com/language/ref/Dot.html - https://reference.wolfram.com/language/ref/Inner.html - - http://uk.mathworks.com/help/matlab/ref/dot.html - http://uk.mathworks.com/help/matlab/ref/mtimes.html - */ - Eval_inner = function() { var difference, i, i1, j1, l1, moretheArguments, o, operands, ref, ref1, ref2, ref3, refinedOperands, results, secondArgument, shift, theArguments; + + // if there are more than two arguments then + // reduce it to a more standard version + // of two arguments, which means we need to + // transform the arguments into a tree of + // inner products e.g. + // inner(a,b,c) becomes inner(a,inner(b,c)) + // this is so we can get to a standard binary-tree + // version that is simpler to manipulate. theArguments = []; theArguments.push(car(cdr(p1))); secondArgument = car(cdr(cdr(p1))); @@ -10406,12 +12183,13 @@ theArguments.push(car(moretheArguments)); moretheArguments = cdr(moretheArguments); } + // make it so e.g. inner(a,b,c) becomes inner(a,inner(b,c)) if (theArguments.length > 2) { push_symbol(INNER); push(theArguments[theArguments.length - 2]); push(theArguments[theArguments.length - 1]); list(3); - for (i = o = 2, ref = theArguments.length; 2 <= ref ? o < ref : o > ref; i = 2 <= ref ? ++o : --o) { + for (i = o = 2, ref = theArguments.length; (2 <= ref ? o < ref : o > ref); i = 2 <= ref ? ++o : --o) { push_symbol(INNER); swap(); push(theArguments[theArguments.length - i - 1]); @@ -10422,10 +12200,17 @@ Eval_inner(); return; } + // TODO we have to take a look at the whole + // sequence of operands and make simplifications + // on that... operands = []; get_innerprod_factors(p1, operands); + //console.log "printing operands --------" + //for i in [0...operands.length] + // console.log "operand " + i + " : " + operands[i] refinedOperands = []; - for (i = i1 = 0, ref1 = operands.length; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { +// removing all identity matrices + for (i = i1 = 0, ref1 = operands.length; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { if (operands[i] === symbol(SYMBOL_IDENTITY_MATRIX)) { continue; } else { @@ -10435,8 +12220,14 @@ operands = refinedOperands; refinedOperands = []; if (operands.length > 1) { + // removing all consecutive pairs of inverses + // so we can answer that inv(a)·a results in the + // identity matrix. We want to catch symbolic inverses + // not numeric inverses, those will just take care + // of themselves when multiplied shift = 0; - for (i = j1 = 0, ref2 = operands.length; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = operands.length; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { + //console.log "comparing if " + operands[i+shift] + " and " + operands[i+shift+1] + " are inverses of each other" if ((i + shift + 1) <= (operands.length - 1)) { if (!(isNumericAtomOrTensor(operands[i + shift]) || isNumericAtomOrTensor(operands[i + shift + 1]))) { push(operands[i + shift]); @@ -10446,6 +12237,7 @@ Eval(); subtract(); difference = pop(); + //console.log "result: " + difference if (isZeroAtomOrTensor(difference)) { shift += 1; } else { @@ -10457,7 +12249,9 @@ } else { break; } + //console.log "i: " + i + " shift: " + shift + " operands.length: " + operands.length if (i + shift === operands.length - 2) { + //console.log "adding last operand 2 " refinedOperands.push(operands[operands.length - 1]); } if (i + shift >= operands.length - 1) { @@ -10466,9 +12260,19 @@ } operands = refinedOperands; } + //console.log "refined operands --------" + //for i in [0...refinedOperands.length] + // console.log "refined operand " + i + " : " + refinedOperands[i] + + //console.log "stack[tos-1]: " + stack[tos-1] + + // now rebuild the arguments, just using the + // refined operands push(symbol(INNER)); + //console.log "rebuilding the argument ----" if (operands.length > 0) { - for (i = l1 = 0, ref3 = operands.length; 0 <= ref3 ? l1 < ref3 : l1 > ref3; i = 0 <= ref3 ? ++l1 : --l1) { + for (i = l1 = 0, ref3 = operands.length; (0 <= ref3 ? l1 < ref3 : l1 > ref3); i = 0 <= ref3 ? ++l1 : --l1) { + //console.log "pushing " + operands[i] push(operands[i]); } } else { @@ -10476,6 +12280,7 @@ push(symbol(SYMBOL_IDENTITY_MATRIX)); return; } + //console.log "list(operands.length): " + (operands.length+1) list(operands.length + 1); p1 = pop(); p1 = cdr(p1); @@ -10492,11 +12297,19 @@ return results; }; + // inner definition inner = function() { var arg1, arg2, arg3, subtractionResult; save(); p2 = pop(); p1 = pop(); + // more in general, when a and b are scalars, + // inner(a*M1, b*M2) is equal to + // a*b*inner(M1,M2), but of course we can only + // "bring out" in a and b the scalars, because + // it's the only commutative part. + // that's going to be trickier to do in general + // but let's start with just the signs. if (isnegativeterm(p2) && isnegativeterm(p1)) { push(p2); negate(); @@ -10505,9 +12318,15 @@ negate(); p1 = pop(); } + // since inner is associative, + // put it in a canonical form i.e. + // inner(inner(a,b),c) -> + // inner(a,inner(b,c)) + // so that we can recognise when they + // are equal. if (isinnerordot(p1)) { - arg1 = car(cdr(p1)); - arg2 = car(cdr(cdr(p1))); + arg1 = car(cdr(p1)); //a + arg2 = car(cdr(cdr(p1))); //b arg3 = p2; p1 = arg1; push(arg2); @@ -10515,6 +12334,9 @@ inner(); p2 = pop(); } + // Check if one of the operands is the identity matrix + // we could maybe use Eval_testeq here but + // this seems to suffice? if (p1 === symbol(SYMBOL_IDENTITY_MATRIX)) { push(p2); restore(); @@ -10539,6 +12361,8 @@ return; } } + // if either operand is a sum then distribute + // (if we are in expanding mode) if (expanding && isadd(p1)) { p1 = cdr(p1); push(zero); @@ -10567,14 +12391,34 @@ } push(p1); push(p2); + // there are 8 remaining cases here, since each of the + // two arguments can only be a scalar/tensor/unknown + // and the tensor - tensor case was caught + // upper in the code if (istensor(p1) && isNumericAtom(p2)) { + // one case covered by this branch: + // tensor - scalar tensor_times_scalar(); } else if (isNumericAtom(p1) && istensor(p2)) { + // one case covered by this branch: + // scalar - tensor scalar_times_tensor(); } else { if (isNumericAtom(p1) || isNumericAtom(p2)) { + // three cases covered by this branch: + // unknown - scalar + // scalar - unknown + // scalar - scalar + // in these cases a normal multiplication + // will be OK multiply(); } else { + // three cases covered by this branch: + // unknown - unknown + // unknown - tensor + // tensor - unknown + // in this case we can't use normal + // multiplication. pop(); pop(); push_symbol(INNER); @@ -10589,6 +12433,7 @@ return restore(); }; + // inner product of tensors p1 and p2 inner_f = function() { var a, ak, b, bk, c, i, i1, j, j1, k, l1, m1, n, n1, ndim, o, o1, ref, ref1, ref2, ref3, ref4, ref5, ref6; i = 0; @@ -10603,22 +12448,38 @@ } a = p1.tensor.elem; b = p2.tensor.elem; + //--------------------------------------------------------------------- + + // ak is the number of rows in tensor A + + // bk is the number of columns in tensor B + + // Example: + + // A[3][3][4] B[4][4][3] + + // 3 3 ak = 3 * 3 = 9 + + // 4 3 bk = 4 * 3 = 12 + + //--------------------------------------------------------------------- ak = 1; - for (i = o = 0, ref = p1.tensor.ndim - 1; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = p1.tensor.ndim - 1; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { ak *= p1.tensor.dim[i]; } bk = 1; - for (i = i1 = 1, ref1 = p2.tensor.ndim; 1 <= ref1 ? i1 < ref1 : i1 > ref1; i = 1 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 1, ref1 = p2.tensor.ndim; (1 <= ref1 ? i1 < ref1 : i1 > ref1); i = 1 <= ref1 ? ++i1 : --i1) { bk *= p2.tensor.dim[i]; } p3 = alloc_tensor(ak * bk); c = p3.tensor.elem; - for (i = j1 = 0, ref2 = ak; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { - for (j = l1 = 0, ref3 = n; 0 <= ref3 ? l1 < ref3 : l1 > ref3; j = 0 <= ref3 ? ++l1 : --l1) { +// new method copied from ginac http://www.ginac.de/ + for (i = j1 = 0, ref2 = ak; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { + for (j = l1 = 0, ref3 = n; (0 <= ref3 ? l1 < ref3 : l1 > ref3); j = 0 <= ref3 ? ++l1 : --l1) { if (isZeroAtomOrTensor(a[i * n + j])) { continue; } - for (k = m1 = 0, ref4 = bk; 0 <= ref4 ? m1 < ref4 : m1 > ref4; k = 0 <= ref4 ? ++m1 : --m1) { + for (k = m1 = 0, ref4 = bk; (0 <= ref4 ? m1 < ref4 : m1 > ref4); k = 0 <= ref4 ? ++m1 : --m1) { push(a[i * n + j]); push(b[j * bk + k]); multiply(); @@ -10628,22 +12489,51 @@ } } } + //--------------------------------------------------------------------- + + // Note on understanding "k * bk + j" + + // k * bk because each element of a column is bk locations apart + + // + j because the beginnings of all columns are in the first bk + // locations + + // Example: n = 2, bk = 6 + + // b111 <- 1st element of 1st column + // b112 <- 1st element of 2nd column + // b113 <- 1st element of 3rd column + // b121 <- 1st element of 4th column + // b122 <- 1st element of 5th column + // b123 <- 1st element of 6th column + + // b211 <- 2nd element of 1st column + // b212 <- 2nd element of 2nd column + // b213 <- 2nd element of 3rd column + // b221 <- 2nd element of 4th column + // b222 <- 2nd element of 5th column + // b223 <- 2nd element of 6th column + + //--------------------------------------------------------------------- if (ndim === 0) { return push(p3.tensor.elem[0]); } else { p3.tensor.ndim = ndim; j = 0; - for (i = n1 = 0, ref5 = p1.tensor.ndim - 1; 0 <= ref5 ? n1 < ref5 : n1 > ref5; i = 0 <= ref5 ? ++n1 : --n1) { + for (i = n1 = 0, ref5 = p1.tensor.ndim - 1; (0 <= ref5 ? n1 < ref5 : n1 > ref5); i = 0 <= ref5 ? ++n1 : --n1) { p3.tensor.dim[i] = p1.tensor.dim[i]; } j = p1.tensor.ndim - 1; - for (i = o1 = 0, ref6 = p2.tensor.ndim - 1; 0 <= ref6 ? o1 < ref6 : o1 > ref6; i = 0 <= ref6 ? ++o1 : --o1) { + for (i = o1 = 0, ref6 = p2.tensor.ndim - 1; (0 <= ref6 ? o1 < ref6 : o1 > ref6); i = 0 <= ref6 ? ++o1 : --o1) { p3.tensor.dim[j + i] = p2.tensor.dim[i + 1]; } return push(p3); } }; + // Algebrite.run('c·(b+a)ᵀ·inv((a+b)ᵀ)·d').toString(); + // Algebrite.run('c*(b+a)ᵀ·inv((a+b)ᵀ)·d').toString(); + // Algebrite.run('(c·(b+a)ᵀ)·(inv((a+b)ᵀ)·d)').toString(); get_innerprod_factors = function(tree, factors_accumulator) { if (!iscons(tree)) { add_factor_to_accumulator(tree, factors_accumulator); @@ -10654,6 +12544,7 @@ return; } if (isinnerordot(tree)) { + // console.log "there is inner at top, recursing on the operands" get_innerprod_factors(car(cdr(tree)), factors_accumulator); get_innerprod_factors(cdr(cdr(tree)), factors_accumulator); return; @@ -10663,32 +12554,369 @@ add_factor_to_accumulator = function(tree, factors_accumulator) { if (tree !== symbol(NIL)) { + // console.log ">> adding to factors_accumulator: " + tree return factors_accumulator.push(tree); } }; - - /* - Table of integrals - - The symbol f is just a dummy symbol for creating a list f(A,B,C,C,...) where - - A is the template expression - - B is the result expression - - C is an optional list of conditional expressions - */ - - itab = ["f(a,a*x)", "f(1/x,log(x))", "f(x^a,x^(a+1)/(a+1))", "f(x^(-2),-x^(-1))", "f(x^(-1/2),2*x^(1/2))", "f(x^(1/2),2/3*x^(3/2))", "f(x,x^2/2)", "f(x^2,x^3/3)", "f(exp(a*x),1/a*exp(a*x))", "f(exp(a*x+b),1/a*exp(a*x+b))", "f(x*exp(a*x^2),exp(a*x^2)/(2*a))", "f(x*exp(a*x^2+b),exp(a*x^2+b)/(2*a))", "f(log(a*x),x*log(a*x)-x)", "f(a^x,a^x/log(a),or(not(number(a)),a>0))", "f(1/(a+x^2),1/sqrt(a)*arctan(x/sqrt(a)),or(not(number(a)),a>0))", "f(1/(a-x^2),1/sqrt(a)*arctanh(x/sqrt(a)))", "f(1/sqrt(a-x^2),arcsin(x/(sqrt(a))))", "f(1/sqrt(a+x^2),log(x+sqrt(a+x^2)))", "f(1/(a+b*x),1/b*log(a+b*x))", "f(1/(a+b*x)^2,-1/(b*(a+b*x)))", "f(1/(a+b*x)^3,-1/(2*b)*1/(a+b*x)^2)", "f(x/(a+b*x),x/b-a*log(a+b*x)/b/b)", "f(x/(a+b*x)^2,1/b^2*(log(a+b*x)+a/(a+b*x)))", "f(x^2/(a+b*x),1/b^2*(1/2*(a+b*x)^2-2*a*(a+b*x)+a^2*log(a+b*x)))", "f(x^2/(a+b*x)^2,1/b^3*(a+b*x-2*a*log(a+b*x)-a^2/(a+b*x)))", "f(x^2/(a+b*x)^3,1/b^3*(log(a+b*x)+2*a/(a+b*x)-1/2*a^2/(a+b*x)^2))", "f(1/x*1/(a+b*x),-1/a*log((a+b*x)/x))", "f(1/x*1/(a+b*x)^2,1/a*1/(a+b*x)-1/a^2*log((a+b*x)/x))", "f(1/x*1/(a+b*x)^3,1/a^3*(1/2*((2*a+b*x)/(a+b*x))^2+log(x/(a+b*x))))", "f(1/x^2*1/(a+b*x),-1/(a*x)+b/a^2*log((a+b*x)/x))", "f(1/x^3*1/(a+b*x),(2*b*x-a)/(2*a^2*x^2)+b^2/a^3*log(x/(a+b*x)))", "f(1/x^2*1/(a+b*x)^2,-(a+2*b*x)/(a^2*x*(a+b*x))+2*b/a^3*log((a+b*x)/x))", "f(1/(a+b*x^2),1/sqrt(a*b)*arctan(x*sqrt(a*b)/a),or(not(number(a*b)),a*b>0))", "f(1/(a+b*x^2),1/(2*sqrt(-a*b))*log((a+x*sqrt(-a*b))/(a-x*sqrt(-a*b))),or(not(number(a*b)),a*b<0))", "f(x/(a+b*x^2),1/2*1/b*log(a+b*x^2))", "f(x^2/(a+b*x^2),x/b-a/b*integral(1/(a+b*x^2),x))", "f(1/(a+b*x^2)^2,x/(2*a*(a+b*x^2))+1/2*1/a*integral(1/(a+b*x^2),x))", "f(1/x*1/(a+b*x^2),1/2*1/a*log(x^2/(a+b*x^2)))", "f(1/x^2*1/(a+b*x^2),-1/(a*x)-b/a*integral(1/(a+b*x^2),x))", "f(1/(a+b*x^3),1/3*1/a*(a/b)^(1/3)*(1/2*log(((a/b)^(1/3)+x)^3/(a+b*x^3))+sqrt(3)*arctan((2*x-(a/b)^(1/3))*(a/b)^(-1/3)/sqrt(3))))", "f(x^2/(a+b*x^3),1/3*1/b*log(a+b*x^3))", "f(x/(a+b*x^4),1/2*sqrt(b/a)/b*arctan(x^2*sqrt(b/a)),or(not(number(a*b)),a*b>0))", "f(x/(a+b*x^4),1/4*sqrt(-b/a)/b*log((x^2-sqrt(-a/b))/(x^2+sqrt(-a/b))),or(not(number(a*b)),a*b<0))", "f(x^3/(a+b*x^4),1/4*1/b*log(a+b*x^4))", "f(sqrt(a+b*x),2/3*1/b*sqrt((a+b*x)^3))", "f(x*sqrt(a+b*x),-2*(2*a-3*b*x)*sqrt((a+b*x)^3)/15/b^2)", "f(x^2*sqrt(a+b*x),2*(8*a^2-12*a*b*x+15*b^2*x^2)*sqrt((a+b*x)^3)/105/b^3)", "f(sqrt(a+b*x)/x,2*sqrt(a+b*x)+a*integral(1/x*1/sqrt(a+b*x),x))", "f(sqrt(a+b*x)/x^2,-sqrt(a+b*x)/x+b/2*integral(1/x*1/sqrt(a+b*x),x))", "f(1/sqrt(a+b*x),2*sqrt(a+b*x)/b)", "f(x/sqrt(a+b*x),-2/3*(2*a-b*x)*sqrt(a+b*x)/b^2)", "f(x^2/sqrt(a+b*x),2/15*(8*a^2-4*a*b*x+3*b^2*x^2)*sqrt(a+b*x)/b^3)", "f(1/x*1/sqrt(a+b*x),1/sqrt(a)*log((sqrt(a+b*x)-sqrt(a))/(sqrt(a+b*x)+sqrt(a))),or(not(number(a)),a>0))", "f(1/x*1/sqrt(a+b*x),2/sqrt(-a)*arctan(sqrt(-(a+b*x)/a)),or(not(number(a)),a<0))", "f(1/x^2*1/sqrt(a+b*x),-sqrt(a+b*x)/a/x-1/2*b/a*integral(1/x*1/sqrt(a+b*x),x))", "f(sqrt(x^2+a),1/2*(x*sqrt(x^2+a)+a*log(x+sqrt(x^2+a))))", "f(1/sqrt(x^2+a),log(x+sqrt(x^2+a)))", "f(1/x*1/sqrt(x^2+a),arcsec(x/sqrt(-a))/sqrt(-a),or(not(number(a)),a<0))", "f(1/x*1/sqrt(x^2+a),-1/sqrt(a)*log((sqrt(a)+sqrt(x^2+a))/x),or(not(number(a)),a>0))", "f(sqrt(x^2+a)/x,sqrt(x^2+a)-sqrt(a)*log((sqrt(a)+sqrt(x^2+a))/x),or(not(number(a)),a>0))", "f(sqrt(x^2+a)/x,sqrt(x^2+a)-sqrt(-a)*arcsec(x/sqrt(-a)),or(not(number(a)),a<0))", "f(x/sqrt(x^2+a),sqrt(x^2+a))", "f(x*sqrt(x^2+a),1/3*sqrt((x^2+a)^3))", "f(sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/4*(x*sqrt((x^2+a^(1/3))^3)+3/2*a^(1/3)*x*sqrt(x^2+a^(1/3))+3/2*a^(2/3)*log(x+sqrt(x^2+a^(1/3)))))", "f(sqrt(-a+x^6-3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/4*(x*sqrt((x^2-a^(1/3))^3)-3/2*a^(1/3)*x*sqrt(x^2-a^(1/3))+3/2*a^(2/3)*log(x+sqrt(x^2-a^(1/3)))))", "f(1/sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),x/a^(1/3)/sqrt(x^2+a^(1/3)))", "f(x/sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),-1/sqrt(x^2+a^(1/3)))", "f(x*sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/5*sqrt((x^2+a^(1/3))^5))", "f(x^2*sqrt(x^2+a),1/4*x*sqrt((x^2+a)^3)-1/8*a*x*sqrt(x^2+a)-1/8*a^2*log(x+sqrt(x^2+a)))", "f(x^3*sqrt(x^2+a),(1/5*x^2-2/15*a)*sqrt((x^2+a)^3),and(number(a),a>0))", "f(x^3*sqrt(x^2+a),sqrt((x^2+a)^5)/5-a*sqrt((x^2+a)^3)/3,and(number(a),a<0))", "f(x^2/sqrt(x^2+a),1/2*x*sqrt(x^2+a)-1/2*a*log(x+sqrt(x^2+a)))", "f(x^3/sqrt(x^2+a),1/3*sqrt((x^2+a)^3)-a*sqrt(x^2+a))", "f(1/x^2*1/sqrt(x^2+a),-sqrt(x^2+a)/a/x)", "f(1/x^3*1/sqrt(x^2+a),-1/2*sqrt(x^2+a)/a/x^2+1/2*log((sqrt(a)+sqrt(x^2+a))/x)/a^(3/2),or(not(number(a)),a>0))", "f(1/x^3*1/sqrt(x^2-a),1/2*sqrt(x^2-a)/a/x^2+1/2*1/(a^(3/2))*arcsec(x/(a^(1/2))),or(not(number(a)),a>0))", "f(x^2*sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/6*x*sqrt((x^2+a^(1/3))^5)-1/24*a^(1/3)*x*sqrt((x^2+a^(1/3))^3)-1/16*a^(2/3)*x*sqrt(x^2+a^(1/3))-1/16*a*log(x+sqrt(x^2+a^(1/3))),or(not(number(a)),a>0))", "f(x^2*sqrt(-a-3*a^(1/3)*x^4+3*a^(2/3)*x^2+x^6),1/6*x*sqrt((x^2-a^(1/3))^5)+1/24*a^(1/3)*x*sqrt((x^2-a^(1/3))^3)-1/16*a^(2/3)*x*sqrt(x^2-a^(1/3))+1/16*a*log(x+sqrt(x^2-a^(1/3))),or(not(number(a)),a>0))", "f(x^3*sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/7*sqrt((x^2+a^(1/3))^7)-1/5*a^(1/3)*sqrt((x^2+a^(1/3))^5),or(not(number(a)),a>0))", "f(x^3*sqrt(-a-3*a^(1/3)*x^4+3*a^(2/3)*x^2+x^6),1/7*sqrt((x^2-a^(1/3))^7)+1/5*a^(1/3)*sqrt((x^2-a^(1/3))^5),or(not(number(a)),a>0))", "f(1/(x-a)/sqrt(x^2-a^2),-sqrt(x^2-a^2)/a/(x-a))", "f(1/(x+a)/sqrt(x^2-a^2),sqrt(x^2-a^2)/a/(x+a))", "f(sqrt(a-x^2),1/2*(x*sqrt(a-x^2)+a*arcsin(x/sqrt(abs(a)))))", "f(1/x*1/sqrt(a-x^2),-1/sqrt(a)*log((sqrt(a)+sqrt(a-x^2))/x),or(not(number(a)),a>0))", "f(sqrt(a-x^2)/x,sqrt(a-x^2)-sqrt(a)*log((sqrt(a)+sqrt(a-x^2))/x),or(not(number(a)),a>0))", "f(x/sqrt(a-x^2),-sqrt(a-x^2))", "f(x*sqrt(a-x^2),-1/3*sqrt((a-x^2)^3))", "f(x^2*sqrt(a-x^2),-x/4*sqrt((a-x^2)^3)+1/8*a*(x*sqrt(a-x^2)+a*arcsin(x/sqrt(a))),or(not(number(a)),a>0))", "f(x^3*sqrt(a-x^2),(-1/5*x^2-2/15*a)*sqrt((a-x^2)^3),or(not(number(a)),a>0))", "f(x^2/sqrt(a-x^2),-x/2*sqrt(a-x^2)+a/2*arcsin(x/sqrt(a)),or(not(number(a)),a>0))", "f(1/x^2*1/sqrt(a-x^2),-sqrt(a-x^2)/a/x,or(not(number(a)),a>0))", "f(sqrt(a-x^2)/x^2,-sqrt(a-x^2)/x-arcsin(x/sqrt(a)),or(not(number(a)),a>0))", "f(sqrt(a-x^2)/x^3,-1/2*sqrt(a-x^2)/x^2+1/2*log((sqrt(a)+sqrt(a-x^2))/x)/sqrt(a),or(not(number(a)),a>0))", "f(sqrt(a-x^2)/x^4,-1/3*sqrt((a-x^2)^3)/a/x^3,or(not(number(a)),a>0))", "f(sqrt(a*x^2+b),x*sqrt(a*x^2+b)/2+b*log(x*sqrt(a)+sqrt(a*x^2+b))/2/sqrt(a),and(number(a),a>0))", "f(sqrt(a*x^2+b),x*sqrt(a*x^2+b)/2+b*arcsin(x*sqrt(-a/b))/2/sqrt(-a),and(number(a),a<0))", "f(sin(a*x),-cos(a*x)/a)", "f(cos(a*x),sin(a*x)/a)", "f(tan(a*x),-log(cos(a*x))/a)", "f(1/tan(a*x),log(sin(a*x))/a)", "f(1/cos(a*x),log(tan(pi/4+a*x/2))/a)", "f(1/sin(a*x),log(tan(a*x/2))/a)", "f(sin(a*x)^2,x/2-sin(2*a*x)/(4*a))", "f(sin(a*x)^3,-cos(a*x)*(sin(a*x)^2+2)/(3*a))", "f(sin(a*x)^4,3/8*x-sin(2*a*x)/(4*a)+sin(4*a*x)/(32*a))", "f(cos(a*x)^2,x/2+sin(2*a*x)/(4*a))", "f(cos(a*x)^3,sin(a*x)*(cos(a*x)^2+2)/(3*a))", "f(cos(a*x)^4,3/8*x+sin(2*a*x)/(4*a)+sin(4*a*x)/(32*a))", "f(1/sin(a*x)^2,-1/(a*tan(a*x)))", "f(1/cos(a*x)^2,tan(a*x)/a)", "f(sin(a*x)*cos(a*x),sin(a*x)^2/(2*a))", "f(sin(a*x)^2*cos(a*x)^2,-sin(4*a*x)/(32*a)+x/8)", "f(sin(a*x)/cos(a*x)^2,1/(a*cos(a*x)))", "f(sin(a*x)^2/cos(a*x),(log(tan(pi/4+a*x/2))-sin(a*x))/a)", "f(cos(a*x)/sin(a*x)^2,-1/(a*sin(a*x)))", "f(1/(sin(a*x)*cos(a*x)),log(tan(a*x))/a)", "f(1/(sin(a*x)*cos(a*x)^2),(1/cos(a*x)+log(tan(a*x/2)))/a)", "f(1/(sin(a*x)^2*cos(a*x)),(log(tan(pi/4+a*x/2))-1/sin(a*x))/a)", "f(1/(sin(a*x)^2*cos(a*x)^2),-2/(a*tan(2*a*x)))", "f(sin(a+b*x),-cos(a+b*x)/b)", "f(cos(a+b*x),sin(a+b*x)/b)", "f(1/(b+b*sin(a*x)),-tan(pi/4-a*x/2)/a/b)", "f(1/(b-b*sin(a*x)),tan(pi/4+a*x/2)/a/b)", "f(1/(b+b*cos(a*x)),tan(a*x/2)/a/b)", "f(1/(b-b*cos(a*x)),-1/tan(a*x/2)/a/b)", "f(1/(a+b*sin(x)),1/sqrt(b^2-a^2)*log((a*tan(x/2)+b-sqrt(b^2-a^2))/(a*tan(x/2)+b+sqrt(b^2-a^2))),b^2-a^2)", "f(1/(a+b*cos(x)),1/sqrt(b^2-a^2)*log((sqrt(b^2-a^2)*tan(x/2)+a+b)/(sqrt(b^2-a^2)*tan(x/2)-a-b)),b^2-a^2)", "f(x*sin(a*x),sin(a*x)/a^2-x*cos(a*x)/a)", "f(x^2*sin(a*x),2*x*sin(a*x)/a^2-(a^2*x^2-2)*cos(a*x)/a^3)", "f(x*cos(a*x),cos(a*x)/a^2+x*sin(a*x)/a)", "f(x^2*cos(a*x),2*x*cos(a*x)/a^2+(a^2*x^2-2)*sin(a*x)/a^3)", "f(arcsin(a*x),x*arcsin(a*x)+sqrt(1-a^2*x^2)/a)", "f(arccos(a*x),x*arccos(a*x)-sqrt(1-a^2*x^2)/a)", "f(arctan(a*x),x*arctan(a*x)-1/2*log(1+a^2*x^2)/a)", "f(x*log(a*x),x^2*log(a*x)/2-x^2/4)", "f(x^2*log(a*x),x^3*log(a*x)/3-1/9*x^3)", "f(log(x)^2,x*log(x)^2-2*x*log(x)+2*x)", "f(1/x*1/(a+log(x)),log(a+log(x)))", "f(log(a*x+b),(a*x+b)*log(a*x+b)/a-x)", "f(log(a*x+b)/x^2,a/b*log(x)-(a*x+b)*log(a*x+b)/b/x)", "f(sinh(x),cosh(x))", "f(cosh(x),sinh(x))", "f(tanh(x),log(cosh(x)))", "f(x*sinh(x),x*cosh(x)-sinh(x))", "f(x*cosh(x),x*sinh(x)-cosh(x))", "f(sinh(x)^2,sinh(2*x)/4-x/2)", "f(tanh(x)^2,x-tanh(x))", "f(cosh(x)^2,sinh(2*x)/4+x/2)", "f(x^3*exp(a*x^2),exp(a*x^2)*(x^2/a-1/(a^2))/2)", "f(x^3*exp(a*x^2+b),exp(a*x^2)*exp(b)*(x^2/a-1/(a^2))/2)", "f(exp(a*x^2),-i*sqrt(pi)*erf(i*sqrt(a)*x)/sqrt(a)/2)", "f(erf(a*x),x*erf(a*x)+exp(-a^2*x^2)/a/sqrt(pi))", "f(x^2*(1-x^2)^(3/2),(x*sqrt(1-x^2)*(-8*x^4+14*x^2-3)+3*arcsin(x))/48)", "f(x^2*(1-x^2)^(5/2),(x*sqrt(1-x^2)*(48*x^6-136*x^4+118*x^2-15)+15*arcsin(x))/384)", "f(x^4*(1-x^2)^(3/2),(-x*sqrt(1-x^2)*(16*x^6-24*x^4+2*x^2+3)+3*arcsin(x))/128)", "f(x*exp(a*x),exp(a*x)*(a*x-1)/(a^2))", "f(x*exp(a*x+b),exp(a*x+b)*(a*x-1)/(a^2))", "f(x^2*exp(a*x),exp(a*x)*(a^2*x^2-2*a*x+2)/(a^3))", "f(x^2*exp(a*x+b),exp(a*x+b)*(a^2*x^2-2*a*x+2)/(a^3))", "f(x^3*exp(a*x),exp(a*x)*x^3/a-3/a*integral(x^2*exp(a*x),x))", "f(x^3*exp(a*x+b),exp(a*x+b)*x^3/a-3/a*integral(x^2*exp(a*x+b),x))", 0]; - + itab = [ + // 1 + "f(a,a*x)", + // 9 (need a caveat for 7 so we can put 9 after 7) + "f(1/x,log(x))", + // 7 + "f(x^a,x^(a+1)/(a+1))", + // five specialisations of case 7 for speed. + // Covers often-occurring exponents: each of + // these case ends up in a dedicated entry, so we + // only have to do one sure-shot match. + "f(x^(-2),-x^(-1))", + "f(x^(-1/2),2*x^(1/2))", + "f(x^(1/2),2/3*x^(3/2))", + "f(x,x^2/2)", + "f(x^2,x^3/3)", + // 12 + "f(exp(a*x),1/a*exp(a*x))", + "f(exp(a*x+b),1/a*exp(a*x+b))", + "f(x*exp(a*x^2),exp(a*x^2)/(2*a))", + "f(x*exp(a*x^2+b),exp(a*x^2+b)/(2*a))", + // 14 + "f(log(a*x),x*log(a*x)-x)", + // 15 + "f(a^x,a^x/log(a),or(not(number(a)),a>0))", + // 16 + "f(1/(a+x^2),1/sqrt(a)*arctan(x/sqrt(a)),or(not(number(a)),a>0))", + // 17 + "f(1/(a-x^2),1/sqrt(a)*arctanh(x/sqrt(a)))", + // 19 + "f(1/sqrt(a-x^2),arcsin(x/(sqrt(a))))", + // 20 + "f(1/sqrt(a+x^2),log(x+sqrt(a+x^2)))", + // 27 + "f(1/(a+b*x),1/b*log(a+b*x))", + // 28 + "f(1/(a+b*x)^2,-1/(b*(a+b*x)))", + // 29 + "f(1/(a+b*x)^3,-1/(2*b)*1/(a+b*x)^2)", + // 30 + "f(x/(a+b*x),x/b-a*log(a+b*x)/b/b)", + // 31 + "f(x/(a+b*x)^2,1/b^2*(log(a+b*x)+a/(a+b*x)))", + // 33 + "f(x^2/(a+b*x),1/b^2*(1/2*(a+b*x)^2-2*a*(a+b*x)+a^2*log(a+b*x)))", + // 34 + "f(x^2/(a+b*x)^2,1/b^3*(a+b*x-2*a*log(a+b*x)-a^2/(a+b*x)))", + // 35 + "f(x^2/(a+b*x)^3,1/b^3*(log(a+b*x)+2*a/(a+b*x)-1/2*a^2/(a+b*x)^2))", + // 37 + "f(1/x*1/(a+b*x),-1/a*log((a+b*x)/x))", + // 38 + "f(1/x*1/(a+b*x)^2,1/a*1/(a+b*x)-1/a^2*log((a+b*x)/x))", + // 39 + "f(1/x*1/(a+b*x)^3,1/a^3*(1/2*((2*a+b*x)/(a+b*x))^2+log(x/(a+b*x))))", + // 40 + "f(1/x^2*1/(a+b*x),-1/(a*x)+b/a^2*log((a+b*x)/x))", + // 41 + "f(1/x^3*1/(a+b*x),(2*b*x-a)/(2*a^2*x^2)+b^2/a^3*log(x/(a+b*x)))", + // 42 + "f(1/x^2*1/(a+b*x)^2,-(a+2*b*x)/(a^2*x*(a+b*x))+2*b/a^3*log((a+b*x)/x))", + // 60 + "f(1/(a+b*x^2),1/sqrt(a*b)*arctan(x*sqrt(a*b)/a),or(not(number(a*b)),a*b>0))", + // 61 + "f(1/(a+b*x^2),1/(2*sqrt(-a*b))*log((a+x*sqrt(-a*b))/(a-x*sqrt(-a*b))),or(not(number(a*b)),a*b<0))", + // 62 is the same as 60 + // 63 + "f(x/(a+b*x^2),1/2*1/b*log(a+b*x^2))", + //64 + "f(x^2/(a+b*x^2),x/b-a/b*integral(1/(a+b*x^2),x))", + //65 + "f(1/(a+b*x^2)^2,x/(2*a*(a+b*x^2))+1/2*1/a*integral(1/(a+b*x^2),x))", + //66 is covered by 61 + //70 + "f(1/x*1/(a+b*x^2),1/2*1/a*log(x^2/(a+b*x^2)))", + //71 + "f(1/x^2*1/(a+b*x^2),-1/(a*x)-b/a*integral(1/(a+b*x^2),x))", + //74 + "f(1/(a+b*x^3),1/3*1/a*(a/b)^(1/3)*(1/2*log(((a/b)^(1/3)+x)^3/(a+b*x^3))+sqrt(3)*arctan((2*x-(a/b)^(1/3))*(a/b)^(-1/3)/sqrt(3))))", + //76 + "f(x^2/(a+b*x^3),1/3*1/b*log(a+b*x^3))", + // float(defint(1/(2+3*X^4),X,0,pi)) gave wrong result. + // Also, the tests related to the indefinite integral + // fail since we rationalise expressions "better", so I'm thinking + // to take this out completely as it seemed to give the + // wrong results in the first place. + //77 + //"f(1/(a+b*x^4),1/2*1/a*(a/b/4)^(1/4)*(1/2*log((x^2+2*(a/b/4)^(1/4)*x+2*(a/b/4)^(1/2))/(x^2-2*(a/b/4)^(1/4)*x+2*(a/b/4)^(1/2)))+arctan(2*(a/b/4)^(1/4)*x/(2*(a/b/4)^(1/2)-x^2))),or(not(number(a*b)),a*b>0))", + //78 + //"f(1/(a+b*x^4),1/2*(-a/b)^(1/4)/a*(1/2*log((x+(-a/b)^(1/4))/(x-(-a/b)^(1/4)))+arctan(x*(-a/b)^(-1/4))),or(not(number(a*b)),a*b<0))", + //79 + "f(x/(a+b*x^4),1/2*sqrt(b/a)/b*arctan(x^2*sqrt(b/a)),or(not(number(a*b)),a*b>0))", + //80 + "f(x/(a+b*x^4),1/4*sqrt(-b/a)/b*log((x^2-sqrt(-a/b))/(x^2+sqrt(-a/b))),or(not(number(a*b)),a*b<0))", + // float(defint(X^2/(2+3*X^4),X,0,pi)) gave wrong result. + // Also, the tests related to the indefinite integral + // fail since we rationalise expressions "better", so I'm thinking + // to take this out completely as it seemed to give the + // wrong results in the first place. + //81 + //"f(x^2/(a+b*x^4),1/4*1/b*(a/b/4)^(-1/4)*(1/2*log((x^2-2*(a/b/4)^(1/4)*x+2*sqrt(a/b/4))/(x^2+2*(a/b/4)^(1/4)*x+2*sqrt(a/b/4)))+arctan(2*(a/b/4)^(1/4)*x/(2*sqrt(a/b/4)-x^2))),or(not(number(a*b)),a*b>0))", + //82 + //"f(x^2/(a+b*x^4),1/4*1/b*(-a/b)^(-1/4)*(log((x-(-a/b)^(1/4))/(x+(-a/b)^(1/4)))+2*arctan(x*(-a/b)^(-1/4))),or(not(number(a*b)),a*b<0))", + //83 + "f(x^3/(a+b*x^4),1/4*1/b*log(a+b*x^4))", + //124 + "f(sqrt(a+b*x),2/3*1/b*sqrt((a+b*x)^3))", + //125 + "f(x*sqrt(a+b*x),-2*(2*a-3*b*x)*sqrt((a+b*x)^3)/15/b^2)", + //126 + "f(x^2*sqrt(a+b*x),2*(8*a^2-12*a*b*x+15*b^2*x^2)*sqrt((a+b*x)^3)/105/b^3)", + //128 + "f(sqrt(a+b*x)/x,2*sqrt(a+b*x)+a*integral(1/x*1/sqrt(a+b*x),x))", + //129 + "f(sqrt(a+b*x)/x^2,-sqrt(a+b*x)/x+b/2*integral(1/x*1/sqrt(a+b*x),x))", + //131 + "f(1/sqrt(a+b*x),2*sqrt(a+b*x)/b)", + //132 + "f(x/sqrt(a+b*x),-2/3*(2*a-b*x)*sqrt(a+b*x)/b^2)", + //133 + "f(x^2/sqrt(a+b*x),2/15*(8*a^2-4*a*b*x+3*b^2*x^2)*sqrt(a+b*x)/b^3)", + //135 + "f(1/x*1/sqrt(a+b*x),1/sqrt(a)*log((sqrt(a+b*x)-sqrt(a))/(sqrt(a+b*x)+sqrt(a))),or(not(number(a)),a>0))", + //136 + "f(1/x*1/sqrt(a+b*x),2/sqrt(-a)*arctan(sqrt(-(a+b*x)/a)),or(not(number(a)),a<0))", + //137 + "f(1/x^2*1/sqrt(a+b*x),-sqrt(a+b*x)/a/x-1/2*b/a*integral(1/x*1/sqrt(a+b*x),x))", + //156 + "f(sqrt(x^2+a),1/2*(x*sqrt(x^2+a)+a*log(x+sqrt(x^2+a))))", + //157 + "f(1/sqrt(x^2+a),log(x+sqrt(x^2+a)))", + //158 + "f(1/x*1/sqrt(x^2+a),arcsec(x/sqrt(-a))/sqrt(-a),or(not(number(a)),a<0))", + //159 + "f(1/x*1/sqrt(x^2+a),-1/sqrt(a)*log((sqrt(a)+sqrt(x^2+a))/x),or(not(number(a)),a>0))", + //160 + "f(sqrt(x^2+a)/x,sqrt(x^2+a)-sqrt(a)*log((sqrt(a)+sqrt(x^2+a))/x),or(not(number(a)),a>0))", + //161 + "f(sqrt(x^2+a)/x,sqrt(x^2+a)-sqrt(-a)*arcsec(x/sqrt(-a)),or(not(number(a)),a<0))", + //162 + "f(x/sqrt(x^2+a),sqrt(x^2+a))", + //163 + "f(x*sqrt(x^2+a),1/3*sqrt((x^2+a)^3))", + //164 need an unexpanded version? + "f(sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/4*(x*sqrt((x^2+a^(1/3))^3)+3/2*a^(1/3)*x*sqrt(x^2+a^(1/3))+3/2*a^(2/3)*log(x+sqrt(x^2+a^(1/3)))))", + // match doesn't work for the following + "f(sqrt(-a+x^6-3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/4*(x*sqrt((x^2-a^(1/3))^3)-3/2*a^(1/3)*x*sqrt(x^2-a^(1/3))+3/2*a^(2/3)*log(x+sqrt(x^2-a^(1/3)))))", + //165 + "f(1/sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),x/a^(1/3)/sqrt(x^2+a^(1/3)))", + //166 + "f(x/sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),-1/sqrt(x^2+a^(1/3)))", + //167 + "f(x*sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/5*sqrt((x^2+a^(1/3))^5))", + //168 + "f(x^2*sqrt(x^2+a),1/4*x*sqrt((x^2+a)^3)-1/8*a*x*sqrt(x^2+a)-1/8*a^2*log(x+sqrt(x^2+a)))", + //169 + "f(x^3*sqrt(x^2+a),(1/5*x^2-2/15*a)*sqrt((x^2+a)^3),and(number(a),a>0))", + //170 + "f(x^3*sqrt(x^2+a),sqrt((x^2+a)^5)/5-a*sqrt((x^2+a)^3)/3,and(number(a),a<0))", + //171 + "f(x^2/sqrt(x^2+a),1/2*x*sqrt(x^2+a)-1/2*a*log(x+sqrt(x^2+a)))", + //172 + "f(x^3/sqrt(x^2+a),1/3*sqrt((x^2+a)^3)-a*sqrt(x^2+a))", + //173 + "f(1/x^2*1/sqrt(x^2+a),-sqrt(x^2+a)/a/x)", + //174 + "f(1/x^3*1/sqrt(x^2+a),-1/2*sqrt(x^2+a)/a/x^2+1/2*log((sqrt(a)+sqrt(x^2+a))/x)/a^(3/2),or(not(number(a)),a>0))", + //175 + "f(1/x^3*1/sqrt(x^2-a),1/2*sqrt(x^2-a)/a/x^2+1/2*1/(a^(3/2))*arcsec(x/(a^(1/2))),or(not(number(a)),a>0))", + //176+ + "f(x^2*sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/6*x*sqrt((x^2+a^(1/3))^5)-1/24*a^(1/3)*x*sqrt((x^2+a^(1/3))^3)-1/16*a^(2/3)*x*sqrt(x^2+a^(1/3))-1/16*a*log(x+sqrt(x^2+a^(1/3))),or(not(number(a)),a>0))", + //176- + "f(x^2*sqrt(-a-3*a^(1/3)*x^4+3*a^(2/3)*x^2+x^6),1/6*x*sqrt((x^2-a^(1/3))^5)+1/24*a^(1/3)*x*sqrt((x^2-a^(1/3))^3)-1/16*a^(2/3)*x*sqrt(x^2-a^(1/3))+1/16*a*log(x+sqrt(x^2-a^(1/3))),or(not(number(a)),a>0))", + //177+ + "f(x^3*sqrt(a+x^6+3*a^(1/3)*x^4+3*a^(2/3)*x^2),1/7*sqrt((x^2+a^(1/3))^7)-1/5*a^(1/3)*sqrt((x^2+a^(1/3))^5),or(not(number(a)),a>0))", + //177- + "f(x^3*sqrt(-a-3*a^(1/3)*x^4+3*a^(2/3)*x^2+x^6),1/7*sqrt((x^2-a^(1/3))^7)+1/5*a^(1/3)*sqrt((x^2-a^(1/3))^5),or(not(number(a)),a>0))", + //196 + "f(1/(x-a)/sqrt(x^2-a^2),-sqrt(x^2-a^2)/a/(x-a))", + //197 + "f(1/(x+a)/sqrt(x^2-a^2),sqrt(x^2-a^2)/a/(x+a))", + //200+ + "f(sqrt(a-x^2),1/2*(x*sqrt(a-x^2)+a*arcsin(x/sqrt(abs(a)))))", + //201 (seems to be handled somewhere else) + //202 + "f(1/x*1/sqrt(a-x^2),-1/sqrt(a)*log((sqrt(a)+sqrt(a-x^2))/x),or(not(number(a)),a>0))", + //203 + "f(sqrt(a-x^2)/x,sqrt(a-x^2)-sqrt(a)*log((sqrt(a)+sqrt(a-x^2))/x),or(not(number(a)),a>0))", + //204 + "f(x/sqrt(a-x^2),-sqrt(a-x^2))", + //205 + "f(x*sqrt(a-x^2),-1/3*sqrt((a-x^2)^3))", + //210 + "f(x^2*sqrt(a-x^2),-x/4*sqrt((a-x^2)^3)+1/8*a*(x*sqrt(a-x^2)+a*arcsin(x/sqrt(a))),or(not(number(a)),a>0))", + //211 + "f(x^3*sqrt(a-x^2),(-1/5*x^2-2/15*a)*sqrt((a-x^2)^3),or(not(number(a)),a>0))", + //214 + "f(x^2/sqrt(a-x^2),-x/2*sqrt(a-x^2)+a/2*arcsin(x/sqrt(a)),or(not(number(a)),a>0))", + //215 + "f(1/x^2*1/sqrt(a-x^2),-sqrt(a-x^2)/a/x,or(not(number(a)),a>0))", + //216 + "f(sqrt(a-x^2)/x^2,-sqrt(a-x^2)/x-arcsin(x/sqrt(a)),or(not(number(a)),a>0))", + //217 + "f(sqrt(a-x^2)/x^3,-1/2*sqrt(a-x^2)/x^2+1/2*log((sqrt(a)+sqrt(a-x^2))/x)/sqrt(a),or(not(number(a)),a>0))", + //218 + "f(sqrt(a-x^2)/x^4,-1/3*sqrt((a-x^2)^3)/a/x^3,or(not(number(a)),a>0))", + // 273 + "f(sqrt(a*x^2+b),x*sqrt(a*x^2+b)/2+b*log(x*sqrt(a)+sqrt(a*x^2+b))/2/sqrt(a),and(number(a),a>0))", + // 274 + "f(sqrt(a*x^2+b),x*sqrt(a*x^2+b)/2+b*arcsin(x*sqrt(-a/b))/2/sqrt(-a),and(number(a),a<0))", + // 290 + "f(sin(a*x),-cos(a*x)/a)", + // 291 + "f(cos(a*x),sin(a*x)/a)", + // 292 + "f(tan(a*x),-log(cos(a*x))/a)", + // 293 + "f(1/tan(a*x),log(sin(a*x))/a)", + // 294 + "f(1/cos(a*x),log(tan(pi/4+a*x/2))/a)", + // 295 + "f(1/sin(a*x),log(tan(a*x/2))/a)", + // 296 + "f(sin(a*x)^2,x/2-sin(2*a*x)/(4*a))", + // 297 + "f(sin(a*x)^3,-cos(a*x)*(sin(a*x)^2+2)/(3*a))", + // 298 + "f(sin(a*x)^4,3/8*x-sin(2*a*x)/(4*a)+sin(4*a*x)/(32*a))", + // 302 + "f(cos(a*x)^2,x/2+sin(2*a*x)/(4*a))", + // 303 + "f(cos(a*x)^3,sin(a*x)*(cos(a*x)^2+2)/(3*a))", + // 304 + "f(cos(a*x)^4,3/8*x+sin(2*a*x)/(4*a)+sin(4*a*x)/(32*a))", + // 308 + "f(1/sin(a*x)^2,-1/(a*tan(a*x)))", + // 312 + "f(1/cos(a*x)^2,tan(a*x)/a)", + // 318 + "f(sin(a*x)*cos(a*x),sin(a*x)^2/(2*a))", + // 320 + "f(sin(a*x)^2*cos(a*x)^2,-sin(4*a*x)/(32*a)+x/8)", + // 326 + "f(sin(a*x)/cos(a*x)^2,1/(a*cos(a*x)))", + // 327 + "f(sin(a*x)^2/cos(a*x),(log(tan(pi/4+a*x/2))-sin(a*x))/a)", + // 328 + "f(cos(a*x)/sin(a*x)^2,-1/(a*sin(a*x)))", + // 329 + "f(1/(sin(a*x)*cos(a*x)),log(tan(a*x))/a)", + // 330 + "f(1/(sin(a*x)*cos(a*x)^2),(1/cos(a*x)+log(tan(a*x/2)))/a)", + // 331 + "f(1/(sin(a*x)^2*cos(a*x)),(log(tan(pi/4+a*x/2))-1/sin(a*x))/a)", + // 333 + "f(1/(sin(a*x)^2*cos(a*x)^2),-2/(a*tan(2*a*x)))", + // 335 + "f(sin(a+b*x),-cos(a+b*x)/b)", + // 336 + "f(cos(a+b*x),sin(a+b*x)/b)", + // 337+ (with the addition of b) + "f(1/(b+b*sin(a*x)),-tan(pi/4-a*x/2)/a/b)", + // 337- (with the addition of b) + "f(1/(b-b*sin(a*x)),tan(pi/4+a*x/2)/a/b)", + // 338 (with the addition of b) + "f(1/(b+b*cos(a*x)),tan(a*x/2)/a/b)", + // 339 (with the addition of b) + "f(1/(b-b*cos(a*x)),-1/tan(a*x/2)/a/b)", + // 340 + "f(1/(a+b*sin(x)),1/sqrt(b^2-a^2)*log((a*tan(x/2)+b-sqrt(b^2-a^2))/(a*tan(x/2)+b+sqrt(b^2-a^2))),b^2-a^2)", // check that b^2-a^2 is not zero + // 341 + "f(1/(a+b*cos(x)),1/sqrt(b^2-a^2)*log((sqrt(b^2-a^2)*tan(x/2)+a+b)/(sqrt(b^2-a^2)*tan(x/2)-a-b)),b^2-a^2)", // check that b^2-a^2 is not zero + // 389 + "f(x*sin(a*x),sin(a*x)/a^2-x*cos(a*x)/a)", + // 390 + "f(x^2*sin(a*x),2*x*sin(a*x)/a^2-(a^2*x^2-2)*cos(a*x)/a^3)", + // 393 + "f(x*cos(a*x),cos(a*x)/a^2+x*sin(a*x)/a)", + // 394 + "f(x^2*cos(a*x),2*x*cos(a*x)/a^2+(a^2*x^2-2)*sin(a*x)/a^3)", + // 441 + "f(arcsin(a*x),x*arcsin(a*x)+sqrt(1-a^2*x^2)/a)", + // 442 + "f(arccos(a*x),x*arccos(a*x)-sqrt(1-a^2*x^2)/a)", + // 443 + "f(arctan(a*x),x*arctan(a*x)-1/2*log(1+a^2*x^2)/a)", + // 485 (with addition of a) + // however commenting out since it's a duplicate of 14 + // "f(log(a*x),x*log(a*x)-x)", + // 486 (with addition of a) + "f(x*log(a*x),x^2*log(a*x)/2-x^2/4)", + // 487 (with addition of a) + "f(x^2*log(a*x),x^3*log(a*x)/3-1/9*x^3)", + // 489 + "f(log(x)^2,x*log(x)^2-2*x*log(x)+2*x)", + // 493 (with addition of a) + "f(1/x*1/(a+log(x)),log(a+log(x)))", + // 499 + "f(log(a*x+b),(a*x+b)*log(a*x+b)/a-x)", + // 500 + "f(log(a*x+b)/x^2,a/b*log(x)-(a*x+b)*log(a*x+b)/b/x)", + // 554 + "f(sinh(x),cosh(x))", + // 555 + "f(cosh(x),sinh(x))", + // 556 + "f(tanh(x),log(cosh(x)))", + // 560 + "f(x*sinh(x),x*cosh(x)-sinh(x))", + // 562 + "f(x*cosh(x),x*sinh(x)-cosh(x))", + // 566 + "f(sinh(x)^2,sinh(2*x)/4-x/2)", + // 569 + "f(tanh(x)^2,x-tanh(x))", + // 572 + "f(cosh(x)^2,sinh(2*x)/4+x/2)", + // ? + "f(x^3*exp(a*x^2),exp(a*x^2)*(x^2/a-1/(a^2))/2)", + // ? + "f(x^3*exp(a*x^2+b),exp(a*x^2)*exp(b)*(x^2/a-1/(a^2))/2)", + // ? + "f(exp(a*x^2),-i*sqrt(pi)*erf(i*sqrt(a)*x)/sqrt(a)/2)", + // ? + "f(erf(a*x),x*erf(a*x)+exp(-a^2*x^2)/a/sqrt(pi))", + // these are needed for the surface integral in the manual + "f(x^2*(1-x^2)^(3/2),(x*sqrt(1-x^2)*(-8*x^4+14*x^2-3)+3*arcsin(x))/48)", + "f(x^2*(1-x^2)^(5/2),(x*sqrt(1-x^2)*(48*x^6-136*x^4+118*x^2-15)+15*arcsin(x))/384)", + "f(x^4*(1-x^2)^(3/2),(-x*sqrt(1-x^2)*(16*x^6-24*x^4+2*x^2+3)+3*arcsin(x))/128)", + "f(x*exp(a*x),exp(a*x)*(a*x-1)/(a^2))", + "f(x*exp(a*x+b),exp(a*x+b)*(a*x-1)/(a^2))", + "f(x^2*exp(a*x),exp(a*x)*(a^2*x^2-2*a*x+2)/(a^3))", + "f(x^2*exp(a*x+b),exp(a*x+b)*(a^2*x^2-2*a*x+2)/(a^3))", + "f(x^3*exp(a*x),exp(a*x)*x^3/a-3/a*integral(x^2*exp(a*x),x))", + "f(x^3*exp(a*x+b),exp(a*x+b)*x^3/a-3/a*integral(x^2*exp(a*x+b),x))", + 0 + ]; + + //define F p3 + //define X p4 + //define N p5 Eval_integral = function() { var doNothing, i, i1, n, o, ref, ref1; i = 0; n = 0; + // evaluate 1st arg to get function F p1 = cdr(p1); push(car(p1)); Eval(); + // evaluate 2nd arg and then... + + // example result of 2nd arg what to do + + // integral(f) nil guess X, N = nil + // integral(f,2) 2 guess X, N = 2 + // integral(f,x) x X = x, N = nil + // integral(f,x,2) x X = x, N = 2 + // integral(f,x,y) x X = x, N = y p1 = cdr(p1); push(car(p1)); Eval(); @@ -10709,6 +12937,7 @@ p4 = pop(); p3 = pop(); while (1) { + // N might be a symbol instead of a number if (isNumericAtom(p5)) { push(p5); n = pop_integer(); @@ -10720,47 +12949,59 @@ } push(p3); if (n >= 0) { - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { push(p4); integral(); } } else { n = -n; - for (i = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { push(p4); derivative(); } } p3 = pop(); + // if N is nil then arglist is exhausted if (p5 === symbol(NIL)) { break; } + // otherwise... + + // N arg1 what to do + + // number nil break + // number number N = arg1, continue + // number symbol X = arg1, N = arg2, continue + + // symbol nil X = N, N = nil, continue + // symbol number X = N, N = arg1, continue + // symbol symbol X = N, N = arg1, continue if (isNumericAtom(p5)) { p1 = cdr(p1); push(car(p1)); Eval(); p5 = pop(); if (p5 === symbol(NIL)) { - break; + break; // arglist exhausted } if (isNumericAtom(p5)) { - doNothing = 1; + doNothing = 1; // N = arg1 } else { p4 = p5; p1 = cdr(p1); push(car(p1)); Eval(); - p5 = pop(); + p5 = pop(); // N = arg2 } } else { p4 = p5; p1 = cdr(p1); push(car(p1)); Eval(); - p5 = pop(); + p5 = pop(); // N = arg1 } } - return push(p3); + return push(p3); // final result }; integral = function() { @@ -10808,7 +13049,7 @@ partition(); p1 = pop(); integral_of_form(); - return multiply(); + return multiply(); // multiply constant part }; integral_of_form = function() { @@ -10816,14 +13057,16 @@ hc = italu_hashcode(p1, p2).toFixed(6); tab = hashed_itab[hc]; if (!tab) { + // debugger + // italu_hashcode(p1, p2) push_symbol(INTEGRAL); push(p1); push(p2); list(3); return; } - push(p1); - push(p2); + push(p1); // free variable + push(p2); // input expression transform(tab, false); p3 = pop(); if (p3 === symbol(NIL)) { @@ -10836,6 +13079,12 @@ } }; + // Implementation of hash codes based on ITALU (An Integral Table Look-Up) + // https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19680004891.pdf + // see Appendix A, page 153 + + // The first two values are from the ITALU paper. + // The others are just arbitrary constants. hashcode_values = { 'x': 0.95532, 'constexp': 1.43762, @@ -10907,6 +13156,9 @@ if (Find(term, x)) { term_hash = italu_hashcode(term, x); } else { + // The original algorithm would skip this, + // but recording that it was present helps + // prevent collisions. term_hash = hashcode_values.constant; } term_set[term_hash.toFixed(6)] = true; @@ -10943,6 +13195,7 @@ if (Find(power, x)) { exp_hash = italu_hashcode(power, x); } else { + // constant to constant = constant if (base_hash === hashcode_values.constant) { return hashcode_values.constant; } @@ -10985,6 +13238,10 @@ $.make_hashed_itab = make_hashed_itab; + // pre-calculated hashed integral table. + // in case the integral table is changed, use this + // Algebrite.make_hashed_itab() + // and copy the resulting JSON in here. hashed_itab = { "1.144166": ["f(a,a*x)"], "1.046770": ["f(1/x,log(x))"], @@ -11112,6 +13369,27 @@ "1.242392": ["f(x^3*exp(a*x+b),exp(a*x+b)*x^3/a-3/a*integral(x^2*exp(a*x+b),x))"] }; + //----------------------------------------------------------------------------- + + // Input: Matrix on stack (must have two dimensions but + // it can be non-numerical) + + // Output: Inverse on stack + + // Example: + + // > inv(((1,2),(3,4)) + // ((-2,1),(3/2,-1/2)) + + // > inv(((a,b),(c,d)) + // ((d / (a d - b c),-b / (a d - b c)),(-c / (a d - b c),a / (a d - b c))) + + // Note: + + // THIS IS DIFFERENT FROM INVERSE OF AN EXPRESSION (inv) + // Uses Gaussian elimination for numerical matrices. + + //----------------------------------------------------------------------------- INV_check_arg = function() { if (!istensor(p1)) { return 0; @@ -11128,18 +13406,29 @@ var accumulator, eachEntry, i, n, o, ref; i = 0; n = 0; + //U **a save(); p1 = pop(); + // an inv just goes away when + // applied to another inv if (isinv(p1)) { push(car(cdr(p1))); restore(); return; } + // inverse goes away in case + // of identity matrix if (isidentitymatrix(p1)) { push(p1); restore(); return; } + // distribute the inverse of a dot + // if in expanding mode + // note that the distribution happens + // in reverse. + // The dot operator is not + // commutative, so, it matters. if (expanding && isinnerordot(p1)) { p1 = cdr(p1); accumulator = []; @@ -11147,7 +13436,7 @@ accumulator.push(car(p1)); p1 = cdr(p1); } - for (eachEntry = o = ref = accumulator.length - 1; ref <= 0 ? o <= 0 : o >= 0; eachEntry = ref <= 0 ? ++o : --o) { + for (eachEntry = o = ref = accumulator.length - 1; (ref <= 0 ? o <= 0 : o >= 0); eachEntry = ref <= 0 ? ++o : --o) { push(accumulator[eachEntry]); inv(); if (eachEntry !== accumulator.length - 1) { @@ -11195,6 +13484,7 @@ return restore(); }; + // inverse using gaussian elimination yyinvg = function() { var h, i, i1, j, j1, l1, n, o, ref, ref1, ref2, ref3; h = 0; @@ -11203,8 +13493,8 @@ n = 0; n = p1.tensor.dim[0]; h = tos; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { - for (j = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { + for (j = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); j = 0 <= ref1 ? ++i1 : --i1) { if (i === j) { push(one); } else { @@ -11212,7 +13502,7 @@ } } } - for (i = j1 = 0, ref2 = n * n; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = n * n; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { push(p1.tensor.elem[i]); } INV_decomp(n); @@ -11220,13 +13510,29 @@ p1.tensor.ndim = 2; p1.tensor.dim[0] = n; p1.tensor.dim[1] = n; - for (i = l1 = 0, ref3 = n * n; 0 <= ref3 ? l1 < ref3 : l1 > ref3; i = 0 <= ref3 ? ++l1 : --l1) { + for (i = l1 = 0, ref3 = n * n; (0 <= ref3 ? l1 < ref3 : l1 > ref3); i = 0 <= ref3 ? ++l1 : --l1) { p1.tensor.elem[i] = stack[h + i]; } moveTos(tos - 2 * n * n); return push(p1); }; + //----------------------------------------------------------------------------- + + // Input: n * n unit matrix on stack + + // n * n operand on stack + + // Output: n * n inverse matrix on stack + + // n * n garbage on stack + + // p2 mangled + + //----------------------------------------------------------------------------- + + //define A(i, j) stack[a + n * (i) + (j)] + //define U(i, j) stack[u + n * (i) + (j)] INV_decomp = function(n) { var a, d, i, i1, j, j1, l1, o, ref, ref1, ref2, ref3, ref4, results, u; a = 0; @@ -11237,9 +13543,11 @@ a = tos - n * n; u = a - n * n; results = []; - for (d = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; d = 0 <= ref ? ++o : --o) { + for (d = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); d = 0 <= ref ? ++o : --o) { + // diagonal element zero? if (equal(stack[a + n * d + d], zero)) { - for (i = i1 = ref1 = d + 1, ref2 = n; ref1 <= ref2 ? i1 < ref2 : i1 > ref2; i = ref1 <= ref2 ? ++i1 : --i1) { +// find a new row + for (i = i1 = ref1 = d + 1, ref2 = n; (ref1 <= ref2 ? i1 < ref2 : i1 > ref2); i = ref1 <= ref2 ? ++i1 : --i1) { if (!equal(stack[a + n * i + d], zero)) { break; } @@ -11247,7 +13555,8 @@ if (i === n) { stop("inverse of singular matrix"); } - for (j = j1 = 0, ref3 = n; 0 <= ref3 ? j1 < ref3 : j1 > ref3; j = 0 <= ref3 ? ++j1 : --j1) { +// exchange rows + for (j = j1 = 0, ref3 = n; (0 <= ref3 ? j1 < ref3 : j1 > ref3); j = 0 <= ref3 ? ++j1 : --j1) { p2 = stack[a + n * d + j]; stack[a + n * d + j] = stack[a + n * i + j]; stack[a + n * i + j] = p2; @@ -11256,8 +13565,9 @@ stack[u + n * i + j] = p2; } } + // multiply the pivot row by 1 / pivot p2 = stack[a + n * d + d]; - for (j = l1 = 0, ref4 = n; 0 <= ref4 ? l1 < ref4 : l1 > ref4; j = 0 <= ref4 ? ++l1 : --l1) { + for (j = l1 = 0, ref4 = n; (0 <= ref4 ? l1 < ref4 : l1 > ref4); j = 0 <= ref4 ? ++l1 : --l1) { if (j > d) { push(stack[a + n * d + j]); push(p2); @@ -11271,16 +13581,19 @@ } results.push((function() { var m1, ref5, results1; +// clear out the column above and below the pivot results1 = []; - for (i = m1 = 0, ref5 = n; 0 <= ref5 ? m1 < ref5 : m1 > ref5; i = 0 <= ref5 ? ++m1 : --m1) { + for (i = m1 = 0, ref5 = n; (0 <= ref5 ? m1 < ref5 : m1 > ref5); i = 0 <= ref5 ? ++m1 : --m1) { if (i === d) { continue; } + // multiplier p2 = stack[a + n * i + d]; results1.push((function() { var n1, ref6, results2; +// add pivot row to i-th row results2 = []; - for (j = n1 = 0, ref6 = n; 0 <= ref6 ? n1 < ref6 : n1 > ref6; j = 0 <= ref6 ? ++n1 : --n1) { + for (j = n1 = 0, ref6 = n; (0 <= ref6 ? n1 < ref6 : n1 > ref6); j = 0 <= ref6 ? ++n1 : --n1) { if (j > d) { push(stack[a + n * i + j]); push(stack[a + n * d + j]); @@ -11307,6 +13620,10 @@ DEBUG_IS = false; + // p is a U + // this routine is a simple check on whether we have + // a basic zero in our hands. It doesn't perform any + // calculations or simplifications. isZeroAtom = function(p) { switch (p.k) { case NUM: @@ -11322,12 +13639,16 @@ return 0; }; + // p is a U + // this routine is a simple check on whether we have + // a basic zero in our hands. It doesn't perform any + // calculations or simplifications. isZeroTensor = function(p) { var i, o, ref; if (p.k !== TENSOR) { return 0; } - for (i = o = 0, ref = p.tensor.nelem; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = p.tensor.nelem; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { if (!isZeroAtomOrTensor(p.tensor.elem[i])) { return 0; } @@ -11335,35 +13656,86 @@ return 1; }; + // p is a U + // this routine is a simple check on whether we have + // a basic zero in our hands. It doesn't perform any + // calculations or simplifications. isZeroAtomOrTensor = function(p) { return isZeroAtom(p) || isZeroTensor(p); }; + // This is a key routine to try to determine whether + // the argument looks like zero/false, or non-zero/true, + // or undetermined. + // This is useful in two instances: + // * to determine if a predicate is true/false + // * to determine if particular quantity is zero + // Note that if one wants to check if we have a simple + // zero atom or tensor in our hands, then the isZeroAtomOrTensor + // routine is sufficient. isZeroLikeOrNonZeroLikeOrUndetermined = function(valueOrPredicate) { var evalledArgument; + // push the argument push(valueOrPredicate); + // just like Eval but turns assignments into + // equality checks Eval_predicate(); evalledArgument = pop(); + // OK first check if we already have + // a simple zero (or simple zero tensor) if (isZeroAtomOrTensor(evalledArgument)) { return 0; } + // also check if we have a simple numeric value, or a tensor + // full of simple numeric values (i.e. straight doubles or fractions). + // In such cases, since we + // just excluded they are zero, then we take it as + // a "true" if (isNumericAtomOrTensor(evalledArgument)) { return 1; } + // if we are here we are in the case of value that + // is not a zero and not a simple numeric value. + // e.g. stuff like + // 'sqrt(2)', or 'sin(45)' or '1+i', or 'a' + // so in such cases let's try to do a float() + // so we might get down to a simple numeric value + // in some of those cases push(evalledArgument); zzfloat(); evalledArgument = pop(); + // anything that could be calculated down to a simple + // numeric value is now indeed either a + // double OR a double with an imaginary component + // e.g. 2.0 or 2.4 + i*5.6 + // (Everything else are things that don't have a numeric + // value e.g. 'a+b') + + // So, let's take care of the case where we have + // a simple numeric value with NO imaginary component, + // things like sqrt(2) or sin(PI) + // by doing the simple numeric + // values checks again if (isZeroAtomOrTensor(evalledArgument)) { return 0; } if (isNumericAtomOrTensor(evalledArgument)) { return 1; } + // here we still have cases of simple numeric values + // WITH an imaginary component e.g. '1+i', + // or things that don't have a numeric value e.g. 'a' + + // so now let's take care of the imaginary numbers: + // since we JUST have to spot "zeros" we can just + // calculate the absolute value and re-do all the checks + // we just did if (Find(evalledArgument, imaginaryunit)) { push(evalledArgument); absValFloat(); Eval_predicate(); evalledArgument = pop(); + // re-do the simple-number checks... if (isZeroAtomOrTensor(evalledArgument)) { return 0; } @@ -11371,9 +13743,15 @@ return 1; } } + // here we have stuff that is not reconducible to any + // numeric value (or tensor with numeric values) e.g. + // 'a+b', so it just means that we just don't know the + // truth value, so we have + // to leave the whole thing unevalled return null; }; + // p is a U isnegativenumber = function(p) { switch (p.k) { case NUM: @@ -11389,6 +13767,7 @@ return 0; }; + // p is a U ispositivenumber = function(p) { switch (p.k) { case NUM: @@ -11404,6 +13783,7 @@ return 0; }; + // p is a U isplustwo = function(p) { switch (p.k) { case NUM: @@ -11419,6 +13799,7 @@ return 0; }; + // p is a U isplusone = function(p) { switch (p.k) { case NUM: @@ -11487,6 +13868,7 @@ } }; + // -------------------------------------- isunivarpolyfactoredorexpandedform = function(p, x) { if (x == null) { push(p); @@ -11501,6 +13883,10 @@ } }; + // -------------------------------------- + // sometimes we want to check if we have a poly in our + // hands, however it's in factored form and we don't + // want to expand it. ispolyfactoredorexpandedform = function(p, x) { return ispolyfactoredorexpandedform_factor(p, x); }; @@ -11540,6 +13926,7 @@ } }; + // -------------------------------------- ispolyexpandedform = function(p, x) { if (Find(p, x)) { return ispolyexpandedform_expr(p, x); @@ -11596,6 +13983,7 @@ } }; + // -------------------------------------- isnegativeterm = function(p) { if (isnegativenumber(p)) { return 1; @@ -11685,6 +14073,11 @@ } }; + // returns 1 if there's a symbol somewhere. + // not used anywhere. Note that PI and POWER are symbols, + // so for example 2^3 would be symbolic + // while -1^(1/2) i.e. 'i' is not, so this can + // be tricky to use. issymbolic = function(p) { if (issymbol(p)) { return 1; @@ -11699,6 +14092,7 @@ } }; + // i.e. 2, 2^3, etc. isintegerfactor = function(p) { if (isinteger(p) || car(p) === symbol(POWER) && isinteger(cadr(p)) && isinteger(caddr(p))) { return 1; @@ -11731,6 +14125,7 @@ } }; + // p is a U, n an int equaln = function(p, n) { switch (p.k) { case NUM: @@ -11746,6 +14141,7 @@ return 0; }; + // p is a U, a and b ints equalq = function(p, a, b) { switch (p.k) { case NUM: @@ -11761,6 +14157,7 @@ return 0; }; + // p == 1/2 ? isoneovertwo = function(p) { if (equalq(p, 1, 2)) { return 1; @@ -11769,6 +14166,7 @@ } }; + // p == -1/2 ? isminusoneovertwo = function(p) { if (equalq(p, -1, 2)) { return 1; @@ -11777,6 +14175,7 @@ } }; + // p == 1/sqrt(2) ? isoneoversqrttwo = function(p) { if (car(p) === symbol(POWER) && equaln(cadr(p), 2) && equalq(caddr(p), -1, 2)) { return 1; @@ -11785,6 +14184,7 @@ } }; + // p == -1/sqrt(2) ? isminusoneoversqrttwo = function(p) { if (car(p) === symbol(MULTIPLY) && equaln(cadr(p), -1) && isoneoversqrttwo(caddr(p)) && length(p) === 3) { return 1; @@ -11814,6 +14214,19 @@ } }; + // n/2 * i * pi ? + + // return value: + + // 0 no + + // 1 1 + + // 2 -1 + + // 3 i + + // 4 -i isquarterturn = function(p) { var minussign, n; n = 0; @@ -11877,6 +14290,13 @@ return n; }; + // special multiple of pi? + + // returns for the following multiples of pi... + + // -4/2 -3/2 -2/2 -1/2 1/2 2/2 3/2 4/2 + + // 4 1 2 3 1 2 3 4 isnpi = function(p) { var doNothing, n; n = 0; @@ -11958,38 +14378,14 @@ } }; - - /* - Laguerre function - - Example - - laguerre(x,3) - - Result - - 1 3 3 2 - - --- x + --- x - 3 x + 1 - 6 2 - - The computation uses the following recurrence relation. - - L(x,0,k) = 1 - - L(x,1,k) = -x + k + 1 - - n*L(x,n,k) = (2*(n-1)+1-x+k)*L(x,n-1,k) - (n-1+k)*L(x,n-2,k) - - In the "for" loop i = n-1 so the recurrence relation becomes - - (i+1)*L(x,n,k) = (2*i+1-x+k)*L(x,n-1,k) - (i+k)*L(x,n-2,k) - */ - Eval_laguerre = function() { + // 1st arg push(cadr(p1)); Eval(); + // 2nd arg push(caddr(p1)); Eval(); + // 3rd arg push(cadddr(p1)); Eval(); p2 = pop(); @@ -12001,6 +14397,12 @@ return laguerre(); }; + //define X p1 + //define N p2 + //define K p3 + //define Y p4 + //define Y0 p5 + //define Y1 p6 laguerre = function() { var n; n = 0; @@ -12041,7 +14443,7 @@ push_integer(0); p6 = pop(); results = []; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p5 = p6; p6 = pop(); push_integer(2 * i + 1); @@ -12063,6 +14465,7 @@ return results; }; + // Find the least common multiple of two expressions. Eval_lcm = function() { var results; p1 = cdr(p1); @@ -12102,21 +14505,6 @@ return inverse(); }; - - /* - Return the leading coefficient of a polynomial. - - Example - - leading(5x^2+x+1,x) - - Result - - 5 - - The result is undefined if P is not a polynomial. - */ - Eval_leading = function() { push(cadr(p1)); Eval(); @@ -12131,60 +14519,35 @@ return leading(); }; + //define P p1 + //define X p2 + //define N p3 leading = function() { save(); p2 = pop(); p1 = pop(); - push(p1); + push(p1); // N = degree of P push(p2); degree(); p3 = pop(); - push(p1); + push(p1); // divide through by X ^ N push(p2); push(p3); power(); divide(); - push(p2); + push(p2); // remove terms that depend on X filter(); return restore(); }; - - /* - Legendre function - - Example - - legendre(x,3,0) - - Result - - 5 3 3 - --- x - --- x - 2 2 - - The computation uses the following recurrence relation. - - P(x,0) = 1 - - P(x,1) = x - - n*P(x,n) = (2*(n-1)+1)*x*P(x,n-1) - (n-1)*P(x,n-2) - - In the "for" loop we have i = n-1 so the recurrence relation becomes - - (i+1)*P(x,n) = (2*i+1)*x*P(x,n-1) - i*P(x,n-2) - - For m > 0 - - P(x,n,m) = (-1)^m * (1-x^2)^(m/2) * d^m/dx^m P(x,n) - */ - Eval_legendre = function() { + // 1st arg push(cadr(p1)); Eval(); + // 2nd arg push(caddr(p1)); Eval(); + // 3rd arg (optional) push(cadddr(p1)); Eval(); p2 = pop(); @@ -12196,6 +14559,12 @@ return legendre(); }; + //define X p1 + //define N p2 + //define M p3 + //define Y p4 + //define Y0 p5 + //define Y1 p6 legendre = function() { save(); __legendre(); @@ -12242,7 +14611,18 @@ push_integer(1); push_integer(0); p6 = pop(); - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { +// i=1 p5 = 0 +// p6 = 1 +// ((2*i+1)*x*p6 - i*p5) / i = x + +// i=2 p5 = 1 +// p6 = x +// ((2*i+1)*x*p6 - i*p5) / i = -1/2 + 3/2*x^2 + +// i=3 p5 = x +// p6 = -1/2 + 3/2*x^2 +// ((2*i+1)*x*p6 - i*p5) / i = -3/2*x + 5/2*x^3 + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p5 = p6; p6 = pop(); push_integer(2 * i + 1); @@ -12258,13 +14638,14 @@ divide(); } results = []; - for (i = i1 = 0, ref1 = m; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = m; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { push(p1); results.push(derivative()); } return results; }; + // moveTos tos * (-1)^m * (1-x^2)^(m/2) __legendre3 = function(m) { if (m === 0) { return; @@ -12293,17 +14674,28 @@ } }; + // Create a list from n things on the stack. + + // n is an integer list = function(n) { var listIterator, o, ref, results; listIterator = 0; push(symbol(NIL)); results = []; - for (listIterator = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; listIterator = 0 <= ref ? ++o : --o) { + for (listIterator = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); listIterator = 0 <= ref ? ++o : --o) { results.push(cons()); } return results; }; + // Natural logarithm. + + // Note that we use the mathematics / Javascript / Mathematica + // convention that "log" is indeed the natural logarithm. + + // In engineering, biology, astronomy, "log" can stand instead + // for the "common" logarithm i.e. base 10. Also note that Google + // calculations use log for the common logarithm. Eval_log = function() { push(cadr(p1)); Eval(); @@ -12347,6 +14739,7 @@ push_double(d); return; } + // rational number and not an integer? if (isfraction(p1)) { push(p1); numerator(); @@ -12357,6 +14750,7 @@ subtract(); return; } + // log(a ^ b) --> b log(a) if (car(p1) === symbol(POWER)) { push(caddr(p1)); push(cadr(p1)); @@ -12364,6 +14758,7 @@ multiply(); return; } + // log(a * b) --> log(a) + log(b) if (car(p1) === symbol(MULTIPLY)) { push_integer(0); p1 = cdr(p1); @@ -12380,14 +14775,131 @@ return list(2); }; + // now this might be a little confusing, so a + // clarification is in order. + // First off, at the scripting level most things + // as they are handled get evalled. + // That means that they are recursively "calculated" + // as much as possible, i.e. variables are recursively + // looked up for their values, operators are applied, + // functions are ivoked, etc. + // I.e. while scripting, most things are + // evalled all the times. + // e.g. if I type + // x = 1+1 + // then x is actually assigned 2, not 1+1 + // Something that helps a little is "quote", e.g. + // If I assign + // x = quote(1+1) + // then x actually contains 1+1, not 2. + // But then x is evaluated as soon as I type + // x // gives "2" as x is evaluated + + // Evaluation is great, but sometimes one wants + // to look at the actual structure of an expression + // or a content of a variable, without those + // being evaluated first. + + // for example I might type + // x = a + b + // a = 1 + // b = 2 + // and from this point on printing the actual + // structure of x is impossible, because from + // now on any evaluation of x will give "3" + // You might say "but you have x defined up there, + // what's the point of printing it out?", to which + // the answer is that one might do further + // substitutions or transformations of special kind + // to x. One might want to look at the structure + // and it might be complex or impossible. + + // So this function does that. + // If it's passed a variable, then it + // DOES NOT eval the variable, RATHER + // it prints the content of the variable without + // evaluating it. + // In the other cases it works like "quote" e.g. + // it just gives the argument as is, again without + // evaluating it. + + // In the following examples, for brevity, I just + // use + // x = quote(1+2) + // instead of this: + // x = a + b + // a = 1 + // b = 2 + // to put a structure in x that is easy to see whether + // it's avaulated or not. + + // So lookup allows this: + // x = quote(1+2) + // print(lookup(x)) # gives 1+2 + + // Note that there would be potentially a way + // to achieve a similar result, you could do: + // x = quote(quote(1+2)) + // print(x) + // but you can't always control x to contain + // two quotes like that... + // note how two "quotes" are needed because + // if you just put one, then + // x would indeed contain 1+2 instead of 3, + // but then print would evaluate that to 3: + // x = quote(1+2) # now x contains 1+2, not 3 + // print(x) # but x evaluated here to 3 + + // Other workarounds would not work: + // x = quote(1+2) + // print(quote(x)) + // would not work because quote(x) literally means 'x' + // so 'x' is printed instead of its content. + + // Note also that lookup allows you to copy + // the structure of a variable to another: + // x = a + b + // a = 1 + // b = 2 + // now: + // y = x # y contains the number 3 and prints to 3 + // y = lookup(x) # y contains "a+b" and prints to 3 + // y = quote(x) # y contains "x" and prints to 3 + // note that in the first and second case y is + // independent from x, i.e. changing x doesn't change y + // while in the last case it is. + + // Another similar simple example is when doing something + // like this: + // x = y + // y = z + // x + // => gives z + // lookup(x) + // => gives y + // i.e. lookup allows you to see the immediate + // content of x, rather than the evaluation which + // would end up in x -> y -> z + // Note that if you invert the order of the assignments i.e. + // y = z + // x = y + // Then at this point x immediately contains z, since the + // assignment x = y is not quoted, hence y is evaluated to z + // when assigned to x. + // lookup(x) + // => gives z Eval_lookup = function() { p1 = cadr(p1); if (!iscons(p1) && cadr(p1).k === SYM) { p1 = get_binding(p1); } - return push(p1); + return push(p1); // Bignum addition and subtraction }; + + //static unsigned int *addf(unsigned int *, unsigned int *) + //static unsigned int *subf(unsigned int *, unsigned int *) + //static int ucmp(unsigned int *, unsigned int *) madd = function(a, b) { return a.add(b); }; @@ -12404,14 +14916,35 @@ return a.subtract(b); }; + // unsigned compare ucmp = function(a, b) { return a.compareAbs(b); }; + //----------------------------------------------------------------------------- + + // Bignum GCD + + // Uses the binary GCD algorithm. + + // See "The Art of Computer Programming" p. 338. + + // mgcd always returns a positive value + + // mgcd(0, 0) = 0 + + // mgcd(u, 0) = |u| + + // mgcd(0, v) = |v| + + //----------------------------------------------------------------------------- mgcd = function(u, v) { return bigInt.gcd(u, v); }; + //if SELFTEST + + // s is a string new_string = function(s) { save(); p1 = new U(); @@ -12425,6 +14958,7 @@ return stop("out of memory"); }; + // both ints push_zero_matrix = function(i, j) { push(alloc_tensor(i * j)); stack[tos - 1].tensor.ndim = 2; @@ -12436,7 +14970,7 @@ var i, o, ref; push_zero_matrix(n, n); i = 0; - for (i = o = 0, ref = n; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { stack[tos - 1].tensor.elem[i * n + i] = one; } return check_tensor_dimensions(stack[tos - 1]); @@ -12459,6 +14993,11 @@ return restore(); }; + // see cmp_expr definition, this + // function alone just does simple structure comparison + // or compares numbers (either rationals or integers or doubles) + // but can't be used alone to test + // more complex mathematical equalities... equal = function(p1, p2) { if (cmp_expr(p1, p2) === 0) { return 1; @@ -12485,6 +15024,19 @@ } }; + // compares whether two expressions + // have the same structure. + // For example this method alone + // would compare "1+1" and "2" + // as different. + // It just so happens though that one oftens + // evaluates the two sides before passing them + // to this function, so chances are that the two + // sides have the same normal form. + // Even a simple evaluation might not cut it + // though... a simplification of both sides + // would then help. And even that might not + // cut it in some cases... cmp_expr = function(p1, p2) { var n; n = 0; @@ -12533,6 +15085,7 @@ if (istensor(p2)) { return 1; } + // recursion here while (iscons(p1) && iscons(p2)) { n = cmp_expr(car(p1), car(p2)); if (n !== 0) { @@ -12615,8 +15168,13 @@ return power(); }; + //__cmp = (p1, p2) -> + // return cmp_expr(p1, p2) + + // n an integer sort_stack = function(n) { var h, subsetOfStack; + //qsort(stack + tos - n, n, sizeof (U *), __cmp) h = tos - n; subsetOfStack = stack.slice(h, h + n); subsetOfStack.sort(cmp_expr); @@ -12627,6 +15185,7 @@ $.length = length; + // Bignum multiplication and division mmul = function(a, b) { return a.multiply(b); }; @@ -12635,7 +15194,7 @@ return a.divide(b); }; - + // a = a + b /* static void addf(unsigned int *a, unsigned int *b, int len) @@ -12648,9 +15207,9 @@ t >>= 32 } } - + // a = a - b - + static void subf(unsigned int *a, unsigned int *b, int len) { @@ -12662,11 +15221,11 @@ t >>= 32 } } - + // a = b * c - + // 0xffffffff + 0xffffffff * 0xffffffff == 0xffffffff00000000 - + static void mulf(unsigned int *a, unsigned int *b, int len, unsigned int c) { @@ -12679,18 +15238,23 @@ } a[i] = (unsigned int) t } - */ - + */ mmod = function(a, b) { return a.mod(b); }; + // return both quotient and remainder of a/b + // we'd have this method as divmod(number) + // but obviously doesn't change the passed parameters mdivrem = function(a, b) { var toReturn; toReturn = a.divmod(b); return [toReturn.quotient, toReturn.remainder]; }; + //if SELFTEST + + // small integer tests Eval_mod = function() { push(cadr(p1)); Eval(); @@ -12745,16 +15309,40 @@ return restore(); }; + // Bignum power + + // a is a bigint, n is a small normal int mpow = function(a, n) { return a.pow(n); }; + //if SELFTEST + + // Bignum prime test (returns 1 if prime, 0 if not) + + // Uses Algorithm P (probabilistic primality test) from p. 395 of + // "The Art of Computer Programming, Volume 2" by Donald E. Knuth. mprime = function(n) { return n.isProbablePrime(); }; + //if SELFTEST + + //----------------------------------------------------------------------------- + + // Bignum root + + // Returns null pointer if not perfect root. + + // The sign of the radicand is ignored. + + //----------------------------------------------------------------------------- mroot = function(n, index) { var i, j, k, o, ref, x, y; + // this doesn't quite work + //return n.pow(1/index + 0.0000000000000001) + + // sign of radicand ignored n = n.abs(); i = 0; j = 0; @@ -12762,6 +15350,7 @@ if (index === 0) { stop("root index is zero"); } + // count number of bits k = 0; while (n.shiftRight(k) > 0) { k++; @@ -12769,19 +15358,24 @@ if (k === 0) { return mint(0); } + // initial guess k = Math.floor((k - 1) / index); j = Math.floor(k / 32 + 1); x = bigInt(j); - for (i = o = 0, ref = j; 0 <= ref ? o < ref : o > ref; i = 0 <= ref ? ++o : --o) { + for (i = o = 0, ref = j; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { + // zero-out the ith bit x = x.and(bigInt(1).shiftLeft(i).not()); } while (k >= 0) { + // set the kth bit x = x.or(bigInt(1).shiftLeft(k)); y = mpow(x, index); switch (mcmp(y, n)) { case 0: return x; case 1: + //mp_clr_bit(x, k) + // clear the kth bit x = x.and(bigInt(1).shiftLeft(k).not()); } k--; @@ -12789,6 +15383,20 @@ return 0; }; + //if SELFTEST + + // Symbolic multiplication + + // multiplication is commutative, so it can't be used + // e.g. on two matrices. + // But it can be used, say, on a scalar and a matrix., + // so the output of a multiplication is not + // always a scalar. + + //extern void append(void) + //static void parse_p1(void) + //static void parse_p2(void) + //static void __normalize_radical_factors(int) Eval_multiply = function() { var results; push(cadr(p1)); @@ -12804,6 +15412,9 @@ return results; }; + // this one doesn't eval the factors, + // so you pass i*(-1)^(1/2), it wouldnt't + // give -1, because i is not evalled multiply = function() { if (esc_flag) { stop("escape key stop"); @@ -12822,9 +15433,11 @@ h = 0; i = 0; n = 0; + // pop operands p2 = pop(); p1 = pop(); h = tos; + // is either operand zero? if (isZeroAtom(p1) || isZeroAtom(p2)) { if (evaluatingAsFloats) { push_double(0.0); @@ -12833,6 +15446,9 @@ } return; } + // is either operand a sum? + + //console.log("yymultiply: expanding: " + expanding) if (expanding && isadd(p1)) { p1 = cdr(p1); if (evaluatingAsFloats) { @@ -12871,12 +15487,14 @@ scalar_times_tensor(); return; } + // tensor times scalar? if (istensor(p1) && !istensor(p2)) { push(p1); push(p2); tensor_times_scalar(); return; } + // adjust operands if (car(p1) === symbol(MULTIPLY)) { p1 = cdr(p1); } else { @@ -12891,6 +15509,7 @@ list(1); p2 = pop(); } + // handle numerical coefficients if (isNumericAtom(car(p1)) && isNumericAtom(car(p2))) { push(car(p1)); push(car(p2)); @@ -12913,6 +15532,14 @@ parse_p1(); parse_p2(); while (iscons(p1) && iscons(p2)) { + // if (car(p1)->gamma && car(p2)->gamma) { + // combine_gammas(h) + // p1 = cdr(p1) + // p2 = cdr(p2) + // parse_p1() + // parse_p2() + // continue + // } if (caar(p1) === symbol(OPERATOR) && caar(p2) === symbol(OPERATOR)) { push_symbol(OPERATOR); push(cdar(p1)); @@ -12947,6 +15574,7 @@ stop("internal error 2"); } } + // push remaining factors, if any while (iscons(p1)) { push(car(p1)); p1 = cdr(p1); @@ -12955,19 +15583,36 @@ push(car(p2)); p2 = cdr(p2); } + // normalize radical factors + + // example: 2*2(-1/2) -> 2^(1/2) + + // must be done after merge because merge may produce radical + + // example: 2^(1/2-a)*2^a -> 2^(1/2) __normalize_radical_factors(h); + // this hack should not be necessary, unless power returns a multiply + + //for (i = h; i < tos; i++) { + // if (car(stack[i]) == symbol(MULTIPLY)) { + // multiply_all(tos - h) + // return + // } + //} if (expanding) { - for (i = o = ref = h, ref1 = tos; ref <= ref1 ? o < ref1 : o > ref1; i = ref <= ref1 ? ++o : --o) { + for (i = o = ref = h, ref1 = tos; (ref <= ref1 ? o < ref1 : o > ref1); i = ref <= ref1 ? ++o : --o) { if (isadd(stack[i])) { multiply_all(tos - h); return; } } } + // n is the number of result factors on the stack n = tos - h; if (n === 1) { return; } + // discard integer 1 if (isrational(stack[h]) && equaln(stack[h], 1)) { if (n === 2) { p7 = pop(); @@ -12986,6 +15631,13 @@ return cons(); }; + // Decompose a factor into base and power. + + // input: car(p1) factor + + // output: p3 factor's base + + // p5 factor's power (possibly 1) parse_p1 = function() { p3 = car(p1); p5 = evaluatingAsFloats ? one_as_double : one; @@ -12995,6 +15647,13 @@ } }; + // Decompose a factor into base and power. + + // input: car(p2) factor + + // output: p4 factor's base + + // p6 factor's power (possibly 1) parse_p2 = function() { p4 = car(p2); p6 = evaluatingAsFloats ? one_as_double : one; @@ -13004,6 +15663,7 @@ } }; + // h an integer combine_factors = function(h) { push(p4); push(p5); @@ -13017,6 +15677,7 @@ multiply_numbers(); return stack[h] = pop(); } else if (car(p7) === symbol(MULTIPLY)) { + // power can return number * factor (i.e. -1 * i) if (isNumericAtom(cadr(p7)) && cdddr(p7) === symbol(NIL)) { push(stack[h]); push(cadr(p7)); @@ -13033,6 +15694,9 @@ gp = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, -6, -7, -8, -3, -4, -5, 13, 14, 15, -16, 9, 10, 11, -12], [0, 0, 6, -1, -11, 10, -2, -15, 14, 12, -5, 4, -9, 16, -8, 7, -13], [0, 0, 7, 11, -1, -9, 15, -2, -13, 5, 12, -3, -10, 8, 16, -6, -14], [0, 0, 8, -10, 9, -1, -14, 13, -2, -4, 3, 12, -11, -7, 6, 16, -15], [0, 0, 3, 2, 15, -14, 1, 11, -10, 16, -8, 7, 13, 12, -5, 4, 9], [0, 0, 4, -15, 2, 13, -11, 1, 9, 8, 16, -6, 14, 5, 12, -3, 10], [0, 0, 5, 14, -13, 2, 10, -9, 1, -7, 6, 16, 15, -4, 3, 12, 11], [0, 0, 13, 12, -5, 4, 16, -8, 7, -1, -11, 10, -3, -2, -15, 14, -6], [0, 0, 14, 5, 12, -3, 8, 16, -6, 11, -1, -9, -4, 15, -2, -13, -7], [0, 0, 15, -4, 3, 12, -7, 6, 16, -10, 9, -1, -5, -14, 13, -2, -8], [0, 0, 16, -9, -10, -11, -13, -14, -15, -3, -4, -5, 1, -6, -7, -8, 2], [0, 0, 9, -16, 8, -7, -12, 5, -4, -2, -15, 14, 6, -1, -11, 10, 3], [0, 0, 10, -8, -16, 6, -5, -12, 3, 15, -2, -13, 7, 11, -1, -9, 4], [0, 0, 11, 7, -6, -16, 4, -3, -12, -14, 13, -2, 8, -10, 9, -1, 5], [0, 0, 12, 13, 14, 15, 9, 10, 11, -6, -7, -8, -2, -3, -4, -5, -1]]; + //if 0 + + // h an int combine_gammas = function(h) { var n; n = gp[Math.floor(p1.gamma)][Math.floor(p2.gamma)]; @@ -13047,6 +15711,11 @@ } }; + // this is useful for example when you are just adding/removing + // factors from an already factored quantity. + // e.g. if you factored x^2 + 3x + 2 into (x+1)(x+2) + // and you want to divide by (x+1) , i.e. you multiply by (x-1)^-1, + // then there is no need to expand. multiply_noexpand = function() { var prev_expanding; prev_expanding = expanding; @@ -13055,6 +15724,9 @@ return expanding = prev_expanding; }; + // multiply n factors on stack + + // n an integer multiply_all = function(n) { var h, i, o, ref; i = 0; @@ -13067,7 +15739,7 @@ } h = tos - n; push(stack[h]); - for (i = o = 1, ref = n; 1 <= ref ? o < ref : o > ref; i = 1 <= ref ? ++o : --o) { + for (i = o = 1, ref = n; (1 <= ref ? o < ref : o > ref); i = 1 <= ref ? ++o : --o) { push(stack[h + i]); multiply(); } @@ -13075,6 +15747,7 @@ return moveTos(h + 1); }; + // n an integer multiply_all_noexpand = function(n) { var prev_expanding; prev_expanding = expanding; @@ -13083,6 +15756,15 @@ return expanding = prev_expanding; }; + //----------------------------------------------------------------------------- + + // Symbolic division, or numeric division if doubles are found. + + // Input: Dividend and divisor on stack + + // Output: Quotient on stack + + //----------------------------------------------------------------------------- divide = function() { if (isNumericAtom(stack[tos - 2]) && isNumericAtom(stack[tos - 1])) { return divide_numbers(); @@ -13092,6 +15774,7 @@ } }; + // this is different from inverse of a matrix (inv)! inverse = function() { if (isNumericAtom(stack[tos - 1])) { return invert_number(); @@ -13134,13 +15817,48 @@ return expanding = prev_expanding; }; + //----------------------------------------------------------------------------- + + // Normalize radical factors + + // Input: stack[h] Coefficient factor, possibly 1 + + // stack[h + 1] Second factor + + // stack[tos - 1] Last factor + + // Output: Reduced coefficent and normalized radicals (maybe) + + // Example: 2*2^(-1/2) -> 2^(1/2) + + // (power number number) is guaranteed to have the following properties: + + // 1. Base is an integer + + // 2. Absolute value of exponent < 1 + + // These properties are assured by the power function. + + //----------------------------------------------------------------------------- + + //define A p1 + //define B p2 + + //define BASE p3 + //define EXPO p4 + + //define TMP p5 + + // h is an int __normalize_radical_factors = function(h) { var i, i1, j1, o, ref, ref1, ref2, ref3, ref4, ref5; i = 0; + // if coeff is 1 or floating then don't bother if (isplusone(stack[h]) || isminusone(stack[h]) || isdouble(stack[h])) { return; } - for (i = o = ref = h + 1, ref1 = tos; ref <= ref1 ? o < ref1 : o > ref1; i = ref <= ref1 ? ++o : --o) { +// if no radicals then don't bother + for (i = o = ref = h + 1, ref1 = tos; (ref <= ref1 ? o < ref1 : o > ref1); i = ref <= ref1 ? ++o : --o) { if (__is_radical_number(stack[i])) { break; } @@ -13148,12 +15866,15 @@ if (i === tos) { return; } + // ok, try to simplify save(); + // numerator push(stack[h]); mp_numerator(); + //console.log("__normalize_radical_factors numerator: " + stack[tos-1]) p1 = pop(); - for (i = i1 = ref2 = h + 1, ref3 = tos; ref2 <= ref3 ? i1 < ref3 : i1 > ref3; i = ref2 <= ref3 ? ++i1 : --i1) { - if (isplusone(p1) || isminusone(p1)) { + for (i = i1 = ref2 = h + 1, ref3 = tos; (ref2 <= ref3 ? i1 < ref3 : i1 > ref3); i = ref2 <= ref3 ? ++i1 : --i1) { + if (isplusone(p1) || isminusone(p1)) { // p1 is A break; } if (!__is_radical_number(stack[i])) { @@ -13161,16 +15882,18 @@ } p3 = cadr(stack[i]); p4 = caddr(stack[i]); - if (!isnegativenumber(p4)) { + if (!isnegativenumber(p4)) { //p4 is EXPO continue; } + // numerator divisible by p3 (base)? push(p1); push(p3); divide(); p5 = pop(); - if (!isinteger(p5)) { + if (!isinteger(p5)) { //p5 is TMP continue; } + // reduce numerator p1 = p5; push_symbol(POWER); push(p3); @@ -13180,11 +15903,13 @@ list(3); stack[i] = pop(); } + // denominator push(stack[h]); mp_denominator(); + //console.log("__normalize_radical_factors denominator: " + stack[tos-1]) p2 = pop(); - for (i = j1 = ref4 = h + 1, ref5 = tos; ref4 <= ref5 ? j1 < ref5 : j1 > ref5; i = ref4 <= ref5 ? ++j1 : --j1) { - if (isplusone(p2)) { + for (i = j1 = ref4 = h + 1, ref5 = tos; (ref4 <= ref5 ? j1 < ref5 : j1 > ref5); i = ref4 <= ref5 ? ++j1 : --j1) { + if (isplusone(p2)) { // p2 is B break; } if (!__is_radical_number(stack[i])) { @@ -13192,16 +15917,21 @@ } p3 = cadr(stack[i]); p4 = caddr(stack[i]); - if (isnegativenumber(p4)) { + if (isnegativenumber(p4)) { //p4 is EXPO continue; } + // denominator divisible by p3? #p3 is BASE push(p2); push(p3); divide(); p5 = pop(); - if (!isinteger(p5)) { + if (!isinteger(p5)) { //p5 is TMP continue; } + //console.log("__new radical p5: " + p5.toString()) + //console.log("__new radical top stack: " + stack[tos-1]) + + // reduce denominator p2 = p5; push_symbol(POWER); push(p3); @@ -13210,6 +15940,13 @@ subtract(); if (dontCreateNewRadicalsInDenominatorWhenEvalingMultiplication) { if (isinteger(p3) && !isinteger(stack[tos - 1]) && isnegativenumber(stack[tos - 1])) { + // bail out, + // we want to avoid going ahead with the subtraction of + // the exponents, because that would turn a perfectly good + // integer exponent in the denominator into a fractional one + // i.e. a radical. + // Note that this only prevents new radicals ending up + // in the denominator, it doesn't fix existing ones. pop(); pop(); pop(); @@ -13220,9 +15957,11 @@ break; } } + //console.log("__new radical exponent: " + stack[tos-1]) list(3); stack[i] = pop(); } + // reconstitute the coefficient push(p1); push(p2); divide(); @@ -13230,7 +15969,11 @@ return restore(); }; + // don't include i + + // p is a U __is_radical_number = function(p) { + // don't use i if (car(p) === symbol(POWER) && isNumericAtom(cadr(p)) && isNumericAtom(caddr(p)) && !isminusone(cadr(p))) { return 1; } else { @@ -13238,6 +15981,25 @@ } }; + //----------------------------------------------------------------------------- + + // > a*hilbert(2) + // ((a,1/2*a),(1/2*a,1/3*a)) + + // Note that "a" is presumed to be a scalar. Is this correct? + + // Yes, because "*" has no meaning if "a" is a tensor. + // To multiply tensors, "dot" or "outer" should be used. + + // > dot(a,hilbert(2)) + // dot(a,((1,1/2),(1/2,1/3))) + + // In this case "a" could be a scalar or tensor so the result is not + // expanded. + + //----------------------------------------------------------------------------- + + // find the roots of a polynomial numerically NROOTS_YMAX = 101; NROOTS_DELTA = 1.0e-6; @@ -13248,14 +16010,17 @@ return Math.sqrt(z.r * z.r + z.i * z.i); }; + // random between -2 and 2 theRandom = 0.0; NROOTS_RANDOM = function() { + //theRandom += 0.2 + //return theRandom return 4.0 * Math.random() - 2.0; }; numericRootOfPolynomial = (function() { - function numericRootOfPolynomial() {} + class numericRootOfPolynomial {}; numericRootOfPolynomial.prototype.r = 0.0; @@ -13263,7 +16028,7 @@ return numericRootOfPolynomial; - })(); + }).call(this); nroots_a = new numericRootOfPolynomial(); @@ -13283,7 +16048,7 @@ nroots_c = []; - for (initNRoots = o = 0, ref = NROOTS_YMAX; 0 <= ref ? o < ref : o > ref; initNRoots = 0 <= ref ? ++o : --o) { + for (initNRoots = o = 0, ref = NROOTS_YMAX; (0 <= ref ? o < ref : o > ref); initNRoots = 0 <= ref ? ++o : --o) { nroots_c[initNRoots] = new numericRootOfPolynomial(); } @@ -13308,14 +16073,17 @@ if (!ispolyexpandedform(p1, p2)) { stop("nroots: polynomial?"); } + // mark the stack h = tos; + // get the coefficients push(p1); push(p2); n = coeff(); if (n > NROOTS_YMAX) { stop("nroots: degree?"); } - for (i = i1 = 0, ref1 = n; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { +// convert the coefficients to real and imaginary doubles + for (i = i1 = 0, ref1 = n; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { push(stack[h + i]); real(); yyfloat(); @@ -13332,7 +16100,9 @@ nroots_c[i].r = p1.d; nroots_c[i].i = p2.d; } + // pop the coefficients moveTos(h); + // n is the number of coefficients, n = deg(p) + 1 monic(n); for (k = j1 = ref2 = n; j1 > 1; k = j1 += -1) { findroot(k); @@ -13349,13 +16119,14 @@ add(); NROOTS_divpoly(k); } + // now make n equal to the number of roots n = tos - h; if (n > 1) { sort_stack(n); p1 = alloc_tensor(n); p1.tensor.ndim = 1; p1.tensor.dim[0] = n; - for (i = l1 = 0, ref3 = n; 0 <= ref3 ? l1 < ref3 : l1 > ref3; i = 0 <= ref3 ? ++l1 : --l1) { + for (i = l1 = 0, ref3 = n; (0 <= ref3 ? l1 < ref3 : l1 > ref3); i = 0 <= ref3 ? ++l1 : --l1) { p1.tensor.elem[i] = stack[h + i]; } moveTos(h); @@ -13363,6 +16134,7 @@ } }; + // divide the polynomial by its leading coefficient monic = function(n) { var i1, k, ref1, t; k = 0; @@ -13370,7 +16142,7 @@ nroots_y.r = nroots_c[n - 1].r; nroots_y.i = nroots_c[n - 1].i; t = nroots_y.r * nroots_y.r + nroots_y.i * nroots_y.i; - for (k = i1 = 0, ref1 = n - 1; 0 <= ref1 ? i1 < ref1 : i1 > ref1; k = 0 <= ref1 ? ++i1 : --i1) { + for (k = i1 = 0, ref1 = n - 1; (0 <= ref1 ? i1 < ref1 : i1 > ref1); k = 0 <= ref1 ? ++i1 : --i1) { nroots_c[k].r = (nroots_c[k].r * nroots_y.r + nroots_c[k].i * nroots_y.i) / t; nroots_c[k].i = (nroots_c[k].i * nroots_y.r - nroots_c[k].r * nroots_y.i) / t; } @@ -13378,6 +16150,7 @@ return nroots_c[n - 1].i = 0.0; }; + // uses the secant method findroot = function(n) { var i1, j, j1, k, nrabs, t; j = 0; @@ -13421,16 +16194,20 @@ nroots_fb.r = nroots_x.r; nroots_fb.i = nroots_x.i; } + // dx = nroots_b - nroots_a nroots_dx.r = nroots_b.r - nroots_a.r; nroots_dx.i = nroots_b.i - nroots_a.i; + // df = fb - fa nroots_df.r = nroots_fb.r - nroots_fa.r; nroots_df.i = nroots_fb.i - nroots_fa.i; + // y = dx / df t = nroots_df.r * nroots_df.r + nroots_df.i * nroots_df.i; if (t === 0.0) { break; } nroots_y.r = (nroots_dx.r * nroots_df.r + nroots_dx.i * nroots_df.i) / t; nroots_y.i = (nroots_dx.i * nroots_df.r - nroots_dx.r * nroots_df.i) / t; + // a = b - y * fb nroots_a.r = nroots_b.r - (nroots_y.r * nroots_fb.r - nroots_y.i * nroots_fb.i); nroots_a.i = nroots_b.i - (nroots_y.r * nroots_fb.i + nroots_y.i * nroots_fb.r); } @@ -13442,25 +16219,30 @@ var i1, k, ref1, results, t; k = 0; t = 0.0; + // x = a nroots_x.r = nroots_a.r; nroots_x.i = nroots_a.i; + // fa = c0 + c1 * x nroots_fa.r = nroots_c[0].r + nroots_c[1].r * nroots_x.r - nroots_c[1].i * nroots_x.i; nroots_fa.i = nroots_c[0].i + nroots_c[1].r * nroots_x.i + nroots_c[1].i * nroots_x.r; results = []; - for (k = i1 = 2, ref1 = n; 2 <= ref1 ? i1 < ref1 : i1 > ref1; k = 2 <= ref1 ? ++i1 : --i1) { + for (k = i1 = 2, ref1 = n; (2 <= ref1 ? i1 < ref1 : i1 > ref1); k = 2 <= ref1 ? ++i1 : --i1) { + // x = a * x t = nroots_a.r * nroots_x.r - nroots_a.i * nroots_x.i; nroots_x.i = nroots_a.r * nroots_x.i + nroots_a.i * nroots_x.r; nroots_x.r = t; + // fa += c[k] * x nroots_fa.r += nroots_c[k].r * nroots_x.r - nroots_c[k].i * nroots_x.i; results.push(nroots_fa.i += nroots_c[k].r * nroots_x.i + nroots_c[k].i * nroots_x.r); } return results; }; + // divide the polynomial by x - a NROOTS_divpoly = function(n) { var i1, j1, k, ref1, ref2, results; k = 0; - for (k = i1 = ref1 = n - 1; ref1 <= 0 ? i1 < 0 : i1 > 0; k = ref1 <= 0 ? ++i1 : --i1) { + for (k = i1 = ref1 = n - 1; (ref1 <= 0 ? i1 < 0 : i1 > 0); k = ref1 <= 0 ? ++i1 : --i1) { nroots_c[k - 1].r += nroots_c[k].r * nroots_a.r - nroots_c[k].i * nroots_a.i; nroots_c[k - 1].i += nroots_c[k].i * nroots_a.r + nroots_c[k].r * nroots_a.i; } @@ -13468,7 +16250,7 @@ stop("nroots: residual error"); } results = []; - for (k = j1 = 0, ref2 = n - 1; 0 <= ref2 ? j1 < ref2 : j1 > ref2; k = 0 <= ref2 ? ++j1 : --j1) { + for (k = j1 = 0, ref2 = n - 1; (0 <= ref2 ? j1 < ref2 : j1 > ref2); k = 0 <= ref2 ? ++j1 : --j1) { nroots_c[k].r = nroots_c[k + 1].r; results.push(nroots_c[k].i = nroots_c[k + 1].i); } @@ -13487,12 +16269,16 @@ theArgument = pop(); if (car(theArgument) === symbol(ADD)) { push(theArgument); + //console.trace "rationalising " rationalize(); theArgument = pop(); } + //console.log "rationalised: " + theArgument if (car(theArgument) === symbol(MULTIPLY) && !isplusone(car(cdr(theArgument)))) { h = tos; theArgument = cdr(theArgument); + //console.log "theArgument inside multiply: " + theArgument + //console.log "first term: " + car(theArgument) while (iscons(theArgument)) { push(car(theArgument)); numerator(); @@ -13509,6 +16295,7 @@ } }; + // Outer product of tensors Eval_outer = function() { var results; p1 = cdr(p1); @@ -13559,16 +16346,16 @@ nelem = p1.tensor.nelem * p2.tensor.nelem; p3 = alloc_tensor(nelem); p3.tensor.ndim = ndim; - for (i = i1 = 0, ref1 = p1.tensor.ndim; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 0, ref1 = p1.tensor.ndim; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { p3.tensor.dim[i] = p1.tensor.dim[i]; } j = i; - for (i = j1 = 0, ref2 = p2.tensor.ndim; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = p2.tensor.ndim; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { p3.tensor.dim[j + i] = p2.tensor.dim[i]; } k = 0; - for (i = l1 = 0, ref3 = p1.tensor.nelem; 0 <= ref3 ? l1 < ref3 : l1 > ref3; i = 0 <= ref3 ? ++l1 : --l1) { - for (j = m1 = 0, ref4 = p2.tensor.nelem; 0 <= ref4 ? m1 < ref4 : m1 > ref4; j = 0 <= ref4 ? ++m1 : --m1) { + for (i = l1 = 0, ref3 = p1.tensor.nelem; (0 <= ref3 ? l1 < ref3 : l1 > ref3); i = 0 <= ref3 ? ++l1 : --l1) { + for (j = m1 = 0, ref4 = p2.tensor.nelem; (0 <= ref4 ? m1 < ref4 : m1 > ref4); j = 0 <= ref4 ? ++m1 : --m1) { push(p1.tensor.elem[i]); push(p2.tensor.elem[j]); multiply(); @@ -13578,23 +16365,6 @@ return push(p3); }; - - /* - Partition a term - - Input stack: - - term (factor or product of factors) - - free variable - - Output stack: - - constant expression - - variable expression - */ - partition = function() { save(); p2 = pop(); @@ -13622,15 +16392,16 @@ return restore(); }; - /* Add a pattern i.e. a substitution rule. Substitution rule needs a template as first argument and what to transform it to as second argument. Optional third argument is a boolean test which adds conditions to when the rule is applied. - */ - + */ + // same as Eval_pattern but only leaves + // NIL on stack at return, hence gives no + // printout Eval_silentpattern = function() { Eval_pattern(); pop(); @@ -13655,13 +16426,19 @@ if (equal(firstArgument, secondArgument)) { stop("recursive pattern"); } + // console.log "Eval_pattern of " + cdr(p1) + // this is likely to create garbage collection + // problems in the C version as it's an + // untracked reference stringKey = "template: " + print_list(firstArgument); stringKey += " tests: " + print_list(thirdArgument); if (DEBUG) { console.log("pattern stringkey: " + stringKey); } patternPosition = userSimplificationsInStringForm.indexOf(stringKey); + // if pattern is not there yet, add it, otherwise replace it if (patternPosition === -1) { + //console.log "adding pattern because it doesn't exist: " + cdr(p1) userSimplificationsInStringForm.push(stringKey); userSimplificationsInListForm.push(cdr(p1)); } else { @@ -13671,23 +16448,25 @@ userSimplificationsInStringForm[patternPosition] = stringKey; userSimplificationsInListForm[patternPosition] = cdr(p1); } + // return the pattern node itself so we can + // give some printout feedback push_symbol(PATTERN); push(cdr(p1)); return list(2); }; - - /* - Clear all patterns - */ - do_clearPatterns = function() { userSimplificationsInListForm = []; return userSimplificationsInStringForm = []; }; Eval_clearpatterns = function() { + // this is likely to create garbage collection + // problems in the C version as it's an + // untracked reference do_clearPatterns(); + + // return nothing return push_symbol(NIL); }; @@ -13711,17 +16490,6 @@ return patternsinfoToBePrinted; }; - - /* - Convert complex z to polar form - - Input: push z - - Output: Result on stack - - polar(z) = abs(z) * exp(i * arg(z)) - */ - Eval_polar = function() { push(cadr(p1)); Eval(); @@ -13729,6 +16497,10 @@ }; polar = function() { + // there are points where we turn polar + // representations into rect, we set a "stack flag" + // here to avoid that, so we don't undo the + // work that we are trying to do. evaluatingPolar++; save(); p1 = pop(); @@ -13744,6 +16516,7 @@ return restore(); }; + // Factor using the Pollard rho method n_factor_number = 0; factor_number = function() { @@ -13751,6 +16524,7 @@ h = 0; save(); p1 = pop(); + // 0 or 1? if (equaln(p1, 0) || equaln(p1, 1) || equaln(p1, -1)) { push(p1); restore(); @@ -13768,6 +16542,9 @@ return restore(); }; + // factor using table look-up, then switch to rho method if necessary + + // From TAOCP Vol. 2 by Knuth, p. 380 (Algorithm A) factor_a = function() { var i1, k; k = 0; @@ -13777,6 +16554,7 @@ } for (k = i1 = 0; i1 < 10000; k = ++i1) { try_kth_prime(k); + // if n_factor_number is 1 then we're done if (n_factor_number.compare(1) === 0) { return; } @@ -13785,18 +16563,20 @@ }; try_kth_prime = function(k) { - var count, d, q, r, ref1; + var count, d, q, r; count = 0; d = mint(primetab[k]); count = 0; while (1) { + // if n_factor_number is 1 then we're done if (n_factor_number.compare(1) === 0) { if (count) { push_factor(d, count); } return; } - ref1 = mdivrem(n_factor_number, d), q = ref1[0], r = ref1[1]; + [q, r] = mdivrem(n_factor_number, d); + // continue looping while remainder is zero if (r.isZero()) { count++; n_factor_number = q; @@ -13807,12 +16587,15 @@ if (count) { push_factor(d, count); } + // q = n_factor_number/d, hence if q < d then + // n_factor_number < d^2 so n_factor_number is prime if (mcmp(q, d) === -1) { push_factor(n_factor_number, 1); return n_factor_number = mint(1); } }; + // From TAOCP Vol. 2 by Knuth, p. 385 (Algorithm B) factor_b = function() { var bigint_one, g, k, l, t, x, xprime; k = 0; @@ -13831,6 +16614,7 @@ if (esc_flag) { stop("esc"); } + // g = gcd(x' - x, n_factor_number) t = msub(xprime, x); t = setSignTo(t, 1); g = mgcd(t, n_factor_number); @@ -13840,6 +16624,7 @@ l *= 2; k = l; } + // x = (x ^ 2 + 1) mod n_factor_number t = mmul(x, x); x = madd(t, bigint_one); t = mmod(x, n_factor_number); @@ -13850,10 +16635,13 @@ if (mcmp(g, n_factor_number) === 0) { return -1; } + // n_factor_number = n_factor_number / g t = mdiv(n_factor_number, g); n_factor_number = t; + // x = x mod n_factor_number t = mmod(x, n_factor_number); x = t; + // xprime = xprime mod n_factor_number t = mmod(xprime, n_factor_number); xprime = t; break; @@ -13879,16 +16667,6 @@ } }; - - /* Power function - - Input: push Base - - push Exponent - - Output: Result on stack - */ - DEBUG_POWER = false; Eval_power = function() { @@ -13914,13 +16692,18 @@ debugger; } n = 0; - p2 = pop(); - p1 = pop(); + p2 = pop(); // exponent + p1 = pop(); // base inputExp = p2; inputBase = p1; + //debugger if (DEBUG_POWER) { console.log("POWER: " + p1 + " ^ " + p2); } + // first, some very basic simplifications right away + + // 1 ^ a -> 1 + // a ^ 0 -> 1 if (equal(p1, one) || isZeroAtomOrTensor(p2)) { if (evaluatingAsFloats) { push_double(1.0); @@ -13932,6 +16715,7 @@ } return; } + // a ^ 1 -> a if (equal(p2, one)) { push(p1); if (DEBUG_POWER) { @@ -13939,6 +16723,7 @@ } return; } + // -1 ^ -1 -> -1 if (isminusone(p1) && isminusone(p2)) { if (evaluatingAsFloats) { push_double(1.0); @@ -13951,6 +16736,7 @@ } return; } + // -1 ^ 1/2 -> i if (isminusone(p1) && (isoneovertwo(p2))) { push(imaginaryunit); if (DEBUG_POWER) { @@ -13958,6 +16744,7 @@ } return; } + // -1 ^ -1/2 -> -i if (isminusone(p1) && isminusoneovertwo(p2)) { push(imaginaryunit); negate(); @@ -13966,6 +16753,7 @@ } return; } + // -1 ^ rational if (isminusone(p1) && !isdouble(p1) && isrational(p2) && !isinteger(p2) && ispositivenumber(p2) && !evaluatingAsFloats) { if (DEBUG_POWER) { console.log(" power: -1 ^ rational"); @@ -13990,12 +16778,16 @@ console.log(" trick applied : " + stack[tos - 1]); } } + // evaluates clock form into + // rectangular form. This seems to give + // slightly better form to some test results. rect(); if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } return; } + // both base and exponent are rational numbers? if (isrational(p1) && isrational(p2)) { if (DEBUG_POWER) { console.log(" power: isrational(p1) && isrational(p2)"); @@ -14008,6 +16800,7 @@ } return; } + // both base and exponent are either rational or double? if (isNumericAtom(p1) && isNumericAtom(p2)) { if (DEBUG_POWER) { console.log(" power: both base and exponent are either rational or double "); @@ -14033,6 +16826,8 @@ } return; } + // if we only assume variables to be real, then |a|^2 = a^2 + // (if x is complex this doesn't hold e.g. i, which makes 1 and -1 if (car(p1) === symbol(ABS) && iseveninteger(p2) && !isZeroAtomOrTensor(get_binding(symbol(ASSUME_REAL_VARIABLES)))) { if (DEBUG_POWER) { console.log(" power: even power of absolute of real value "); @@ -14045,6 +16840,7 @@ } return; } + // e^log(...) if (p1 === symbol(E) && car(p2) === symbol(LOG)) { push(cadr(p2)); if (DEBUG_POWER) { @@ -14052,6 +16848,7 @@ } return; } + // e^some_float if (p1 === symbol(E) && isdouble(p2)) { if (DEBUG_POWER) { console.log(" power: p1 == symbol(E) && isdouble(p2) "); @@ -14062,6 +16859,9 @@ } return; } + // complex number in exponential form, get it to rectangular + // but only if we are not in the process of calculating a polar form, + // otherwise we'd just undo the work we want to do if (p1 === symbol(E) && Find(p2, imaginaryunit) !== 0 && Find(p2, symbol(PI)) !== 0 && !evaluatingPolar) { push_symbol(POWER); push(p1); @@ -14080,6 +16880,11 @@ return; } } + // (a * b) ^ c -> (a ^ c) * (b ^ c) + // note that we can't in general do this, for example + // sqrt(x*y) != x^(1/2) y^(1/2) (counterexample" x = -1 and y = -1) + // BUT we can carve-out here some cases where this + // transformation is correct if (car(p1) === symbol(MULTIPLY) && isinteger(p2)) { if (DEBUG_POWER) { console.log(" power: (a * b) ^ c -> (a ^ c) * (b ^ c) "); @@ -14101,11 +16906,18 @@ } return; } + // (a ^ b) ^ c -> a ^ (b * c) + // note that we can't in general do this, for example + // sqrt(x^y) != x^(1/2 y) (counterexample x = -1) + // BUT we can carve-out here some cases where this + // transformation is correct + + // simple numeric check to see if a is a number > 0 is_a_moreThanZero = false; if (isNumericAtom(cadr(p1))) { is_a_moreThanZero = sign(compare_numbers(cadr(p1), zero)); } - if (car(p1) === symbol(POWER) && (isinteger(p2) || is_a_moreThanZero)) { + if (car(p1) === symbol(POWER) && (isinteger(p2) || is_a_moreThanZero)) { // when a is >= 0 push(cadr(p1)); push(caddr(p1)); push(p2); @@ -14137,6 +16949,8 @@ } return; } + // when expanding, + // (a + b) ^ n -> (a + b) * (a + b) ... if (expanding && isadd(p1) && isNumericAtom(p2)) { push(p2); n = pop_integer(); @@ -14151,6 +16965,7 @@ return; } } + // sin(x) ^ 2n -> (1 - cos(x) ^ 2) ^ n if (trigmode === 1 && car(p1) === symbol(SIN) && iseveninteger(p2)) { if (DEBUG_POWER) { console.log(" power: trigmode == 1 && car(p1) == symbol(SIN) && iseveninteger(p2) "); @@ -14170,6 +16985,7 @@ } return; } + // cos(x) ^ 2n -> (1 - sin(x) ^ 2) ^ n if (trigmode === 2 && car(p1) === symbol(COS) && iseveninteger(p2)) { if (DEBUG_POWER) { console.log(" power: trigmode == 2 && car(p1) == symbol(COS) && iseveninteger(p2) "); @@ -14189,15 +17005,25 @@ } return; } + // complex number? (just number, not expression) if (iscomplexnumber(p1)) { if (DEBUG_POWER) { console.log(" power - handling the case (a + ib) ^ n"); } + // integer power? + + // n will be negative here, positive n already handled if (isinteger(p2)) { + // / \ n + // -n | a - ib | + // (a + ib) = | -------- | + // | 2 2 | + // \ a + b / push(p1); conjugate(); p3 = pop(); push(p3); + // gets the denominator push(p3); push(p1); multiply(); @@ -14212,6 +17038,7 @@ } return; } + // noninteger or floating power? if (isNumericAtom(p2)) { push(p1); abs(); @@ -14223,13 +17050,23 @@ push(p2); multiply(); if (evaluatingAsFloats || (iscomplexnumberdouble(p1) && isdouble(p2))) { + // remember that the "double" type is + // toxic, i.e. it propagates, so we do + // need to evaluate PI to its actual double + // value push_double(Math.PI); } else { + //console.log("power pushing PI when p1 is: " + p1 + " and p2 is:" + p2) push(symbol(PI)); } divide(); power(); multiply(); + // if we calculate the power making use of arctan: + // * it prevents nested radicals from being simplified + // * results become really hard to manipulate afterwards + // * we can't go back to other forms. + // so leave the power as it is. if (avoidCalculatingPowersIntoArctans) { if (Find(stack[tos - 1], symbol(ARCTAN))) { pop(); @@ -14245,6 +17082,21 @@ return; } } + + //push(p1) + //abs() + //push(p2) + //power() + //push(symbol(E)) + //push(p1) + //arg() + //push(p2) + //multiply() + //push(imaginaryunit) + //multiply() + //power() + //multiply() + if (simplify_polar()) { if (DEBUG_POWER) { console.log(" power: using simplify_polar"); @@ -14263,17 +17115,45 @@ } }; + //----------------------------------------------------------------------------- + + // Compute the power of a sum + + // Input: p1 sum + + // n exponent + + // Output: Result on stack + + // Note: + + // Uses the multinomial series (see Math World) + + // n n! n1 n2 nk + // (a1 + a2 + ... + ak) = sum (--------------- a1 a2 ... ak ) + // n1! n2! ... nk! + + // The sum is over all n1 ... nk such that n1 + n2 + ... + nk = n. + + //----------------------------------------------------------------------------- + + // first index is the term number 0..k-1, second index is the exponent 0..n + + //define A(i, j) frame[(i) * (n + 1) + (j)] power_sum = function(n) { var a, i, i1, j, j1, k, l1, ref1, ref2, ref3; a = []; i = 0; j = 0; k = 0; + // number of terms in the sum k = length(p1) - 1; + // local frame push_frame(k * (n + 1)); + // array of powers p1 = cdr(p1); - for (i = i1 = 0, ref1 = k; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { - for (j = j1 = 0, ref2 = n; 0 <= ref2 ? j1 <= ref2 : j1 >= ref2; j = 0 <= ref2 ? ++j1 : --j1) { + for (i = i1 = 0, ref1 = k; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { + for (j = j1 = 0, ref2 = n; (0 <= ref2 ? j1 <= ref2 : j1 >= ref2); j = 0 <= ref2 ? ++j1 : --j1) { push(car(p1)); push_integer(j); power(); @@ -14284,7 +17164,7 @@ push_integer(n); factorial(); p1 = pop(); - for (i = l1 = 0, ref3 = k; 0 <= ref3 ? l1 < ref3 : l1 > ref3; i = 0 <= ref3 ? ++l1 : --l1) { + for (i = l1 = 0, ref3 = k; (0 <= ref3 ? l1 < ref3 : l1 > ref3); i = 0 <= ref3 ? ++l1 : --l1) { a[i] = 0; } push(zero); @@ -14292,30 +17172,64 @@ return pop_frame(k * (n + 1)); }; + //----------------------------------------------------------------------------- + + // Compute multinomial sum + + // Input: k number of factors + + // n overall exponent + + // a partition array + + // i partition array index + + // m partition remainder + + // p1 n! + + // A factor array + + // Output: Result on stack + + // Note: + + // Uses recursive descent to fill the partition array. + + //----------------------------------------------------------------------------- + + //int k, int n, int *a, int i, int m multinomial_sum = function(k, n, a, i, m) { var i1, j, j1, l1, ref1, ref2, ref3; j = 0; if (i < k - 1) { - for (j = i1 = 0, ref1 = m; 0 <= ref1 ? i1 <= ref1 : i1 >= ref1; j = 0 <= ref1 ? ++i1 : --i1) { + for (j = i1 = 0, ref1 = m; (0 <= ref1 ? i1 <= ref1 : i1 >= ref1); j = 0 <= ref1 ? ++i1 : --i1) { a[i] = j; multinomial_sum(k, n, a, i + 1, m - j); } return; } a[i] = m; + // coefficient push(p1); - for (j = j1 = 0, ref2 = k; 0 <= ref2 ? j1 < ref2 : j1 > ref2; j = 0 <= ref2 ? ++j1 : --j1) { + for (j = j1 = 0, ref2 = k; (0 <= ref2 ? j1 < ref2 : j1 > ref2); j = 0 <= ref2 ? ++j1 : --j1) { push_integer(a[j]); factorial(); divide(); } - for (j = l1 = 0, ref3 = k; 0 <= ref3 ? l1 < ref3 : l1 > ref3; j = 0 <= ref3 ? ++l1 : --l1) { +// factors + for (j = l1 = 0, ref3 = k; (0 <= ref3 ? l1 < ref3 : l1 > ref3); j = 0 <= ref3 ? ++l1 : --l1) { push(stack[frame + j * (n + 1) + a[j]]); multiply(); } return add(); }; + // exp(n/2 i pi) ? + + // p2 is the exponent expression + + // clobbers p3 simplify_polar = function() { var doNothing, n; n = 0; @@ -14373,6 +17287,15 @@ return 0; }; + //----------------------------------------------------------------------------- + + // Look up the nth prime + + // Input: n on stack (0 < n < 10001) + + // Output: nth prime on stack + + //----------------------------------------------------------------------------- Eval_prime = function() { push(cadr(p1)); Eval(); @@ -14394,28 +17317,41 @@ codeGen = false; + // this is only invoked when user invokes + // "print" explicitly Eval_print = function() { stringsEmittedByUserPrintouts += _print(cdr(p1), printMode); return push(symbol(NIL)); }; + // this is only invoked when user invokes + // "print2dascii" explicitly Eval_print2dascii = function() { stringsEmittedByUserPrintouts += _print(cdr(p1), PRINTMODE_2DASCII); return push(symbol(NIL)); }; + // this is only invoked when user invokes + // "printcomputer" explicitly Eval_printcomputer = function() { stringsEmittedByUserPrintouts += _print(cdr(p1), PRINTMODE_COMPUTER); return push(symbol(NIL)); }; + // this is only invoked when user invokes + // "printlatex" explicitly Eval_printlatex = function() { stringsEmittedByUserPrintouts += _print(cdr(p1), PRINTMODE_LATEX); return push(symbol(NIL)); }; + // this is only invoked when user invokes + // "printhuman" explicitly Eval_printhuman = function() { var original_test_flag; + // test flag needs to be suspended + // because otherwise "printcomputer" mode + // will happen. original_test_flag = test_flag; test_flag = 0; stringsEmittedByUserPrintouts += _print(cdr(p1), PRINTMODE_HUMAN); @@ -14423,6 +17359,8 @@ return push(symbol(NIL)); }; + // this is only invoked when user invokes + // "printlist" explicitly Eval_printlist = function() { var beenPrinted; beenPrinted = _print(cdr(p1), PRINTMODE_LIST); @@ -14437,15 +17375,6 @@ push(car(p)); Eval(); p2 = pop(); - - /* - if (issymbol(car(p)) && car(p) != p2) - push_symbol(SETQ); - push(car(p)); - push(p2); - list(3); - p2 = pop(); - */ origPrintMode = printMode; if (passedPrintMode === PRINTMODE_COMPUTER) { printMode = PRINTMODE_COMPUTER; @@ -14502,6 +17431,7 @@ originalCodeGen = codeGen; codeGen = false; returnedString = print_expr(p); + // some variables might contain underscores, escape those returnedString = returnedString.replace(/_/g, "\\_"); printMode = origPrintMode; codeGen = originalCodeGen; @@ -14521,12 +17451,12 @@ print_base_of_denom = function(p1) { var accumulator; accumulator = ""; - if (isfraction(p1) || car(p1) === symbol(ADD) || car(p1) === symbol(MULTIPLY) || car(p1) === symbol(POWER) || lessp(p1, zero)) { + if (isfraction(p1) || car(p1) === symbol(ADD) || car(p1) === symbol(MULTIPLY) || car(p1) === symbol(POWER) || lessp(p1, zero)) { // p1 is BASE accumulator += print_char('('); accumulator += print_expr(p1); accumulator += print_char(')'); } else { - accumulator += print_expr(p1); + accumulator += print_expr(p1); // p1 is BASE } return accumulator; }; @@ -14534,30 +17464,39 @@ print_expo_of_denom = function(p2) { var accumulator; accumulator = ""; - if (isfraction(p2) || car(p2) === symbol(ADD) || car(p2) === symbol(MULTIPLY) || car(p2) === symbol(POWER)) { + if (isfraction(p2) || car(p2) === symbol(ADD) || car(p2) === symbol(MULTIPLY) || car(p2) === symbol(POWER)) { // p2 is EXPO accumulator += print_char('('); accumulator += print_expr(p2); accumulator += print_char(')'); } else { - accumulator += print_expr(p2); + accumulator += print_expr(p2); // p2 is EXPO } return accumulator; }; + // prints stuff after the divide symbol "/" + + // d is the number of denominators + + //define BASE p1 + //define EXPO p2 print_denom = function(p, d) { var accumulator; accumulator = ""; save(); p1 = cadr(p); p2 = caddr(p); - if (isminusone(p2)) { + if (isminusone(p2)) { // p2 is EXPO accumulator += print_base_of_denom(p1); restore(); return accumulator; } - if (d === 1) { + if (d === 1) { // p2 is EXPO accumulator += print_char('('); } + // prepare the exponent + // (needs to be negated) + // before printing it out push(p2); negate(); p2 = pop(); @@ -14569,6 +17508,8 @@ return accumulator; }; + //define A p3 + //define B p4 print_a_over_b = function(p) { var accumulator, d, doNothing, n; accumulator = ""; @@ -14576,6 +17517,7 @@ n = 0; d = 0; save(); + // count numerators and denominators n = 0; d = 0; p1 = cdr(p); @@ -14588,16 +17530,16 @@ push(p2); mp_denominator(); p4 = pop(); - if (!isplusone(p3)) { + if (!isplusone(p3)) { // p3 is A n++; } - if (!isplusone(p4)) { + if (!isplusone(p4)) { // p4 is B d++; } p1 = cdr(p1); } else { p3 = one; - p4 = one; + p4 = one; // p4 is B } while (iscons(p1)) { p2 = car(p1); @@ -14608,6 +17550,7 @@ } p1 = cdr(p1); } + //debugger if (printMode === PRINTMODE_LATEX) { accumulator += print_str('\\frac{'); } @@ -14619,7 +17562,7 @@ if (isrational(car(p1))) { p1 = cdr(p1); } - if (!isplusone(p3)) { + if (!isplusone(p3)) { // p3 is A accumulator += print_factor(p3); flag = 1; } @@ -14652,7 +17595,7 @@ if (isrational(car(p1))) { p1 = cdr(p1); } - if (!isplusone(p4)) { + if (!isplusone(p4)) { // p4 is B accumulator += print_factor(p4); flag = 1; } @@ -14735,13 +17678,29 @@ } if (car(p) === symbol(MULTIPLY)) { p = cdr(p); + // coeff -1? if (isminusone(car(p))) { + // print_char('-') p = cdr(p); } previousFactorWasANumber = false; + // print the first factor ------------ if (isNumericAtom(car(p))) { previousFactorWasANumber = true; } + // this numberOneOverSomething thing is so that + // we show things of the form + // numericFractionOfForm1/something * somethingElse + // as + // somethingElse / something + // so for example 1/2 * sqrt(2) is rendered as + // sqrt(2)/2 + // rather than the first form, which looks confusing. + // NOTE that you might want to avoid this + // when printing polynomials, as it could be nicer + // to show the numeric coefficients well separated from + // the variable, but we'll see when we'll + // come to it if it's an issue. numberOneOverSomething = false; if (printMode === PRINTMODE_LATEX && iscons(cdr(p)) && isNumberOneOverSomething(car(p))) { numberOneOverSomething = true; @@ -14754,9 +17713,20 @@ accumulator += print_factor(car(p)); } p = cdr(p); + // print all the other factors ------- while (iscons(p)) { + // check if we end up having a case where two numbers + // are next to each other. In those cases, latex needs + // to insert a \cdot otherwise they end up + // right next to each other and read like one big number if (printMode === PRINTMODE_LATEX) { if (previousFactorWasANumber) { + // if what comes next is a power and the base + // is a number, then we are in the case + // of consecutive numbers. + // Note that sqrt() i.e when exponent is 1/2 + // doesn't count because the radical gives + // a nice graphical separation already. if (caar(p) === symbol(POWER)) { if (isNumericAtom(car(cdr(car(p))))) { if (!isfraction(car(cdr(cdr(car(p)))))) { @@ -14980,7 +17950,7 @@ accumulator += print_expr(functionBody); accumulator += print_str(" \\,"); p = originalIntegral; - for (i = i1 = 1, ref1 = numberOfIntegrals; 1 <= ref1 ? i1 <= ref1 : i1 >= ref1; i = 1 <= ref1 ? ++i1 : --i1) { + for (i = i1 = 1, ref1 = numberOfIntegrals; (1 <= ref1 ? i1 <= ref1 : i1 >= ref1); i = 1 <= ref1 ? ++i1 : --i1) { theVariable = cdr(p); accumulator += print_str(" \\mathrm{d} "); accumulator += print_expr(car(theVariable)); @@ -14999,21 +17969,37 @@ return accumulator; }; + // j scans the dimensions + // k is an increment for all the printed elements + // since they are all together in sequence in one array print_tensor_inner = function(p, j, k) { - var accumulator, i, i1, j1, ref1, ref2, ref3, retString; + var accumulator, i, i1, j1, ref1, ref2, retString; accumulator = ""; accumulator += print_str("["); + // only the last dimension prints the actual elements + // e.g. in a matrix, the first dimension contains + // vectors, not elements, and the second dimension + // actually contains the elements + + // if not the last dimension, we are just printing wrappers + // and recursing down i.e. we print the next dimension if (j < p.tensor.ndim - 1) { - for (i = i1 = 0, ref1 = p.tensor.dim[j]; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { - ref2 = print_tensor_inner(p, j + 1, k), k = ref2[0], retString = ref2[1]; + for (i = i1 = 0, ref1 = p.tensor.dim[j]; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { + [k, retString] = print_tensor_inner(p, j + 1, k); accumulator += retString; + // add separator between elements dimensions + // "above" the inner-most dimension if (i !== p.tensor.dim[j] - 1) { accumulator += print_str(","); } } } else { - for (i = j1 = 0, ref3 = p.tensor.dim[j]; 0 <= ref3 ? j1 < ref3 : j1 > ref3; i = 0 <= ref3 ? ++j1 : --j1) { +// if we reached the last dimension, we print the actual +// elements + for (i = j1 = 0, ref2 = p.tensor.dim[j]; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { accumulator += print_expr(p.tensor.elem[k]); + // add separator between elements in the + // inner-most dimension if (i !== p.tensor.dim[j] - 1) { accumulator += print_str(","); } @@ -15033,29 +18019,51 @@ return accumulator; }; + // firstLevel is needed because printing a matrix + // is not exactly an elegant recursive procedure: + // the vector on the first level prints the latex + // "wrap", while the vectors that make up the + // rows don't. so it's a bit asymmetric and this + // flag helps. + // j scans the dimensions + // k is an increment for all the printed elements + // since they are all together in sequence in one array print_tensor_inner_latex = function(firstLevel, p, j, k) { - var accumulator, i, i1, j1, ref1, ref2, ref3, retString; + var accumulator, i, i1, j1, ref1, ref2, retString; accumulator = ""; + // open the outer latex wrap if (firstLevel) { accumulator += "\\begin{bmatrix} "; } + // only the last dimension prints the actual elements + // e.g. in a matrix, the first dimension contains + // vectors, not elements, and the second dimension + // actually contains the elements + + // if not the last dimension, we are just printing wrappers + // and recursing down i.e. we print the next dimension if (j < p.tensor.ndim - 1) { - for (i = i1 = 0, ref1 = p.tensor.dim[j]; 0 <= ref1 ? i1 < ref1 : i1 > ref1; i = 0 <= ref1 ? ++i1 : --i1) { - ref2 = print_tensor_inner_latex(0, p, j + 1, k), k = ref2[0], retString = ref2[1]; + for (i = i1 = 0, ref1 = p.tensor.dim[j]; (0 <= ref1 ? i1 < ref1 : i1 > ref1); i = 0 <= ref1 ? ++i1 : --i1) { + [k, retString] = print_tensor_inner_latex(0, p, j + 1, k); accumulator += retString; if (i !== p.tensor.dim[j] - 1) { + // add separator between rows accumulator += print_str(" \\\\ "); } } } else { - for (i = j1 = 0, ref3 = p.tensor.dim[j]; 0 <= ref3 ? j1 < ref3 : j1 > ref3; i = 0 <= ref3 ? ++j1 : --j1) { +// if we reached the last dimension, we print the actual +// elements + for (i = j1 = 0, ref2 = p.tensor.dim[j]; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { accumulator += print_expr(p.tensor.elem[k]); + // separator between elements in each row if (i !== p.tensor.dim[j] - 1) { accumulator += print_str(" & "); } k++; } } + // close the outer latex wrap if (firstLevel) { accumulator += " \\end{bmatrix}"; } @@ -15091,6 +18099,9 @@ accumulator = "\\left\\{ \\begin{array}{ll}"; p = cdr(p); while (iscons(p)) { + // odd number of parameters means that the + // last argument becomes the default case + // i.e. the one without a test. if (cdr(p) === symbol(NIL)) { accumulator += "{"; accumulator += print_expr(car(p)); @@ -15103,6 +18114,8 @@ accumulator += "} & if & "; accumulator += print_expr(car(p)); accumulator += " \\\\\\\\"; + // test unsuccessful, continue to the + // next pair of test,value p = cddr(p); } accumulator = accumulator.substring(0, accumulator.length - 4); @@ -15115,6 +18128,9 @@ p = cdr(p); howManyIfs = 0; while (iscons(p)) { + // odd number of parameters means that the + // last argument becomes the default case + // i.e. the one without a test. if (cdr(p) === symbol(NIL)) { accumulator += "else {"; accumulator += "return (" + print_expr(car(p)) + ");"; @@ -15127,6 +18143,8 @@ accumulator += "if (" + print_expr(car(p)) + "){"; accumulator += "return (" + print_expr(cadr(p)) + ");"; accumulator += "}"; + // test unsuccessful, continue to the + // next pair of test,value howManyIfs++; p = cddr(p); } @@ -15277,9 +18295,11 @@ print_power = function(base, exponent) { var accumulator, denomExponent, newExponent, numExponent; accumulator = ""; + //debugger if (DEBUG) { console.log("power base: " + base + " " + " exponent: " + exponent); } + // quick check is this is actually a square root. if (isoneovertwo(exponent)) { if (equaln(base, 2)) { if (codeGen) { @@ -15327,6 +18347,13 @@ return accumulator; } if ((equaln(get_binding(symbol(PRINT_LEAVE_X_ALONE)), 0)) || base.printname !== "x") { + // if the exponent is negative then + // we invert the base BUT we don't do + // that if the base is "e", because for + // example when trigonometric functions are + // expressed in terms of exponential functions + // that would be really confusing, one wants to + // keep "e" as the base and the negative exponent if (base !== symbol(E)) { if (isminusone(exponent)) { if (printMode === PRINTMODE_LATEX) { @@ -15394,8 +18421,17 @@ } } if (printMode === PRINTMODE_LATEX && isplusone(exponent)) { + // if we are in latex mode we turn many + // radicals into a radix sign with a power + // underneath, and the power is often one + // (e.g. square root turns into a radical + // with a power one underneath) so handle + // this case simply here, just print the base accumulator += print_expr(base); } else { + // print the base, + // determining if it needs to be + // wrapped in parentheses or not if (isadd(base) || isnegativenumber(base)) { accumulator += print_str('('); accumulator += print_expr(base); @@ -15415,12 +18451,19 @@ } else { accumulator += print_factor(base); } + // print the power symbol + //debugger if (printMode === PRINTMODE_HUMAN && !test_flag) { + //print_str(" ^ ") accumulator += print_str(power_str); } else { accumulator += print_str("^"); } + // print the exponent if (printMode === PRINTMODE_LATEX) { + // in latex mode, one can omit the curly braces + // wrapping the exponent if the exponent is only + // one character long if (print_expr(exponent).length > 1) { accumulator += print_str("{"); accumulator += print_expr(exponent); @@ -15465,6 +18508,7 @@ print_factor = function(p, omitParens) { var accumulator, base, exponent, fbody, parameters, returned; + // debugger accumulator = ""; if (isNumericAtom(p)) { accumulator += print_number(p, false); @@ -15521,6 +18565,21 @@ accumulator += print_power(base, exponent); return accumulator; } + // if (car(p) == _list) { + // print_str("{") + // p = cdr(p) + // if (iscons(p)) { + // print_expr(car(p)) + // p = cdr(p) + // } + // while (iscons(p)) { + // print_str(",") + // print_expr(car(p)) + // p = cdr(p) + // } + // print_str("}") + // return + // } if (car(p) === symbol(FUNCTION)) { fbody = cadr(p); if (!codeGen) { @@ -15561,6 +18620,7 @@ accumulator += print_ABS_latex(p); return accumulator; } else if (car(p) === symbol(SQRT) && printMode === PRINTMODE_LATEX) { + //debugger accumulator += print_SQRT_latex(p); return accumulator; } else if (car(p) === symbol(TRANSPOSE)) { @@ -15636,6 +18696,10 @@ accumulator += print_SUM_codegen(p); return accumulator; } + //else if car(p) == symbol(QUOTE) + // if printMode == PRINTMODE_LATEX + // print_expr(cadr(p)) + // return accumulator } else if (car(p) === symbol(PRODUCT)) { if (printMode === PRINTMODE_LATEX) { accumulator += print_PRODUCT_latex(p); @@ -15743,6 +18807,10 @@ } } if (iscons(p)) { + //if (car(p) == symbol(FORMAL) && cadr(p)->k == SYM) { + // print_str(((struct symbol *) cadr(p))->name) + // return + //} accumulator += print_factor(car(p)); p = cdr(p); if (!omitParens) { @@ -15810,8 +18878,10 @@ accumulator += ')'; break; case STR: + //print_str("\"") accumulator += p.str; break; + //print_str("\"") case NUM: case DOUBLE: accumulator += print_number(p, true); @@ -15851,9 +18921,13 @@ } }; + // don't consider the leading fraction + // we want 2/3*a*b*c instead of 2*a*b*c/3 any_denominators = function(p) { var q; p = cdr(p); + // if (isfraction(car(p))) + // return 1 while (iscons(p)) { q = car(p); if (is_denominator(q)) { @@ -15864,31 +18938,49 @@ return 0; }; - /* - + Prints in "2d", e.g. instead of 1/(x+1)^2 : - + 1 ---------- 2 (1 + x) - + Note that although this looks more natural, a) it's not parsable and b) it can be occasionally be ambiguous, such as: - + 1 ---- 2 x - + is 1/x^2 but it also looks a little like x^(1/2) - */ + */ + //----------------------------------------------------------------------------- + + // Examples: + + // 012345678 + // -2 ......... + // -1 ......... + // 0 ..hello.. x=2, y=0, h=1, w=5 + // 1 ......... + // 2 ......... + + // 012345678 + // -2 ......... + // -1 ..355.... + // 0 ..---.... x=2, y=-1, h=3, w=3 + // 1 ..113.... + // 2 ......... + + //----------------------------------------------------------------------------- YMAX = 10000; glyph = (function() { - function glyph() {} + class glyph {}; glyph.prototype.c = 0; @@ -15898,11 +18990,12 @@ return glyph; - })(); + }).call(this); + // will contain glyphs chartab = []; - for (charTabIndex = i1 = 0, ref1 = YMAX; 0 <= ref1 ? i1 < ref1 : i1 > ref1; charTabIndex = 0 <= ref1 ? ++i1 : --i1) { + for (charTabIndex = i1 = 0, ref1 = YMAX; (0 <= ref1 ? i1 < ref1 : i1 > ref1); charTabIndex = 0 <= ref1 ? ++i1 : --i1) { chartab[charTabIndex] = new glyph(); } @@ -15916,6 +19009,9 @@ display_flag = 0; + // this is not really the translated version, + // the original is in window.cpp and is + // rather more complex printchar_nowrap = function(character) { var accumulator; accumulator = ""; @@ -15928,7 +19024,7 @@ }; print2dascii = function(p) { - var beenPrinted, h, ref2, w, y; + var beenPrinted, h, w, y; h = 0; w = 0; y = 0; @@ -15937,7 +19033,8 @@ level = 0; emit_x = 0; emit_top_expr(p); - ref2 = get_size(0, yindex), h = ref2[0], w = ref2[1], y = ref2[2]; + // if too wide then print flat + [h, w, y] = get_size(0, yindex); if (w > 100) { printline(p); restore(); @@ -15985,6 +19082,10 @@ }; emit_expr = function(p) { + // if (level > 0) { + // printexpr(p) + // return + // } expr_level++; if (car(p) === symbol(ADD)) { p = cdr(p); @@ -16025,6 +19126,8 @@ var results; if (car(p) === symbol(ADD)) { p = cdr(p); + // if (__is_negative(car(p))) + // __emit_char('-') emit_term(car(p)); p = cdr(p); results = []; @@ -16043,6 +19146,8 @@ } return results; } else { + // if (__is_negative(p)) + // __emit_char('-') return emit_term(p); } }; @@ -16083,6 +19188,10 @@ var count, q; count = 0; p = cdr(p); + // if (isfraction(car(p))) { + // count++ + // p = cdr(p) + // } while (iscons(p)) { q = car(p); if (isdenominator(q)) { @@ -16093,6 +19202,7 @@ return count; }; + // n is the number of denominators, not counting a fraction like 1/2 emit_multiply = function(p, n) { var results; if (n === 0) { @@ -16112,6 +19222,7 @@ } else { emit_numerators(p); __emit_char('/'); + // need grouping if more than one denominator if (n > 1 || isfraction(cadr(p))) { __emit_char('('); emit_denominators(p); @@ -16122,6 +19233,10 @@ } }; + //define A p3 + //define B p4 + + // sign of term has already been emitted emit_fraction = function(p, d) { var count, doNothing, k1, k2, n, x; count = 0; @@ -16139,14 +19254,16 @@ p3 = pop(); push(cadr(p)); mp_denominator(); - p4 = pop(); + p4 = pop(); // p4 is B } if (isdouble(cadr(p))) { push(cadr(p)); absval(); - p3 = pop(); + p3 = pop(); // p3 is A } - if (isplusone(p3)) { + + // count numerators + if (isplusone(p3)) { // p3 is A n = 0; } else { n = 1; @@ -16164,14 +19281,17 @@ } p1 = cdr(p1); } + // emit numerators x = emit_x; k1 = yindex; count = 0; - if (!isplusone(p3)) { + if (!isplusone(p3)) { // p3 is A emit_number(p3, 0); count++; } + // skip over "multiply" p1 = cdr(p); + // skip over numerical coefficient, already handled if (isNumericAtom(car(p1))) { p1 = cdr(p1); } @@ -16195,9 +19315,10 @@ if (count === 0) { __emit_char('1'); } + // emit denominators k2 = yindex; count = 0; - if (!isplusone(p4)) { + if (!isplusone(p4)) { // p4 is B emit_number(p4, 0); count++; d++; @@ -16221,6 +19342,7 @@ return restore(); }; + // p points to a multiply emit_numerators = function(p) { var doNothing, n; save(); @@ -16262,6 +19384,7 @@ return restore(); }; + // p points to a multiply emit_denominators = function(p) { var n; save(); @@ -16291,6 +19414,7 @@ emit_factor = function(p) { if (istensor(p)) { if (level === 0) { + //emit_tensor(p) emit_flat_tensor(p); } else { emit_flat_tensor(p); @@ -16310,6 +19434,9 @@ return; } if (iscons(p)) { + //if (car(p) == symbol(FORMAL) && cadr(p).k == SYM) + // emit_symbol(cadr(p)) + //else emit_function(p); return; } @@ -16343,7 +19470,7 @@ push(p); mp_denominator(); p4 = pop(); - if (isplusone(p4)) { + if (isplusone(p4)) { // p4 is B emit_number(p3, 0); restore(); return; @@ -16352,11 +19479,12 @@ k1 = yindex; emit_number(p3, 0); k2 = yindex; - emit_number(p4, 0); + emit_number(p4, 0); // p4 is B fixup_fraction(x, k1, k2); return restore(); }; + // if it's a factor then it doesn't need parens around it, i.e. 1/sin(theta)^2 isfactor = function(p) { if (iscons(p) && car(p) !== symbol(ADD) && car(p) !== symbol(MULTIPLY) && car(p) !== symbol(POWER)) { return 1; @@ -16411,12 +19539,15 @@ } return; } + // special case: 1 over something if (__is_negative(caddr(p))) { x = emit_x; k1 = yindex; __emit_char('1'); k2 = yindex; + //level++ emit_denominator(p, 1); + //level-- fixup_fraction(x, k1, k2); return; } @@ -16433,10 +19564,14 @@ return fixup_power(k1, k2); }; + // if n == 1 then emit as expr (no parens) + + // p is a power emit_denominator = function(p, n) { var k1, k2; k1 = 0; k2 = 0; + // special case: 1 over something if (isminusone(caddr(p))) { if (n === 1) { emit_expr(cadr(p)); @@ -16446,12 +19581,14 @@ return; } k1 = yindex; + // emit base if (isfactor(cadr(p))) { emit_factor(cadr(p)); } else { emit_subexpr(cadr(p)); } k2 = yindex; + // emit exponent, don't emit minus sign level++; emit_unsigned_expr(caddr(p)); level--; @@ -16479,6 +19616,7 @@ p = cdr(p); while (iscons(p)) { __emit_char(','); + //__emit_char(' ') emit_expr(car(p)); p = cdr(p); } @@ -16532,7 +19670,7 @@ } pPrintName = get_printname(p); results = []; - for (i = j1 = 0, ref2 = pPrintName.length; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = pPrintName.length; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { results.push(__emit_char(pPrintName[i])); } return results; @@ -16543,14 +19681,14 @@ i = 0; pString = p.str; __emit_char('"'); - for (i = j1 = 0, ref2 = pString.length; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = pString.length; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { __emit_char(pString[i]); } return __emit_char('"'); }; fixup_fraction = function(x, k1, k2) { - var dx, dy, h1, h2, i, j1, ref2, ref3, ref4, results, w, w1, w2, y, y1, y2; + var dx, dy, h1, h2, i, j1, ref2, results, w, w1, w2, y, y1, y2; dx = 0; dy = 0; i = 0; @@ -16562,14 +19700,15 @@ h2 = 0; w2 = 0; y2 = 0; - ref2 = get_size(k1, k2), h1 = ref2[0], w1 = ref2[1], y1 = ref2[2]; - ref3 = get_size(k2, yindex), h2 = ref3[0], w2 = ref3[1], y2 = ref3[2]; + [h1, w1, y1] = get_size(k1, k2); + [h2, w2, y2] = get_size(k2, yindex); if (w2 > w1) { - dx = (w2 - w1) / 2; + dx = (w2 - w1) / 2; // shift numerator right } else { dx = 0; } dx++; + // this is how much is below the baseline y = y1 + h1 - 1; dy = -y - 1; move(k1, k2, dx, dy); @@ -16589,14 +19728,14 @@ w += 2; emit_x = x; results = []; - for (i = j1 = 0, ref4 = w; 0 <= ref4 ? j1 < ref4 : j1 > ref4; i = 0 <= ref4 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = w; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { results.push(__emit_char('-')); } return results; }; fixup_power = function(k1, k2) { - var dy, h1, h2, ref2, ref3, w1, w2, y1, y2; + var dy, h1, h2, w1, w2, y1, y2; dy = 0; h1 = 0; w1 = 0; @@ -16604,9 +19743,11 @@ h2 = 0; w2 = 0; y2 = 0; - ref2 = get_size(k1, k2), h1 = ref2[0], w1 = ref2[1], y1 = ref2[2]; - ref3 = get_size(k2, yindex), h2 = ref3[0], w2 = ref3[1], y2 = ref3[2]; + [h1, w1, y1] = get_size(k1, k2); + [h2, w2, y2] = get_size(k2, yindex); + // move superscript to baseline dy = -y2 - h2 + 1; + // now move above base dy += y1 - 1; return move(k2, yindex, 0, dy); }; @@ -16615,13 +19756,14 @@ var i, j1, ref2, ref3, results; i = 0; results = []; - for (i = j1 = ref2 = j, ref3 = k; ref2 <= ref3 ? j1 < ref3 : j1 > ref3; i = ref2 <= ref3 ? ++j1 : --j1) { + for (i = j1 = ref2 = j, ref3 = k; (ref2 <= ref3 ? j1 < ref3 : j1 > ref3); i = ref2 <= ref3 ? ++j1 : --j1) { chartab[i].x += dx; results.push(chartab[i].y += dy); } return results; }; + // finds the bounding rectangle and vertical position get_size = function(j, k) { var h, i, j1, max_x, max_y, min_x, min_y, ref2, ref3, w, y; i = 0; @@ -16629,7 +19771,7 @@ max_x = chartab[j].x; min_y = chartab[j].y; max_y = chartab[j].y; - for (i = j1 = ref2 = j + 1, ref3 = k; ref2 <= ref3 ? j1 < ref3 : j1 > ref3; i = ref2 <= ref3 ? ++j1 : --j1) { + for (i = j1 = ref2 = j + 1, ref3 = k; (ref2 <= ref3 ? j1 < ref3 : j1 > ref3); i = ref2 <= ref3 ? ++j1 : --j1) { if (chartab[i].x < min_x) { min_x = chartab[i].x; } @@ -16671,7 +19813,7 @@ var i, j1, ref2, results; i = 0; results = []; - for (i = j1 = 0, ref2 = s.length; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = s.length; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { results.push(__emit_char(s[i])); } return results; @@ -16687,7 +19829,7 @@ if (tmpString[0] === '-' && emit_sign === 0) { tmpString = tmpString.substring(1); } - for (i = j1 = 0, ref2 = tmpString.length; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = tmpString.length; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { __emit_char(tmpString[i]); } tmpString = p.q.b.toString(); @@ -16696,7 +19838,7 @@ } __emit_char('/'); results = []; - for (i = l1 = 0, ref3 = tmpString.length; 0 <= ref3 ? l1 < ref3 : l1 > ref3; i = 0 <= ref3 ? ++l1 : --l1) { + for (i = l1 = 0, ref3 = tmpString.length; (0 <= ref3 ? l1 < ref3 : l1 > ref3); i = 0 <= ref3 ? ++l1 : --l1) { results.push(__emit_char(tmpString[i])); } return results; @@ -16707,13 +19849,14 @@ tmpString = tmpString.substring(1); } results1 = []; - for (i = m1 = 0, ref4 = tmpString.length; 0 <= ref4 ? m1 < ref4 : m1 > ref4; i = 0 <= ref4 ? ++m1 : --m1) { + for (i = m1 = 0, ref4 = tmpString.length; (0 <= ref4 ? m1 < ref4 : m1 > ref4); i = 0 <= ref4 ? ++m1 : --m1) { results1.push(__emit_char(tmpString[i])); } return results1; } }; + // a and b are glyphs cmpGlyphs = function(a, b) { if (a.y < b.y) { return -1; @@ -16734,12 +19877,18 @@ var accumulator, i, j1, ref2, subsetOfStack, x, y; i = 0; accumulator = ""; + + // now sort the glyphs by their vertical positions, + // since we are going to build a string where obviously the + // "upper" line has to printed out first, followed by + // a new line, followed by the other lines. + //qsort(chartab, yindex, sizeof (struct glyph), __cmp) subsetOfStack = chartab.slice(0, yindex); subsetOfStack.sort(cmpGlyphs); chartab = [].concat(subsetOfStack).concat(chartab.slice(yindex)); x = 0; y = chartab[0].y; - for (i = j1 = 0, ref2 = yindex; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = yindex; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { while (chartab[i].y > y) { accumulator += printchar('\n'); x = 0; @@ -16771,12 +19920,13 @@ tmpBuffer = buffer; sIndex = 0; i = 0; + //qsort(chartab, yindex, sizeof (struct glyph), __cmp) subsetOfStack = chartab.slice(0, yindex); subsetOfStack.sort(cmpGlyphs); chartab = [].concat(subsetOfStack).concat(chartab.slice(yindex)); x = 0; y = chartab[0].y; - for (i = j1 = 0, ref2 = yindex; 0 <= ref2 ? j1 < ref2 : j1 > ref2; i = 0 <= ref2 ? ++j1 : --j1) { + for (i = j1 = 0, ref2 = yindex; (0 <= ref2 ? j1 < ref2 : j1 > ref2); i = 0 <= ref2 ? ++j1 : --j1) { while (chartab[i].y > y) { tmpBuffer[sIndex++] = '\n'; x = 0; @@ -16795,7 +19945,7 @@ N = 100; oneElement = (function() { - function oneElement() {} + class oneElement {}; oneElement.prototype.x = 0; @@ -16811,12 +19961,12 @@ return oneElement; - })(); + }).call(this); elem = []; for (elelmIndex = j1 = 0; j1 < 10000; elelmIndex = ++j1) { - elem[elelmIndex] = new oneElement; + elem[elelmIndex] = new oneElement(); } SPACE_BETWEEN_COLUMNS = 3; @@ -16824,7 +19974,7 @@ SPACE_BETWEEN_ROWS = 1; emit_tensor = function(p) { - var col, dx, dy, eh, ew, h, i, l1, m1, n, n1, ncol, nrow, o1, ref2, ref3, ref4, ref5, ref6, row, w, x, y; + var col, dx, dy, eh, ew, h, i, l1, m1, n, n1, ncol, nrow, o1, ref2, ref3, ref4, ref5, row, w, x, y; i = 0; n = 0; nrow = 0; @@ -16854,17 +20004,24 @@ emit_flat_tensor(p); return; } + // horizontal coordinate of the matrix + + //if 0 + //emit_x += 2; # make space for left paren + //endif x = emit_x; - for (i = l1 = 0, ref2 = n; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { +// emit each element + for (i = l1 = 0, ref2 = n; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { elem[i].index = yindex; elem[i].x = emit_x; emit_expr(p.tensor.elem[i]); elem[i].count = yindex - elem[i].index; - ref3 = get_size(elem[i].index, yindex), elem[i].h = ref3[0], elem[i].w = ref3[1], elem[i].y = ref3[2]; + [elem[i].h, elem[i].w, elem[i].y] = get_size(elem[i].index, yindex); } + // find element height and width eh = 0; ew = 0; - for (i = m1 = 0, ref4 = n; 0 <= ref4 ? m1 < ref4 : m1 > ref4; i = 0 <= ref4 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = n; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { if (elem[i].h > eh) { eh = elem[i].h; } @@ -16872,61 +20029,37 @@ ew = elem[i].w; } } + // this is the overall height of the matrix h = nrow * eh + (nrow - 1) * SPACE_BETWEEN_ROWS; + // this is the overall width of the matrix w = ncol * ew + (ncol - 1) * SPACE_BETWEEN_COLUMNS; + // this is the vertical coordinate of the matrix y = -(h / 2); - for (row = n1 = 0, ref5 = nrow; 0 <= ref5 ? n1 < ref5 : n1 > ref5; row = 0 <= ref5 ? ++n1 : --n1) { - for (col = o1 = 0, ref6 = ncol; 0 <= ref6 ? o1 < ref6 : o1 > ref6; col = 0 <= ref6 ? ++o1 : --o1) { +// move elements around + for (row = n1 = 0, ref4 = nrow; (0 <= ref4 ? n1 < ref4 : n1 > ref4); row = 0 <= ref4 ? ++n1 : --n1) { + for (col = o1 = 0, ref5 = ncol; (0 <= ref5 ? o1 < ref5 : o1 > ref5); col = 0 <= ref5 ? ++o1 : --o1) { i = row * ncol + col; + // first move to upper left corner of matrix dx = x - elem[i].x; dy = y - elem[i].y; move(elem[i].index, elem[i].index + elem[i].count, dx, dy); + // now move to official position dx = 0; if (col > 0) { dx = col * (ew + SPACE_BETWEEN_COLUMNS); } dy = 0; - if (row > 0) { - dy = row * (eh + SPACE_BETWEEN_ROWS); - } - dx += (ew - elem[i].w) / 2; - dy += (eh - elem[i].h) / 2; - move(elem[i].index, elem[i].index + elem[i].count, dx, dy); - } - } - return emit_x = x + w; - - /* - if 0 - - * left brace - - for (i = 0; i < h; i++) { - if (yindex == YMAX) - break - chartab[yindex].c = '|' - chartab[yindex].x = x - 2 - chartab[yindex].y = y + i - yindex++ - } - - * right brace - - emit_x++ - - for (i = 0; i < h; i++) { - if (yindex == YMAX) - break - chartab[yindex].c = '|' - chartab[yindex].x = emit_x - chartab[yindex].y = y + i - yindex++ + if (row > 0) { + dy = row * (eh + SPACE_BETWEEN_ROWS); + } + // small correction for horizontal centering + dx += (ew - elem[i].w) / 2; + // small correction for vertical centering + dy += (eh - elem[i].h) / 2; + move(elem[i].index, elem[i].index + elem[i].count, dx, dy); } - - emit_x++ - - endif - */ + } + return emit_x = x + w; }; emit_flat_tensor = function(p) { @@ -16937,7 +20070,7 @@ var i, l1, ref2; i = 0; __emit_char('('); - for (i = l1 = 0, ref2 = p.tensor.dim[j]; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p.tensor.dim[j]; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { if (j + 1 === p.tensor.ndim) { emit_expr(p.tensor.elem[k]); k = k + 1; @@ -16952,16 +20085,27 @@ return k; }; + // 'product' function + + //define A p3 + //define B p4 + //define I p5 + //define X p6 + + // leaves the product at the top of the stack Eval_product = function() { var body, i, indexVariable, j, k, l1, oldIndexVariableValue, ref2, ref3; i = 0; j = 0; k = 0; + // 1st arg body = cadr(p1); + // 2nd arg (index) indexVariable = caddr(p1); if (!issymbol(indexVariable)) { stop("sum: 2nd arg?"); } + // 3rd arg (lower limit) push(cadddr(p1)); Eval(); j = pop_integer(); @@ -16969,6 +20113,7 @@ push(p1); return; } + // 4th arg (upper limit) push(caddddr(p1)); Eval(); k = pop_integer(); @@ -16976,9 +20121,11 @@ push(p1); return; } + // remember contents of the index + // variable so we can put it back after the loop oldIndexVariableValue = get_binding(indexVariable); push_integer(1); - for (i = l1 = ref2 = j, ref3 = k; ref2 <= ref3 ? l1 <= ref3 : l1 >= ref3; i = ref2 <= ref3 ? ++l1 : --l1) { + for (i = l1 = ref2 = j, ref3 = k; (ref2 <= ref3 ? l1 <= ref3 : l1 >= ref3); i = ref2 <= ref3 ? ++l1 : --l1) { push_integer(i); p5 = pop(); set_binding(indexVariable, p5); @@ -16993,35 +20140,71 @@ console.log("product - result: " + stack[tos - 1].toString()); } } + // put back the index variable to original content return set_binding(indexVariable, oldIndexVariableValue); }; + // Add rational numbers + + // Input: tos-2 addend + + // tos-1 addend + + // Output: sum on stack qadd = function() { var gcdBetweenNumeratorAndDenominator, qadd_ab, qadd_ba, qadd_denominator, qadd_frac1, qadd_frac2, qadd_numerator, resultSum; + // a, qadd_ab, b, qadd_ba, c are all bigNum + // we are adding the fractions qadd_frac1 + qadd_frac2 i.e. + // qadd_frac1.q.a/qadd_frac1.q.b + qadd_frac2.q.a/qadd_frac2.q.b qadd_frac2 = pop(); qadd_frac1 = pop(); qadd_ab = mmul(qadd_frac1.q.a, qadd_frac2.q.b); qadd_ba = mmul(qadd_frac1.q.b, qadd_frac2.q.a); qadd_numerator = madd(qadd_ab, qadd_ba); + //mfree(qadd_ab) + //mfree(qadd_ba) + + // zero? if (MZERO(qadd_numerator)) { + //console.log "qadd IS ZERO" + //mfree(qadd_numerator) push(zero); return; } qadd_denominator = mmul(qadd_frac1.q.b, qadd_frac2.q.b); gcdBetweenNumeratorAndDenominator = mgcd(qadd_numerator, qadd_denominator); + //console.log "gcd("+qadd_numerator+","+qadd_denominator+"): " + gcdBetweenNumeratorAndDenominator gcdBetweenNumeratorAndDenominator = makeSignSameAs(gcdBetweenNumeratorAndDenominator, qadd_denominator); + //console.log "qadd qadd_denominator: " + qadd_denominator + //console.log "qadd gcdBetweenNumeratorAndDenominator: " + gcdBetweenNumeratorAndDenominator resultSum = new U(); resultSum.k = NUM; resultSum.q.a = mdiv(qadd_numerator, gcdBetweenNumeratorAndDenominator); resultSum.q.b = mdiv(qadd_denominator, gcdBetweenNumeratorAndDenominator); + //console.log "qadd resultSum.q.a: " + resultSum.q.a + //console.log "qadd resultSum.q.b: " + resultSum.q.b + + //mfree(qadd_numerator) + //mfree(qadd_denominator) + //mfree(gcdBetweenNumeratorAndDenominator) return push(resultSum); }; + //console.log "qadd result: " + resultSum + + // Divide rational numbers + + // Input: tos-2 dividend + + // tos-1 divisor + + // Output: quotient on stack qdiv = function() { var aa, bb, c; save(); p2 = pop(); p1 = pop(); + // zero? if (MZERO(p2.q.a)) { stop("divide by zero"); } @@ -17042,11 +20225,19 @@ return restore(); }; + // Multiply rational numbers + + // Input: tos-2 multiplicand + + // tos-1 multiplier + + // Output: product on stack qmul = function() { var aa, bb, c; save(); p2 = pop(); p1 = pop(); + // zero? if (MZERO(p1.q.a) || MZERO(p2.q.a)) { push(zero); restore(); @@ -17060,44 +20251,55 @@ p1.k = NUM; p1.q.a = mdiv(aa, c); p1.q.b = mdiv(bb, c); + //mfree(aa) + //mfree(bb) push(p1); return restore(); }; + // Rational power function qpow = function() { save(); qpowf(); return restore(); }; + //define BASE p1 + //define EXPO p2 qpowf = function() { var a, b, expo, t, x, y; expo = 0; + //unsigned int a, b, *t, *x, *y p2 = pop(); p1 = pop(); - if (isplusone(p1) || isZeroAtomOrTensor(p2)) { + if (isplusone(p1) || isZeroAtomOrTensor(p2)) { // p1 is BASE # p2 is EXPO push_integer(1); return; } - if (isminusone(p1) && isoneovertwo(p2)) { + // if (-1)^(1/2) -> leave it as is + if (isminusone(p1) && isoneovertwo(p2)) { // p1 is BASE # p2 is EXPO push(imaginaryunit); return; } - if (isZeroAtomOrTensor(p1)) { - if (isnegativenumber(p2)) { + // if base is zero then return 0 + if (isZeroAtomOrTensor(p1)) { // p1 is BASE + if (isnegativenumber(p2)) { // p2 is EXPO stop("divide by zero"); } push(zero); return; } - if (isplusone(p2)) { + // if exponent is 1 then return base + if (isplusone(p2)) { // p2 is EXPO push(p1); return; } - if (isinteger(p2)) { + // if exponent is integer then power + if (isinteger(p2)) { // p2 is EXPO push(p2); expo = pop_integer(); if (isNaN(expo)) { + // expo greater than 32 bits push_symbol(POWER); push(p1); push(p2); @@ -17120,12 +20322,16 @@ push(p3); return; } - if (isminusone(p1)) { + // from here on out the exponent is NOT an integer + + // if base is -1 then normalize polar angle + if (isminusone(p1)) { // p1 is BASE push(p2); normalize_angle(); return; } - if (isnegativenumber(p1)) { + // if base is negative then (-N)^M -> N^M * (-1)^M + if (isnegativenumber(p1)) { // p1 is BASE push(p1); negate(); push(p2); @@ -17136,7 +20342,7 @@ multiply(); return; } - if (!isinteger(p1)) { + if (!isinteger(p1)) { // p1 is BASE push(p1); mp_numerator(); push(p2); @@ -17149,15 +20355,18 @@ multiply(); return; } - if (is_small_integer(p1)) { + // At this point p1 (BASE) is a positive integer. + + // If p1 (BASE) is small then factor it. + if (is_small_integer(p1)) { // p1 is BASE push(p1); push(p2); quickfactor(); return; } - if (!isSmall(p2.q.a) || !isSmall(p2.q.b)) { + if (!isSmall(p2.q.a) || !isSmall(p2.q.b)) { // p2 is EXPO push_symbol(POWER); - push(p1); + push(p1); // p1 is BASE push(p2); list(3); return; @@ -17173,9 +20382,10 @@ return; } y = mpow(x, a); + //mfree(x) p3 = new U(); p3.k = NUM; - if (p2.q.a.isNegative()) { + if (p2.q.a.isNegative()) { // p2 is EXPO p3.q.a = bigInt(1); p3.q.b = y; } else { @@ -17185,36 +20395,74 @@ return push(p3); }; + //----------------------------------------------------------------------------- + + // Normalize the angle of unit imaginary, i.e. (-1) ^ N + + // Input: N on stack (must be rational, not float) + + // Output: Result on stack + + // Note: + + // n = q * d + r + + // Example: + // n d q r + + // (-1)^(8/3) -> (-1)^(2/3) 8 3 2 2 + // (-1)^(7/3) -> (-1)^(1/3) 7 3 2 1 + // (-1)^(5/3) -> -(-1)^(2/3) 5 3 1 2 + // (-1)^(4/3) -> -(-1)^(1/3) 4 3 1 1 + // (-1)^(2/3) -> (-1)^(2/3) 2 3 0 2 + // (-1)^(1/3) -> (-1)^(1/3) 1 3 0 1 + + // (-1)^(-1/3) -> -(-1)^(2/3) -1 3 -1 2 + // (-1)^(-2/3) -> -(-1)^(1/3) -2 3 -1 1 + // (-1)^(-4/3) -> (-1)^(2/3) -4 3 -2 2 + // (-1)^(-5/3) -> (-1)^(1/3) -5 3 -2 1 + // (-1)^(-7/3) -> -(-1)^(2/3) -7 3 -3 2 + // (-1)^(-8/3) -> -(-1)^(1/3) -8 3 -3 1 + + //----------------------------------------------------------------------------- + + //define A p1 + //define Q p2 + //define R p3 normalize_angle = function() { save(); p1 = pop(); - if (isinteger(p1)) { - if (p1.q.a.isOdd()) { - push_integer(-1); + if (isinteger(p1)) { // p1 is A + if (p1.q.a.isOdd()) { // p1 is A + push_integer(-1); // odd exponent } else { - push_integer(1); + push_integer(1); // even exponent } restore(); return; } + // floor push(p1); bignum_truncate(); p2 = pop(); - if (isnegativenumber(p1)) { - push(p2); + if (isnegativenumber(p1)) { // p1 is A + push(p2); // p2 is Q push_integer(-1); add(); - p2 = pop(); + p2 = pop(); // p2 is Q } + + // remainder (always positive) push(p1); push(p2); subtract(); p3 = pop(); push_symbol(POWER); push_integer(-1); - push(p3); + push(p3); // p3 is R list(3); - if (p2.q.a.isOdd()) { + // negate if quotient is odd + if (p2.q.a.isOdd()) { // p2 is Q negate(); } return restore(); @@ -17224,6 +20472,20 @@ return isSmall(p.q.a); }; + //----------------------------------------------------------------------------- + + // Factor small numerical powers + + // Input: tos-2 Base (positive integer < 2^31 - 1) + + // tos-1 Exponent + + // Output: Expr on stack + + //----------------------------------------------------------------------------- + + //define BASE p1 + //define EXPO p2 quickfactor = function() { var h, i, l1, n, ref2, stackIndex; i = 0; @@ -17242,6 +20504,11 @@ multiply(); quickpower(); } + // stack has n results from factor_number_raw() + + // on top of that are all the expressions from quickpower() + + // multiply the quickpower() results multiply_all(tos - h - n); p1 = pop(); moveTos(h); @@ -17249,6 +20516,7 @@ return restore(); }; + // p1 (BASE) is a prime number so power is simpler quickpower = function() { var expo; expo = 0; @@ -17287,6 +20555,9 @@ return restore(); }; + //if SELFTEST + + // Divide polynomials Eval_quotient = function() { push(cadr(p1)); Eval(); @@ -17302,6 +20573,25 @@ return divpoly(); }; + //----------------------------------------------------------------------------- + + // Divide polynomials + + // Input: tos-3 Dividend + + // tos-2 Divisor + + // tos-1 x + + // Output: tos-1 Quotient + + //----------------------------------------------------------------------------- + + //define DIVIDEND p1 + //define DIVISOR p2 + //define X p3 + //define Q p4 + //define QUOTIENT p5 divpoly = function() { var dividend, divisor, h, i, l1, m, n, ref2, x; h = 0; @@ -17309,6 +20599,7 @@ m = 0; n = 0; x = 0; + //U **dividend, **divisor save(); p3 = pop(); p2 = pop(); @@ -17330,7 +20621,7 @@ push(stack[divisor + n]); divide(); p4 = pop(); - for (i = l1 = 0, ref2 = n; 0 <= ref2 ? l1 <= ref2 : l1 >= ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = n; (0 <= ref2 ? l1 <= ref2 : l1 >= ref2); i = 0 <= ref2 ? ++l1 : --l1) { push(stack[dividend + x + i]); push(stack[divisor + i]); push(p4); @@ -17383,6 +20674,7 @@ printf("rationalize: this is the input expr:\n"); printline(theArgument); } + // get common denominator push(one); multiply_denominators(theArgument); commonDenominator = pop(); @@ -17390,6 +20682,7 @@ printf("rationalize: this is the common denominator:\n"); printline(commonDenominator); } + // multiply each term by common denominator push(zero); eachTerm = cdr(theArgument); while (iscons(eachTerm)) { @@ -17403,11 +20696,13 @@ printf("rationalize: original expr times common denominator:\n"); printline(stack[tos - 1]); } + // collect common factors Condense(); if (DEBUG) { printf("rationalize: after factoring:\n"); printline(stack[tos - 1]); } + // divide by common denominator push(commonDenominator); divide(); if (DEBUG) { @@ -17452,16 +20747,19 @@ } push(p); p = caddr(p); + // like x^(-2) ? if (isnegativenumber(p)) { inverse(); __lcm(); return; } + // like x^(-a) ? if (car(p) === symbol(MULTIPLY) && isnegativenumber(cadr(p))) { inverse(); __lcm(); return; } + // no match return pop(); }; @@ -17471,12 +20769,12 @@ push(theTensor); Eval(); theTensor = pop(); - if (!istensor(theTensor)) { + if (!istensor(theTensor)) { // might be zero push(theTensor); return; } n = theTensor.tensor.nelem; - for (i = l1 = 0, ref2 = n; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = n; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { push(theTensor.tensor.elem[i]); rationalize(); theTensor.tensor.elem[i] = pop(); @@ -17499,18 +20797,6 @@ return restore(); }; - - /* - Returns the real part of complex z - - z real(z) - - ------- - - a + i b a - - exp(i a) cos(a) - */ - Eval_real = function() { push(cadr(p1)); Eval(); @@ -17530,15 +20816,6 @@ return restore(); }; - - /* - Convert complex z to rectangular form - - Input: push z - - Output: Result on stack - */ - DEBUG_RECT = false; Eval_rect = function() { @@ -17558,6 +20835,10 @@ if (DEBUG_RECT) { console.log("any clock forms in : " + input + " ? " + findPossibleClockForm(input)); } + // if we assume real variables, then the + // rect of any symbol is the symbol itself + // (note that 'i' is not a symbol, it's made of (-1)^(1/2)) + // otherwise we have to leave unevalled if (issymbol(p1)) { if (DEBUG_RECT) { console.log(" rect: simple symbol: " + input); @@ -17569,13 +20850,15 @@ push(p1); list(2); } - } else if (!isZeroAtomOrTensor(get_binding(symbol(ASSUME_REAL_VARIABLES))) && !findPossibleExponentialForm(p1) && !findPossibleClockForm(p1) && !(Find(p1, symbol(SIN)) && Find(p1, symbol(COS)) && Find(p1, imaginaryunit))) { + } else if (!isZeroAtomOrTensor(get_binding(symbol(ASSUME_REAL_VARIABLES))) && !findPossibleExponentialForm(p1) && !findPossibleClockForm(p1) && !(Find(p1, symbol(SIN)) && Find(p1, symbol(COS)) && Find(p1, imaginaryunit))) { // no polar form? if (DEBUG_RECT) { console.log(" rect: simple symbol: " + input); } push(p1); + // ib } else if (car(p1) === symbol(MULTIPLY) && isimaginaryunit(cadr(p1)) && !isZeroAtomOrTensor(get_binding(symbol(ASSUME_REAL_VARIABLES)))) { push(p1); + // sum } else if (car(p1) === symbol(ADD)) { if (DEBUG_RECT) { console.log(" rect - " + input + " is a sum "); @@ -17589,6 +20872,9 @@ p1 = cdr(p1); } } else { + // try to get to the rectangular form by doing + // abs(p1) * (cos (theta) + i * sin(theta)) + // where theta is arg(p1) if (DEBUG_RECT) { console.log(" rect - " + input + " is NOT a sum "); } @@ -17630,11 +20916,18 @@ } }; + //define POLY p1 + //define X p2 + //define A p3 + //define B p4 + //define C p5 + //define Y p6 show_power_debug = false; performing_roots = false; Eval_roots = function() { + // A == B -> A - B p2 = cadr(p1); if (car(p2) === symbol(SETQ) || car(p2) === symbol(TESTEQ)) { push(cadr(p2)); @@ -17656,6 +20949,7 @@ push(p2); } } + // 2nd arg, x push(caddr(p1)); Eval(); p2 = pop(); @@ -17676,9 +20970,11 @@ hasImaginaryCoeff = function(k) { var h, i, imaginaryCoefficients, l1, ref2; + //polycoeff = tos imaginaryCoefficients = false; h = tos; for (i = l1 = ref2 = k; l1 > 0; i = l1 += -1) { + //console.log "hasImaginaryCoeff - coeff.:" + stack[tos-i].toString() if (iscomplexnumber(stack[tos - i])) { imaginaryCoefficients = true; break; @@ -17689,6 +20985,10 @@ isSimpleRoot = function(k) { var h, i, isSimpleRootPolynomial, l1, ref2; + //polycoeff = tos + + //tos-n Coefficient of x^0 + //tos-1 Coefficient of x^(n-1) if (k > 2) { isSimpleRootPolynomial = true; h = tos; @@ -17710,25 +21010,36 @@ normalisedCoeff = function() { var divideBy, i, k, l1, m1, miniStack, ref2, ref3; k = coeff(); + //console.log("->" + tos) divideBy = stack[tos - 1]; miniStack = []; - for (i = l1 = 1, ref2 = k; 1 <= ref2 ? l1 <= ref2 : l1 >= ref2; i = 1 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 1, ref2 = k; (1 <= ref2 ? l1 <= ref2 : l1 >= ref2); i = 1 <= ref2 ? ++l1 : --l1) { miniStack.push(pop()); } - for (i = m1 = ref3 = k - 1; ref3 <= 0 ? m1 <= 0 : m1 >= 0; i = ref3 <= 0 ? ++m1 : --m1) { +//console.log(tos) + for (i = m1 = ref3 = k - 1; (ref3 <= 0 ? m1 <= 0 : m1 >= 0); i = ref3 <= 0 ? ++m1 : --m1) { push(miniStack[i]); push(divideBy); divide(); } + //console.log(tos) return k; }; + // takes the polynomial and the + // variable on the stack roots = function() { var h, i, k, l1, lastCoeff, leadingCoeff, n, ref2; h = 0; i = 0; n = 0; save(); + // the simplification of nested radicals uses + // "roots", which in turn uses simplification + // of nested radicals. Usually there is no problem, + // one level of recursion does the job. Beyond that, + // we probably got stuck in a strange case of infinite + // recursion, so bail out and return NIL. if (recursionLevelNestedRadicalsRemoval > 1) { pop(); pop(); @@ -17775,7 +21086,7 @@ p1 = alloc_tensor(n); p1.tensor.ndim = 1; p1.tensor.dim[0] = n; - for (i = l1 = 0, ref2 = n; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = n; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p1.tensor.elem[i] = stack[h + i]; } moveTos(h); @@ -17784,12 +21095,20 @@ return performing_roots = false; }; + // ok to generate these roots take a look at their form + // in the case of even and odd exponents here: + // http://www.wolframalpha.com/input/?i=roots+x%5E14+%2B+1 + // http://www.wolframalpha.com/input/?i=roots+ax%5E14+%2B+b + // http://www.wolframalpha.com/input/?i=roots+x%5E15+%2B+1 + // http://www.wolframalpha.com/input/?i=roots+a*x%5E15+%2B+b getSimpleRoots = function(n, leadingCoeff, lastCoeff) { var aSol, commonPart, l1, m1, ref2, ref3, rootsOfOne; if (DEBUG) { console.log("getSimpleRoots"); } save(); + //tos-n Coefficient of x^0 + //tos-1 Coefficient of x^(n-1) n = n - 1; push(lastCoeff); push_rational(1, n); @@ -17812,7 +21131,7 @@ negate(); } } else { - for (rootsOfOne = m1 = 1, ref3 = n; 1 <= ref3 ? m1 <= ref3 : m1 >= ref3; rootsOfOne = 1 <= ref3 ? ++m1 : --m1) { + for (rootsOfOne = m1 = 1, ref3 = n; (1 <= ref3 ? m1 <= ref3 : m1 >= ref3); rootsOfOne = 1 <= ref3 ? ++m1 : --m1) { push(commonPart); push_integer(-1); push_rational(rootsOfOne, n); @@ -17829,8 +21148,8 @@ roots2 = function() { var k; save(); - p2 = pop(); - p1 = pop(); + p2 = pop(); // the polynomial variable + p1 = pop(); // the polynomial push(p1); push(p2); push(p1); @@ -17847,6 +21166,8 @@ } if (car(p1) === symbol(MULTIPLY)) { p1 = cdr(p1); + // scan through all the factors + // and find the roots of each of them while (iscons(p1)) { push(car(p1)); push(p2); @@ -17880,10 +21201,29 @@ return restore(); }; + //----------------------------------------------------------------------------- + + // Input: stack[tos - 2] polynomial + + // stack[tos - 1] dependent symbol + + // Output: stack roots on stack + + // (input args are popped first) + + //----------------------------------------------------------------------------- + + // note that for many quadratic, cubic and quartic polynomials we don't + // actually end up using the quadratic/cubic/quartic formulas in here, + // since there is a chance we factored the polynomial and in so + // doing we found some solutions and lowered the degree. mini_solve = function(n) { var C_CHECKED_AS_NOT_ZERO, Q_CHECKED_AS_NOT_ZERO, R_18_a_b_c_d, R_27_a2_d, R_2_b3, R_3_a, R_3_a_C, R_3_a_c, R_4_DELTA03, R_6_a, R_6_a_C, R_C, R_C_over_3a, R_C_simplified_toCheckIfZero, R_DELTA0, R_DELTA0_simplified_toCheckIfZero, R_DELTA0_toBeCheckedIfZero, R_DELTA1, R_Q, R_Q_simplified_toCheckIfZero, R_S, R_S_simplified_toCheckIfZero, R_a2, R_a2_d, R_a2_d2, R_a3, R_a_b_c, R_a_b_c_d, R_a_c, R_b2, R_b2_c2, R_b3, R_b3_d, R_c2, R_c3, R_d2, R_determinant, R_determinant_simplified_toCheckIfZero, R_e2, R_e3, R_m, R_m27_a2_d2, R_m4_a_c3, R_m4_b3_d, R_m9_a_b_c, R_m_b_over_3a, R_minus_4S2_minus_2p, R_minus_b_over_4a, R_p, R_principalCubicRoot, R_q, R_q_over_S, R_r, S_CHECKED_AS_NOT_ZERO, ThreePPlus2M, TwoQOversqrtPPlus2M, biquadraticSolutions, choiceOfRadicalInQSoSIsNotZero, coeff2, coeff3, coeff4, depressedSolutions, eachSolution, flipSignOFQSoCIsNotZero, flipSignOFRadicalSoQIsNotZero, i_sqrt3, l1, len, len1, len2, m1, n1, one_minus_i_sqrt3, one_plus_i_sqrt3, ref2, ref3, ref4, resolventCubicSolutions, root_solution, sqrtPPlus2M, toBeCheckedIFZero; + //console.log "mini_solve >>>>>>>>>>>>>>>>>>>>>>>> tos:" + tos save(); + // AX + B, X = -B/A if (n === 2) { + //console.log "mini_solve >>>>>>>>> 1st degree" p3 = pop(); p4 = pop(); push(p4); @@ -17893,45 +21233,71 @@ restore(); return; } + // AX^2 + BX + C, X = (-B +/- (B^2 - 4AC)^(1/2)) / (2A) if (n === 3) { - p3 = pop(); - p4 = pop(); - p5 = pop(); + //console.log "mini_solve >>>>>>>>> 2nd degree" + p3 = pop(); // A + p4 = pop(); // B + p5 = pop(); // C + + // B^2 push(p4); push_integer(2); power(); + // 4AC push_integer(4); push(p3); multiply(); push(p5); multiply(); + // B^2 - 4AC subtract(); + //(B^2 - 4AC)^(1/2) push_rational(1, 2); power(); + //p6 is (B^2 - 4AC)^(1/2) p6 = pop(); push(p6); push(p4); - subtract(); + subtract(); // -B + (B^2 - 4AC)^(1/2) + + // 1/2A push(p3); push_integer(2); multiply(); divide(); + //simplify() + //rationalize() + // tos - 1 now is 1st root: (-B + (B^2 - 4AC)^(1/2)) / (2A) push(p6); push(p4); add(); + // tos - 1 now is B + (B^2 - 4AC)^(1/2) + // tos - 2: 1st root: (-B + (B^2 - 4AC)^(1/2)) / (2A) negate(); + // tos - 1 now is -B -(B^2 - 4AC)^(1/2) + // tos - 2: 1st root: (-B + (B^2 - 4AC)^(1/2)) / (2A) + + // 1/2A again push(p3); divide(); push_rational(1, 2); multiply(); + //simplify() + //rationalize() + // tos - 1: 2nd root: (-B - (B^2 - 4AC)^(1/2)) / (2A) + // tos - 2: 1st root: (-B + (B^2 - 4AC)^(1/2)) / (2A) restore(); return; } + //if (n == 4) if (n === 4 || n === 5) { - p3 = pop(); - p4 = pop(); - p5 = pop(); - p6 = pop(); + p3 = pop(); // A + p4 = pop(); // B + p5 = pop(); // C + p6 = pop(); // D + + // C - only related calculations push(p5); push(p5); multiply(); @@ -17940,6 +21306,7 @@ push(p5); multiply(); R_c3 = pop(); + // B - only related calculations push(p4); push(p4); multiply(); @@ -17960,6 +21327,7 @@ push_integer(2); multiply(); R_2_b3 = pop(); + // A - only related calculations push(p3); push(p3); multiply(); @@ -17992,6 +21360,7 @@ push_integer(2); multiply(); R_6_a = pop(); + // mixed calculations push(p3); push(p5); multiply(); @@ -18040,6 +21409,10 @@ if (DEBUG) { console.log(">>>>>>>>>>>>>>>> actually using cubic formula <<<<<<<<<<<<<<< "); } + //console.log ">>>> A:" + p3.toString() + //console.log ">>>> B:" + p4.toString() + //console.log ">>>> C:" + p5.toString() + //console.log ">>>> D:" + p6.toString() if (DEBUG) { console.log("cubic: D0: " + R_DELTA0.toString()); } @@ -18056,6 +21429,10 @@ if (DEBUG) { console.log("cubic: D0 as float: " + R_DELTA0_toBeCheckedIfZero.toString()); } + //if isZeroAtomOrTensor(R_DELTA0_toBeCheckedIfZero) + // console.log " *********************************** D0 IS ZERO" + + // DETERMINANT push(R_18_a_b_c_d); push(R_m4_b3_d); push(R_b2_c2); @@ -18071,6 +21448,7 @@ if (DEBUG) { console.log("cubic: DETERMINANT: " + R_determinant.toString()); } + // R_DELTA1 push(R_2_b3); push(R_m9_a_b_c); push(R_27_a2_d); @@ -18080,6 +21458,7 @@ if (DEBUG) { console.log("cubic: D1: " + R_DELTA1.toString()); } + // R_Q push(R_DELTA1); push_integer(2); power(); @@ -18094,7 +21473,7 @@ if (DEBUG) { console.log(" cubic: DETERMINANT IS ZERO and delta0 is zero"); } - push(R_m_b_over_3a); + push(R_m_b_over_3a); // just same solution three times restore(); return; } else { @@ -18113,13 +21492,16 @@ push(R_DELTA0); push_integer(2); multiply(); - divide(); + divide(); // first solution root_solution = pop(); + push(root_solution); // pushing two of them on the stack push(root_solution); - push(root_solution); + // second solution here + // 4abc push(R_a_b_c); push_integer(4); multiply(); + // -9a*a*d push(p3); push(p3); push(p6); @@ -18128,13 +21510,17 @@ multiply(); multiply(); negate(); + // -9*b^3 push(R_b3); negate(); + // sum the three terms add(); add(); + // denominator is a*delta0 push(p3); push(R_DELTA0); multiply(); + // build the fraction divide(); restore(); return; @@ -18143,6 +21529,7 @@ C_CHECKED_AS_NOT_ZERO = false; flipSignOFQSoCIsNotZero = false; while (!C_CHECKED_AS_NOT_ZERO) { + // R_C push(R_Q); if (flipSignOFQSoCIsNotZero) { negate(); @@ -18182,6 +21569,7 @@ push_integer(2); multiply(); R_6_a_C = pop(); + // imaginary parts calculations push(imaginaryunit); push_integer(3); push_rational(1, 2); @@ -18200,52 +21588,60 @@ push(R_3_a); divide(); R_C_over_3a = pop(); - push(R_m_b_over_3a); + // first solution + push(R_m_b_over_3a); // first term push(R_C_over_3a); - negate(); + negate(); // second term push(R_DELTA0); push(R_3_a_C); divide(); - negate(); + negate(); // third term + // now add the three terms together add(); add(); simplify(); - push(R_m_b_over_3a); + // second solution + push(R_m_b_over_3a); // first term push(R_C_over_3a); push(one_plus_i_sqrt3); multiply(); push_integer(2); - divide(); + divide(); // second term push(one_minus_i_sqrt3); push(R_DELTA0); multiply(); push(R_6_a_C); - divide(); + divide(); // third term + // now add the three terms together add(); add(); simplify(); - push(R_m_b_over_3a); + // third solution + push(R_m_b_over_3a); // first term push(R_C_over_3a); push(one_minus_i_sqrt3); multiply(); push_integer(2); - divide(); + divide(); // second term push(one_plus_i_sqrt3); push(R_DELTA0); multiply(); push(R_6_a_C); - divide(); + divide(); // third term + // now add the three terms together add(); add(); simplify(); restore(); return; } + // See http://www.sscc.edu/home/jdavidso/Math/Catalog/Polynomials/Fourth/Fourth.html + // for a description of general shapes and properties of fourth degree polynomials if (n === 5) { if (DEBUG) { console.log(">>>>>>>>>>>>>>>> actually using quartic formula <<<<<<<<<<<<<<< "); } - p7 = pop(); + p7 = pop(); // E if (isZeroAtomOrTensor(p4) && isZeroAtomOrTensor(p6) && !isZeroAtomOrTensor(p5) && !isZeroAtomOrTensor(p7)) { if (DEBUG) { console.log("biquadratic case"); @@ -18280,10 +21676,12 @@ restore(); return; } + // D - only related calculations push(p6); push(p6); multiply(); R_d2 = pop(); + // E - only related calculations push(p7); push(p7); multiply(); @@ -18292,42 +21690,43 @@ push(p7); multiply(); R_e3 = pop(); + // DETERMINANT push_integer(256); push(R_a3); push(R_e3); multiply(); - multiply(); + multiply(); // first term 256 a^3 e^3 push_integer(-192); push(R_a2_d); push(R_e2); push(p4); multiply(); multiply(); - multiply(); + multiply(); // second term -192 a^3 b d e^2 push_integer(-128); push(R_a2); push(R_c2); push(R_e2); multiply(); multiply(); - multiply(); + multiply(); // third term -128 a^2 c^2 e^2 push_integer(144); push(R_a2_d2); push(p5); push(p7); multiply(); multiply(); - multiply(); + multiply(); // fourth term 144 a^2 c d^2 e push(R_m27_a2_d2); push(R_d2); - multiply(); + multiply(); // fifth term -27 a^2 d^4 push_integer(144); push(R_a_b_c); push(p4); push(R_e2); multiply(); multiply(); - multiply(); + multiply(); // sixth term 144 a b^2 c e^2 push_integer(-6); push(p3); push(R_b2); @@ -18336,60 +21735,63 @@ multiply(); multiply(); multiply(); - multiply(); + multiply(); // seventh term -6 a b^2 d^2 e push_integer(-80); push(R_a_b_c_d); push(p5); push(p7); multiply(); multiply(); - multiply(); + multiply(); // eigth term -80 a b c^2 d e push_integer(18); push(R_a_b_c_d); push(R_d2); multiply(); - multiply(); + multiply(); // ninth term 18 a b c d^3 push_integer(16); push(R_a_c); push(R_c3); push(p7); multiply(); multiply(); - multiply(); + multiply(); // tenth term 16 a c^4 e push_integer(-4); push(R_a_c); push(R_c2); push(R_d2); multiply(); multiply(); - multiply(); + multiply(); // eleventh term -4 a c^3 d^2 push_integer(-27); push(R_b3); push(p4); push(R_e2); multiply(); multiply(); - multiply(); + multiply(); // twelveth term -27 b^4 e^2 push_integer(18); push(R_b3_d); push(p5); push(p7); multiply(); multiply(); - multiply(); + multiply(); // thirteenth term 18 b^3 c d e push(R_m4_b3_d); push(R_d2); - multiply(); + multiply(); // fourteenth term -4 b^3 d^3 push_integer(-4); push(R_b2_c2); push(p5); push(p7); multiply(); multiply(); - multiply(); + multiply(); // fifteenth term -4 b^2 c^3 e push(R_b2_c2); push(R_d2); - multiply(); + multiply(); // sixteenth term b^2 c^2 d^2 + + // add together the sixteen terms by doing + // fifteen adds add(); add(); add(); @@ -18409,23 +21811,27 @@ if (DEBUG) { console.log("R_determinant: " + R_determinant.toString()); } - push(R_c2); + // DELTA0 + push(R_c2); // term one of DELTA0 push_integer(-3); push(p4); push(p6); multiply(); - multiply(); + multiply(); // term two of DELTA0 push_integer(12); push(p3); push(p7); multiply(); - multiply(); + multiply(); // term three of DELTA0 + + // add the three terms together add(); add(); R_DELTA0 = pop(); if (DEBUG) { console.log("R_DELTA0: " + R_DELTA0.toString()); } + // DELTA1 push_integer(2); push(R_c3); multiply(); @@ -18451,6 +21857,7 @@ push(p7); multiply(); multiply(); + // add the five terms together add(); add(); add(); @@ -18459,6 +21866,7 @@ if (DEBUG) { console.log("R_DELTA1: " + R_DELTA1.toString()); } + // p push_integer(8); push(R_a_c); multiply(); @@ -18474,6 +21882,7 @@ if (DEBUG) { console.log("p: " + R_p.toString()); } + // q push(R_b3); push_integer(-4); push(R_a_b_c); @@ -18548,6 +21957,7 @@ if (DEBUG) { console.log("q for depressed quartic: " + R_q.toString()); } + // convert to depressed quartic push(p4); push_integer(4); power(); @@ -18644,17 +22054,16 @@ R_p = p5; R_q = p6; R_r = p7; - /* * Descartes' solution * https://en.wikipedia.org/wiki/Quartic_function#Descartes.27_solution * finding the "u" in the depressed equation - + push_integer(2) push(R_p) multiply() coeff2 = pop() - + push_integer(-4) push(R_p) push_integer(2) @@ -18663,43 +22072,43 @@ push(R_r) multiply() coeff3 = pop() - + push(R_q) push_integer(2) power() negate() coeff4 = pop() - + * now build the polynomial push(symbol(SECRETX)) push_integer(3) power() - + push(coeff2) push(symbol(SECRETX)) push_integer(2) power() multiply() - + push(coeff3) push(symbol(SECRETX)) multiply() - + push(coeff4) - + add() add() add() - + console.log("Descarte's resolventCubic: " + stack[tos-1].toString()) push(symbol(SECRETX)) - + roots() - + resolventCubicSolutions = pop() console.log("Descarte's resolventCubic solutions: " + resolventCubicSolutions) console.log("tos: " + tos) - + R_u = null #R_u = resolventCubicSolutions.tensor.elem[1] for eachSolution in resolventCubicSolutions.tensor.elem @@ -18709,20 +22118,20 @@ multiply() push(R_p) add() - + absValFloat() toBeCheckedIFZero = pop() console.log("abs value is: " + eachSolution) if !isZeroAtomOrTensor(toBeCheckedIFZero) R_u = eachSolution break - + console.log("chosen solution: " + R_u) - + push(R_u) negate() R_s = pop() - + push(R_p) push(R_u) push_integer(2) @@ -18735,7 +22144,7 @@ push_integer(2) divide() R_t = pop() - + push(R_p) push(R_u) push_integer(2) @@ -18748,44 +22157,47 @@ push_integer(2) divide() R_v = pop() - + * factoring the quartic into two quadratics: - + * now build the polynomial push(symbol(SECRETX)) push_integer(2) power() - + push(R_s) push(symbol(SECRETX)) multiply() - + push(R_t) - + add() add() - + console.log("factored quartic 1: " + stack[tos-1].toString()) - + push(symbol(SECRETX)) push_integer(2) power() - + push(R_u) push(symbol(SECRETX)) multiply() - + push(R_v) - + add() add() - + console.log("factored quartic 2: " + stack[tos-1].toString()) pop() - + restore() return */ + // Ferrari's solution + // https://en.wikipedia.org/wiki/Quartic_function#Ferrari.27s_solution + // finding the "m" in the depressed equation push_rational(5, 2); push(R_p); multiply(); @@ -18842,6 +22254,7 @@ } R_m = null; ref4 = resolventCubicSolutions.tensor.elem; + //R_m = resolventCubicSolutions.tensor.elem[1] for (n1 = 0, len2 = ref4.length; n1 < len2; n1++) { eachSolution = ref4[n1]; if (DEBUG) { @@ -18889,6 +22302,7 @@ multiply(); add(); ThreePPlus2M = pop(); + // solution1 push(sqrtPPlus2M); push(ThreePPlus2M); push(TwoQOversqrtPPlus2M); @@ -18900,6 +22314,7 @@ add(); push_integer(2); divide(); + // solution2 push(sqrtPPlus2M); push(ThreePPlus2M); push(TwoQOversqrtPPlus2M); @@ -18911,6 +22326,7 @@ subtract(); push_integer(2); divide(); + // solution3 push(sqrtPPlus2M); negate(); push(ThreePPlus2M); @@ -18923,6 +22339,7 @@ add(); push_integer(2); divide(); + // solution4 push(sqrtPPlus2M); negate(); push(ThreePPlus2M); @@ -18938,6 +22355,7 @@ restore(); return; } + // Q --------------------------- push(R_determinant); simplify(); absValFloat(); @@ -18952,27 +22370,37 @@ Q_CHECKED_AS_NOT_ZERO = false; flipSignOFRadicalSoQIsNotZero = false; while (!Q_CHECKED_AS_NOT_ZERO) { + // D1 under the outer radical push(R_DELTA1); + // D1^2 under the inner radical push(R_DELTA1); push_integer(2); power(); + // 4*D0^3 under the inner radical push_integer(-4); push(R_DELTA0); push_integer(3); power(); multiply(); + // addition under the inner radical add(); + // the second radical push_rational(1, 2); power(); if (flipSignOFRadicalSoQIsNotZero) { negate(); } + // the addition under the outer radical add(); + // content of outer radical divided by two push_integer(2); divide(); if (DEBUG) { console.log("content of cubic root: " + stack[tos - 1].toString()); } + // outer radical calculation: cubic root + // now we actually have to find all the roots + // because we have to pick the one that makes S != 0 push_rational(1, 3); power(); simplify(); @@ -19050,6 +22478,7 @@ console.log("tos: " + tos); } } + // S push_rational(-2, 3); push(R_p); multiply(); @@ -19058,6 +22487,9 @@ push(R_Q); divide(); add(); + //rationalize() + //console.log("rationalised: " + stack[tos-1].toString()) + //simplify() push(R_3_a); divide(); add(); @@ -19071,6 +22503,7 @@ if (DEBUG) { console.log("S " + R_S.toString()); } + // now check if S is zero push(R_S); simplify(); absValFloat(); @@ -19090,6 +22523,7 @@ console.log("tos: " + tos); } } + // ---------------------------- if (DEBUG) { console.log("tos: " + tos); } @@ -19117,7 +22551,8 @@ if (DEBUG) { console.log("tos before putting together the 4 solutions: " + tos); } - push(R_minus_b_over_4a); + // first solution + push(R_minus_b_over_4a); // first term push(R_S); subtract(); push(R_minus_4S2_minus_2p); @@ -19129,7 +22564,8 @@ divide(); add(); simplify(); - push(R_minus_b_over_4a); + // second solution + push(R_minus_b_over_4a); // first term push(R_S); subtract(); push(R_minus_4S2_minus_2p); @@ -19141,7 +22577,8 @@ divide(); subtract(); simplify(); - push(R_minus_b_over_4a); + // third solution + push(R_minus_b_over_4a); // first term push(R_S); add(); push(R_minus_4S2_minus_2p); @@ -19153,7 +22590,8 @@ divide(); add(); simplify(); - push(R_minus_b_over_4a); + // fourth solution + push(R_minus_b_over_4a); // first term push(R_S); add(); push(R_minus_4S2_minus_2p); @@ -19210,6 +22648,49 @@ return push_integer(Math.round(p1.d)); }; + // This scanner uses the recursive descent method. + + // The char pointers token_str and scan_str are pointers to the input string as + // in the following example. + + // | g | a | m | m | a | | a | l | p | h | a | + // ^ ^ + // token_str scan_str + + // The char pointer token_buf points to a malloc buffer. + + // | g | a | m | m | a | \0 | + // ^ + // token_buf + + // In the sequence of method invocations for scanning, + // first we do the calls for scanning the operands + // of the operators of least precedence. + // So, since precedence in maths goes something like + // (form high to low) exponents, mult/div, plus/minus + // so we scan first for terms, then factors, then powers. + // That's the general idea, but of course we also have to deal + // with things like parens, non-commutative + // dot (or inner) product, assignments and tests, + // function calls etc. + // Note that a^1/2 is, correctly, a/2, not, incorrectly, sqrt(a), + // see comment in related test in power.coffee for more about this. + + // Notes: + + // Formerly add() and multiply() were used to construct expressions but + // this preevaluation caused problems. + + // For example, suppose A has the floating point value inf. + + // Before, the expression A/A resulted in 1 because the scanner would + // divide the symbols. + + // After removing add() and multiply(), A/A results in nan which is the + // correct result. + + // The functions negate() and inverse() are used but they do not cause + // problems with preevaluation of symbols. T_INTEGER = 1001; T_DOUBLE = 1002; @@ -19262,12 +22743,23 @@ assignmentFound = null; + // Returns number of chars scanned and expr on stack. + + // Returns zero when nothing left to scan. + + // takes a string scanned = ""; scan = function(s) { if (DEBUG) { console.log("#### scanning " + s); } + //if s=="y=x" + // debugger + //if s=="y" + // debugger + //if s=="i=sqrt(-1)" + // debugger lastFoundSymbol = null; symbolsRightOfAssignment = []; symbolsLeftOfAssignment = []; @@ -19294,6 +22786,7 @@ return token_str - input_str; }; + // takes a string scan_meta = function(s) { scanned = s; meta_mode = 1; @@ -19328,21 +22821,30 @@ get_next_token(); push_symbol(SETQ); swap(); + // if it's a := then add a quote if (assignmentIsOfQuotedType) { push_symbol(QUOTE); } scan_relation(); + // if it's a := then you have to list + // together the quote and its argument if (assignmentIsOfQuotedType) { list(2); } list(3); isSymbolLeftOfAssignment = true; if (codeGen) { + // in case of re-assignment, the symbol on the + // left will also be in the set of the symbols + // on the right. In that case just remove it from + // the symbols on the right. indexOfSymbolLeftOfAssignment = symbolsRightOfAssignment.indexOf(symbolLeftOfAssignment); if (indexOfSymbolLeftOfAssignment !== -1) { symbolsRightOfAssignment.splice(indexOfSymbolLeftOfAssignment, 1); symbolsHavingReassignments.push(symbolLeftOfAssignment); } + + // print out the immediate dependencies if (DEBUG) { console.log("locally, " + symbolLeftOfAssignment + " depends on: "); for (l1 = 0, len = symbolsRightOfAssignment.length; l1 < len; l1++) { @@ -19350,10 +22852,16 @@ console.log(" " + i); } } + // ok add the local dependencies to the existing + // dependencies of this left-value symbol + + // create the exiting dependencies list if it doesn't exist if (symbolsDependencies[symbolLeftOfAssignment] == null) { symbolsDependencies[symbolLeftOfAssignment] = []; } existingDependencies = symbolsDependencies[symbolLeftOfAssignment]; +// copy over the new dependencies to the existing +// dependencies avoiding repetitions for (m1 = 0, len1 = symbolsRightOfAssignment.length; m1 < len1; m1++) { i = symbolsRightOfAssignment[m1]; if (existingDependencies.indexOf(i) === -1) { @@ -19458,8 +22966,8 @@ case T_INTEGER: case T_DOUBLE: case T_STRING: - if (newline_flag) { - scan_str = token_str; + if (newline_flag) { // implicit mul can't cross line + scan_str = token_str; // better error display return 0; } else { return 1; @@ -19474,6 +22982,7 @@ } }; + // calculate away consecutive constants multiply_consecutive_constants = function(tos, h) { if (tos > h + 1 && isNumericAtom(stack[tos - 2]) && isNumericAtom(stack[tos - 1])) { return multiply(); @@ -19492,6 +23001,12 @@ get_next_token(); scan_factor(); } else if (token === '/') { + // in case of 1/... then + // we scanned the 1, we get rid + // of it because otherwise it becomes + // an extra factor that wasn't there and + // things like + // 1/(2*a) become 1*(1/(2*a)) simplify_1_in_products(tos, h); get_next_token(); scan_factor(); @@ -19531,6 +23046,7 @@ }; scan_index = function(h) { + //console.log "[ as index" get_next_token(); push_symbol(INDEX); swap(); @@ -19549,6 +23065,7 @@ scan_factor = function() { var firstFactorIsNumber, h; h = tos; + //console.log "scan_factor token: " + token firstFactorIsNumber = false; if (token === '(') { scan_subexpr(); @@ -19557,6 +23074,8 @@ } else if (token === T_FUNCTION) { scan_function_call_with_function_name(); } else if (token === '[') { + //console.log "[ as tensor" + //debugger scan_tensor(); } else if (token === T_INTEGER) { firstFactorIsNumber = true; @@ -19571,10 +23090,21 @@ } else { scan_error("syntax error"); } + // after the main initial part of the factor that + // we just scanned above, + // we can get an arbitrary about of appendages + // of the form ...[...](...)... + // If the main part is not a number, then these are all, respectively, + // - index references (as opposed to tensor definition) and + // - function calls without an explicit function name + // (instead of subexpressions or parameters of function + // definitions or function calls with an explicit function + // name), respectively while (token === '[' || token === '(' && newline_flag === 0 && !firstFactorIsNumber) { if (token === '[') { scan_index(h); } else if (token === '(') { + //console.log "( as function call without function name " scan_function_call_without_function_name(); } } @@ -19600,7 +23130,7 @@ console.log("... adding symbol: " + theSymbol + " to the set of the symbols right of assignment"); } prefixVar = ""; - for (i = l1 = 1, ref2 = functionInvokationsScanningStack.length; 1 <= ref2 ? l1 < ref2 : l1 > ref2; i = 1 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 1, ref2 = functionInvokationsScanningStack.length; (1 <= ref2 ? l1 < ref2 : l1 > ref2); i = 1 <= ref2 ? ++l1 : --l1) { if (functionInvokationsScanningStack[i] !== "") { prefixVar += functionInvokationsScanningStack[i] + "_" + i + "_"; } @@ -19617,7 +23147,7 @@ console.log("... adding symbol: " + theSymbol + " to the set of the symbols left of assignment"); } prefixVar = ""; - for (i = l1 = 1, ref2 = functionInvokationsScanningStack.length; 1 <= ref2 ? l1 < ref2 : l1 > ref2; i = 1 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 1, ref2 = functionInvokationsScanningStack.length; (1 <= ref2 ? l1 < ref2 : l1 > ref2); i = 1 <= ref2 ? ++l1 : --l1) { if (functionInvokationsScanningStack[i] !== "") { prefixVar += functionInvokationsScanningStack[i] + "_" + i + "_"; } @@ -19648,6 +23178,7 @@ } else { push(usr_symbol(token_buf)); } + //console.log "found symbol: " + token_buf if (scanningParameters.length === 0) { if (DEBUG) { console.log("out of scanning parameters, processing " + token_buf); @@ -19683,7 +23214,7 @@ if (DEBUG) { console.log("-- scan_function_call_with_function_name start"); } - n = 1; + n = 1; // the parameter number as we scan parameters p = new U(); p = usr_symbol(token_buf); push(p); @@ -19695,38 +23226,43 @@ if (!isSymbolLeftOfAssignment) { addSymbolRightOfAssignment(token_buf); } - get_next_token(); - get_next_token(); + get_next_token(); // open parens + get_next_token(); // 1st parameter scanningParameters.push(true); if (token !== ')') { scan_stmt(); n++; while (token === ',') { get_next_token(); + // roots' disappearing variable, if there, is the second one if (n === 2 && functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("roots") !== -1) { symbolsRightOfAssignment = symbolsRightOfAssignment.filter(function(x) { return !(new RegExp("roots_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x); }); skipRootVariableToBeSolved = true; } + // sums' disappearing variable, is alsways the second one if (n === 2 && functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("sum") !== -1) { symbolsRightOfAssignment = symbolsRightOfAssignment.filter(function(x) { return !(new RegExp("sum_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x); }); skipRootVariableToBeSolved = true; } + // product's disappearing variable, is alsways the second one if (n === 2 && functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("product") !== -1) { symbolsRightOfAssignment = symbolsRightOfAssignment.filter(function(x) { return !(new RegExp("product_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x); }); skipRootVariableToBeSolved = true; } + // for's disappearing variable, is alsways the second one if (n === 2 && functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("for") !== -1) { symbolsRightOfAssignment = symbolsRightOfAssignment.filter(function(x) { return !(new RegExp("for_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x); }); skipRootVariableToBeSolved = true; } + // defint's disappearing variables can be in positions 2,5,8... if (functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("defint") !== -1 && (n === 2 || (n > 2 && ((n - 2) % 3 === 0)))) { symbolsRightOfAssignment = symbolsRightOfAssignment.filter(function(x) { return !(new RegExp("defint_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x); @@ -19737,6 +23273,8 @@ skipRootVariableToBeSolved = false; n++; } + // todo refactor this, there are two copies + // this catches the case where the "roots" variable is not specified if (n === 2 && functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("roots") !== -1) { symbolsRightOfAssignment = symbolsRightOfAssignment.filter(function(x) { return !(new RegExp("roots_" + (functionInvokationsScanningStack.length - 1) + "_" + "x")).test(x); @@ -19744,7 +23282,7 @@ } } scanningParameters.pop(); - for (i = l1 = 0, ref2 = symbolsRightOfAssignment.length; 0 <= ref2 ? l1 <= ref2 : l1 >= ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = symbolsRightOfAssignment.length; (0 <= ref2 ? l1 <= ref2 : l1 >= ref2); i = 0 <= ref2 ? ++l1 : --l1) { if (symbolsRightOfAssignment[i] != null) { if (functionName === "roots") { symbolsRightOfAssignment[i] = symbolsRightOfAssignment[i].replace(new RegExp("roots_" + (functionInvokationsScanningStack.length - 1) + "_"), ""); @@ -19784,11 +23322,15 @@ if (DEBUG) { console.log("-- scan_function_call_without_function_name start"); } + // the function will have to be looked up + // at runtime (i.e. we need to evaulate something to find it + // e.g. it might be inside a tensor, so we'd need to evaluate + // a tensor element access in that case) push_symbol(EVAL); swap(); list(2); - n = 1; - get_next_token(); + n = 1; // the parameter number as we scan parameters + get_next_token(); // left paren scanningParameters.push(true); if (token !== ')') { scan_stmt(); @@ -19810,6 +23352,7 @@ } }; + // scan subexpression scan_subexpr = function() { var n; n = 0; @@ -19831,6 +23374,7 @@ scan_error("[ expected"); } get_next_token(); + //console.log "scanning the next statement" scan_stmt(); n = 1; while (token === ',') { @@ -19838,6 +23382,7 @@ scan_stmt(); n++; } + //console.log "building tensor with elements number: " + n build_tensor(n); if (token !== ']') { scan_error("] expected"); @@ -19847,6 +23392,7 @@ scan_error = function(errmsg) { errorMessage = ""; + // try not to put question mark on orphan line while (input_str !== scan_str) { if ((scanned[input_str] === '\n' || scanned[input_str] === '\r') && input_str + 1 === scan_str) { break; @@ -19861,14 +23407,23 @@ return stop(errmsg); }; + // There are n expressions on the stack, possibly tensors. + + // This function assembles the stack expressions into a single tensor. + + // For example, at the top level of the expression ((a,b),(c,d)), the vectors + // (a,b) and (c,d) would be on the stack. + + // takes an integer build_tensor = function(n) { var i, l1, ref2; + // int i, j, k, ndim, nelem i = 0; save(); p2 = alloc_tensor(n); p2.tensor.ndim = 1; p2.tensor.dim[0] = n; - for (i = l1 = 0, ref2 = n; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = n; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p2.tensor.elem[i] = stack[tos - n + i]; } check_tensor_dimensions(p2); @@ -19891,7 +23446,10 @@ } }; + //if token == ')' + // debugger get_token = function() { + // skip spaces while (isspace(scanned[scan_str])) { if (scanned[scan_str] === '\n' || scanned[scan_str] === '\r') { token = T_NEWLINE; @@ -19901,10 +23459,12 @@ scan_str++; } token_str = scan_str; + // end of string? if (scan_str === scanned.length) { token = ""; return; } + // number? if (isdigit(scanned[scan_str]) || scanned[scan_str] === '.') { while (isdigit(scanned[scan_str])) { scan_str++; @@ -19927,6 +23487,7 @@ update_token_buf(token_str, scan_str); return; } + // symbol? if (isalpha(scanned[scan_str])) { while (isalnumorunderscore(scanned[scan_str])) { scan_str++; @@ -19939,9 +23500,11 @@ update_token_buf(token_str, scan_str); return; } + // string ? if (scanned[scan_str] === '"') { scan_str++; while (scanned[scan_str] !== '"') { + //if (scan_str == scanned.length || scanned[scan_str] == '\n' || scanned[scan_str] == '\r') if (scan_str === scanned.length - 1) { scan_str++; scan_error("runaway string"); @@ -19954,6 +23517,7 @@ update_token_buf(token_str + 1, scan_str - 1); return; } + // comment? if (scanned[scan_str] === '#' || scanned[scan_str] === '-' && scanned[scan_str + 1] === '-') { while (scanned[scan_str] && scanned[scan_str] !== '\n' && scanned[scan_str] !== '\r') { scan_str++; @@ -19964,16 +23528,23 @@ token = T_NEWLINE; return; } + // quote-assignment if (scanned[scan_str] === ':' && scanned[scan_str + 1] === '=') { scan_str += 2; token = T_QUOTASSIGN; return; } + // relational operator? if (scanned[scan_str] === '=' && scanned[scan_str + 1] === '=') { scan_str += 2; token = T_EQ; return; } + // != operator. It's a little odd because + // "!" is not a "not", which would make things consistent. + // (it's used for factorial). + // An alternative would be to use "<>" but it's not used + // a lot in other languages... if (scanned[scan_str] === '!' && scanned[scan_str + 1] === '=') { scan_str += 2; token = T_NEQ; @@ -19989,15 +23560,24 @@ token = T_GTEQ; return; } + // single char token return token = scanned[scan_str++]; }; + // both strings update_token_buf = function(a, b) { return token_buf = scanned.substring(a, b); }; $.scan = scan; + //----------------------------------------------------------------------------- + + // Author : philippe.billet@noos.fr + + // sgn sign function + + //----------------------------------------------------------------------------- Eval_sgn = function() { push(cadr(p1)); Eval(); @@ -20010,6 +23590,7 @@ return restore(); }; + //define X p1 yysgn = function() { p1 = pop(); if (isdouble(p1)) { @@ -20058,7 +23639,6 @@ multiply(); return; } - /* push_integer(2) push(p1) @@ -20066,12 +23646,13 @@ multiply() push_integer(-1) add() - */ + */ push_symbol(SGN); push(p1); return list(2); }; + // shape of tensor Eval_shape = function() { push(cadr(p1)); Eval(); @@ -20085,10 +23666,11 @@ t = 0; ai = []; an = []; - for (i = l1 = 0, ref2 = MAXDIM; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = MAXDIM; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { ai[i] = 0; an[i] = 0; } + //U **a, **b save(); p1 = pop(); if (!istensor(p1)) { @@ -20103,7 +23685,7 @@ p2 = alloc_tensor(ndim); p2.tensor.ndim = 1; p2.tensor.dim[0] = ndim; - for (i = m1 = 0, ref3 = ndim; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = ndim; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { push_integer(p1.tensor.dim[i]); p2.tensor.elem[i] = pop(); } @@ -20111,40 +23693,41 @@ return restore(); }; - /* Simplify factorials - + The following script - + F(n,k) = k binomial(n,k) (F(n,k) + F(n,k-1)) / F(n+1,k) - + generates - + k! n! n! (1 - k + n)! k! n! -------------------- + -------------------- - ---------------------- (-1 + k)! (1 + n)! (1 + n)! (-k + n)! k (-1 + k)! (1 + n)! - + Simplify each term to get - + k 1 - k + n 1 ------- + ----------- - ------- 1 + n 1 + n 1 + n - + Then simplify the sum to get - + n ------- 1 + n - */ + */ + // simplify factorials term-by-term Eval_simfac = function() { push(cadr(p1)); Eval(); return simfac(); }; + //if 1 simfac = function() { var h; h = 0; @@ -20166,7 +23749,7 @@ return restore(); }; - + //else /* void simfac(void) @@ -20197,26 +23780,28 @@ } restore() } - + #endif */ - simfac_term = function() { var doNothing, h; h = 0; save(); p1 = pop(); + // if not a product of factors then done if (car(p1) !== symbol(MULTIPLY)) { push(p1); restore(); return; } + // push all factors h = tos; p1 = cdr(p1); while (p1 !== symbol(NIL)) { push(car(p1)); p1 = cdr(p1); } + // keep trying until no more to do while (yysimfac(h)) { doNothing = 1; } @@ -20224,17 +23809,19 @@ return restore(); }; + // try all pairs of factors yysimfac = function(h) { var i, j, l1, m1, ref2, ref3, ref4, ref5; i = 0; j = 0; - for (i = l1 = ref2 = h, ref3 = tos; ref2 <= ref3 ? l1 < ref3 : l1 > ref3; i = ref2 <= ref3 ? ++l1 : --l1) { + for (i = l1 = ref2 = h, ref3 = tos; (ref2 <= ref3 ? l1 < ref3 : l1 > ref3); i = ref2 <= ref3 ? ++l1 : --l1) { p1 = stack[i]; - for (j = m1 = ref4 = h, ref5 = tos; ref4 <= ref5 ? m1 < ref5 : m1 > ref5; j = ref4 <= ref5 ? ++m1 : --m1) { + for (j = m1 = ref4 = h, ref5 = tos; (ref4 <= ref5 ? m1 < ref5 : m1 > ref5); j = ref4 <= ref5 ? ++m1 : --m1) { if (i === j) { continue; } p2 = stack[j]; + // n! / n -> (n - 1)! if (car(p1) === symbol(FACTORIAL) && car(p2) === symbol(POWER) && isminusone(caddr(p2)) && equal(cadr(p1), cadr(p2))) { push(cadr(p1)); push(one); @@ -20244,6 +23831,7 @@ stack[j] = one; return 1; } + // n / n! -> 1 / (n - 1)! if (car(p2) === symbol(POWER) && isminusone(caddr(p2)) && caadr(p2) === symbol(FACTORIAL) && equal(p1, cadadr(p2))) { push(p1); push_integer(-1); @@ -20254,6 +23842,7 @@ stack[j] = one; return 1; } + // (n + 1) n! -> (n + 1)! if (car(p2) === symbol(FACTORIAL)) { push(p1); push(cadr(p2)); @@ -20267,6 +23856,7 @@ return 1; } } + // 1 / ((n + 1) n!) -> 1 / (n + 1)! if (car(p1) === symbol(POWER) && isminusone(caddr(p1)) && car(p2) === symbol(POWER) && isminusone(caddr(p2)) && caadr(p2) === symbol(FACTORIAL)) { push(cadr(p1)); push(cadr(cadr(p2))); @@ -20281,6 +23871,9 @@ return 1; } } + // (n + 1)! / n! -> n + 1 + + // n! / (n + 1)! -> 1 / (n + 1) if (car(p1) === symbol(FACTORIAL) && car(p2) === symbol(POWER) && isminusone(caddr(p2)) && caadr(p2) === symbol(FACTORIAL)) { push(cadr(p1)); push(cadr(cadr(p2))); @@ -20332,6 +23925,14 @@ runUserDefinedSimplifications = function() { var atLeastOneSuccessInRouldOfRulesApplications, eachConsecutiveRuleApplication, eachSimplification, l1, len, len1, m1, numberOfRulesApplications, originalexpanding, success; + // ----------------------- + // unfortunately for the time being user + // specified simplifications are only + // run in things which don't contain + // integrals. + // Doesn't work yet, could be because of + // some clobbering as "transform" is called + // recursively? if (userSimplificationsInListForm.length !== 0 && !Find(cadr(p1), symbol(INTEGRAL))) { originalexpanding = expanding; expanding = false; @@ -20393,10 +23994,16 @@ } }; + // ------------------------ simplifyForCodeGeneration = function() { save(); runUserDefinedSimplifications(); codeGen = true; + // in "codeGen" mode we completely + // eval and simplify the function bodies + // because we really want to resolve all + // the variables indirections and apply + // all the simplifications we can. simplify_main(); codeGen = false; return restore(); @@ -20411,12 +24018,20 @@ simplify_main = function() { var args, fbody; p1 = pop(); + // when we do code generation, we proceed to + // fully evaluate and simplify the body of + // a function, so we resolve all variables + // indirections and we simplify everything + // we can given the current assignments. if (codeGen && car(p1) === symbol(FUNCTION)) { fbody = cadr(p1); push(fbody); + // let's simplify the body so we give it a + // compact form eval(); simplify(); p3 = pop(); + // replace the evaled body args = caddr(p1); push_symbol(FUNCTION); push(p3); @@ -20451,6 +24066,12 @@ f9(); simplify_polarRect(); if (do_simplify_nested_radicals) { + // if there is some de-nesting then + // re-run a simplification because + // the shape of the expression might + // have changed significantly. + // e.g. simplify(14^(1/2) - (16 - 4*7^(1/2))^(1/2)) + // needs some more semplification after the de-nesting. if (simplify_nested_radicals()) { if (DEBUG) { console.log("de-nesting successful into: " + p1.toString()); @@ -20470,21 +24091,22 @@ i = 0; p2 = alloc_tensor(p1.tensor.nelem); p2.tensor.ndim = p1.tensor.ndim; - for (i = l1 = 0, ref2 = p1.tensor.ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p1.tensor.ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p2.tensor.dim[i] = p1.tensor.dim[i]; } - for (i = m1 = 0, ref3 = p1.tensor.nelem; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = p1.tensor.nelem; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { push(p1.tensor.elem[i]); simplify(); p2.tensor.elem[i] = pop(); } check_tensor_dimensions(p2); if (isZeroAtomOrTensor(p2)) { - p2 = zero; + p2 = zero; // null tensor becomes scalar zero } return push(p2); }; + // try rationalizing f1 = function() { if (car(p1) !== symbol(ADD)) { return; @@ -20497,6 +24119,7 @@ } }; + // try condensing f2 = function() { if (car(p1) !== symbol(ADD)) { return; @@ -20509,6 +24132,7 @@ } }; + // this simplifies forms like (A-B) / (B-A) f3 = function() { push(p1); rationalize(); @@ -20527,6 +24151,7 @@ carp1 = car(p1); miao = cdr(p1); if (carp1 === symbol(MULTIPLY) || isinnerordot(p1)) { + // both operands a transpose? if ((car(car(cdr(p1))) === symbol(TRANSPOSE)) && (car(car(cdr(cdr(p1)))) === symbol(TRANSPOSE))) { if (DEBUG) { console.log("maybe collecting a transpose " + p1); @@ -20559,6 +24184,7 @@ } }; + // try expanding denominators f4 = function() { if (isZeroAtomOrTensor(p1)) { return; @@ -20575,6 +24201,7 @@ } }; + // simplifies trig forms simplify_trig = function() { save(); p1 = pop(); @@ -20605,6 +24232,7 @@ } }; + // if it's a sum then try to simplify each term f9 = function() { var oldp1, oldp2; if (car(p1) !== symbol(ADD)) { @@ -20654,6 +24282,8 @@ push(polyVar); factor(); theGCD = pop(); + // if there are no common factors then + // bail if (isone(theGCD)) { return; } @@ -20661,6 +24291,7 @@ push(polyVar); factor(); push(theGCD); + //divide() inverse(); multiply_noexpand(); simplify(); @@ -20669,11 +24300,13 @@ push(polyVar); factor(); push(theGCD); + //divide() inverse(); multiply_noexpand(); simplify(); sasa = stack[tos - 1].toString(); divide(); + //simplify() Condense(); sasa = stack[tos - 1].toString(); p2 = pop(); @@ -20682,7 +24315,11 @@ } }; + // things like 6*(cos(2/9*pi)+i*sin(2/9*pi)) + // where we have sin and cos, those might start to + // look better in clock form i.e. 6*(-1)^(2/9) simplify_rectToClock = function() { + //debugger if (Find(p1, symbol(SIN)) === 0 && Find(p1, symbol(COS)) === 0) { return; } @@ -20718,18 +24355,23 @@ return; } if (equal(car(p1), symbol(POWER)) && isminusone(cadr(p1))) { + // base we just said is minus 1 push(one); negate(); + // exponent push(caddr(p1)); polarRectAMinusOneBase(); power(); + // try to simplify it using polar and rect polar(); rect(); } else if (iscons(p1)) { h = tos; while (iscons(p1)) { + //console.log("recursing on: " + car(p1).toString()) push(car(p1)); polarRectAMinusOneBase(); + //console.log("...transformed into: " + stack[tos-1].toString()) p1 = cdr(p1); } list(tos - h); @@ -20757,17 +24399,30 @@ } push(p1); somethingSimplified = take_care_of_nested_radicals(); + // in this paragraph we check whether we can collect + // common factors without complicating the expression + // in particular we want to avoid + // collecting radicals like in this case where + // we collect sqrt(2): + // 2-2^(1/2) into 2^(1/2)*(-1+2^(1/2)) + // but we do like to collect other non-radicals e.g. + // 17/2+3/2*5^(1/2) into 1/2*(17+3*5^(1/2)) + // so what we do is we count the powers and we check + // which version has the least number of them. simplificationWithoutCondense = stack[tos - 1]; prev_expanding = expanding; expanding = 0; yycondense(); expanding = prev_expanding; simplificationWithCondense = pop(); + //console.log("occurrences of powers in " + simplificationWithoutCondense + " :" + countOccurrencesOfSymbol(symbol(POWER),simplificationWithoutCondense)) + //console.log("occurrences of powers in " + simplificationWithCondense + " :" + countOccurrencesOfSymbol(symbol(POWER),simplificationWithCondense)) if (countOccurrencesOfSymbol(symbol(POWER), simplificationWithoutCondense) < countOccurrencesOfSymbol(symbol(POWER), simplificationWithCondense)) { push(simplificationWithoutCondense); } else { push(simplificationWithCondense); } + // we got out result, wrap up p1 = pop(); return somethingSimplified; }; @@ -20782,10 +24437,13 @@ } save(); p1 = pop(); + //console.log("take_care_of_nested_radicals p1: " + p1.toString()) if (equal(car(p1), symbol(POWER))) { + //console.log("ok it's a power ") base = cadr(p1); exponent = caddr(p1); if (!isminusone(exponent) && equal(car(base), symbol(ADD)) && isfraction(exponent) && (equalq(exponent, 1, 3) || equalq(exponent, 1, 2))) { + //console.log("ok there is a radix with a term inside") firstTerm = cadr(base); push(firstTerm); take_care_of_nested_radicals(); @@ -20794,24 +24452,31 @@ push(secondTerm); take_care_of_nested_radicals(); pop(); + //console.log("possible double radical term1: " + firstTerm) + //console.log("possible double radical term2: " + secondTerm) numberOfTerms = 0; countingTerms = base; while (cdr(countingTerms) !== symbol(NIL)) { numberOfTerms++; countingTerms = cdr(countingTerms); } + //console.log("number of terms: " + numberOfTerms) if (numberOfTerms > 2) { + //console.log("too many terms under outer radix ") push(p1); restore(); return false; } + // list here all the factors commonInnerExponent = null; commonBases = []; termsThatAreNotPowers = []; if (car(secondTerm) === symbol(MULTIPLY)) { + // product of factors secondTermFactor = cdr(secondTerm); if (iscons(secondTermFactor)) { while (iscons(secondTermFactor)) { + //console.log("second term factor BIS: " + car(secondTermFactor).toString()) potentialPower = car(secondTermFactor); if (car(potentialPower) === symbol(POWER)) { innerbase = cadr(potentialPower); @@ -20822,6 +24487,7 @@ commonBases.push(innerbase); } else { if (equal(innerexponent, commonInnerExponent)) { + //console.log("common base: " + innerbase.toString()) commonBases.push(innerbase); } else { @@ -20829,6 +24495,8 @@ } } } else { + //console.log("no common bases here ") + //console.log("this one is a power base: " + innerbase + " , exponent: " + innerexponent) termsThatAreNotPowers.push(potentialPower); } secondTermFactor = cdr(secondTermFactor); @@ -20838,6 +24506,7 @@ innerbase = cadr(secondTerm); innerexponent = caddr(secondTerm); if ((commonInnerExponent == null) && equalq(innerexponent, 1, 2)) { + //console.log("tackling double radical 2: " + p1.toString()) commonInnerExponent = innerexponent; commonBases.push(innerbase); } @@ -20848,27 +24517,33 @@ return false; } A = firstTerm; + //console.log("A: " + A.toString()) push_integer(1); for (l1 = 0, len = commonBases.length; l1 < len; l1++) { i = commonBases[l1]; push(i); multiply(); } + //console.log("basis with common exponent: " + i.toString()) C = pop(); + //console.log("C: " + C.toString()) push_integer(1); for (m1 = 0, len1 = termsThatAreNotPowers.length; m1 < len1; m1++) { i = termsThatAreNotPowers[m1]; push(i); multiply(); } + //console.log("terms that are not powers: " + i.toString()) B = pop(); + //console.log("B: " + B.toString()) if (equalq(exponent, 1, 3)) { push(A); negate(); push(C); multiply(); push(B); - divide(); + divide(); // 4th coeff + //console.log("constant coeff " + stack[tos-1].toString()) checkSize = pop(); push(checkSize); real(); @@ -20881,7 +24556,8 @@ push(checkSize); push_integer(3); push(C); - multiply(); + multiply(); // 3rd coeff + //console.log("next coeff " + stack[tos-1].toString()) checkSize = pop(); push(checkSize); real(); @@ -20899,7 +24575,7 @@ push(A); multiply(); push(B); - divide(); + divide(); // 2nd coeff checkSize = pop(); push(checkSize); real(); @@ -20912,11 +24588,13 @@ return false; } push(checkSize); + //console.log("next coeff " + stack[tos-1].toString()) push(symbol(SECRETX)); push_integer(2); power(); multiply(); - push_integer(1); + push_integer(1); // 1st coeff + //console.log("next coeff " + stack[tos-1].toString()) push(symbol(SECRETX)); push_integer(3); power(); @@ -20925,7 +24603,7 @@ add(); add(); } else if (equalq(exponent, 1, 2)) { - push(C); + push(C); // 3th coeff checkSize = pop(); push(checkSize); real(); @@ -20936,11 +24614,12 @@ return false; } push(checkSize); + //console.log("constant coeff " + stack[tos-1].toString()) push_integer(-2); push(A); multiply(); push(B); - divide(); + divide(); // 2nd coeff checkSize = pop(); push(checkSize); real(); @@ -20952,9 +24631,11 @@ return false; } push(checkSize); + //console.log("next coeff " + stack[tos-1].toString()) push(symbol(SECRETX)); multiply(); - push_integer(1); + push_integer(1); // 1st coeff + //console.log("next coeff " + stack[tos-1].toString()) push(symbol(SECRETX)); push_integer(2); power(); @@ -20962,8 +24643,10 @@ add(); add(); } + //console.log("whole polynomial: " + stack[tos-1].toString()) push(symbol(SECRETX)); recursionLevelNestedRadicalsRemoval++; + //console.log("invoking roots at recursion level: " + recursionLevelNestedRadicalsRemoval) roots(); recursionLevelNestedRadicalsRemoval--; if (equal(stack[tos - 1], symbol(NIL))) { @@ -20975,6 +24658,9 @@ restore(); return false; } + //console.log("all solutions: " + stack[tos-1].toString()) + + // exclude the solutions with radicals possibleSolutions = []; ref2 = stack[tos - 1].tensor.elem; for (n1 = 0, len2 = ref2.length; n1 < len2; n1++) { @@ -20983,7 +24669,9 @@ possibleSolutions.push(eachSolution); } } - pop(); + pop(); // popping the tensor with the solutions + + //console.log("possible solutions: " + possibleSolutions.toString()) if (possibleSolutions.length === 0) { push(p1); restore(); @@ -20991,6 +24679,7 @@ } possibleRationalSolutions = []; realOfpossibleRationalSolutions = []; +//console.log("checking the one with maximum real part ") for (o1 = 0, len3 = possibleSolutions.length; o1 < len3; o1++) { i = possibleSolutions[o1]; push(i); @@ -21001,7 +24690,7 @@ } whichRationalSolution = realOfpossibleRationalSolutions.indexOf(Math.max.apply(Math, realOfpossibleRationalSolutions)); SOLUTION = possibleRationalSolutions[whichRationalSolution]; - + //console.log("picked solution: " + SOLUTION) /* #possibleNewExpressions = [] #realOfPossibleNewExpressions = [] @@ -21009,7 +24698,7 @@ lowercase_b = null for SOLUTION in possibleSolutions console.log("testing solution: " + SOLUTION.toString()) - + debugger if equalq(exponent,1,3) push(A) @@ -21038,9 +24727,9 @@ push_rational(1,2) power() console.log("b is: " + stack[tos-1].toString()) - + lowercase_b = pop() - + if !Find(lowercase_b, symbol(POWER)) break */ @@ -21056,6 +24745,7 @@ multiply(); add(); divide(); + //console.log("argument of cubic root: " + stack[tos-1].toString()) push_rational(1, 3); power(); } else if (equalq(exponent, 1, 2)) { @@ -21066,9 +24756,11 @@ push(C); add(); divide(); + //console.log("argument of cubic root: " + stack[tos-1].toString()) push_rational(1, 2); power(); } + //console.log("b is: " + stack[tos-1].toString()) lowercase_b = pop(); if (lowercase_b == null) { push(p1); @@ -21079,6 +24771,7 @@ push(SOLUTION); multiply(); if (equalq(exponent, 1, 3)) { + //console.log("a is: " + stack[tos-1].toString()) lowercase_a = pop(); push(lowercase_b); push(C); @@ -21089,6 +24782,7 @@ add(); simplify(); } else if (equalq(exponent, 1, 2)) { + //console.log("a could be: " + stack[tos-1].toString()) lowercase_a = pop(); push(lowercase_b); push(C); @@ -21099,13 +24793,16 @@ add(); simplify(); possibleNewExpression = pop(); + //console.log("verifying if " + possibleNewExpression + " is positive") push(possibleNewExpression); real(); yyfloat(); possibleNewExpressionValue = pop(); if (!isnegativenumber(possibleNewExpressionValue)) { + //console.log("... it is positive") push(possibleNewExpression); } else { + //console.log("... it is NOT positive") push(lowercase_b); negate(); lowercase_b = pop(); @@ -21122,7 +24819,21 @@ simplify(); } } + // possibleNewExpression is now at top of stack + + //console.log("potential new expression: " + stack[tos-1].toString()) p1 = pop(); + //newExpression = pop() + //debugger + //push(newExpression) + //real() + //yyfloat() + //possibleNewExpressions.push(newExpression) + //realOfPossibleNewExpressions.push(pop().d) + + //whichExpression = realOfPossibleNewExpressions.indexOf(Math.max.apply(Math, realOfPossibleNewExpressions)) + //p1 = possibleNewExpressions[whichExpression] + //console.log("final new expression: " + p1.toString()) push(p1); restore(); return true; @@ -21135,8 +24846,10 @@ h = tos; anyRadicalSimplificationWorked = false; while (iscons(p1)) { + //console.log("recursing on: " + car(p1).toString()) push(car(p1)); anyRadicalSimplificationWorked = anyRadicalSimplificationWorked || take_care_of_nested_radicals(); + //console.log("...transformed into: " + stack[tos-1].toString()) p1 = cdr(p1); } list(tos - h); @@ -21150,16 +24863,22 @@ throw new Error("control flow should never reach here"); }; + // Sine function of numerical and symbolic arguments Eval_sin = function() { + //console.log "sin ---- " push(cadr(p1)); Eval(); return sine(); }; + //console.log "sin end ---- " sine = function() { + //console.log "sine ---- " save(); p1 = pop(); if (car(p1) === symbol(ADD)) { + // sin of a sum can be further decomposed into + //sin(alpha+beta) = sin(alpha)*cos(beta)+sin(beta)*cos(alpha) sine_of_angle_sum(); } else { sine_of_angle(); @@ -21167,11 +24886,21 @@ return restore(); }; + //console.log "sine end ---- " + + // Use angle sum formula for special angles. + + //define A p3 + //define B p4 + + // decompose sum sin(alpha+beta) into + // sin(alpha)*cos(beta)+sin(beta)*cos(alpha) sine_of_angle_sum = function() { + //console.log "sin of angle sum ---- " p2 = cdr(p1); while (iscons(p2)) { p4 = car(p2); - if (isnpi(p4)) { + if (isnpi(p4)) { // p4 is B push(p1); push(p4); subtract(); @@ -21189,11 +24918,13 @@ add(); return; } + //console.log "sin of angle sum end ---- " p2 = cdr(p2); } return sine_of_angle(); }; + //console.log "sin of angle sum end ---- " sine_of_angle = function() { var d, n; if (car(p1) === symbol(ARCSIN)) { @@ -21208,6 +24939,7 @@ push_double(d); return; } + // sine function is antisymmetric, sin(-x) = -sin(x) if (isnegative(p1)) { push(p1); negate(); @@ -21215,6 +24947,9 @@ negate(); return; } + // sin(arctan(x)) = x / sqrt(1 + x^2) + + // see p. 173 of the CRC Handbook of Mathematical Sciences if (car(p1) === symbol(ARCTAN)) { push(cadr(p1)); push_integer(1); @@ -21227,6 +24962,15 @@ multiply(); return; } + // multiply by 180/pi to go from radians to degrees. + // we go from radians to degrees because it's much + // easier to calculate symbolic results of most (not all) "classic" + // angles (e.g. 30,45,60...) if we calculate the degrees + // and the we do a switch on that. + // Alternatively, we could look at the fraction of pi + // (e.g. 60 degrees is 1/3 pi) but that's more + // convoluted as we'd need to look at both numerator and + // denominator. push(p1); push_integer(180); multiply(); @@ -21237,12 +24981,17 @@ } divide(); n = pop_integer(); + // most "good" (i.e. compact) trigonometric results + // happen for a round number of degrees. There are some exceptions + // though, e.g. 22.5 degrees, which we don't capture here. if (n < 0 || isNaN(n)) { push(symbol(SIN)); push(p1); list(2); return; } + // values of some famous angles. Many more here: + // https://en.wikipedia.org/wiki/Trigonometric_constants_expressed_in_real_radicals switch (n % 360) { case 0: case 180: @@ -21292,6 +25041,9 @@ } }; + // exp(x) - exp(-x) + // sinh(x) = ---------------- + // 2 Eval_sinh = function() { push(cadr(p1)); Eval(); @@ -21324,23 +25076,10 @@ push(zero); return; } - push_symbol(SINH); - push(p1); - return list(2); - }; - - - /* - Substitute new expr for old expr in expr. - - Input: push expr - - push old expr - - push new expr - - Output: Result on stack - */ + push_symbol(SINH); + push(p1); + return list(2); + }; subst = function() { var i, l1, m1, ref2, ref3; @@ -21356,10 +25095,10 @@ if (istensor(p1)) { p4 = alloc_tensor(p1.tensor.nelem); p4.tensor.ndim = p1.tensor.ndim; - for (i = l1 = 0, ref2 = p1.tensor.ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p1.tensor.ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p4.tensor.dim[i] = p1.tensor.dim[i]; } - for (i = m1 = 0, ref3 = p1.tensor.nelem; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = p1.tensor.nelem; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { push(p1.tensor.elem[i]); push(p2); push(p3); @@ -21386,16 +25125,27 @@ return restore(); }; + // 'sum' function + + //define A p3 + //define B p4 + //define I p5 + //define X p6 + + // leaves the sum at the top of the stack Eval_sum = function() { var body, i, indexVariable, j, k, l1, ref2, ref3; i = 0; j = 0; k = 0; + // 1st arg body = cadr(p1); + // 2nd arg (index) indexVariable = caddr(p1); if (!issymbol(indexVariable)) { stop("sum: 2nd arg?"); } + // 3rd arg (lower limit) push(cadddr(p1)); Eval(); j = pop_integer(); @@ -21403,6 +25153,7 @@ push(p1); return; } + // 4th arg (upper limit) push(caddddr(p1)); Eval(); k = pop_integer(); @@ -21410,9 +25161,11 @@ push(p1); return; } + // remember contents of the index + // variable so we can put it back after the loop p4 = get_binding(indexVariable); push_integer(0); - for (i = l1 = ref2 = j, ref3 = k; ref2 <= ref3 ? l1 <= ref3 : l1 >= ref3; i = ref2 <= ref3 ? ++l1 : --l1) { + for (i = l1 = ref2 = j, ref3 = k; (ref2 <= ref3 ? l1 <= ref3 : l1 >= ref3); i = ref2 <= ref3 ? ++l1 : --l1) { push_integer(i); p5 = pop(); set_binding(indexVariable, p5); @@ -21420,9 +25173,11 @@ Eval(); add(); } + // put back the index variable to original content return set_binding(indexVariable, p4); }; + // Tangent function of numerical and symbolic arguments Eval_tan = function() { push(cadr(p1)); Eval(); @@ -21452,6 +25207,7 @@ push_double(d); return; } + // tan function is antisymmetric, tan(-x) = -tan(x) if (isnegative(p1)) { push(p1); negate(); @@ -21459,6 +25215,15 @@ negate(); return; } + // multiply by 180/pi to go from radians to degrees. + // we go from radians to degrees because it's much + // easier to calculate symbolic results of most (not all) "classic" + // angles (e.g. 30,45,60...) if we calculate the degrees + // and the we do a switch on that. + // Alternatively, we could look at the fraction of pi + // (e.g. 60 degrees is 1/3 pi) but that's more + // convoluted as we'd need to look at both numerator and + // denominator. push(p1); push_integer(180); multiply(); @@ -21469,6 +25234,9 @@ } divide(); n = pop_integer(); + // most "good" (i.e. compact) trigonometric results + // happen for a round number of degrees. There are some exceptions + // though, e.g. 22.5 degrees, which we don't capture here. if (n < 0 || isNaN(n)) { push(symbol(TAN)); push(p1); @@ -21517,6 +25285,9 @@ } }; + // exp(2 x) - 1 + // tanh(x) = -------------- + // exp(2 x) + 1 Eval_tanh = function() { var d; d = 0.0; @@ -21544,21 +25315,12 @@ return list(2); }; - - /* - Taylor expansion of a function - - push(F) - push(X) - push(N) - push(A) - taylor() - */ - Eval_taylor = function() { + // 1st arg p1 = cdr(p1); push(car(p1)); Eval(); + // 2nd arg p1 = cdr(p1); push(car(p1)); Eval(); @@ -21568,27 +25330,34 @@ } else { push(p2); } + // 3rd arg p1 = cdr(p1); push(car(p1)); Eval(); p2 = pop(); if (p2 === symbol(NIL)) { - push_integer(24); + push_integer(24); // default number of terms } else { push(p2); } + // 4th arg p1 = cdr(p1); push(car(p1)); Eval(); p2 = pop(); if (p2 === symbol(NIL)) { - push_integer(0); + push_integer(0); // default expansion point } else { push(p2); } return taylor(); }; + //define F p1 + //define X p2 + //define N p3 + //define A p4 + //define C p5 taylor = function() { var i, k, l1, ref2; i = 0; @@ -21617,7 +25386,7 @@ Eval(); push_integer(1); p5 = pop(); - for (i = l1 = 1, ref2 = k; 1 <= ref2 ? l1 <= ref2 : l1 >= ref2; i = 1 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 1, ref2 = k; (1 <= ref2 ? l1 <= ref2 : l1 >= ref2); i = 1 <= ref2 ? ++l1 : --l1) { push(p1); push(p2); derivative(); @@ -21646,33 +25415,33 @@ return restore(); }; - + //(docs are generated from top-level comments, keep an eye on the formatting!) /* tensor ===================================================================== - + Tags ---- scripting, JS, internal, treenode, general concept - + General description ------------------- Tensors are a strange in-between of matrices and "computer" rectangular data structures. - + Tensors, unlike matrices, and like rectangular data structures, can have an arbitrary number of dimensions (rank), although a tensor with rank zero is just a scalar. - + Tensors, like matrices and unlike many computer rectangular data structures, must be "contiguous" i.e. have no empty spaces within its size, and "uniform", i.e. each element must have the same shape and hence the same rank. - + Also tensors have necessarily to make a distinction between row vectors, column vectors (which have a rank of 2) and uni-dimensional vectors (rank 1). They look very similar but they are fundamentally different. - + Tensors are 1-indexed, as per general math notation, and like Fortran, Lua, Mathematica, SASL, MATLAB, Julia, Erlang and APL. - + Tensors with elements that are also tensors get promoted to a higher rank , this is so we can represent and get the rank of a matrix correctly. Example: @@ -21688,88 +25457,129 @@ Implication of it all is that you can't put arbitrary tensors inside tensors (like you would do to represent block matrices) Rather, all tensors inside tensors must have same shape (and hence, rank) - + Limitations ----------- n.a. - + Implementation info ------------------- Tensors are implemented... - */ + */ + // Called from the "eval" module to evaluate tensor elements. + // p1 points to the tensor operand. Eval_tensor = function() { var a, b, i, l1, m1, ndim, nelem, ref2, ref3; i = 0; ndim = 0; nelem = 0; + //U **a, **b + + //--------------------------------------------------------------------- + + // create a new tensor for the result + + //--------------------------------------------------------------------- check_tensor_dimensions(p1); nelem = p1.tensor.nelem; ndim = p1.tensor.ndim; p2 = alloc_tensor(nelem); p2.tensor.ndim = ndim; - for (i = l1 = 0, ref2 = ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p2.tensor.dim[i] = p1.tensor.dim[i]; } + //--------------------------------------------------------------------- + + // b = Eval(a) + + //--------------------------------------------------------------------- a = p1.tensor.elem; b = p2.tensor.elem; check_tensor_dimensions(p2); - for (i = m1 = 0, ref3 = nelem; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = nelem; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { + //console.log "push/pop: pushing element a of " + i push(a[i]); Eval(); + //console.log "push/pop: popping into element b of " + i b[i] = pop(); } check_tensor_dimensions(p1); check_tensor_dimensions(p2); + //--------------------------------------------------------------------- + + // push the result + + //--------------------------------------------------------------------- push(p2); return promote_tensor(); }; + //----------------------------------------------------------------------------- + + // Add tensors + + // Input: Operands on stack + + // Output: Result on stack + + //----------------------------------------------------------------------------- tensor_plus_tensor = function() { var a, b, c, i, l1, m1, n1, ndim, nelem, ref2, ref3, ref4; i = 0; ndim = 0; nelem = 0; + //U **a, **b, **c save(); p2 = pop(); p1 = pop(); + // are the dimension lists equal? ndim = p1.tensor.ndim; if (ndim !== p2.tensor.ndim) { push(symbol(NIL)); restore(); return; } - for (i = l1 = 0, ref2 = ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { if (p1.tensor.dim[i] !== p2.tensor.dim[i]) { push(symbol(NIL)); restore(); return; } } + // create a new tensor for the result nelem = p1.tensor.nelem; p3 = alloc_tensor(nelem); p3.tensor.ndim = ndim; - for (i = m1 = 0, ref3 = ndim; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = ndim; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { p3.tensor.dim[i] = p1.tensor.dim[i]; } + // c = a + b a = p1.tensor.elem; b = p2.tensor.elem; c = p3.tensor.elem; - for (i = n1 = 0, ref4 = nelem; 0 <= ref4 ? n1 < ref4 : n1 > ref4; i = 0 <= ref4 ? ++n1 : --n1) { + for (i = n1 = 0, ref4 = nelem; (0 <= ref4 ? n1 < ref4 : n1 > ref4); i = 0 <= ref4 ? ++n1 : --n1) { push(a[i]); push(b[i]); add(); c[i] = pop(); } + // push the result push(p3); return restore(); }; + //----------------------------------------------------------------------------- + + // careful not to reorder factors + + //----------------------------------------------------------------------------- tensor_times_scalar = function() { var a, b, i, l1, m1, ndim, nelem, ref2, ref3; i = 0; ndim = 0; nelem = 0; + //U **a, **b save(); p2 = pop(); p1 = pop(); @@ -21777,12 +25587,12 @@ nelem = p1.tensor.nelem; p3 = alloc_tensor(nelem); p3.tensor.ndim = ndim; - for (i = l1 = 0, ref2 = ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p3.tensor.dim[i] = p1.tensor.dim[i]; } a = p1.tensor.elem; b = p3.tensor.elem; - for (i = m1 = 0, ref3 = nelem; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = nelem; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { push(a[i]); push(p2); multiply(); @@ -21797,6 +25607,7 @@ i = 0; ndim = 0; nelem = 0; + //U **a, **b save(); p2 = pop(); p1 = pop(); @@ -21804,12 +25615,12 @@ nelem = p2.tensor.nelem; p3 = alloc_tensor(nelem); p3.tensor.ndim = ndim; - for (i = l1 = 0, ref2 = ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p3.tensor.dim[i] = p2.tensor.dim[i]; } a = p2.tensor.elem; b = p3.tensor.elem; - for (i = m1 = 0, ref3 = nelem; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = nelem; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { push(p1); push(a[i]); multiply(); @@ -21834,12 +25645,18 @@ } }; + //----------------------------------------------------------------------------- + + // gradient of tensor + + //----------------------------------------------------------------------------- d_tensor_tensor = function() { var a, b, c, i, j, l1, m1, n1, ndim, nelem, ref2, ref3, ref4; i = 0; j = 0; ndim = 0; nelem = 0; + //U **a, **b, **c ndim = p1.tensor.ndim; nelem = p1.tensor.nelem; if (ndim + 1 >= MAXDIM) { @@ -21851,15 +25668,15 @@ } p3 = alloc_tensor(nelem * p2.tensor.nelem); p3.tensor.ndim = ndim + 1; - for (i = l1 = 0, ref2 = ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p3.tensor.dim[i] = p1.tensor.dim[i]; } p3.tensor.dim[ndim] = p2.tensor.dim[0]; a = p1.tensor.elem; b = p2.tensor.elem; c = p3.tensor.elem; - for (i = m1 = 0, ref3 = nelem; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { - for (j = n1 = 0, ref4 = p2.tensor.nelem; 0 <= ref4 ? n1 < ref4 : n1 > ref4; j = 0 <= ref4 ? ++n1 : --n1) { + for (i = m1 = 0, ref3 = nelem; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { + for (j = n1 = 0, ref4 = p2.tensor.nelem; (0 <= ref4 ? n1 < ref4 : n1 > ref4); j = 0 <= ref4 ? ++n1 : --n1) { push(a[i]); push(b[j]); derivative(); @@ -21869,14 +25686,20 @@ return push(p3); }; + //----------------------------------------------------------------------------- + + // gradient of scalar + + //----------------------------------------------------------------------------- d_scalar_tensor = function() { var a, b, i, l1, ref2; + //U **a, **b p3 = alloc_tensor(p2.tensor.nelem); p3.tensor.ndim = 1; p3.tensor.dim[0] = p2.tensor.dim[0]; a = p2.tensor.elem; b = p3.tensor.elem; - for (i = l1 = 0, ref2 = p2.tensor.nelem; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p2.tensor.nelem; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { push(p1); push(a[i]); derivative(); @@ -21885,17 +25708,23 @@ return push(p3); }; + //----------------------------------------------------------------------------- + + // Derivative of tensor + + //----------------------------------------------------------------------------- d_tensor_scalar = function() { var a, b, i, l1, m1, ref2, ref3; i = 0; + //U **a, **b p3 = alloc_tensor(p1.tensor.nelem); p3.tensor.ndim = p1.tensor.ndim; - for (i = l1 = 0, ref2 = p1.tensor.ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p1.tensor.ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p3.tensor.dim[i] = p1.tensor.dim[i]; } a = p1.tensor.elem; b = p3.tensor.elem; - for (i = m1 = 0, ref3 = p1.tensor.nelem; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = p1.tensor.nelem; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { push(a[i]); push(p2); derivative(); @@ -21913,7 +25742,7 @@ if (p1.tensor.ndim > p2.tensor.ndim) { return 1; } - for (i = l1 = 0, ref2 = p1.tensor.ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p1.tensor.ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { if (p1.tensor.dim[i] < p2.tensor.dim[i]) { return -1; } @@ -21921,7 +25750,7 @@ return 1; } } - for (i = m1 = 0, ref3 = p1.tensor.nelem; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = p1.tensor.nelem; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { if (equal(p1.tensor.elem[i], p2.tensor.elem[i])) { continue; } @@ -21934,11 +25763,23 @@ return 0; }; + //----------------------------------------------------------------------------- + + // Raise a tensor to a power + + // Input: p1 tensor + + // p2 exponent + + // Output: Result on stack + + //----------------------------------------------------------------------------- power_tensor = function() { var i, k, l1, m1, n, ref2, ref3, results; i = 0; k = 0; n = 0; + // first and last dims must be equal k = p1.tensor.ndim - 1; if (p1.tensor.dim[0] !== p1.tensor.dim[k]) { push_symbol(POWER); @@ -21965,7 +25806,7 @@ p1.tensor.ndim = 2; p1.tensor.dim[0] = n; p1.tensor.dim[1] = n; - for (i = l1 = 0, ref2 = n; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = n; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p1.tensor.elem[n * i + i] = one; } check_tensor_dimensions(p1); @@ -21980,7 +25821,7 @@ } push(p1); results = []; - for (i = m1 = 1, ref3 = n; 1 <= ref3 ? m1 < ref3 : m1 > ref3; i = 1 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 1, ref3 = n; (1 <= ref3 ? m1 < ref3 : m1 > ref3); i = 1 <= ref3 ? ++m1 : --m1) { push(p1); inner(); if (isZeroAtomOrTensor(stack[tos - 1])) { @@ -21999,10 +25840,10 @@ p1 = pop(); p2 = alloc_tensor(p1.tensor.nelem); p2.tensor.ndim = p1.tensor.ndim; - for (i = l1 = 0, ref2 = p1.tensor.ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p1.tensor.ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p2.tensor.dim[i] = p1.tensor.dim[i]; } - for (i = m1 = 0, ref3 = p1.tensor.nelem; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = p1.tensor.nelem; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { p2.tensor.elem[i] = p1.tensor.elem[i]; } check_tensor_dimensions(p1); @@ -22011,6 +25852,7 @@ return restore(); }; + // Tensors with elements that are also tensors get promoted to a higher rank. promote_tensor = function() { var i, j, k, l1, m1, n1, ndim, nelem, o1, q1, ref2, ref3, ref4, ref5, ref6; i = 0; @@ -22026,7 +25868,7 @@ return; } p2 = p1.tensor.elem[0]; - for (i = l1 = 1, ref2 = p1.tensor.nelem; 1 <= ref2 ? l1 < ref2 : l1 > ref2; i = 1 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 1, ref2 = p1.tensor.nelem; (1 <= ref2 ? l1 < ref2 : l1 > ref2); i = 1 <= ref2 ? ++l1 : --l1) { if (!compatible(p2, p1.tensor.elem[i])) { stop("Cannot promote tensor due to inconsistent tensor components."); } @@ -22043,16 +25885,16 @@ nelem = p1.tensor.nelem * p2.tensor.nelem; p3 = alloc_tensor(nelem); p3.tensor.ndim = ndim; - for (i = m1 = 0, ref3 = p1.tensor.ndim; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = p1.tensor.ndim; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { p3.tensor.dim[i] = p1.tensor.dim[i]; } - for (j = n1 = 0, ref4 = p2.tensor.ndim; 0 <= ref4 ? n1 < ref4 : n1 > ref4; j = 0 <= ref4 ? ++n1 : --n1) { + for (j = n1 = 0, ref4 = p2.tensor.ndim; (0 <= ref4 ? n1 < ref4 : n1 > ref4); j = 0 <= ref4 ? ++n1 : --n1) { p3.tensor.dim[i + j] = p2.tensor.dim[j]; } k = 0; - for (i = o1 = 0, ref5 = p1.tensor.nelem; 0 <= ref5 ? o1 < ref5 : o1 > ref5; i = 0 <= ref5 ? ++o1 : --o1) { + for (i = o1 = 0, ref5 = p1.tensor.nelem; (0 <= ref5 ? o1 < ref5 : o1 > ref5); i = 0 <= ref5 ? ++o1 : --o1) { p2 = p1.tensor.elem[i]; - for (j = q1 = 0, ref6 = p2.tensor.nelem; 0 <= ref6 ? q1 < ref6 : q1 > ref6; j = 0 <= ref6 ? ++q1 : --q1) { + for (j = q1 = 0, ref6 = p2.tensor.nelem; (0 <= ref6 ? q1 < ref6 : q1 > ref6); j = 0 <= ref6 ? ++q1 : --q1) { p3.tensor.elem[k++] = p2.tensor.elem[j]; } } @@ -22073,7 +25915,7 @@ if (p.tensor.ndim !== q.tensor.ndim) { return 0; } - for (i = l1 = 0, ref2 = p.tensor.ndim; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p.tensor.ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { if (p.tensor.dim[i] !== q.tensor.dim[i]) { return 0; } @@ -22081,11 +25923,17 @@ return 1; }; + // If the number of args is odd then the last arg is the default result. + // Works like a switch statement. Could also be used for piecewise + // functions? TODO should probably be called "switch"? Eval_test = function() { var checkResult, orig; orig = p1; p1 = cdr(p1); while (iscons(p1)) { + // odd number of parameters means that the + // last argument becomes the default case + // i.e. the one without a test. if (cdr(p1) === symbol(NIL)) { push(car(p1)); Eval(); @@ -22093,21 +25941,37 @@ } checkResult = isZeroLikeOrNonZeroLikeOrUndetermined(car(p1)); if (checkResult == null) { + // we couldn't determine the result + // of a test. This means we can't conclude + // anything about the result of the + // overall test, so we must bail + // with the unevalled test push(orig); return; } else if (checkResult) { + // test succesful, we found out output push(cadr(p1)); Eval(); return; } else { + // test unsuccessful, continue to the + // next pair of test,value p1 = cddr(p1); } } + // no test matched and there was no + // catch-all case, so we return zero. return push_integer(0); }; + // we test A==B by first subtracting and checking if we symbolically + // get zero. If not, we evaluate to float and check if we get a zero. + // If we get another NUMBER then we know they are different. + // If we get something else, then we don't know and we return the + // unaveluated test, which is the same as saying "maybe". Eval_testeq = function() { var checkResult, orig, subtractionResult; + // first try without simplifyng both sides orig = p1; push(cadr(p1)); Eval(); @@ -22115,6 +25979,13 @@ Eval(); subtract(); subtractionResult = pop(); + // OK so we are doing something tricky here + // we are using isZeroLikeOrNonZeroLikeOrUndetermined to check if the result + // is zero or not zero or unknown. + // isZeroLikeOrNonZeroLikeOrUndetermined has some routines + // to determine the zero-ness/non-zero-ness or + // undeterminate-ness of things so we use + // that here and down below. checkResult = isZeroLikeOrNonZeroLikeOrUndetermined(subtractionResult); if (checkResult) { push_integer(0); @@ -22123,6 +25994,9 @@ push_integer(1); return; } + // we didn't get a simple numeric result but + // let's try again after doing + // a simplification on both sides push(cadr(p1)); Eval(); simplify(); @@ -22139,9 +26013,13 @@ push_integer(1); return; } + // if we didn't get to a number then we + // don't know whether the quantities are + // different so do nothing return push(orig); }; + // Relational operators expect a numeric result for operand difference. Eval_testge = function() { var comparison, orig; orig = p1; @@ -22202,52 +26080,74 @@ } }; + // not definition Eval_not = function() { var checkResult, wholeAndExpression; wholeAndExpression = p1; checkResult = isZeroLikeOrNonZeroLikeOrUndetermined(cadr(p1)); if (checkResult == null) { + // inconclusive test on predicate return push(wholeAndExpression); } else if (checkResult) { + // true -> false return push_integer(0); } else { + // false -> true return push_integer(1); } }; - /* and ===================================================================== - + Tags ---- scripting, JS, internal, treenode, general concept - + Parameters ---------- a,b,... - + General description ------------------- Logical-and of predicate expressions. - */ + */ + // and definition Eval_and = function() { var andPredicates, checkResult, somePredicateUnknown, wholeAndExpression; wholeAndExpression = p1; andPredicates = cdr(wholeAndExpression); somePredicateUnknown = false; while (iscons(andPredicates)) { + // eval each predicate checkResult = isZeroLikeOrNonZeroLikeOrUndetermined(car(andPredicates)); if (checkResult == null) { + // here we have stuff that is not reconducible to any + // numeric value (or tensor with numeric values) e.g. + // 'a+b', so it just means that we just don't know the + // truth value of this particular predicate. + // We'll track the fact that we found an unknown + // predicate and we continue with the other predicates. + // (note that in case some subsequent predicate will be false, + // it won't matter that we found some unknowns and + // the whole test will be immediately zero). somePredicateUnknown = true; andPredicates = cdr(andPredicates); } else if (checkResult) { + // found a true, move on to the next predicate andPredicates = cdr(andPredicates); } else if (!checkResult) { + // found a false, enough to falsify everything and return push_integer(0); return; } } + // We checked all the predicates and none of them + // was false. So they were all either true or unknown. + // Now, if even just one was unknown, we'll have to call this + // test as inconclusive and return the whole test expression. + // If all the predicates were known, then we can conclude + // that the test returns true. if (somePredicateUnknown) { return push(wholeAndExpression); } else { @@ -22255,23 +26155,42 @@ } }; + // or definition Eval_or = function() { var checkResult, orPredicates, somePredicateUnknown, wholeOrExpression; wholeOrExpression = p1; orPredicates = cdr(wholeOrExpression); somePredicateUnknown = false; while (iscons(orPredicates)) { + // eval each predicate checkResult = isZeroLikeOrNonZeroLikeOrUndetermined(car(orPredicates)); if (checkResult == null) { + // here we have stuff that is not reconducible to any + // numeric value (or tensor with numeric values) e.g. + // 'a+b', so it just means that we just don't know the + // truth value of this particular predicate. + // We'll track the fact that we found an unknown + // predicate and we continue with the other predicates. + // (note that in case some subsequent predicate will be false, + // it won't matter that we found some unknowns and + // the whole test will be immediately zero). somePredicateUnknown = true; orPredicates = cdr(orPredicates); } else if (checkResult) { + // found a true, enough to return true push_integer(1); return; } else if (!checkResult) { + // found a false, move on to the next predicate orPredicates = cdr(orPredicates); } } + // We checked all the predicates and none of them + // was true. So they were all either false or unknown. + // Now, if even just one was unknown, we'll have to call this + // test as inconclusive and return the whole test expression. + // If all the predicates were known, then we can conclude + // that the test returns false. if (somePredicateUnknown) { return push(wholeOrExpression); } else { @@ -22279,6 +26198,12 @@ } }; + // use subtract for cases like A < A + 1 + + // TODO you could be smarter here and + // simplify both sides only in the case + // of "relational operator: cannot determine..." + // a bit like we do in Eval_testeq cmp_args = function() { var t; t = 0; @@ -22290,13 +26215,16 @@ simplify(); subtract(); p1 = pop(); + // try floating point if necessary if (p1.k !== NUM && p1.k !== DOUBLE) { push(p1); yyfloat(); Eval(); p1 = pop(); } + //console.log "comparison: " + p1.toString() if (isZeroAtomOrTensor(p1)) { + //console.log "comparison isZero " return 0; } switch (p1.k) { @@ -22308,6 +26236,7 @@ } break; case DOUBLE: + //console.log "comparison p1.d: " + p1.d if (p1.d < 0.0) { t = -1; } else { @@ -22315,52 +26244,60 @@ } break; default: + //console.log "comparison is null" t = null; } return t; }; - /* Transform an expression using a pattern. The pattern can come from the integrals table or the user-defined patterns. - + The expression and free variable are on the stack. - + The argument s is a null terminated list of transform rules. - + For example, see the itab (integrals table) - + Internally, the following symbols are used: - + F input expression - + X free variable, i.e. F of X - + A template expression - + B result expression - + C list of conditional expressions - + Puts the final expression on top of stack (whether it's transformed or not) and returns true is successful, false if not. - */ + */ + // p1 and p2 are tmps + + //define F p3 + //define X p4 + //define A p5 + //define B p6 + //define C p7 transform = function(s, generalTransform) { var bookmarkTosToPrintDecomps, eachTransformEntry, i, l1, len, len1, m1, n1, numberOfDecomps, ref2, restTerm, secondTerm, success, theTransform, transform_h, transformationSuccessful, transformedTerms; transform_h = 0; save(); p1 = null; - p4 = pop(); - p3 = pop(); + p4 = pop(); // X i.e. free variable + p3 = pop(); // F i.e. input expression if (DEBUG) { console.log(" !!!!!!!!! transform on: " + p3); } saveMetaBindings(); set_binding(symbol(METAX), p4); + // put constants in F(X) on the stack transform_h = tos; push_integer(1); push(p3); @@ -22372,7 +26309,7 @@ numberOfDecomps = tos - bookmarkTosToPrintDecomps; if (DEBUG) { console.log(" " + numberOfDecomps + " decomposed elements ====== "); - for (i = l1 = 0, ref2 = numberOfDecomps; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = numberOfDecomps; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { console.log(" decomposition element " + i + ": " + stack[tos - 1 - i]); } } @@ -22387,12 +26324,18 @@ console.log("scanning table entry " + theTransform); } push(theTransform); + // replacements of meta variables. Note that we don't + // use scan_meta because the pattern is not a string + // that we have to parse, it's a tree already. + // replace a_ with METAA in the passed transformation push(symbol(SYMBOL_A_UNDERSCORE)); push(symbol(METAA)); subst(); + // replace b_ with METAB in the passed transformation push(symbol(SYMBOL_B_UNDERSCORE)); push(symbol(METAB)); subst(); + // replace x_ with METAX in the passed transformation push(symbol(SYMBOL_X_UNDERSCORE)); push(symbol(METAX)); subst(); @@ -22403,7 +26346,6 @@ } p6 = cadr(p1); p7 = cddr(p1); - /* p5 = p1.tensor.elem[0] p6 = p1.tensor.elem[1] @@ -22411,10 +26353,15 @@ push p1.tensor.elem[i] list(p1.tensor.elem.length - 2) p7 = pop() - */ + */ if (f_equals_a(transform_h, generalTransform)) { + // successful transformation, + // transformed result is in p6 transformationSuccessful = true; } else { + // the match failed but perhaps we can match + // something lower down in the tree, so + // let's recurse the tree if (DEBUG) { console.log("p3 at this point: " + p3); } @@ -22438,6 +26385,8 @@ if (DEBUG) { console.log("testing: " + secondTerm); } + //if (secondTerm+"") == "eig(A x,transpose(A x))()" + // debugger if (DEBUG) { console.log("about to try to simplify other term: " + secondTerm); } @@ -22448,13 +26397,15 @@ console.log("tried to simplify other term: " + secondTerm + " ...successful?: " + success + " ...transformed: " + transformedTerms[transformedTerms.length - 1]); } } + // recreate the tree we were passed, + // but with all the terms being transformed if (transformedTerms.length !== 0) { for (m1 = 0, len = transformedTerms.length; m1 < len; m1++) { i = transformedTerms[m1]; push(i); } list(transformedTerms.length); - p6 = pop(); + p6 = pop(); // "integrals" mode } } } @@ -22473,7 +26424,6 @@ p5 = cadr(p1); p6 = caddr(p1); p7 = cdddr(p1); - /* p5 = p1.tensor.elem[0] p6 = p1.tensor.elem[1] @@ -22481,8 +26431,10 @@ push p1.tensor.elem[i] list(p1.tensor.elem.length - 2) p7 = pop() - */ + */ if (f_equals_a(transform_h, generalTransform)) { + // there is a successful transformation, + // transformed result is in p6 transformationSuccessful = true; break; } @@ -22491,12 +26443,17 @@ } moveTos(transform_h); if (transformationSuccessful) { + //console.log "transformation successful" + // a transformation was successful push(p6); Eval(); p1 = pop(); + //console.log "...into: " + p1 transformationSuccessful = true; } else { + // transformations failed if (generalTransform) { + // result = original expression p1 = p3; } else { p1 = symbol(NIL); @@ -22520,20 +26477,22 @@ return set_binding(symbol(METAA), pop()); }; + // search for a METAA and METAB such that F = A f_equals_a = function(h, generalTransform) { var fea_i, fea_j, l1, m1, originalexpanding, ref2, ref3, ref4, ref5; fea_i = 0; fea_j = 0; - for (fea_i = l1 = ref2 = h, ref3 = tos; ref2 <= ref3 ? l1 < ref3 : l1 > ref3; fea_i = ref2 <= ref3 ? ++l1 : --l1) { + for (fea_i = l1 = ref2 = h, ref3 = tos; (ref2 <= ref3 ? l1 < ref3 : l1 > ref3); fea_i = ref2 <= ref3 ? ++l1 : --l1) { set_binding(symbol(METAA), stack[fea_i]); if (DEBUG) { console.log(" binding METAA to " + get_binding(symbol(METAA))); } - for (fea_j = m1 = ref4 = h, ref5 = tos; ref4 <= ref5 ? m1 < ref5 : m1 > ref5; fea_j = ref4 <= ref5 ? ++m1 : --m1) { + for (fea_j = m1 = ref4 = h, ref5 = tos; (ref4 <= ref5 ? m1 < ref5 : m1 > ref5); fea_j = ref4 <= ref5 ? ++m1 : --m1) { set_binding(symbol(METAB), stack[fea_j]); if (DEBUG) { console.log(" binding METAB to " + get_binding(symbol(METAB))); } + // now test all the conditions (it's an and between them) p1 = p7; while (iscons(p1)) { push(car(p1)); @@ -22545,6 +26504,8 @@ p1 = cdr(p1); } if (iscons(p1)) { + // conditions are not met, + // skip to the next binding of metas continue; } push(p3); @@ -22572,16 +26533,19 @@ console.log("binding METAX to " + get_binding(symbol(METAX))); console.log("comparing " + p3 + " to: " + p5); } - return 1; + return 1; // yes } } } - return 0; + return 0; // no }; + // Transpose tensor indices Eval_transpose = function() { push(cadr(p1)); Eval(); + // add default params if they + // have not been passed if (cddr(p1) === symbol(NIL)) { push_integer(1); push_integer(2); @@ -22606,19 +26570,25 @@ t = 0; ai = []; an = []; - for (i = l1 = 0, ref2 = MAXDIM; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = MAXDIM; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { ai[i] = 0; an[i] = 0; } + //U **a, **b save(); - p3 = pop(); - p2 = pop(); - p1 = pop(); + // by default p3 is 2 and p2 is 1 + p3 = pop(); // index to be transposed + p2 = pop(); // other index to be transposed + p1 = pop(); // what needs to be transposed + + // a transposition just goes away when + // applied to a scalar if (isNumericAtom(p1)) { push(p1); restore(); return; } + // transposition goes away for identity matrix if ((isplusone(p2) && isplustwo(p3)) || (isplusone(p3) && isplustwo(p2))) { if (isidentitymatrix(p1)) { push(p1); @@ -22626,6 +26596,9 @@ return; } } + // a transposition just goes away when + // applied to another transposition with + // the same columns to be switched if (istranspose(p1)) { innerTranspSwitch1 = car(cdr(cdr(p1))); innerTranspSwitch2 = car(cdr(cdr(cdr(p1)))); @@ -22635,11 +26608,15 @@ return; } } + // if operand is a sum then distribute + // (if we are in expanding mode) if (expanding && isadd(p1)) { p1 = cdr(p1); push(zero); while (iscons(p1)) { push(car(p1)); + // add the dimensions to switch but only if + // they are not the default ones. push(p2); push(p3); transpose(); @@ -22649,11 +26626,15 @@ restore(); return; } + // if operand is a multiplication then distribute + // (if we are in expanding mode) if (expanding && ismultiply(p1)) { p1 = cdr(p1); push(one); while (iscons(p1)) { push(car(p1)); + // add the dimensions to switch but only if + // they are not the default ones. push(p2); push(p3); transpose(); @@ -22663,6 +26644,12 @@ restore(); return; } + // distribute the transpose of a dot + // if in expanding mode + // note that the distribution happens + // in reverse as per tranpose rules. + // The dot operator is not + // commutative, so, it matters. if (expanding && isinnerordot(p1)) { p1 = cdr(p1); accumulator = []; @@ -22670,7 +26657,7 @@ accumulator.push([car(p1), p2, p3]); p1 = cdr(p1); } - for (eachEntry = m1 = ref3 = accumulator.length - 1; ref3 <= 0 ? m1 <= 0 : m1 >= 0; eachEntry = ref3 <= 0 ? ++m1 : --m1) { + for (eachEntry = m1 = ref3 = accumulator.length - 1; (ref3 <= 0 ? m1 <= 0 : m1 >= 0); eachEntry = ref3 <= 0 ? ++m1 : --m1) { push(accumulator[eachEntry][0]); push(accumulator[eachEntry][1]); push(accumulator[eachEntry][2]); @@ -22684,6 +26671,7 @@ } if (!istensor(p1)) { if (!isZeroAtomOrTensor(p1)) { + //stop("transpose: tensor expected, 1st arg is not a tensor") push_symbol(TRANSPOSE); push(p1); if ((!isplusone(p2) || !isplustwo(p3)) && (!isplusone(p3) || !isplustwo(p2))) { @@ -22702,6 +26690,14 @@ } ndim = p1.tensor.ndim; nelem = p1.tensor.nelem; + // is it a vector? + // so here it's something curious - note how vectors are + // not really special two-dimensional matrices, but rather + // 1-dimension objects (like tensors can be). So since + // they have one dimension, transposition has no effect. + // (as opposed as if they were special two-dimensional + // matrices) + // see also Ran Pan, Tensor Transpose and Its Properties. CoRR abs/1411.1503 (2014) if (ndim === 1) { push(p1); restore(); @@ -22718,18 +26714,21 @@ m--; p2 = alloc_tensor(nelem); p2.tensor.ndim = ndim; - for (i = n1 = 0, ref4 = ndim; 0 <= ref4 ? n1 < ref4 : n1 > ref4; i = 0 <= ref4 ? ++n1 : --n1) { + for (i = n1 = 0, ref4 = ndim; (0 <= ref4 ? n1 < ref4 : n1 > ref4); i = 0 <= ref4 ? ++n1 : --n1) { p2.tensor.dim[i] = p1.tensor.dim[i]; } p2.tensor.dim[l] = p1.tensor.dim[m]; p2.tensor.dim[m] = p1.tensor.dim[l]; a = p1.tensor.elem; b = p2.tensor.elem; - for (i = o1 = 0, ref5 = ndim; 0 <= ref5 ? o1 < ref5 : o1 > ref5; i = 0 <= ref5 ? ++o1 : --o1) { +// init tensor index + for (i = o1 = 0, ref5 = ndim; (0 <= ref5 ? o1 < ref5 : o1 > ref5); i = 0 <= ref5 ? ++o1 : --o1) { ai[i] = 0; an[i] = p1.tensor.dim[i]; } - for (i = q1 = 0, ref6 = nelem; 0 <= ref6 ? q1 < ref6 : q1 > ref6; i = 0 <= ref6 ? ++q1 : --q1) { +// copy components from a to b + for (i = q1 = 0, ref6 = nelem; (0 <= ref6 ? q1 < ref6 : q1 > ref6); i = 0 <= ref6 ? ++q1 : --q1) { + // swap indices l and m t = ai[l]; ai[l] = ai[m]; ai[m] = t; @@ -22737,9 +26736,10 @@ an[l] = an[m]; an[m] = t; k = 0; - for (j = r1 = 0, ref7 = ndim; 0 <= ref7 ? r1 < ref7 : r1 > ref7; j = 0 <= ref7 ? ++r1 : --r1) { + for (j = r1 = 0, ref7 = ndim; (0 <= ref7 ? r1 < ref7 : r1 > ref7); j = 0 <= ref7 ? ++r1 : --r1) { k = (k * an[j]) + ai[j]; } + // swap indices back t = ai[l]; ai[l] = ai[m]; ai[m] = t; @@ -22747,7 +26747,17 @@ an[l] = an[m]; an[m] = t; b[k] = a[i]; - for (j = s1 = ref8 = ndim - 1; ref8 <= 0 ? s1 <= 0 : s1 >= 0; j = ref8 <= 0 ? ++s1 : --s1) { +// increment tensor index + + // Suppose the tensor dimensions are 2 and 3. +// Then the tensor index ai increments as follows: +// 00 -> 01 +// 01 -> 02 +// 02 -> 10 +// 10 -> 11 +// 11 -> 12 +// 12 -> 00 + for (j = s1 = ref8 = ndim - 1; (ref8 <= 0 ? s1 <= 0 : s1 >= 0); j = ref8 <= 0 ? ++s1 : --s1) { if (++ai[j] < an[j]) { break; } @@ -22758,24 +26768,37 @@ return restore(); }; + // Evaluate a user defined function + //define F p3 # F is the function body + //define A p4 # A is the formal argument list + //define B p5 # B is the calling argument list + //define S p6 # S is the argument substitution list + + // we got here because there was a function invocation and + // it's not been parsed (and consequently tagged) as any + // system function. + // So we are dealing with another function. + // The function could be actually defined, or not yet, + // so we'll deal with both cases. /* d ===================================================================== - + Tags ---- scripting, JS, internal, treenode, general concept - + Parameters ---------- f,x - + General description ------------------- Returns the partial derivative of f with respect to x. x can be a vector e.g. [x,y]. - */ + */ Eval_user_function = function() { var bodyAndFormalArguments, h; + // Use "derivative" instead of "d" if there is no user function "d" if (DEBUG) { console.log("Eval_user_function evaluating: " + car(p1)); } @@ -22783,8 +26806,18 @@ Eval_derivative(); return; } + // normally car(p1) is a symbol with the function name + // but it could be something that has to be + // evaluated to get to the function definition instead + // (e.g. the function is an element of an array) + // so we do an eval to sort it all out. push(car(p1)); Eval(); + // we expect to find either the body and + // formula arguments, OR, if the function + // has not been defined yet, then the + // function will just contain its own name, as + // all undefined variables do. bodyAndFormalArguments = pop(); if (isNumericAtom(bodyAndFormalArguments)) { stop("expected function invocation, found multiplication instead. Use '*' symbol explicitly for multiplication."); @@ -22793,10 +26826,14 @@ } else if (isstr(bodyAndFormalArguments)) { stop("expected function, found string instead."); } - p3 = car(cdr(bodyAndFormalArguments)); + p3 = car(cdr(bodyAndFormalArguments)); // p3 is function body F + // p4 is the formal argument list + // that is also contained here in the FUNCTION node p4 = car(cdr(cdr(bodyAndFormalArguments))); p5 = cdr(p1); - if ((car(bodyAndFormalArguments) !== symbol(FUNCTION)) || (bodyAndFormalArguments === car(p1))) { + // next check is whether evaluation did nothing, so the function is undefined + if ((car(bodyAndFormalArguments) !== symbol(FUNCTION)) || (bodyAndFormalArguments === car(p1))) { // p3 is F + // leave everything as it was and return h = tos; push(bodyAndFormalArguments); p1 = p5; @@ -22808,29 +26845,39 @@ list(tos - h); return; } + // Create the argument substitution list p6(S) p1 = p4; p2 = p5; h = tos; while (iscons(p1) && iscons(p2)) { push(car(p1)); push(car(p2)); + // why explicitly Eval the parameters when + // the body of the function is + // evalled anyways? Commenting it out. All tests pass... + //Eval() p1 = cdr(p1); p2 = cdr(p2); } list(tos - h); p6 = pop(); push(p3); - if (iscons(p6)) { + if (iscons(p6)) { // p6 is S push(p6); rewrite_args(); } + //console.log "rewritten body: " + stack[tos-1] return Eval(); }; + // Rewrite by expanding symbols that contain args rewrite_args = function() { var h, n; n = 0; save(); + // subst. list which is a list + // where each consecutive pair + // is what needs to be substituted and with what p2 = pop(); p1 = pop(); if (istensor(p1)) { @@ -22841,12 +26888,19 @@ if (iscons(p1)) { h = tos; if (car(p1) === car(p2)) { + // rewrite a function in + // the body with the one + // passed from the paramaters push_symbol(EVAL); push(car(cdr(p2))); list(2); } else { + // if there is no match + // then no substitution necessary push(car(p1)); } + // continue recursively to + // rewrite the rest of the body p1 = cdr(p1); while (iscons(p1)) { push(car(p1)); @@ -22863,6 +26917,11 @@ restore(); return 0; } + // Here we are in a symbol case + // so we need to substitute + + // Check if there is a direct match + // of symbols right away p3 = p2; while (iscons(p3)) { if (p1 === car(p3)) { @@ -22872,6 +26931,8 @@ } p3 = cddr(p3); } + // Get the symbol's content, if _that_ + // matches then do the substitution p3 = get_binding(p1); push(p3); if (p1 !== p3) { @@ -22879,7 +26940,7 @@ n = rewrite_args(); if (n === 0) { pop(); - push(p1); + push(p1); // restore if not rewritten with arg } } restore(); @@ -22893,7 +26954,7 @@ push(p1); copy_tensor(); p1 = pop(); - for (i = l1 = 0, ref2 = p1.tensor.nelem; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p1.tensor.nelem; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { push(p1.tensor.elem[i]); push(p2); n += rewrite_args(); @@ -22910,7 +26971,7 @@ k = []; m = 0; n = 0; - for (i = l1 = 0, ref2 = MAXDIM; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = MAXDIM; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { k[i] = 0; } m = 1; @@ -22921,6 +26982,8 @@ Eval(); i = pop_integer(); if (i < 1 || isNaN(i)) { + // if the input is nonsensical + // just return 0 push(zero); return; } @@ -22934,47 +26997,12 @@ } p1 = alloc_tensor(m); p1.tensor.ndim = n; - for (i = m1 = 0, ref3 = n; 0 <= ref3 ? m1 < ref3 : m1 > ref3; i = 0 <= ref3 ? ++m1 : --m1) { + for (i = m1 = 0, ref3 = n; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { p1.tensor.dim[i] = k[i]; } return push(p1); }; - - /* - // up to 100 blocks of 100,000 atoms - - #define M 100 - #define N 100000 - - U *mem[M] - int mcount - - U *free_list - int free_count - - U * - alloc(void) - { - U *p - if (free_count == 0) { - if (mcount == 0) - alloc_mem() - else { - gc() - if (free_count < N * mcount / 2) - alloc_mem() - } - if (free_count == 0) - stop("atom space exhausted") - } - p = free_list - free_list = free_list->u.cons.cdr - free_count-- - return p - } - */ - allocatedId = 0; alloc_tensor = function(nelem) { @@ -22984,35 +27012,36 @@ p.k = TENSOR; p.tensor = new tensor(); p.tensor.nelem = nelem; - for (i = l1 = 0, ref2 = nelem; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = nelem; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { p.tensor.elem[i] = zero; } p.tensor.allocatedId = allocatedId; + //if allocatedId == 9 + // debugger allocatedId++; check_tensor_dimensions(p); return p; }; - /* // garbage collector - + void gc(void) { int i, j U *p - + // tag everything - + for (i = 0; i < mcount; i++) { p = mem[i] for (j = 0; j < N; j++) p[j].tag = 1 } - + // untag what's used - + untag(p0) untag(p1) untag(p2) @@ -23023,26 +27052,26 @@ untag(p7) untag(p8) untag(p9) - + untag(one) untag(zero) untag(imaginaryunit) - + for (i = 0; i < NSYM; i++) { untag(binding[i]) untag(arglist[i]) } - + for (i = 0; i < tos; i++) untag(stack[i]) - + for (i = (int) (frame - stack); i < TOS; i++) untag(stack[i]) - + // collect everything that's still tagged - + free_count = 0 - + for (i = 0; i < mcount; i++) { p = mem[i] for (j = 0; j < N; j++) { @@ -23068,12 +27097,12 @@ } } } - + void untag(U *p) { int i - + if (iscons(p)) { do { if (p->tag == 0) @@ -23085,7 +27114,7 @@ untag(p) return } - + if (p->tag) { p->tag = 0 if (istensor(p)) { @@ -23094,9 +27123,9 @@ } } } - + // get memory for 100,000 atoms - + void alloc_mem(void) { @@ -23116,23 +27145,23 @@ free_list = p free_count += N } - + void print_mem_info(void) { char buf[100] - + sprintf(buf, "%d blocks (%d bytes/block)\n", N * mcount, (int) sizeof (U)) printstr(buf) - + sprintf(buf, "%d free\n", free_count) printstr(buf) - + sprintf(buf, "%d used\n", N * mcount - free_count) printstr(buf) } - */ - + */ + // returns 1 if expr p contains expr q, otherweise returns 0 Find = function(p, q) { var i, l1, ref2; i = 0; @@ -23140,7 +27169,7 @@ return 1; } if (istensor(p)) { - for (i = l1 = 0, ref2 = p.tensor.nelem; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p.tensor.nelem; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { if (Find(p.tensor.elem[i], q)) { return 1; } @@ -23156,6 +27185,8 @@ return 0; }; + // find stuff like (-1)^(something (but disregard + // imaginary units which are in the form (-1)^(1/2)) findPossibleClockForm = function(p) { var i, l1, ref2; i = 0; @@ -23164,14 +27195,16 @@ } if (car(p) === symbol(POWER) && !isinteger(caddr(p1))) { if (Find(cadr(p), imaginaryunit)) { + //console.log "found i^fraction " + p return 1; } } if (car(p) === symbol(POWER) && equaln(cadr(p), -1) && !isinteger(caddr(p1))) { + //console.log "found -1^fraction in " + p return 1; } if (istensor(p)) { - for (i = l1 = 0, ref2 = p.tensor.nelem; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p.tensor.nelem; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { if (findPossibleClockForm(p.tensor.elem[i])) { return 1; } @@ -23187,6 +27220,7 @@ return 0; }; + // find stuff like (e)^(i something) findPossibleExponentialForm = function(p) { var i, l1, ref2; i = 0; @@ -23194,7 +27228,7 @@ return Find(caddr(p), imaginaryunit); } if (istensor(p)) { - for (i = l1 = 0, ref2 = p.tensor.nelem; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p.tensor.nelem; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { if (findPossibleExponentialForm(p.tensor.elem[i])) { return 1; } @@ -23214,15 +27248,18 @@ init = function() { var i, l1, ref2; + //debugger + //console.log "DOING AN INIT ========================================================================" i = 0; flag = 0; reset_after_error(); chainOfUserSymbolsNotFunctionsBeingEvaluated = []; - if (flag) { + if (flag) { // already initted return; } flag = 1; - for (i = l1 = 0, ref2 = NSYM; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { +// total clearout of symbol table + for (i = l1 = 0, ref2 = NSYM; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { symtab[i] = new U(); symtab[i].k = SYM; binding[i] = symtab[i]; @@ -23231,39 +27268,35 @@ return defn(); }; - - /* cross ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept, script_defined - - Parameters - ---------- - u,v - - General description - ------------------- - Returns the cross product of vectors u and v. - */ - - - /* curl ===================================================================== - - Tags - ---- - scripting, JS, internal, treenode, general concept, script_defined - - Parameters - ---------- - u - - General description - ------------------- - Returns the curl of vector u. - */ - - defn_str = ["version=\"" + version + "\"", "e=exp(1)", "i=sqrt(-1)", "autoexpand=1", "assumeRealVariables=1", "trange=[-pi,pi]", "xrange=[-10,10]", "yrange=[-10,10]", "last=0", "trace=0", "forceFixedPrintout=1", "maxFixedPrintoutDigits=6", "printLeaveEAlone=1", "printLeaveXAlone=0", "cross(u,v)=[u[2]*v[3]-u[3]*v[2],u[3]*v[1]-u[1]*v[3],u[1]*v[2]-u[2]*v[1]]", "curl(v)=[d(v[3],y)-d(v[2],z),d(v[1],z)-d(v[3],x),d(v[2],x)-d(v[1],y)]", "div(v)=d(v[1],x)+d(v[2],y)+d(v[3],z)", "ln(x)=log(x)"]; + defn_str = [ + "version=\"" + version + "\"", + "e=exp(1)", + "i=sqrt(-1)", + "autoexpand=1", + "assumeRealVariables=1", + "trange=[-pi,pi]", + "xrange=[-10,10]", + "yrange=[-10,10]", + "last=0", + "trace=0", + "forceFixedPrintout=1", + "maxFixedPrintoutDigits=20", + "printLeaveEAlone=1", + "printLeaveXAlone=0", + // cross definition + "cross(u,v)=[u[2]*v[3]-u[3]*v[2],u[3]*v[1]-u[1]*v[3],u[1]*v[2]-u[2]*v[1]]", + // curl definition + "curl(v)=[d(v[3],y)-d(v[2],z),d(v[1],z)-d(v[3],x),d(v[2],x)-d(v[1],y)]", + // div definition + "div(v)=d(v[1],x)+d(v[2],y)+d(v[3],z)", + // Note that we use the mathematics / Javascript / Mathematica + // convention that "log" is indeed the natural logarithm. + + // In engineering, biology, astronomy, "log" can stand instead + // for the "common" logarithm i.e. base 10. Also note that Google + // calculations use log for the common logarithm. + "ln(x)=log(x)" + ]; defn = function() { var definitionOfInterest, defn_i, l1, originalCodeGen, ref2; @@ -23353,6 +27386,7 @@ std_symbol("isinteger", ISINTEGER); std_symbol("isprime", ISPRIME); std_symbol("laguerre", LAGUERRE); + // std_symbol("laplace", LAPLACE) std_symbol("lcm", LCM); std_symbol("leading", LEADING); std_symbol("legendre", LEGENDRE); @@ -23427,8 +27461,8 @@ std_symbol("trace", TRACE); std_symbol("forceFixedPrintout", FORCE_FIXED_PRINTOUT); std_symbol("maxFixedPrintoutDigits", MAX_FIXED_PRINTOUT_DIGITS); - std_symbol("~", YYE); - std_symbol("$DRAWX", DRAWX); + std_symbol("~", YYE); // tilde so sort puts it after other symbols + std_symbol("$DRAWX", DRAWX); // special purpose internal symbols std_symbol("$METAA", METAA); std_symbol("$METAB", METAB); std_symbol("$METAX", METAX); @@ -23459,9 +27493,11 @@ std_symbol("$C5", C5); std_symbol("$C6", C6); defineSomeHandyConstants(); + // don't add all these functions to the + // symbolsDependencies, clone the original originalCodeGen = codeGen; codeGen = false; - for (defn_i = l1 = 0, ref2 = defn_str.length; 0 <= ref2 ? l1 < ref2 : l1 > ref2; defn_i = 0 <= ref2 ? ++l1 : --l1) { + for (defn_i = l1 = 0, ref2 = defn_str.length; (0 <= ref2 ? l1 < ref2 : l1 > ref2); defn_i = 0 <= ref2 ? ++l1 : --l1) { definitionOfInterest = defn_str[defn_i]; scan(definitionOfInterest); if (DEBUG) { @@ -23472,16 +27508,18 @@ Eval(); pop(); } + // restore the symbol dependencies as they were before. return codeGen = originalCodeGen; }; defineSomeHandyConstants = function() { push_integer(0); - zero = pop(); + zero = pop(); // must be untagged in gc push_integer(1); - one = pop(); + one = pop(); // must be untagged in gc push_double(1.0); one_as_double = pop(); + // i is the square root of -1 i.e. -1 ^ 1/2 push_symbol(POWER); if (DEBUG) { console.log(print_list(stack[tos - 1])); @@ -23498,13 +27536,23 @@ if (DEBUG) { console.log(print_list(stack[tos - 1])); } - return imaginaryunit = pop(); + return imaginaryunit = pop(); // must be untagged in gc }; + // Bignum compare + + // returns + + // -1 a < b + + // 0 a = b + + // 1 a > b mcmp = function(a, b) { return a.compare(b); }; + // a is a bigint, n is a normal int mcmpint = function(a, n) { var b, t; b = bigInt(n); @@ -23524,30 +27572,51 @@ doubleToReasonableString = function(d) { var maxFixedPrintoutDigits, stringRepresentation; + // when generating code, print out + // the standard JS Number printout if (codeGen) { return "" + d; } if (isZeroAtomOrTensor(get_binding(symbol(FORCE_FIXED_PRINTOUT)))) { stringRepresentation = "" + d; + // manipulate the string so that it can be parsed by + // Algebrite (something like 1.23e-123 wouldn't cut it because + // that would be parsed as 1.23*e - 123) if (printMode === PRINTMODE_LATEX) { + // 1.0\mathrm{e}{-10} looks much better than the plain 1.0e-10 if (/\d*\.\d*e.*/gm.test(stringRepresentation)) { stringRepresentation = stringRepresentation.replace(/e(.*)/gm, "\\mathrm{e}{$1}"); } else { + // if there is no dot in the mantissa, add it so we see it's + // a double and not a perfect number + // e.g. 1e-10 becomes 1.0\mathrm{e}{-10} stringRepresentation = stringRepresentation.replace(/(\d+)e(.*)/gm, "$1.0\\mathrm{e}{$2}"); } } else { if (/\d*\.\d*e.*/gm.test(stringRepresentation)) { stringRepresentation = stringRepresentation.replace(/e(.*)/gm, "*10^($1)"); } else { + // if there is no dot in the mantissa, add it so we see it's + // a double and not a perfect number + // e.g. 1e-10 becomes 1.0e-10 stringRepresentation = stringRepresentation.replace(/(\d+)e(.*)/gm, "$1.0*10^($2)"); } } } else { push(get_binding(symbol(MAX_FIXED_PRINTOUT_DIGITS))); maxFixedPrintoutDigits = pop_integer(); + //console.log "maxFixedPrintoutDigits: " + maxFixedPrintoutDigits + //console.log "type: " + typeof(maxFixedPrintoutDigits) + //console.log "toFixed: " + d.toFixed(maxFixedPrintoutDigits) stringRepresentation = "" + d.toFixed(maxFixedPrintoutDigits); + // remove any trailing zeroes after the dot + // see https://stackoverflow.com/questions/26299160/using-regex-how-do-i-remove-the-trailing-zeros-from-a-decimal-number stringRepresentation = stringRepresentation.replace(/(\.\d*?[1-9])0+$/gm, "$1"); + // in case there are only zeroes after the dot, removes the dot too stringRepresentation = stringRepresentation.replace(/\.0+$/gm, ""); + // we actually want to give a hint to user that + // it's a double, so add a trailing ".0" if there + // is no decimal point if (stringRepresentation.indexOf(".") === -1) { stringRepresentation += ".0"; } @@ -23558,8 +27627,10 @@ return stringRepresentation; }; + // does nothing clear_term = function() {}; + // s is a string here anyways isspace = function(s) { if (s == null) { return false; @@ -23578,6 +27649,7 @@ if (str == null) { return false; } + //Check for non-alphabetic characters and space return str.search(/[^A-Za-z]/) === -1; }; @@ -23585,6 +27657,7 @@ if (str == null) { return false; } + //Check for non-alphabetic characters and space return str.search(/[^A-Za-z_]/) === -1; }; @@ -23616,6 +27689,10 @@ return n; }; + // this probably works out to be + // more general than just counting symbols, it can + // probably count instances of anything you pass as + // first argument but didn't try it. countOccurrencesOfSymbol = function(needle, p) { var n; n = 0; @@ -23632,11 +27709,13 @@ return n; }; + // returns the total number of elements + // in an expression countsize = function(p) { var i, l1, n, ref2; n = 0; if (istensor(p)) { - for (i = l1 = 0, ref2 = p.tensor.nelem; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p.tensor.nelem; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { n += p.tensor.elem[i]; } } else if (iscons(p)) { @@ -23650,16 +27729,55 @@ return n; }; + //jmp_buf stop_return, draw_stop_return + + // s is a string here stop = function(s) { var message; + //if (draw_flag == 2) + // longjmp(draw_stop_return, 1) + //else errorMessage += "Stop: "; errorMessage += s; + //debugger message = errorMessage; errorMessage = ''; moveTos(0); throw new Error(message); }; + + //longjmp(stop_return, 1) + + // Figuring out dependencies is key to automatically + // generating a method signature when generating JS code + // from algebrite scripts. + // This is important because the user can keep using normal Algebrite + // scripting without special notations. + // Basically the process consists of figuring out + // the "ground variables" that are needed to compute each variable. + // Now there are two ways of doing this: + // * at parse time + // * after running the scripts + // Doing it at parse time means that we can't track simplifications + // canceling-out some variables for example. But on the other side + // it's very quick and the user can somehow see what the signature is + // going to look like (assuming tha code is rather simple), or anyways + // is going to easily make sense of the generated signature. + // Doing it after execution on the other hand would allow us to see + // if some variable cancel-out. But if variables cancel out then + // they might do so according to some run-time behaviour that the user + // might struggle to keep track of. + // So the effort for the user to make sense of the signature in the first case + // is similar to the effort of keeping tab of types in a typed language. + // While in the second case the effort is similar to running the + // code and simplifications in her head. + + // If we just want to compute the dependencies, we don't need to do + // anything costly, we don't "run" the code and we don't simplify + // the code. Just finding the plain dependencies + // TODO change the name of this function, as it doesn't just find the + // dependencies. It also runs it and generates the JS code. findDependenciesInScript = function(stringToBeParsed, dontGenerateCode) { var allReturnedLatexStrings, allReturnedPlainStrings, bodyForReadableSummaryOfGeneratedCode, cyclesDescriptions, deQuotedDep, dependencyInfo, eachDependency, error, generatedBody, generatedCode, i, indexOfEachReplacement, indexOfPartRemainingToBeParsed, inited, key, l1, len, len1, len2, len3, len4, len5, len6, len7, m1, n, n1, newUserSymbol, o1, origPrintMode, originalUserSymbol, parameters, q1, r1, readableSummaryOfGeneratedCode, recursedDependencies, ref2, replacementsFrom, replacementsTo, s1, scriptEvaluation, stringToBeRun, t1, testableString, timeStartFromAlgebra, toBePrinted, u1, userVariablesMentioned, value, variablesWithCycles; if (DEBUG) { @@ -23676,11 +27794,17 @@ allReturnedPlainStrings = ""; allReturnedLatexStrings = ""; n = 0; + // we are going to store the dependencies _of the block as a whole_ + // so all affected variables in the whole block are lumped + // together, and same for the variable that affect those, we + // lump them all together. dependencyInfo = { affectsVariables: [], affectedBy: [] }; stringToBeRun = stringToBeParsed; + // parse the input. This collects the + // dependency information while (1) { try { errorMessage = ""; @@ -23700,6 +27824,7 @@ console.log(error); } errorMessage = error + ""; + //debugger reset_after_error(); break; } @@ -23709,6 +27834,8 @@ indexOfPartRemainingToBeParsed += n; } testableString = ""; + // print out all local dependencies as collected by this + // parsing pass if (DEBUG) { console.log("all local dependencies ----------------"); } @@ -23733,6 +27860,7 @@ testableString += "; "; } testableString += ". "; + // print out the symbols with re-assignments: if (DEBUG) { console.log("Symbols with reassignments ----------------"); } @@ -23745,6 +27873,7 @@ } } testableString += ". "; + // print out the symbols that appear in expressions without assignments if (DEBUG) { console.log("Symbols in expressions without assignments ----------------"); } @@ -23757,11 +27886,14 @@ } } testableString += ". "; + // ALL Algebrite code is affected by any pattern changing dependencyInfo.affectedBy.push("PATTERN_DEPENDENCY"); if (patternHasBeenFound) { dependencyInfo.affectsVariables.push("PATTERN_DEPENDENCY"); testableString += " - PATTERN_DEPENDENCY inserted - "; } + // print out all global dependencies as collected by this + // parsing pass if (DEBUG) { console.log("All dependencies recursively ----------------"); } @@ -23782,6 +27914,7 @@ console.log(error); } errorMessage = error + ""; + //debugger init(); } if (errorMessage === "") { @@ -23820,7 +27953,30 @@ if (DEBUG) { console.log(" code generation:" + key + " is: " + get_binding(usr_symbol(key)).toString()); } + // we really want to make an extra effort + // to generate simplified code, so + // run a "simplify" on the content of each + // variable that we are generating code for. + // Note that the variable + // will still point to un-simplified structures, + // we only simplify the generated code. push(get_binding(usr_symbol(key))); + // Since we go and simplify all variables we meet, + // we have to replace each variable passed as a parameter + // with something entirely new, so that there is no chance + // that it might evoke previous values in the external scope + // as in this case: + // a = 2 + // f(a) = a+1+b + // we don't want 'a' in the body of f to be simplified to 2 + // There are two cases: 1) the variable actually was already in + // the symbol table, in which case there is going to be this new + // one prepended with AVOID_BINDING_TO_EXTERNAL_SCOPE_VALUE, and + // we'll have to remove up this variable later. + // OR 2) the variable wasn't already in the symbol table, in which + // case we directly create this one, which means that we'll have + // to rename it later to the correct name without the prepended + // part. replacementsFrom = []; replacementsTo = []; for (s1 = 0, len6 = recursedDependencies.length; s1 < len6; s1++) { @@ -23847,9 +28003,11 @@ console.log(error); } errorMessage = error + ""; + //debugger init(); } - for (indexOfEachReplacement = t1 = 0, ref2 = replacementsFrom.length; 0 <= ref2 ? t1 < ref2 : t1 > ref2; indexOfEachReplacement = 0 <= ref2 ? ++t1 : --t1) { + for (indexOfEachReplacement = t1 = 0, ref2 = replacementsFrom.length; (0 <= ref2 ? t1 < ref2 : t1 > ref2); indexOfEachReplacement = 0 <= ref2 ? ++t1 : --t1) { + //console.log "replacing back " + replacementsTo[indexOfEachReplacement] + " into: " + replacementsFrom[indexOfEachReplacement] push(replacementsTo[indexOfEachReplacement]); push(replacementsFrom[indexOfEachReplacement]); subst(); @@ -23857,6 +28015,9 @@ clearRenamedVariablesToAvoidBindingToExternalScope(); if (errorMessage === "") { toBePrinted = pop(); + // we have to get all the variables used on the right side + // here. I.e. to print the arguments it's better to look at the + // actual method body after simplification. userVariablesMentioned = []; collectUserSymbols(toBePrinted, userVariablesMentioned); allReturnedPlainStrings = ""; @@ -23872,7 +28033,6 @@ generatedCode += "// " + key + " is part of a cyclic dependency, no code generated."; readableSummaryOfGeneratedCode += "#" + key + " is part of a cyclic dependency, no code generated."; } else { - /* * using this paragraph instead of the following one * creates methods signatures that @@ -23893,9 +28053,13 @@ if recursedDependencies.indexOf(i.substring(1)) == -1 parameters += i.substring(1) + ", " */ + // remove all native functions from the + // parameters as well. userVariablesMentioned = userVariablesMentioned.filter(function(x) { return predefinedSymbolsInGlobalScope_doNotTrackInDependencies.indexOf(x + "") === -1; }); + // remove the variable that are not in the dependency list + // i.e. only allow the variables that are in the dependency list userVariablesMentioned = userVariablesMentioned.filter(function(x) { return recursedDependencies.indexOf(x + "") !== -1 || recursedDependencies.indexOf("\'" + x + "") !== -1; }); @@ -23907,6 +28071,7 @@ parameters += i.printname + ", "; } } + // eliminate the last ", " for printout clarity parameters = parameters.replace(/, $/gm, ""); parameters += ")"; generatedCode += key + " = function " + parameters + " { return ( " + generatedBody + " ); }"; @@ -23925,8 +28090,10 @@ } } } + // eliminate the last new line generatedCode = generatedCode.replace(/\n$/gm, ""); readableSummaryOfGeneratedCode = readableSummaryOfGeneratedCode.replace(/\n$/gm, ""); + // cleanup symbolsDependencies = {}; symbolsHavingReassignments = []; patternHasBeenFound = false; @@ -23943,6 +28110,7 @@ recursiveDependencies = function(variableToBeChecked, arrayWhereDependenciesWillBeAdded, variablesAlreadyFleshedOut, variablesWithCycles, chainBeingChecked, cyclesDescriptions) { var cyclesDescription, i, k, l1, len, len1, m1, ref2; variablesAlreadyFleshedOut.push(variableToBeChecked); + // recursive dependencies can only be descended if the variable is not bound to a parameter if (symbolsDependencies[chainBeingChecked[chainBeingChecked.length - 1]] != null) { if (symbolsDependencies[chainBeingChecked[chainBeingChecked.length - 1]].indexOf("'" + variableToBeChecked) !== -1) { if (DEBUG) { @@ -23956,14 +28124,22 @@ } chainBeingChecked.push(variableToBeChecked); if (symbolsDependencies[variableToBeChecked] == null) { + // end case: the passed variable has no dependencies + // so there is nothing else to do if (arrayWhereDependenciesWillBeAdded.indexOf(variableToBeChecked) === -1) { arrayWhereDependenciesWillBeAdded.push(variableToBeChecked); } return arrayWhereDependenciesWillBeAdded; } else { ref2 = symbolsDependencies[variableToBeChecked]; + // recursion case: we have to dig deeper for (l1 = 0, len = ref2.length; l1 < len; l1++) { i = ref2[l1]; + // check that there is no recursion in dependencies + // we do that by keeping a list of variables that + // have already been "fleshed-out". If we encounter + // any of those "fleshed-out" variables while + // fleshing out, then there is a cycle if (chainBeingChecked.indexOf(i) !== -1) { if (DEBUG) { console.log(" found cycle:"); @@ -23984,18 +28160,26 @@ } cyclesDescription += " ... then " + i + " again"; cyclesDescriptions.push(cyclesDescription); + //if DEBUG then console.log " --> cycle through " + i + // we want to flesh-out i but it's already been + // fleshed-out, just add it to the variables + // with cycles and move on + // todo refactor this, there are two copies of these two lines if (variablesWithCycles.indexOf(i) === -1) { variablesWithCycles.push(i); } } else { + // flesh-out i recursively recursiveDependencies(i, arrayWhereDependenciesWillBeAdded, variablesAlreadyFleshedOut, variablesWithCycles, chainBeingChecked, cyclesDescriptions); chainBeingChecked.pop(); } } + //variablesAlreadyFleshedOut.pop() return arrayWhereDependenciesWillBeAdded; } }; + // parses and runs one statement/expression at a time inited = false; latexErrorSign = "\\rlap{\\large\\color{red}\\bigtriangleup}{\\ \\ \\tiny\\color{red}!}"; @@ -24009,9 +28193,13 @@ theErrorMessage = theErrorMessage.replace("->", "} \\rightarrow \\text{"); theErrorMessage = theErrorMessage.replace("?", "}\\enspace " + latexErrorSign + " \\enspace \\text{"); theErrorMessage = "$$\\text{" + theErrorMessage.replace(/\n/g, "") + "}$$"; + //console.log "theErrorMessage: " + theErrorMessage return theErrorMessage; }; + // there are around a dozen different unicodes that + // represent some sort of middle dot, let's catch the most + // common and turn them into what we can process normaliseDots = function(stringToNormalise) { stringToNormalise = stringToNormalise.replace(new RegExp(String.fromCharCode(8901), 'g'), String.fromCharCode(dotprod_unicode)); stringToNormalise = stringToNormalise.replace(new RegExp(String.fromCharCode(8226), 'g'), String.fromCharCode(dotprod_unicode)); @@ -24023,13 +28211,12 @@ TIMING_DEBUGS = false; - run = function(stringToBeRun, generateLatex) { + run = function(stringToBeRun, generateLatex = false) { var allReturnedLatexStrings, allReturnedPlainStrings, collectedLatexResult, collectedPlainResult, error, errorWhileExecution, i, indexOfPartRemainingToBeParsed, n, stringToBeReturned, theErrorMessage, timeStart, timingDebugWrite; - if (generateLatex == null) { - generateLatex = false; - } timeStart = new Date().getTime(); + //stringToBeRun = stringToBeRun + "\n" stringToBeRun = normaliseDots(stringToBeRun); + //console.log "run running: " + stringToBeRun if (stringToBeRun === "selftest") { selftest(); return; @@ -24045,6 +28232,8 @@ allReturnedLatexStrings = ""; while (1) { try { + // while we can keep scanning commands out of the + // passed input AND we can execute them... errorMessage = ""; check_stack(); n = scan(stringToBeRun.substring(indexOfPartRemainingToBeParsed)); @@ -24055,8 +28244,10 @@ if (PRINTOUTRESULT) { console.log(error); } + //debugger allReturnedPlainStrings += error.message; if (generateLatex) { + //debugger theErrorMessage = turnErrorMessageToLatex(error.message); allReturnedLatexStrings += theErrorMessage; } @@ -24066,12 +28257,24 @@ if (n === 0) { break; } + // if debug mode then print the source text + + //if (equaln(get_binding(symbol(TRACE)), 1)) { + // for (i = 0 i < n i++) + // if (s[i] != '\r') + // printchar(s[i]) + // if (s[n - 1] != '\n') # n is not zero, see above + // printchar('\n') + //} indexOfPartRemainingToBeParsed += n; push(p1); + //debugger errorWhileExecution = false; try { stringsEmittedByUserPrintouts = ""; top_level_eval(); + //console.log "emitted string after top_level_eval(): >" + stringsEmittedByUserPrintouts + "<" + //console.log "allReturnedPlainStrings string after top_level_eval(): >" + allReturnedPlainStrings + "<" p2 = pop(); check_stack(); if (isstr(p2)) { @@ -24082,14 +28285,20 @@ console.log("\n"); } } + // if the return value is nil there isn't much point + // in adding "nil" to the printout if (p2 === symbol(NIL)) { + //collectedPlainResult = stringsEmittedByUserPrintouts collectedPlainResult = stringsEmittedByUserPrintouts; if (generateLatex) { collectedLatexResult = "$$" + stringsEmittedByUserPrintouts + "$$"; } } else { + //console.log "emitted string before collectPlainStringFromReturnValue: >" + stringsEmittedByUserPrintouts + "<" + //console.log "allReturnedPlainStrings string before collectPlainStringFromReturnValue: >" + allReturnedPlainStrings + "<" collectedPlainResult = print_expr(p2); collectedPlainResult += "\n"; + //console.log "collectedPlainResult: >" + collectedPlainResult + "<" if (generateLatex) { collectedLatexResult = "$$" + collectLatexStringFromReturnValue(p2) + "$$"; if (DEBUG) { @@ -24109,6 +28318,7 @@ console.log(collectedPlainResult); } } + //alert collectedPlainResult if (PRINTOUTRESULT) { if (DEBUG) { console.log("display:"); @@ -24187,6 +28397,9 @@ } }; + // cannot reference symbols yet + + // returns nil on stack if no result to print top_level_eval = function() { var evalledArgument, originalArgument, shouldAutoexpand; if (DEBUG) { @@ -24202,16 +28415,24 @@ originalArgument = top(); Eval(); evalledArgument = top(); + // "draw", "for" and "setq" return "nil", there is no result to print if (evalledArgument === symbol(NIL)) { return; } + // update "last" to contain the last result set_binding(symbol(LAST), evalledArgument); if (!isZeroAtomOrTensor(get_binding(symbol(BAKE)))) { bake(); evalledArgument = top(); } + // If user asked explicitly asked to evaluate "i" or "j" and + // they represent the imaginary unit (-1)^(1/2), then + // show (-1)^(1/2). if ((originalArgument === symbol(SYMBOL_I) || originalArgument === symbol(SYMBOL_J)) && isimaginaryunit(evalledArgument)) { + // In all other cases, replace all instances of (-1)^(1/2) in the result + // with the symbol "i" or "j" depending on which one + // represents the imaginary unit } else if (isimaginaryunit(get_binding(symbol(SYMBOL_J)))) { push(imaginaryunit); push_symbol(SYMBOL_J); @@ -24229,7 +28450,14 @@ } }; + // this is called when the whole notebook is re-run + // so we get the chance of clearing the whole state from + // scratch. + // In practice, the state we need to clear that persists + // across blocks are only the patterns, so + // just eject those. clearAlgebraEnvironment = function() { + //console.log "CLEARING clearAlgebraEnvironment =============================================================" return do_clearall(); }; @@ -24238,15 +28466,21 @@ if (DEBUG) { console.log("computeDependenciesFromAlgebra!!!"); } + // return findDependenciesInScript(codeFromAlgebraBlock, true)[6] + + // TODO this part below is duplicated from computeResultsAndJavaScriptFromAlgebra + // ...should refactor. originalcodeFromAlgebraBlock = codeFromAlgebraBlock; keepState = true; called_from_Algebra_block = true; + //console.log "codeFromAlgebraBlock: " + codeFromAlgebraBlock codeFromAlgebraBlock = normaliseDots(codeFromAlgebraBlock); if (!keepState) { userSimplificationsInListForm = []; userSimplificationsInProgramForm = ""; for (l1 = 0, len = userSimplificationsInListForm.length; l1 < len; l1++) { i = userSimplificationsInListForm[l1]; + //console.log "silentpattern(" + car(i) + ","+cdr(i)+")" userSimplificationsInProgramForm += "silentpattern(" + car(i) + "," + car(cdr(i)) + "," + car(cdr(cdr(i))) + ")\n"; } do_clearall(); @@ -24268,7 +28502,7 @@ }; computeResultsAndJavaScriptFromAlgebra = function(codeFromAlgebraBlock) { - var code, dependencyInfo, i, keepState, l1, latexResult, len, len1, m1, originalcodeFromAlgebraBlock, readableSummaryOfCode, ref2, result, stringToBeRun, testableStringIsIgnoredHere, timeStartFromAlgebra, userSimplificationsInProgramForm; + var code, dependencyInfo, i, keepState, l1, latexResult, len, len1, m1, originalcodeFromAlgebraBlock, readableSummaryOfCode, result, stringToBeRun, testableStringIsIgnoredHere, timeStartFromAlgebra, userSimplificationsInProgramForm; originalcodeFromAlgebraBlock = codeFromAlgebraBlock; keepState = true; called_from_Algebra_block = true; @@ -24276,6 +28510,11 @@ if (TIMING_DEBUGS) { console.log(" --------- computeResultsAndJavaScriptFromAlgebra input: " + codeFromAlgebraBlock + " at: " + (new Date())); } + // we start "clean" each time: + // clear all the symbols and then re-define + // the "starting" symbols. + + //console.log "codeFromAlgebraBlock: " + codeFromAlgebraBlock codeFromAlgebraBlock = normaliseDots(codeFromAlgebraBlock); stringToBeRun = codeFromAlgebraBlock; if (DEBUG) { @@ -24291,6 +28530,7 @@ userSimplificationsInProgramForm = ""; for (m1 = 0, len1 = userSimplificationsInListForm.length; m1 < len1; m1++) { i = userSimplificationsInListForm[m1]; + //console.log "silentpattern(" + car(i) + ","+cdr(i)+")" userSimplificationsInProgramForm += "silentpattern(" + car(i) + "," + car(cdr(i)) + "," + car(cdr(cdr(i))) + ")\n"; } do_clearall(); @@ -24299,7 +28539,8 @@ console.log("codeFromAlgebraBlock including patterns: " + codeFromAlgebraBlock); } } - ref2 = findDependenciesInScript(codeFromAlgebraBlock), testableStringIsIgnoredHere = ref2[0], result = ref2[1], code = ref2[2], readableSummaryOfCode = ref2[3], latexResult = ref2[4], errorMessage = ref2[5], dependencyInfo = ref2[6]; + //debugger + [testableStringIsIgnoredHere, result, code, readableSummaryOfCode, latexResult, errorMessage, dependencyInfo] = findDependenciesInScript(codeFromAlgebraBlock); called_from_Algebra_block = false; if (readableSummaryOfCode !== "" || errorMessage !== "") { result += "\n" + readableSummaryOfCode; @@ -24313,15 +28554,24 @@ } latexResult = latexResult.replace(/\n/g, "\n\n"); } + // remove empty results altogether from latex output, which happens + // for example for assignments to variables or + // functions definitions latexResult = latexResult.replace(/\n*/, ""); latexResult = latexResult.replace(/\$\$\$\$\n*/g, ""); code = code.replace(/Math\./g, ""); code = code.replace(/\n/g, "\n\n"); + //console.log "code: " + code + //console.log "result: " + result + //console.log "latexResult: " + latexResult if (TIMING_DEBUGS) { console.log("computeResultsAndJavaScriptFromAlgebra time (total time from notebook and back) for: " + stringToBeRun + " : " + ((new Date().getTime()) - timeStartFromAlgebra) + "ms"); } return { + //code: "// no code generated yet\n//try again later" + //code: "console.log('some passed code is run'); window.something = 1;" code: code, + // TODO temporarily pass latex in place of standard result too result: latexResult, latexResult: latexResult, dependencyInfo: dependencyInfo @@ -24338,8 +28588,28 @@ (typeof exports !== "undefined" && exports !== null ? exports : this).clearAlgebraEnvironment = clearAlgebraEnvironment; + // _______ + // | | <- stack + // | | + // |_______| + // | | <- stack + tos + // | | + // | | + // |_______| + // | | <- frame + // |_______| + // <- stack + TOS + + // The stack grows from low memory towards high memory. This is so that + // multiple expressions can be pushed on the stack and then accessed as an + // array. + + // The frame area holds local variables and grows from high memory towards + // low memory. The frame area makes local variables visible to the garbage + // collector. tos = 0; + // p is a U nil_symbols = 0; push = function(p) { @@ -24349,23 +28619,35 @@ if (p.isZero != null) { debugger; } + //console.log "pushing " + //console.log print_list(p) if (p === symbol(NIL)) { nil_symbols++; if (DEBUG) { console.log("pushing symbol(NIL) #" + nil_symbols); } } + //if nil_symbols == 111 + // debugger if (tos >= frame) { stop("stack overflow"); } return stack[tos++] = p; }; + // returns a U moveTos = function(stackPos) { if (tos <= stackPos) { + // we are moving the stack pointer + // "up" the stack (as if we were doing a push) tos = stackPos; return; } + // we are moving the stack pointer + // "down" the stack i.e. as if we were + // doing a pop, we can zero- + // out all the elements that we pass + // so we can reclaim the memory while (tos > stackPos) { stack[tos] = null; tos--; @@ -24378,6 +28660,8 @@ pop = function() { var elementToBeReturned; + //popsNum++ + //console.log "pop #" + popsNum if (tos === 0) { debugger; stop("stack underflow"); @@ -24386,10 +28670,17 @@ debugger; } elementToBeReturned = stack[--tos]; + + // give a chance to the garbage + // collection to reclaim space + // This is JS-specific, it would + // actually make the C garbage + // collector useless. stack[tos] = null; return elementToBeReturned; }; + // n is an integer push_frame = function(n) { var i, l1, ref2, results; i = 0; @@ -24399,12 +28690,13 @@ stop("frame overflow, circular reference?"); } results = []; - for (i = l1 = 0, ref2 = n; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = n; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { results.push(stack[frame + i] = symbol(NIL)); } return results; }; + // n is an integer pop_frame = function(n) { frame += n; if (frame > TOS) { @@ -24447,16 +28739,21 @@ return frame += 10; }; + // Local U * is OK here because there is no functional path to the garbage collector. swap = function() { var p, q; + //U *p, *q + // p and q are both Us p = pop(); q = pop(); push(p); return push(q); }; + // Local U * is OK here because there is no functional path to the garbage collector. dupl = function() { var p; + //U *p p = pop(); push(p); return push(p); @@ -24474,6 +28771,9 @@ $.pop = pop; + // The symbol table is a simple array of struct U. + + // put symbol at index n Eval_symbolsinfo = function() { var symbolsinfoToBePrinted; symbolsinfoToBePrinted = symbolsinfo(); @@ -24487,7 +28787,7 @@ symbolsinfo = function() { var bindingi, i, l1, ref2, ref3, symbolsinfoToBePrinted, symtabi; symbolsinfoToBePrinted = ""; - for (i = l1 = ref2 = NIL + 1, ref3 = symtab.length; ref2 <= ref3 ? l1 < ref3 : l1 > ref3; i = ref2 <= ref3 ? ++l1 : --l1) { + for (i = l1 = ref2 = NIL + 1, ref3 = symtab.length; (ref2 <= ref3 ? l1 < ref3 : l1 > ref3); i = ref2 <= ref3 ? ++l1 : --l1) { if (symtab[i].printname === "") { if (isSymbolReclaimable[i] === false) { break; @@ -24502,6 +28802,11 @@ return symbolsinfoToBePrinted; }; + // s is a string, n is an int + // TODO: elsewhere when we create a symbol we + // rather prefer to create a new entry. Here we just + // reuse the existing one. If that can never be a problem + // then explain why, otherwise do create a new entry. std_symbol = function(s, n, latexPrint) { var p; p = symtab[n]; @@ -24516,14 +28821,52 @@ } }; + // symbol lookup, or symbol creation if symbol doesn't exist yet + // this happens often from the scanner. When the scanner sees something + // like myVar = 2, it create a tree (SETQ ("myVar" symbol as created/looked up here (2))) + // user-defined functions also have a usr symbol. + + // Note that some symbols like, say, "abs", + // are picked up by the scanner directly as keywords, + // so they are not looked up via this. + // So in fact you could redefine abs to be abs(x) = x + // but still abs would be picked up by the scanner as a particular + // node type and calls to abs() will be always to the "native" abs + + // Also note that some symbols such as "zero" are (strangely) not picked up by + // the scanner as special nodes, rather they are identified as keywords + // (e.g. not redefinable) at time of symbol lookup (in Eval_sym) and + // evalled, where eval has a case for ZERO. + + // Also note that there are a number of symbols, such as a,b,c,x,y,z,... + // that are actually created by std_symbols. + // They are not special node types (like abs), they are normal symbols + // that are looked up, but the advantage is that since they are often + // used internally by algebrite, we create the symbol in advance and + // we can reference the symbol entry in a clean way + // (e.g. symbol(SYMBOL_X)) rather than + // by looking up a string. + + // s is a string usr_symbol = function(s) { var i, l1, ref2; + //console.log "usr_symbol of " + s + //if s == "aaa" + // debugger + + // find either the existing symbol, or if we + // reach an empty symbol (printname == "") then + // re-use that location. i = 0; - for (i = l1 = 0, ref2 = NSYM; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = NSYM; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { if (s === symtab[i].printname) { + // found the symbol return symtab[i]; } if (symtab[i].printname === "") { + // found an entry in the symbol table + // with no printname, exit the loop + // and re-use this location break; } } @@ -24533,11 +28876,19 @@ symtab[i] = new U(); symtab[i].k = SYM; symtab[i].printname = s; + // say that we just created the symbol + // then, binding[the new symbol entry] + // by default points to the symbol. + // So the value of an unassigned symbol will + // be just its name. binding[i] = symtab[i]; isSymbolReclaimable[i] = false; return symtab[i]; }; + // get the symbol's printname + + // p is a U get_printname = function(p) { if (p.k !== SYM) { stop("symbol error"); @@ -24545,13 +28896,20 @@ return p.printname; }; + // p and q are both U + // there are two Us at play here. One belongs to the + // symtab array and is the variable name. + // The other one is the U with the content, and that + // one will go in the corresponding "binding" array entry. set_binding = function(p, q) { var indexFound; if (p.k !== SYM) { stop("symbol error"); } + //console.log "setting binding of " + p.toString() + " to: " + q.toString() + //if p.toString() == "aaa" + // debugger indexFound = symtab.indexOf(p); - /* if indexFound == -1 debugger @@ -24560,7 +28918,7 @@ indexFound = i console.log "remedied an index not found!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" break - */ + */ if (symtab.indexOf(p, indexFound + 1) !== -1) { console.log("ops, more than one element!"); debugger; @@ -24572,13 +28930,16 @@ return binding[indexFound] = q; }; + // p is a U get_binding = function(p) { var indexFound; if (p.k !== SYM) { stop("symbol error"); } + //console.log "getting binding of " + p.toString() + //if p.toString() == "aaa" + // debugger indexFound = symtab.indexOf(p); - /* if indexFound == -1 debugger @@ -24587,7 +28948,7 @@ indexFound = i console.log "remedied an index not found!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" break - */ + */ if (symtab.indexOf(p, indexFound + 1) !== -1) { console.log("ops, more than one element!"); debugger; @@ -24595,21 +28956,31 @@ if (DEBUG) { console.log("lookup >> get_binding lookup " + indexFound); } + //if indexFound == 139 + // debugger + //if indexFound == 137 + // debugger return binding[indexFound]; }; + // the concept of user symbol is a little fuzzy + // beucase mathematics is full of symbols that actually + // have a special meaning, e.g. e,i,I in some cases j... is_usr_symbol = function(p) { var theSymnum; if (p.k !== SYM) { return false; } theSymnum = symnum(p); + // see "defs" file for the naming of the symbols if (theSymnum > PI && theSymnum !== SYMBOL_I && theSymnum !== SYMBOL_IDENTITY_MATRIX) { return true; } return false; }; + // get symbol's number from ptr + // p is U lookupsTotal = 0; symnum = function(p) { @@ -24626,17 +28997,29 @@ if (DEBUG) { console.log("lookup >> symnum lookup " + indexFound + " lookup # " + lookupsTotal); } + //if lookupsTotal == 21 + // debugger + //if indexFound == 79 + // debugger return indexFound; }; + // push indexed symbol + + // k is an int push_symbol = function(k) { return push(symtab[k]); }; clear_symbols = function() { var i, l1, ref2, ref3, results; +// we can clear just what's assignable. +// everything before NIL is not assignable, +// so there is no need to clear it. results = []; - for (i = l1 = ref2 = NIL + 1, ref3 = NSYM; ref2 <= ref3 ? l1 < ref3 : l1 > ref3; i = ref2 <= ref3 ? ++l1 : --l1) { + for (i = l1 = ref2 = NIL + 1, ref3 = NSYM; (ref2 <= ref3 ? l1 < ref3 : l1 > ref3); i = ref2 <= ref3 ? ++l1 : --l1) { + // stop at the first empty + // entry that is not reclaimable if (symtab[i].printname === "") { if (isSymbolReclaimable[i] === false) { break; @@ -24652,11 +29035,12 @@ return results; }; - collectUserSymbols = function(p, accumulator) { + //symtab[i].printname = "" + //binding[i] = symtab[i] + + // collect all the variables in a tree + collectUserSymbols = function(p, accumulator = []) { var i, l1, ref2; - if (accumulator == null) { - accumulator = []; - } if (is_usr_symbol(p)) { if (accumulator.indexOf(p) === -1) { accumulator.push(p); @@ -24664,7 +29048,7 @@ } } if (istensor(p)) { - for (i = l1 = 0, ref2 = p.tensor.nelem; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = p.tensor.nelem; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { collectUserSymbols(p.tensor.elem[i], accumulator); } return; @@ -24695,6 +29079,7 @@ parse_internal = function(argu) { if (typeof argu === 'string') { return scan(argu); + // now its in the stack } else if (typeof argu === 'number') { if (argu % 1 === 0) { return push_integer(argu); @@ -24702,6 +29087,7 @@ return push_double(argu); } } else if (argu instanceof U) { + // hey look its a U return push(argu); } else { console.warn('unknown argument type', argu); @@ -24723,9 +29109,11 @@ return data; }; - exec = function() { - var argu, argus, error, fn, l1, len, name, result; - name = arguments[0], argus = 2 <= arguments.length ? slice.call(arguments, 1) : []; + // exec handles the running ia JS of all the algebrite + // functions. The function name is passed in "name" and + // the corresponding function is pushed at the top of the stack + exec = function(name, ...argus) { + var argu, error, fn, l1, len, result; fn = get_binding(usr_symbol(name)); check_stack(); push(fn); @@ -24769,20 +29157,27 @@ frozenContents = []; frozenPatterns = []; frozenHash = ""; - for (i = l1 = 0, ref2 = symtab.length; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + for (i = l1 = 0, ref2 = symtab.length; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { + //if symtab[i].printname == "" + // if isSymbolReclaimable[i] == false + // break + // else + // continue if (isSymbolReclaimable[i] === false) { frozenSymbols.push(symtab[i]); frozenContents.push(binding[i]); } } + // just clone them frozenPatterns = userSimplificationsInListForm.slice(0); return [frozenSymbols, frozenContents, frozenPatterns, zero, one, imaginaryunit, getStateHash()]; }; unfreeze = function(frozen) { var frozenContents, frozenPatterns, frozenSymbols, i, l1, ref2; - frozenSymbols = frozen[0], frozenContents = frozen[1], frozenPatterns = frozen[2], zero = frozen[3], one = frozen[4], imaginaryunit = frozen[5]; - for (i = l1 = 0, ref2 = frozenSymbols.length; 0 <= ref2 ? l1 < ref2 : l1 > ref2; i = 0 <= ref2 ? ++l1 : --l1) { + [frozenSymbols, frozenContents, frozenPatterns, zero, one, imaginaryunit] = frozen; +//clear_symbols() + for (i = l1 = 0, ref2 = frozenSymbols.length; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { symtab[i] = frozenSymbols[i]; binding[i] = frozenContents[i]; } @@ -24802,7 +29197,7 @@ getStateHash = function() { var bindingi, frozenHash, i, l1, len, m1, ref2, ref3, symtabi; frozenHash = ""; - for (i = l1 = ref2 = NIL + 1, ref3 = symtab.length; ref2 <= ref3 ? l1 < ref3 : l1 > ref3; i = ref2 <= ref3 ? ++l1 : --l1) { + for (i = l1 = ref2 = NIL + 1, ref3 = symtab.length; (ref2 <= ref3 ? l1 < ref3 : l1 > ref3); i = ref2 <= ref3 ? ++l1 : --l1) { if (symtab[i].printname === "") { if (isSymbolReclaimable[i] === false) { break; diff --git a/runtime/init.coffee b/runtime/init.coffee index 2fac86a7..aa5ef32a 100644 --- a/runtime/init.coffee +++ b/runtime/init.coffee @@ -68,7 +68,7 @@ defn_str = [ "last=0", "trace=0", "forceFixedPrintout=1", - "maxFixedPrintoutDigits=6", + "maxFixedPrintoutDigits=20", "printLeaveEAlone=1", "printLeaveXAlone=0", # cross definition