(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_MULTIPLY, DEBUG_POWER, DEBUG_RATIONALIZE, DEBUG_RECT, DEBUG_SIMPLIFY, 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, 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, isminussqrtthreeovertwo, 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, issqrtthree, issqrtthreeovertwo, 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_integer, 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, performing_roots, polar, polarRectAMinusOneBase, 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; bigInt = require('big-integer'); // also change the version in the package.json file version = "1.4.0"; SELFTEST = 1; // size of the symbol table NSYM = 1000; DEBUG = false; PRINTOUTRESULT = false; // printing-related constants PRINTMODE_LATEX = "PRINTMODE_LATEX"; PRINTMODE_2DASCII = "PRINTMODE_2DASCII"; PRINTMODE_COMPUTER = "PRINTMODE_COMPUTER"; PRINTMODE_HUMAN = "PRINTMODE_HUMAN"; PRINTMODE_LIST = "PRINTMODE_LIST"; // when the user uses the generic "print" statement // this setting kicks-in. printMode = PRINTMODE_COMPUTER; dontCreateNewRadicalsInDenominatorWhenEvalingMultiplication = true; recursionLevelNestedRadicalsRemoval = 0; do_simplify_nested_radicals = true; avoidCalculatingPowersIntoArctans = true; rational = (function() { // 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() { 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 = ""; U.prototype.str = ""; U.prototype.tensor = null; // rational number a over b U.prototype.q = null; // will point to a rational U.prototype.d = 0.0; // a double U.prototype.k = 0; U.prototype.tag = 0; return U; }).call(this); errorMessage = ""; // the following enum is for struct U, member k CONS = 0; NUM = 1; DOUBLE = 2; STR = 3; TENSOR = 4; SYM = 5; // the following enum is for indexing the symbol table // standard functions first, then nil, then everything else counter = 0; ABS = counter++; ADD = counter++; ADJ = counter++; AND = counter++; APPROXRATIO = counter++; ARCCOS = counter++; ARCCOSH = counter++; ARCSIN = counter++; ARCSINH = counter++; ARCTAN = counter++; ARCTANH = counter++; ARG = counter++; ATOMIZE = counter++; BESSELJ = counter++; BESSELY = counter++; BINDING = counter++; BINOMIAL = counter++; CEILING = counter++; CHECK = counter++; CHOOSE = counter++; CIRCEXP = counter++; CLEAR = counter++; CLEARALL = counter++; CLEARPATTERNS = counter++; CLOCK = counter++; COEFF = counter++; COFACTOR = counter++; CONDENSE = counter++; CONJ = counter++; CONTRACT = counter++; COS = counter++; COSH = counter++; DECOMP = counter++; DEFINT = counter++; DEGREE = counter++; DENOMINATOR = counter++; DERIVATIVE = counter++; DET = counter++; DIM = counter++; DIRAC = counter++; DIVISORS = counter++; DO = counter++; DOT = counter++; DRAW = counter++; DSOLVE = counter++; EIGEN = counter++; EIGENVAL = counter++; EIGENVEC = counter++; ERF = counter++; ERFC = counter++; EVAL = counter++; EXP = counter++; EXPAND = counter++; EXPCOS = counter++; EXPSIN = counter++; FACTOR = counter++; FACTORIAL = counter++; FACTORPOLY = counter++; FILTER = counter++; FLOATF = counter++; FLOOR = counter++; FOR = counter++; FUNCTION = counter++; GAMMA = counter++; GCD = counter++; HERMITE = counter++; HILBERT = counter++; IMAG = counter++; INDEX = counter++; INNER = counter++; INTEGRAL = counter++; INV = counter++; INVG = counter++; ISINTEGER = counter++; ISPRIME = counter++; LAGUERRE = counter++; // LAPLACE = counter++ LCM = counter++; LEADING = counter++; LEGENDRE = counter++; LOG = counter++; LOOKUP = counter++; MOD = counter++; MULTIPLY = counter++; NOT = counter++; NROOTS = counter++; NUMBER = counter++; NUMERATOR = counter++; OPERATOR = counter++; OR = counter++; OUTER = counter++; PATTERN = counter++; PATTERNSINFO = counter++; POLAR = counter++; POWER = counter++; PRIME = counter++; PRINT_LEAVE_E_ALONE = counter++; PRINT_LEAVE_X_ALONE = counter++; PRINT = counter++; PRINT2DASCII = counter++; PRINTFULL = counter++; PRINTLATEX = counter++; PRINTLIST = counter++; PRINTPLAIN = counter++; PRODUCT = counter++; QUOTE = counter++; QUOTIENT = counter++; RANK = counter++; RATIONALIZE = counter++; REAL = counter++; ROUND = counter++; YYRECT = counter++; ROOTS = counter++; SETQ = counter++; SGN = counter++; SILENTPATTERN = counter++; SIMPLIFY = counter++; SIN = counter++; SINH = counter++; SHAPE = counter++; SQRT = counter++; STOP = counter++; SUBST = counter++; SUM = counter++; SYMBOLSINFO = counter++; TAN = counter++; TANH = counter++; TAYLOR = counter++; TEST = counter++; TESTEQ = counter++; TESTGE = counter++; TESTGT = counter++; TESTLE = counter++; TESTLT = counter++; TRANSPOSE = counter++; UNIT = counter++; ZERO = 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++; LAST_PRINT = counter++; LAST_2DASCII_PRINT = counter++; LAST_FULL_PRINT = counter++; LAST_LATEX_PRINT = counter++; LAST_LIST_PRINT = counter++; LAST_PLAIN_PRINT = counter++; AUTOEXPAND = counter++; BAKE = counter++; ASSUME_REAL_VARIABLES = counter++; TRACE = counter++; FORCE_FIXED_PRINTOUT = counter++; MAX_FIXED_PRINTOUT_DIGITS = counter++; YYE = counter++; DRAWX = counter++; // special purpose internal symbols METAA = counter++; METAB = counter++; METAX = counter++; SECRETX = counter++; VERSION = counter++; PI = counter++; SYMBOL_A = counter++; SYMBOL_B = counter++; SYMBOL_C = counter++; SYMBOL_D = counter++; SYMBOL_I = counter++; SYMBOL_J = counter++; SYMBOL_N = counter++; SYMBOL_R = counter++; SYMBOL_S = counter++; SYMBOL_T = counter++; SYMBOL_X = counter++; SYMBOL_Y = counter++; SYMBOL_Z = counter++; SYMBOL_IDENTITY_MATRIX = counter++; SYMBOL_A_UNDERSCORE = counter++; SYMBOL_B_UNDERSCORE = counter++; SYMBOL_X_UNDERSCORE = counter++; C1 = counter++; C2 = counter++; C3 = counter++; C4 = counter++; C5 = counter++; C6 = 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; MAX_PROGRAM_SIZE = 100001; MAXPRIMETAB = 10000; MAX_CONSECUTIVE_APPLICATIONS_OF_ALL_RULES = 5; 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 = []; symbolsInExpressionsWithoutAssignments = []; patternHasBeenFound = false; 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() { 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.ndim = 0; // number of dimensions tensor.prototype.dim = null; // dimension length, for each dimension tensor.prototype.nelem = 0; // total number of elements tensor.prototype.elem = null; // an array containing all the data return tensor; }).call(this); display = (function() { class display {}; display.prototype.h = 0; display.prototype.w = 0; display.prototype.n = 0; display.prototype.a = []; // will contain an array of c,x,y (color,x,y) return display; }).call(this); text_metric = (function() { class text_metric {}; text_metric.prototype.ascent = 0; text_metric.prototype.descent = 0; text_metric.prototype.width = 0; return text_metric; }).call(this); tos = 0; // top of stack expanding = 0; evaluatingAsFloats = 0; evaluatingPolar = 0; fmt_x = 0; fmt_index = 0; fmt_level = 0; verbosing = 0; primetab = (function() { var ceil, i, j, primes; primes = [2]; i = 3; while (primes.length < MAXPRIMETAB) { j = 0; ceil = Math.sqrt(i); while (j < primes.length && primes[j] <= ceil) { if (i % primes[j] === 0) { j = -1; break; } j++; } if (j !== -1) { primes.push(i); } i += 2; } primes[MAXPRIMETAB] = 0; return primes; })(); esc_flag = 0; draw_flag = 0; mtotal = 0; trigmode = 0; logbuf = ""; 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 = []; // will contain U stack = []; // will contain *U frame = 0; p0 = null; // will contain U p1 = null; // will contain U p2 = null; // will contain U p3 = null; // will contain U p4 = null; // will contain U p5 = null; // will contain U p6 = null; // will contain U p7 = null; // will contain U p8 = null; // will contain U p9 = null; // will contain U zero = null; // will contain U one = null; // will contain U one_as_double = null; imaginaryunit = null; // will contain U out_buf = ""; out_count = 0; test_flag = 0; codeGen = false; draw_stop_return = null; // extern jmp_buf ????? userSimplificationsInListForm = []; userSimplificationsInStringForm = []; transpose_unicode = 7488; dotprod_unicode = 183; symbol = function(x) { return symtab[x]; }; iscons = function(p) { return p.k === CONS; }; isrational = function(p) { return p.k === NUM; }; isdouble = function(p) { return p.k === DOUBLE; }; isNumericAtom = function(p) { return isrational(p) || isdouble(p); }; isstr = function(p) { return p.k === STR; }; istensor = function(p) { if (p == null) { debugger; } else { return p.k === TENSOR; } }; // 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) { if (!isNumericAtomOrTensor(a[i])) { //console.log "non-numeric element: " + a[i] return 0; } } return 1; }; issymbol = function(p) { return p.k === SYM; }; iskeyword = function(p) { return issymbol(p) && symnum(p) < NIL; }; car = function(p) { if (iscons(p)) { return p.cons.car; } else { return symbol(NIL); } }; cdr = function(p) { if (iscons(p)) { return p.cons.cdr; } else { return symbol(NIL); } }; caar = function(p) { return car(car(p)); }; cadr = function(p) { return car(cdr(p)); }; cdar = function(p) { return cdr(car(p)); }; cddr = function(p) { return cdr(cdr(p)); }; caadr = function(p) { return car(car(cdr(p))); }; caddr = function(p) { return car(cdr(cdr(p))); }; cadar = function(p) { return car(cdr(car(p))); }; cdadr = function(p) { return cdr(car(cdr(p))); }; cddar = function(p) { return cdr(cdr(car(p))); }; cdddr = function(p) { return cdr(cdr(cdr(p))); }; caaddr = function(p) { return car(car(cdr(cdr(p)))); }; cadadr = function(p) { return car(cdr(car(cdr(p)))); }; caddar = function(p) { return car(cdr(cdr(car(p)))); }; cdaddr = function(p) { return cdr(car(cdr(cdr(p)))); }; cadddr = function(p) { return car(cdr(cdr(cdr(p)))); }; cddddr = function(p) { return cdr(cdr(cdr(cdr(p)))); }; caddddr = function(p) { return car(cdr(cdr(cdr(cdr(p))))); }; cadaddr = function(p) { return car(cdr(car(cdr(cdr(p))))); }; cddaddr = function(p) { return cdr(cdr(car(cdr(cdr(p))))); }; caddadr = function(p) { return car(cdr(cdr(car(cdr(p))))); }; cdddaddr = function(p) { return cdr(cdr(cdr(car(cdr(cdr(p)))))); }; caddaddr = function(p) { return car(cdr(cdr(car(cdr(cdr(p)))))); }; // not used yet listLength = function(p) { var startCount; startCount = -1; while (iscons(p)) { p = cdr(p); startCount++; } return startCount; }; // not used yet nthCadr = function(p, n) { var startCount; startCount = 0; while (startCount <= n) { p = cdr(p); startCount++; } return car(p); }; isadd = function(p) { return car(p) === symbol(ADD); }; ismultiply = function(p) { return car(p) === symbol(MULTIPLY); }; ispower = function(p) { return car(p) === symbol(POWER); }; isfactorial = function(p) { return car(p) === symbol(FACTORIAL); }; isinnerordot = function(p) { return (car(p) === symbol(INNER)) || (car(p) === symbol(DOT)); }; istranspose = function(p) { return car(p) === symbol(TRANSPOSE); }; isinv = function(p) { 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); }; MSIGN = function(p) { if (p.isPositive()) { return 1; } else if (p.isZero()) { return 0; } else { return -1; } }; MLENGTH = function(p) { return p.toString().length; }; MZERO = function(p) { return p.isZero(); }; MEQUAL = function(p, n) { if (p == null) { debugger; } return p.equals(n); }; reset_after_error = function() { moveTos(0); esc_flag = 0; draw_flag = 0; frame = TOS; evaluatingAsFloats = 0; return evaluatingPolar = 0; }; $ = typeof exports !== "undefined" && exports !== null ? exports : this; $.version = version; $.isadd = isadd; $.ismultiply = ismultiply; $.ispower = ispower; $.isfactorial = isfactorial; $.car = car; $.cdr = cdr; $.caar = caar; $.cadr = cadr; $.cdar = cdar; $.cddr = cddr; $.caadr = caadr; $.caddr = caddr; $.cadar = cadar; $.cdadr = cdadr; $.cddar = cddar; $.cdddr = cdddr; $.caaddr = caaddr; $.cadadr = cadadr; $.caddar = caddar; $.cdaddr = cdaddr; $.cadddr = cadddr; $.cddddr = cddddr; $.caddddr = caddddr; $.cadaddr = cadaddr; $.cddaddr = cddaddr; $.caddadr = caddadr; $.cdddaddr = cdddaddr; $.caddaddr = caddaddr; $.symbol = symbol; $.iscons = iscons; $.isrational = isrational; $.isdouble = isdouble; $.isNumericAtom = isNumericAtom; $.isstr = isstr; $.istensor = istensor; $.issymbol = issymbol; $.iskeyword = iskeyword; $.CONS = CONS; $.NUM = NUM; $.DOUBLE = DOUBLE; $.STR = STR; $.TENSOR = TENSOR; $.SYM = SYM; //(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() { push(cadr(p1)); Eval(); return abs(); }; absValFloat = function() { Eval(); absval(); Eval(); 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(); if (DEBUG_ABS) { console.trace(">>>> ABS of " + theArgument); } numerator(); if (DEBUG_ABS) { console.log("ABS numerator " + stack[tos - 1]); } absval(); if (DEBUG_ABS) { console.log("ABSVAL numerator: " + stack[tos - 1]); } push(theArgument); denominator(); if (DEBUG_ABS) { console.log("ABS denominator: " + stack[tos - 1]); } absval(); if (DEBUG_ABS) { console.log("ABSVAL denominator: " + stack[tos - 1]); } divide(); if (DEBUG_ABS) { console.log("ABSVAL divided: " + stack[tos - 1]); } if (DEBUG_ABS) { return console.log("<<<<<<< ABS"); } }; absval = function() { var anyFactorsYet, input; save(); p1 = pop(); input = p1; 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"); } push(zero); if (DEBUG_ABS) { console.log(" --> ABS of " + input + " : " + stack[tos - 1]); } restore(); return; } if (isnegativenumber(p1)) { if (DEBUG_ABS) { console.log(" abs: " + p1 + " just a negative"); } push(p1); negate(); restore(); return; } if (ispositivenumber(p1)) { if (DEBUG_ABS) { console.log(" abs: " + p1 + " just a positive"); } push(p1); if (DEBUG_ABS) { console.log(" --> ABS of " + input + " : " + stack[tos - 1]); } restore(); return; } if (p1 === symbol(PI)) { if (DEBUG_ABS) { console.log(" abs: " + p1 + " of PI"); } push(p1); if (DEBUG_ABS) { console.log(" --> ABS of " + input + " : " + stack[tos - 1]); } 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"); } if (DEBUG_ABS) { console.log("abs of a sum"); } // sum push(p1); rect(); // convert polar terms, if any p1 = pop(); push(p1); real(); push_integer(2); power(); push(p1); imag(); push_integer(2); power(); add(); push_rational(1, 2); power(); simplify_trig(); if (DEBUG_ABS) { console.log(" --> ABS of " + input + " : " + stack[tos - 1]); } restore(); return; } if (car(p1) === symbol(POWER) && equaln(cadr(p1), -1)) { 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"); } push_double(1.0); } else { if (DEBUG_ABS) { console.log(" abs: symbolic, so result is 1"); } push_integer(1); } if (DEBUG_ABS) { console.log(" --> ABS of " + input + " : " + stack[tos - 1]); } 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"); } push(cadr(p1)); abs(); push(caddr(p1)); power(); if (DEBUG_ABS) { console.log(" --> ABS of " + input + " : " + stack[tos - 1]); } 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(); if (DEBUG_ABS) { console.log(" --> ABS of " + input + " : " + stack[tos - 1]); } restore(); return; } if (car(p1) === symbol(MULTIPLY)) { if (DEBUG_ABS) { console.log(" abs: " + p1 + " is a product"); } // product anyFactorsYet = false; p1 = cdr(p1); while (iscons(p1)) { push(car(p1)); absval(); if (anyFactorsYet) { multiply(); } anyFactorsYet = true; p1 = cdr(p1); } if (DEBUG_ABS) { console.log(" --> ABS of " + input + " : " + stack[tos - 1]); } restore(); return; } if (car(p1) === symbol(ABS)) { if (DEBUG_ABS) { console.log(" abs: " + p1 + " is abs of a abs"); } // abs of a abs push_symbol(ABS); push(cadr(p1)); list(2); if (DEBUG_ABS) { console.log(" --> ABS of " + input + " : " + stack[tos - 1]); } restore(); return; } /* * Evaluation via zzfloat() * ...while this is in theory a powerful mechanism, I've commented it * out because I've refined this method enough to not need this. * 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 * (or its negation) as the result. 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) if DEBUG_ABS then console.log " --> ABS of " + input + " : " + stack[tos-1] restore() return */ if (istensor(p1)) { absval_tensor(); restore(); return; } if (isnegativeterm(p1) || (car(p1) === symbol(ADD) && isnegativeterm(cadr(p1)))) { push(p1); negate(); p1 = pop(); } if (DEBUG_ABS) { console.log(" abs: " + p1 + " is nothing decomposable"); } push_symbol(ABS); push(p1); list(2); if (DEBUG_ABS) { console.log(" --> ABS of " + input + " : " + stack[tos - 1]); } return restore(); }; // also called the "norm" of a vector absval_tensor = function() { if (p1.tensor.ndim !== 1) { stop("abs(tensor) with tensor rank > 1"); } push(p1); push(p1); conjugate(); inner(); push_rational(1, 2); power(); simplify(); return Eval(); }; flag = 0; Eval_add = function() { var h; h = tos; p1 = cdr(p1); while (iscons(p1)) { push(car(p1)); Eval(); p2 = pop(); push_terms(p2); p1 = cdr(p1); } return add_terms(tos - h); }; // Add n terms, returns one expression on the stack. stackAddsCount = 0; add_terms = function(n) { var h, i, i1, j1, o, ref, ref1, results, s, subsetOfStack; stackAddsCount++; 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) { console.log(print_list(stack[i])); } } for (i = i1 = 0; i1 < 10; i = ++i1) { if (n < 2) { 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)); if (flag === 0) { break; } n = combine_terms(h, n); } moveTos(h + n); switch (n) { case 0: if (evaluatingAsFloats) { push_double(0.0); } else { push(zero); } break; case 1: break; default: list(n); p1 = pop(); push_symbol(ADD); push(p1); cons(); } 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) { 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) { 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)) { p1 = cdr(p1); if (isNumericAtom(car(p1))) { p1 = cdr(p1); if (cdr(p1) === symbol(NIL)) { p1 = car(p1); } } } if (car(p2) === symbol(MULTIPLY)) { p2 = cdr(p2); if (isNumericAtom(car(p2))) { p2 = cdr(p2); if (cdr(p2) === symbol(NIL)) { p2 = car(p2); } } } 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(); p3 = stack[s + i]; p4 = stack[s + i + 1]; if (istensor(p3) && istensor(p4)) { push(p3); push(p4); tensor_plus_tensor(); 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) { stack[s + j] = stack[s + j + 1]; } n--; i--; } i++; continue; } if (istensor(p3) || istensor(p4)) { i++; continue; } if (isNumericAtom(p3) && isNumericAtom(p4)) { push(p3); push(p4); 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) { 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) { stack[s + j] = stack[s + j + 1]; } n--; } i--; i++; continue; } if (isNumericAtom(p3) || isNumericAtom(p4)) { i++; continue; } if (evaluatingAsFloats) { p1 = one_as_double; p2 = one_as_double; } else { p1 = one; p2 = one; } t = 0; if (car(p3) === symbol(MULTIPLY)) { p3 = cdr(p3); t = 1; if (isNumericAtom(car(p3))) { p1 = car(p3); p3 = cdr(p3); if (cdr(p3) === symbol(NIL)) { p3 = car(p3); t = 0; } } } if (car(p4) === symbol(MULTIPLY)) { p4 = cdr(p4); if (isNumericAtom(car(p4))) { p2 = car(p4); p4 = cdr(p4); if (cdr(p4) === symbol(NIL)) { p4 = car(p4); } } } if (!equal(p3, p4)) { i++; continue; } push(p1); push(p2); 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) { stack[s + j] = stack[s + j + 2]; } n -= 2; i--; i++; continue; } push(p1); if (t) { push(symbol(MULTIPLY)); push(p3); cons(); } else { push(p3); } 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) { stack[s + j] = stack[s + j + 1]; } n--; i--; // this i++ is to match the while i++; } return n; }; push_terms = function(p) { var results; if (car(p) === symbol(ADD)) { p = cdr(p); results = []; while (iscons(p)) { push(car(p)); results.push(p = cdr(p)); } return results; } else if (!isZeroAtom(p)) { return push(p); } }; // add two expressions add = function() { var h; save(); p2 = pop(); p1 = pop(); h = tos; push_terms(p1); push_terms(p2); add_terms(tos - h); return restore(); }; add_all = function(k) { var h, i, o, ref, s; i = 0; save(); s = tos - k; h = tos; 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); p1 = pop(); moveTos(tos - k); push(p1); return restore(); }; subtract = function() { negate(); return add(); }; Eval_adj = function() { push(cadr(p1)); Eval(); return adj(); }; adj = function() { var doNothing, i, i1, j, n, o, ref, ref1; i = 0; j = 0; n = 0; save(); p1 = pop(); if (istensor(p1) && p1.tensor.ndim === 2 && p1.tensor.dim[0] === p1.tensor.dim[1]) { doNothing = 1; } else { stop("adj: square matrix expected"); } n = p1.tensor.dim[0]; p2 = alloc_tensor(n * n); 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) { cofactor(p1, n, i, j); p2.tensor.elem[n * j + i] = pop(); // transpose } } push(p2); return restore(); }; Eval_approxratio = function() { var theArgument; theArgument = cadr(p1); push(theArgument); return approxratioRecursive(); }; approxratioRecursive = function() { var i, i1, o, ref, ref1; i = 0; save(); p1 = pop(); 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) { 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) { push(p1.tensor.elem[i]); approxratioRecursive(); p4.tensor.elem[i] = pop(); check_tensor_dimensions(p4); } push(p4); } else if (p1.k === DOUBLE) { push(p1); approxOneRatioOnly(); } else if (iscons(p1)) { push(car(p1)); approxratioRecursive(); push(cdr(p1)); approxratioRecursive(); cons(); } else { push(p1); } return restore(); }; approxOneRatioOnly = function() { var numberOfDigitsAfterTheDot, precision, splitBeforeAndAfterDot, supposedlyTheFloat, theFloat, theRatio; zzfloat(); supposedlyTheFloat = pop(); if (supposedlyTheFloat.k === DOUBLE) { theFloat = supposedlyTheFloat.d; splitBeforeAndAfterDot = theFloat.toString().split("."); if (splitBeforeAndAfterDot.length === 2) { numberOfDigitsAfterTheDot = splitBeforeAndAfterDot[1].length; precision = 1 / Math.pow(10, numberOfDigitsAfterTheDot); theRatio = floatToRatioRoutine(theFloat, precision); push_rational(theRatio[0], theRatio[1]); } else { push_integer(theFloat); } 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; FractionDenominator = void 0; DecimalSign = void 0; Z = void 0; PreviousDenominator = void 0; ScratchValue = void 0; ret = [0, 0]; 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) { DecimalSign = -1.0; } else { DecimalSign = 1.0; } 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; ret[1] = FractionDenominator; return ret; } if (decimal < 1.0e-19) { // X = 0 already taken care of FractionNumerator = DecimalSign; FractionDenominator = 9999999999999999999.0; ret[0] = FractionNumerator; ret[1] = FractionDenominator; return ret; } if (decimal > 1.0e19) { FractionNumerator = 9999999999999999999.0 * DecimalSign; FractionDenominator = 1.0; ret[0] = FractionNumerator; ret[1] = FractionDenominator; return ret; } Z = decimal; PreviousDenominator = 0.0; FractionDenominator = 1.0; while (true) { Z = 1.0 / (Z - Math.floor(Z)); ScratchValue = FractionDenominator; 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; } } FractionNumerator = DecimalSign * FractionNumerator; ret[0] = FractionNumerator; ret[1] = FractionDenominator; return ret; }; approx_just_an_integer = 0; approx_sine_of_rational = 1; approx_sine_of_pi_times_rational = 2; approx_rationalOfPi = 3; approx_radicalOfRatio = 4; approx_nothingUseful = 5; approx_ratioOfRadical = 6; approx_rationalOfE = 7; approx_logarithmsOfRationals = 8; approx_rationalsOfLogarithms = 9; approxRationalsOfRadicals = function(theFloat) { var bestResultSoFar, complexity, error, hypothesis, i, i1, j, len, likelyMultiplier, minimumComplexity, numberOfDigitsAfterTheDot, o, precision, ratio, ref, result, splitBeforeAndAfterDot; splitBeforeAndAfterDot = theFloat.toString().split("."); if (splitBeforeAndAfterDot.length === 2) { numberOfDigitsAfterTheDot = splitBeforeAndAfterDot[1].length; precision = 1 / Math.pow(10, numberOfDigitsAfterTheDot); } else { 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]; } } } } return bestResultSoFar; }; approxRadicalsOfRationals = function(theFloat) { var bestResultSoFar, complexity, error, hypothesis, i, i1, j, len, len1, likelyMultiplier, minimumComplexity, numberOfDigitsAfterTheDot, o, precision, ratio, ref, ref1, result, splitBeforeAndAfterDot; splitBeforeAndAfterDot = theFloat.toString().split("."); if (splitBeforeAndAfterDot.length === 2) { numberOfDigitsAfterTheDot = splitBeforeAndAfterDot[1].length; precision = 1 / Math.pow(10, numberOfDigitsAfterTheDot); } else { 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]; } } } } return bestResultSoFar; }; approxRadicals = function(theFloat) { var approxRadicalsOfRationalsResult, approxRationalsOfRadicalsResult, numberOfDigitsAfterTheDot, precision, splitBeforeAndAfterDot; splitBeforeAndAfterDot = theFloat.toString().split("."); if (splitBeforeAndAfterDot.length === 2) { numberOfDigitsAfterTheDot = splitBeforeAndAfterDot[1].length; precision = 1 / Math.pow(10, numberOfDigitsAfterTheDot); } else { 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; } approxRadicalsOfRationalsResult = approxRadicalsOfRationals(theFloat); if (approxRadicalsOfRationalsResult != null) { return approxRadicalsOfRationalsResult; } return null; }; approxLogs = function(theFloat) { var approxLogsOfRationalsResult, approxRationalsOfLogsResult, numberOfDigitsAfterTheDot, precision, splitBeforeAndAfterDot; splitBeforeAndAfterDot = theFloat.toString().split("."); if (splitBeforeAndAfterDot.length === 2) { numberOfDigitsAfterTheDot = splitBeforeAndAfterDot[1].length; precision = 1 / Math.pow(10, numberOfDigitsAfterTheDot); } else { 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; } approxLogsOfRationalsResult = approxLogsOfRationals(theFloat); if (approxLogsOfRationalsResult != null) { return approxLogsOfRationalsResult; } return null; }; approxRationalsOfLogs = function(theFloat) { var bestResultSoFar, complexity, error, hypothesis, i, i1, j, likelyMultiplier, minimumComplexity, numberOfDigitsAfterTheDot, o, precision, ratio, result, splitBeforeAndAfterDot; splitBeforeAndAfterDot = theFloat.toString().split("."); if (splitBeforeAndAfterDot.length === 2) { numberOfDigitsAfterTheDot = splitBeforeAndAfterDot[1].length; precision = 1 / Math.pow(10, numberOfDigitsAfterTheDot); } else { return ["" + Math.floor(theFloat), approx_just_an_integer, Math.floor(theFloat), 1, 2]; } 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]; } } } } return bestResultSoFar; }; approxLogsOfRationals = function(theFloat) { var bestResultSoFar, complexity, error, hypothesis, i, i1, j, likelyMultiplier, minimumComplexity, numberOfDigitsAfterTheDot, o, precision, ratio, result, splitBeforeAndAfterDot; splitBeforeAndAfterDot = theFloat.toString().split("."); if (splitBeforeAndAfterDot.length === 2) { numberOfDigitsAfterTheDot = splitBeforeAndAfterDot[1].length; precision = 1 / Math.pow(10, numberOfDigitsAfterTheDot); } else { return ["" + Math.floor(theFloat), approx_just_an_integer, Math.floor(theFloat), 1, 2]; } 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]; } } } } return bestResultSoFar; }; approxRationalsOfPowersOfE = function(theFloat) { var bestResultSoFar, complexity, error, hypothesis, i, i1, j, likelyMultiplier, minimumComplexity, numberOfDigitsAfterTheDot, o, precision, ratio, result, splitBeforeAndAfterDot; splitBeforeAndAfterDot = theFloat.toString().split("."); if (splitBeforeAndAfterDot.length === 2) { numberOfDigitsAfterTheDot = splitBeforeAndAfterDot[1].length; precision = 1 / Math.pow(10, numberOfDigitsAfterTheDot); } else { return ["" + Math.floor(theFloat), approx_just_an_integer, Math.floor(theFloat), 1, 2]; } 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]; } } } } return bestResultSoFar; }; approxRationalsOfPowersOfPI = function(theFloat) { var bestResultSoFar, complexity, error, hypothesis, i, i1, j, likelyMultiplier, minimumComplexity, numberOfDigitsAfterTheDot, o, precision, ratio, result, splitBeforeAndAfterDot; splitBeforeAndAfterDot = theFloat.toString().split("."); if (splitBeforeAndAfterDot.length === 2) { numberOfDigitsAfterTheDot = splitBeforeAndAfterDot[1].length; precision = 1 / Math.pow(10, numberOfDigitsAfterTheDot); } else { return ["" + Math.floor(theFloat), approx_just_an_integer, Math.floor(theFloat), 1, 2]; } 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; }; approxTrigonometric = function(theFloat) { var approxSineOfRationalMultiplesOfPIResult, approxSineOfRationalsResult, numberOfDigitsAfterTheDot, precision, splitBeforeAndAfterDot; splitBeforeAndAfterDot = theFloat.toString().split("."); if (splitBeforeAndAfterDot.length === 2) { numberOfDigitsAfterTheDot = splitBeforeAndAfterDot[1].length; precision = 1 / Math.pow(10, numberOfDigitsAfterTheDot); } else { 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; } approxSineOfRationalMultiplesOfPIResult = approxSineOfRationalMultiplesOfPI(theFloat); if (approxSineOfRationalMultiplesOfPIResult != null) { return approxSineOfRationalMultiplesOfPIResult; } return null; }; approxSineOfRationals = function(theFloat) { var bestResultSoFar, complexity, error, fraction, hypothesis, i, i1, j, likelyMultiplier, minimumComplexity, numberOfDigitsAfterTheDot, o, precision, ratio, result, splitBeforeAndAfterDot; splitBeforeAndAfterDot = theFloat.toString().split("."); if (splitBeforeAndAfterDot.length === 2) { numberOfDigitsAfterTheDot = splitBeforeAndAfterDot[1].length; precision = 1 / Math.pow(10, numberOfDigitsAfterTheDot); } else { return ["" + Math.floor(theFloat), approx_just_an_integer, Math.floor(theFloat), 1, 2]; } 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]; } } } } return bestResultSoFar; }; approxSineOfRationalMultiplesOfPI = function(theFloat) { var bestResultSoFar, complexity, error, fraction, hypothesis, i, i1, j, likelyMultiplier, minimumComplexity, numberOfDigitsAfterTheDot, o, precision, ratio, result, splitBeforeAndAfterDot; splitBeforeAndAfterDot = theFloat.toString().split("."); if (splitBeforeAndAfterDot.length === 2) { numberOfDigitsAfterTheDot = splitBeforeAndAfterDot[1].length; precision = 1 / Math.pow(10, numberOfDigitsAfterTheDot); } else { return ["" + Math.floor(theFloat), approx_just_an_integer, Math.floor(theFloat), 1, 2]; } 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]; } } } } return bestResultSoFar; }; approxAll = function(theFloat) { var LOG_EXPLANATIONS, approxLogsResult, approxRadicalsResult, approxRationalsOfPowersOfEResult, approxRationalsOfPowersOfPIResult, approxTrigonometricResult, bestApproxSoFar, constantsSum, constantsSumMin, numberOfDigitsAfterTheDot, precision, splitBeforeAndAfterDot; splitBeforeAndAfterDot = theFloat.toString().split("."); if (splitBeforeAndAfterDot.length === 2) { numberOfDigitsAfterTheDot = splitBeforeAndAfterDot[1].length; precision = 1 / Math.pow(10, numberOfDigitsAfterTheDot); } else { return ["" + Math.floor(theFloat), approx_just_an_integer, Math.floor(theFloat), 1, 2]; } console.log("precision: " + precision); constantsSumMin = Number.MAX_VALUE; constantsSum = 0; bestApproxSoFar = null; LOG_EXPLANATIONS = true; approxRadicalsResult = approxRadicals(theFloat); if (approxRadicalsResult != null) { constantsSum = simpleComplexityMeasure(approxRadicalsResult); if (constantsSum < constantsSumMin) { if (LOG_EXPLANATIONS) { console.log("better explanation by approxRadicals: " + approxRadicalsResult + " complexity: " + constantsSum); } constantsSumMin = constantsSum; bestApproxSoFar = approxRadicalsResult; } else { if (LOG_EXPLANATIONS) { console.log("subpar explanation by approxRadicals: " + approxRadicalsResult + " complexity: " + constantsSum); } } } approxLogsResult = approxLogs(theFloat); if (approxLogsResult != null) { constantsSum = simpleComplexityMeasure(approxLogsResult); if (constantsSum < constantsSumMin) { if (LOG_EXPLANATIONS) { console.log("better explanation by approxLogs: " + approxLogsResult + " complexity: " + constantsSum); } constantsSumMin = constantsSum; bestApproxSoFar = approxLogsResult; } else { if (LOG_EXPLANATIONS) { console.log("subpar explanation by approxLogs: " + approxLogsResult + " complexity: " + constantsSum); } } } approxRationalsOfPowersOfEResult = approxRationalsOfPowersOfE(theFloat); if (approxRationalsOfPowersOfEResult != null) { constantsSum = simpleComplexityMeasure(approxRationalsOfPowersOfEResult); if (constantsSum < constantsSumMin) { if (LOG_EXPLANATIONS) { console.log("better explanation by approxRationalsOfPowersOfE: " + approxRationalsOfPowersOfEResult + " complexity: " + constantsSum); } constantsSumMin = constantsSum; bestApproxSoFar = approxRationalsOfPowersOfEResult; } else { if (LOG_EXPLANATIONS) { console.log("subpar explanation by approxRationalsOfPowersOfE: " + approxRationalsOfPowersOfEResult + " complexity: " + constantsSum); } } } approxRationalsOfPowersOfPIResult = approxRationalsOfPowersOfPI(theFloat); if (approxRationalsOfPowersOfPIResult != null) { constantsSum = simpleComplexityMeasure(approxRationalsOfPowersOfPIResult); if (constantsSum < constantsSumMin) { if (LOG_EXPLANATIONS) { console.log("better explanation by approxRationalsOfPowersOfPI: " + approxRationalsOfPowersOfPIResult + " complexity: " + constantsSum); } constantsSumMin = constantsSum; bestApproxSoFar = approxRationalsOfPowersOfPIResult; } else { if (LOG_EXPLANATIONS) { console.log("subpar explanation by approxRationalsOfPowersOfPI: " + approxRationalsOfPowersOfPIResult + " complexity: " + constantsSum); } } } approxTrigonometricResult = approxTrigonometric(theFloat); if (approxTrigonometricResult != null) { constantsSum = simpleComplexityMeasure(approxTrigonometricResult); if (constantsSum < constantsSumMin) { if (LOG_EXPLANATIONS) { console.log("better explanation by approxTrigonometric: " + approxTrigonometricResult + " complexity: " + constantsSum); } constantsSumMin = constantsSum; bestApproxSoFar = approxTrigonometricResult; } else { if (LOG_EXPLANATIONS) { console.log("subpar explanation by approxTrigonometric: " + approxTrigonometricResult + " complexity: " + constantsSum); } } } return bestApproxSoFar; }; simpleComplexityMeasure = function(aResult, b, c) { 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; case approx_rationalOfE: theSum = Math.pow(3, Math.abs(aResult[3])) * Math.abs(aResult[2]); break; default: theSum = 0; } theSum += Math.abs(aResult[2]) * (Math.abs(aResult[3]) + Math.abs(aResult[4])); } else { theSum += Math.abs(aResult) * (Math.abs(b) + Math.abs(c)); } // heavily discount unit constants if (aResult[2] === 1) { theSum -= 1; } else { theSum += 1; } if (aResult[3] === 1) { theSum -= 1; } else { theSum += 1; } if (aResult[4] === 1) { theSum -= 1; } else { theSum += 1; } if (theSum < 0) { theSum = 0; } return theSum; }; testApprox = function() { var error, fraction, i, i1, i2, i3, j, j1, j2, j3, k3, l1, l2, l3, len, len1, len2, len3, len4, len5, len6, len7, m1, m2, m3, n1, n2, o, o1, o2, originalValue, q1, q2, r1, r2, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, returned, returnedFraction, returnedValue, s1, s2, t1, t2, u1, u2, v1, v2, value, x1, x2, z1, z2; ref = [2, 3, 5, 6, 7, 8, 10]; for (o = 0, len = ref.length; o < len; o++) { i = ref[o]; ref1 = [2, 3, 5, 6, 7, 8, 10]; for (i1 = 0, len1 = ref1.length; i1 < len1; i1++) { j = ref1[i1]; if (i === j) { continue; // this is just 1 } console.log("testapproxRadicals testing: " + "1 * sqrt( " + i + " ) / " + j); fraction = i / j; value = Math.sqrt(i) / j; returned = approxRadicals(value); returnedValue = returned[2] * Math.sqrt(returned[3]) / returned[4]; if (Math.abs(value - returnedValue) > 1e-15) { console.log("fail testapproxRadicals: " + "1 * sqrt( " + i + " ) / " + j + " . obtained: " + returned); } } } ref2 = [2, 3, 5, 6, 7, 8, 10]; for (j1 = 0, len2 = ref2.length; j1 < len2; j1++) { i = ref2[j1]; ref3 = [2, 3, 5, 6, 7, 8, 10]; for (l1 = 0, len3 = ref3.length; l1 < len3; l1++) { j = ref3[l1]; if (i === j) { continue; // this is just 1 } console.log("testapproxRadicals testing with 4 digits: " + "1 * sqrt( " + i + " ) / " + j); fraction = i / j; originalValue = Math.sqrt(i) / j; value = originalValue.toFixed(4); returned = approxRadicals(value); returnedValue = returned[2] * Math.sqrt(returned[3]) / returned[4]; if (Math.abs(originalValue - returnedValue) > 1e-15) { console.log("fail testapproxRadicals with 4 digits: " + "1 * sqrt( " + i + " ) / " + j + " . obtained: " + returned); } } } ref4 = [2, 3, 5, 6, 7, 8, 10]; for (m1 = 0, len4 = ref4.length; m1 < len4; m1++) { i = ref4[m1]; ref5 = [2, 3, 5, 6, 7, 8, 10]; for (n1 = 0, len5 = ref5.length; n1 < len5; n1++) { j = ref5[n1]; if (i === j) { continue; // this is just 1 } console.log("testapproxRadicals testing: " + "1 * sqrt( " + i + " / " + j + " )"); fraction = i / j; value = Math.sqrt(i / j); returned = approxRadicals(value); if (returned != null) { returnedValue = returned[2] * Math.sqrt(returned[3] / returned[4]); if (returned[1] === approx_radicalOfRatio && Math.abs(value - returnedValue) > 1e-15) { console.log("fail testapproxRadicals: " + "1 * sqrt( " + i + " / " + j + " ) . obtained: " + returned); } } } } ref6 = [1, 2, 3, 5, 6, 7, 8, 10]; for (o1 = 0, len6 = ref6.length; o1 < len6; o1++) { i = ref6[o1]; ref7 = [1, 2, 3, 5, 6, 7, 8, 10]; for (q1 = 0, len7 = ref7.length; q1 < len7; q1++) { j = ref7[q1]; if (i === 1 && j === 1) { continue; } console.log("testapproxRadicals testing with 4 digits:: " + "1 * sqrt( " + i + " / " + j + " )"); fraction = i / j; originalValue = Math.sqrt(i / j); value = originalValue.toFixed(4); returned = approxRadicals(value); returnedValue = returned[2] * Math.sqrt(returned[3] / returned[4]); if (returned[1] === approx_radicalOfRatio && Math.abs(originalValue - returnedValue) > 1e-15) { console.log("fail testapproxRadicals with 4 digits:: " + "1 * sqrt( " + i + " / " + j + " ) . obtained: " + returned); } } } for (i = r1 = 1; r1 <= 5; i = ++r1) { for (j = s1 = 1; s1 <= 5; j = ++s1) { console.log("testApproxAll testing: " + "1 * log(" + i + " ) / " + j); fraction = i / j; value = Math.log(i) / j; returned = approxAll(value); returnedValue = returned[2] * Math.log(returned[3]) / returned[4]; if (Math.abs(value - returnedValue) > 1e-15) { console.log("fail testApproxAll: " + "1 * log(" + i + " ) / " + j + " . obtained: " + returned); } } } for (i = t1 = 1; t1 <= 5; i = ++t1) { for (j = u1 = 1; u1 <= 5; j = ++u1) { console.log("testApproxAll testing with 4 digits: " + "1 * log(" + i + " ) / " + j); fraction = i / j; originalValue = Math.log(i) / j; value = originalValue.toFixed(4); returned = approxAll(value); returnedValue = returned[2] * Math.log(returned[3]) / returned[4]; if (Math.abs(originalValue - returnedValue) > 1e-15) { console.log("fail testApproxAll with 4 digits: " + "1 * log(" + i + " ) / " + j + " . obtained: " + returned); } } } for (i = v1 = 1; v1 <= 5; i = ++v1) { for (j = x1 = 1; x1 <= 5; j = ++x1) { console.log("testApproxAll testing: " + "1 * log(" + i + " / " + j + " )"); fraction = i / j; value = Math.log(i / j); returned = approxAll(value); returnedValue = returned[2] * Math.log(returned[3] / returned[4]); if (Math.abs(value - returnedValue) > 1e-15) { console.log("fail testApproxAll: " + "1 * log(" + i + " / " + j + " )" + " . obtained: " + returned); } } } for (i = z1 = 1; z1 <= 5; i = ++z1) { for (j = i2 = 1; i2 <= 5; j = ++i2) { console.log("testApproxAll testing with 4 digits: " + "1 * log(" + i + " / " + j + " )"); fraction = i / j; originalValue = Math.log(i / j); value = originalValue.toFixed(4); returned = approxAll(value); returnedValue = returned[2] * Math.log(returned[3] / returned[4]); if (Math.abs(originalValue - returnedValue) > 1e-15) { console.log("fail testApproxAll with 4 digits: " + "1 * log(" + i + " / " + j + " )" + " . obtained: " + returned); } } } for (i = j2 = 1; j2 <= 2; i = ++j2) { for (j = l2 = 1; l2 <= 12; j = ++l2) { console.log("testApproxAll testing: " + "1 * (e ^ " + i + " ) / " + j); fraction = i / j; value = Math.pow(Math.E, i) / j; returned = approxAll(value); returnedValue = returned[2] * Math.pow(Math.E, returned[3]) / returned[4]; if (Math.abs(value - returnedValue) > 1e-15) { console.log("fail testApproxAll: " + "1 * (e ^ " + i + " ) / " + j + " . obtained: " + returned); } } } for (i = m2 = 1; m2 <= 2; i = ++m2) { for (j = n2 = 1; n2 <= 12; j = ++n2) { console.log("approxRationalsOfPowersOfE testing with 4 digits: " + "1 * (e ^ " + i + " ) / " + j); fraction = i / j; originalValue = Math.pow(Math.E, i) / j; value = originalValue.toFixed(4); returned = approxRationalsOfPowersOfE(value); returnedValue = returned[2] * Math.pow(Math.E, returned[3]) / returned[4]; if (Math.abs(originalValue - returnedValue) > 1e-15) { console.log("fail approxRationalsOfPowersOfE with 4 digits: " + "1 * (e ^ " + i + " ) / " + j + " . obtained: " + returned); } } } for (i = o2 = 1; o2 <= 2; i = ++o2) { for (j = q2 = 1; q2 <= 12; j = ++q2) { console.log("testApproxAll testing: " + "1 * pi ^ " + i + " / " + j); fraction = i / j; value = Math.pow(Math.PI, i) / j; returned = approxAll(value); returnedValue = returned[2] * Math.pow(Math.PI, returned[3]) / returned[4]; if (Math.abs(value - returnedValue) > 1e-15) { console.log("fail testApproxAll: " + "1 * pi ^ " + i + " / " + j + " ) . obtained: " + returned); } } } for (i = r2 = 1; r2 <= 2; i = ++r2) { for (j = s2 = 1; s2 <= 12; j = ++s2) { console.log("approxRationalsOfPowersOfPI testing with 4 digits: " + "1 * pi ^ " + i + " / " + j); fraction = i / j; originalValue = Math.pow(Math.PI, i) / j; value = originalValue.toFixed(4); returned = approxRationalsOfPowersOfPI(value); returnedValue = returned[2] * Math.pow(Math.PI, returned[3]) / returned[4]; if (Math.abs(originalValue - returnedValue) > 1e-15) { console.log("fail approxRationalsOfPowersOfPI with 4 digits: " + "1 * pi ^ " + i + " / " + j + " ) . obtained: " + returned); } } } for (i = t2 = 1; t2 <= 4; i = ++t2) { for (j = u2 = 1; u2 <= 4; j = ++u2) { console.log("testApproxAll testing: " + "1 * sin( " + i + "/" + j + " )"); fraction = i / j; value = Math.sin(fraction); returned = approxAll(value); returnedFraction = returned[3] / returned[4]; returnedValue = returned[2] * Math.sin(returnedFraction); if (Math.abs(value - returnedValue) > 1e-15) { console.log("fail testApproxAll: " + "1 * sin( " + i + "/" + j + " ) . obtained: " + returned); } } } // 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 + " )"); fraction = i / j; originalValue = Math.sin(fraction); value = originalValue.toFixed(5); returned = approxAll(value); if (returned == null) { console.log("fail testApproxAll with 5 digits: " + "1 * sin( " + i + "/" + j + " ) . obtained: undefined "); } returnedFraction = returned[3] / returned[4]; returnedValue = returned[2] * Math.sin(returnedFraction); error = Math.abs(originalValue - returnedValue); if (error > 1e-14) { console.log("fail testApproxAll with 5 digits: " + "1 * sin( " + i + "/" + j + " ) . obtained: " + returned + " error: " + error); } } } // 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 + " )"); fraction = i / j; originalValue = Math.sin(fraction); value = originalValue.toFixed(4); returned = approxAll(value); if (returned == null) { console.log("fail testApproxAll with 4 digits: " + "1 * sin( " + i + "/" + j + " ) . obtained: undefined "); } returnedFraction = returned[3] / returned[4]; returnedValue = returned[2] * Math.sin(returnedFraction); error = Math.abs(originalValue - returnedValue); if (error > 1e-14) { console.log("fail testApproxAll with 4 digits: " + "1 * sin( " + i + "/" + j + " ) . obtained: " + returned + " error: " + error); } } } value = 0; if (approxAll(value)[0] !== "0") { console.log("fail testApproxAll: 0"); } value = 0.0; if (approxAll(value)[0] !== "0") { console.log("fail testApproxAll: 0.0"); } value = 0.00; if (approxAll(value)[0] !== "0") { console.log("fail testApproxAll: 0.00"); } value = 0.000; if (approxAll(value)[0] !== "0") { console.log("fail testApproxAll: 0.000"); } value = 0.0000; if (approxAll(value)[0] !== "0") { console.log("fail testApproxAll: 0.0000"); } value = 1; if (approxAll(value)[0] !== "1") { console.log("fail testApproxAll: 1"); } value = 1.0; if (approxAll(value)[0] !== "1") { console.log("fail testApproxAll: 1.0"); } value = 1.00; if (approxAll(value)[0] !== "1") { console.log("fail testApproxAll: 1.00"); } value = 1.000; if (approxAll(value)[0] !== "1") { console.log("fail testApproxAll: 1.000"); } value = 1.0000; if (approxAll(value)[0] !== "1") { console.log("fail testApproxAll: 1.0000"); } value = 1.00000; if (approxAll(value)[0] !== "1") { console.log("fail testApproxAll: 1.00000"); } value = Math.sqrt(2); if (approxAll(value)[0] !== "1 * sqrt( 2 ) / 1") { console.log("fail testApproxAll: Math.sqrt(2)"); } value = 1.41; 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"); } value = 0.6; if (approxLogs(value)[0] !== "1 * log( 2 ) / 1") { console.log("fail approxLogs: 0.6"); } value = 0.69; if (approxLogs(value)[0] !== "1 * log( 2 ) / 1") { console.log("fail approxLogs: 0.69"); } value = 0.7; if (approxLogs(value)[0] !== "1 * log( 2 ) / 1") { console.log("fail approxLogs: 0.7"); } value = 1.09; if (approxLogs(value)[0] !== "1 * log( 3 ) / 1") { console.log("fail approxLogs: 1.09"); } value = 1.09; if (approxAll(value)[0] !== "1 * log( 3 ) / 1") { console.log("fail approxAll: 1.09"); } value = 1.098; if (approxAll(value)[0] !== "1 * log( 3 ) / 1") { console.log("fail approxAll: 1.098"); } value = 1.1; if (approxAll(value)[0] !== "1 * log( 3 ) / 1") { console.log("fail approxAll: 1.1"); } value = 1.11; if (approxAll(value)[0] !== "1 * log( 3 ) / 1") { console.log("fail approxAll: 1.11"); } value = Math.sqrt(3); if (approxAll(value)[0] !== "1 * sqrt( 3 ) / 1") { console.log("fail testApproxAll: Math.sqrt(3)"); } value = 1.0000; if (approxAll(value)[0] !== "1") { console.log("fail testApproxAll: 1.0000"); } value = 3.141592; if (approxAll(value)[0] !== "1 * (pi ^ 1 ) / 1 )") { console.log("fail testApproxAll: 3.141592"); } value = 31.41592; if (approxAll(value)[0] !== "10 * (pi ^ 1 ) / 1 )") { console.log("fail testApproxAll: 31.41592"); } value = 314.1592; if (approxAll(value)[0] !== "100 * (pi ^ 1 ) / 1 )") { console.log("fail testApproxAll: 314.1592"); } value = 31415926.53589793; if (approxAll(value)[0] !== "10000000 * (pi ^ 1 ) / 1 )") { console.log("fail testApproxAll: 31415926.53589793"); } value = Math.sqrt(2); if (approxTrigonometric(value)[0] !== "2 * sin( 1/4 * pi )") { console.log("fail approxTrigonometric: Math.sqrt(2)"); } value = Math.sqrt(3); if (approxTrigonometric(value)[0] !== "2 * sin( 1/3 * pi )") { console.log("fail approxTrigonometric: Math.sqrt(3)"); } value = (Math.sqrt(6) - Math.sqrt(2)) / 4; if (approxAll(value)[0] !== "1 * sin( 1/12 * pi )") { console.log("fail testApproxAll: (Math.sqrt(6) - Math.sqrt(2))/4"); } value = Math.sqrt(2 - Math.sqrt(2)) / 2; if (approxAll(value)[0] !== "1 * sin( 1/8 * pi )") { console.log("fail testApproxAll: Math.sqrt(2 - Math.sqrt(2))/2"); } value = (Math.sqrt(6) + Math.sqrt(2)) / 4; if (approxAll(value)[0] !== "1 * sin( 5/12 * pi )") { console.log("fail testApproxAll: (Math.sqrt(6) + Math.sqrt(2))/4"); } value = Math.sqrt(2 + Math.sqrt(3)) / 2; if (approxAll(value)[0] !== "1 * sin( 5/12 * pi )") { console.log("fail testApproxAll: Math.sqrt(2 + Math.sqrt(3))/2"); } value = (Math.sqrt(5) - 1) / 4; if (approxAll(value)[0] !== "1 * sin( 1/10 * pi )") { console.log("fail testApproxAll: (Math.sqrt(5) - 1)/4"); } value = Math.sqrt(10 - 2 * Math.sqrt(5)) / 4; 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)"); } value = 1836.15267; if (approxRationalsOfPowersOfPI(value)[0] !== "6 * (pi ^ 5 ) / 1 )") { console.log("fail approxRationalsOfPowersOfPI: 1836.15267"); } for (i = j3 = 1; j3 <= 13; i = ++j3) { for (j = k3 = 1; k3 <= 13; j = ++k3) { 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); if (Math.abs(value - returnedValue) > 1e-15) { console.log("fail approxTrigonometric: " + "1 * sin( " + i + "/" + j + " * pi ) . obtained: " + returned); } } } 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; } console.log("approxTrigonometric testing with 4 digits: " + "1 * sin( " + i + "/" + j + " * pi )"); 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); error = Math.abs(originalValue - returnedValue); if (error > 1e-14) { console.log("fail approxTrigonometric with 4 digits: " + "1 * sin( " + i + "/" + j + " * pi ) . obtained: " + returned + " error: " + error); } } } return console.log("testApprox done"); }; $.approxRadicals = approxRadicals; $.approxRationalsOfLogs = approxRationalsOfLogs; $.approxAll = approxAll; $.testApprox = testApprox; Eval_arccos = function() { push(cadr(p1)); Eval(); return arccos(); }; arccos = function() { var d, errno, n; n = 0; d = 0.0; save(); p1 = pop(); if (car(p1) === symbol(COS)) { push(cadr(p1)); restore(); return; } if (isdouble(p1)) { errno = 0; d = Math.acos(p1.d); if (errno) { stop("arccos function argument is not in the interval [-1,1]"); } push_double(d); 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); } else { push_rational(1, 4); push_symbol(PI); multiply(); } 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); } else { push_rational(3, 4); push_symbol(PI); multiply(); } restore(); return; } // if p1 == sqrt(3)/2 then return 1/6*pi (30 degrees) if (issqrtthreeovertwo(p1)) { if (evaluatingAsFloats) { push_double(Math.PI / 6.0); } else { push_rational(1, 6); push_symbol(PI); multiply(); } restore(); return; } // if p1 == -sqrt(3)/2 then return 5/6*pi (150 degrees) if (isminussqrtthreeovertwo(p1)) { if (evaluatingAsFloats) { push_double(5.0 * Math.PI / 6.0); } else { push_rational(5, 6); push_symbol(PI); multiply(); } restore(); return; } if (!isrational(p1)) { push_symbol(ARCCOS); push(p1); list(2); restore(); return; } push(p1); push_integer(2); multiply(); n = pop_integer(); switch (n) { case -2: if (evaluatingAsFloats) { push_double(Math.PI); } else { push_symbol(PI); } break; case -1: if (evaluatingAsFloats) { push_double(Math.PI * 2.0 / 3.0); } else { push_rational(2, 3); push_symbol(PI); multiply(); } break; case 0: if (evaluatingAsFloats) { push_double(Math.PI / 2.0); } else { push_rational(1, 2); push_symbol(PI); multiply(); } break; case 1: if (evaluatingAsFloats) { push_double(Math.PI / 3.0); } else { push_rational(1, 3); push_symbol(PI); multiply(); } break; case 2: if (evaluatingAsFloats) { push_double(0.0); } else { push(zero); } break; default: push_symbol(ARCCOS); push(p1); list(2); } return restore(); }; Eval_arccosh = function() { push(cadr(p1)); Eval(); return arccosh(); }; arccosh = function() { var d; d = 0.0; save(); p1 = pop(); if (car(p1) === symbol(COSH)) { push(cadr(p1)); restore(); return; } if (isdouble(p1)) { d = p1.d; if (d < 1.0) { stop("arccosh function argument is less than 1.0"); } d = Math.log(d + Math.sqrt(d * d - 1.0)); push_double(d); restore(); return; } if (isplusone(p1)) { push(zero); restore(); return; } push_symbol(ARCCOSH); push(p1); list(2); return restore(); }; Eval_arcsin = function() { push(cadr(p1)); Eval(); return arcsin(); }; arcsin = function() { var d, errno, n; n = 0; d = 0; save(); p1 = pop(); if (car(p1) === symbol(SIN)) { push(cadr(p1)); restore(); return; } if (isdouble(p1)) { errno = 0; d = Math.asin(p1.d); if (errno) { stop("arcsin function argument is not in the interval [-1,1]"); } push_double(d); 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); multiply(); 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); } else { push_rational(-1, 4); push_symbol(PI); multiply(); } restore(); return; } // if p1 == sqrt(3)/2 then return 1/3*pi (60 degrees) if (issqrtthreeovertwo(p1)) { if (evaluatingAsFloats) { push_double(Math.PI / 3.0); } else { push_rational(1, 3); push_symbol(PI); multiply(); } restore(); return; } // if p1 == -sqrt(3)/2 then return -1/3*pi (-60 degrees) if (isminussqrtthreeovertwo(p1)) { if (evaluatingAsFloats) { push_double(-Math.PI / 3.0); } else { push_rational(-1, 3); push_symbol(PI); multiply(); } restore(); return; } if (!isrational(p1)) { push_symbol(ARCSIN); push(p1); list(2); restore(); return; } push(p1); push_integer(2); multiply(); n = pop_integer(); switch (n) { case -2: if (evaluatingAsFloats) { push_double(-Math.PI / 2.0); } else { push_rational(-1, 2); push_symbol(PI); multiply(); } break; case -1: if (evaluatingAsFloats) { push_double(-Math.PI / 6.0); } else { push_rational(-1, 6); push_symbol(PI); multiply(); } break; case 0: if (evaluatingAsFloats) { push_double(0.0); } else { push(zero); } break; case 1: if (evaluatingAsFloats) { push_double(Math.PI / 6.0); } else { push_rational(1, 6); push_symbol(PI); multiply(); } break; case 2: if (evaluatingAsFloats) { push_double(Math.PI / 2.0); } else { push_rational(1, 2); push_symbol(PI); multiply(); } break; default: push_symbol(ARCSIN); push(p1); list(2); } return restore(); }; Eval_arcsinh = function() { push(cadr(p1)); Eval(); return arcsinh(); }; arcsinh = function() { var d; d = 0.0; save(); p1 = pop(); if (car(p1) === symbol(SINH)) { push(cadr(p1)); restore(); return; } if (isdouble(p1)) { d = p1.d; d = Math.log(d + Math.sqrt(d * d + 1.0)); push_double(d); restore(); return; } if (isZeroAtomOrTensor(p1)) { push(zero); restore(); return; } push_symbol(ARCSINH); push(p1); list(2); return restore(); }; Eval_arctan = function() { push(cadr(p1)); Eval(); return arctan(); }; arctan = function() { var d, errno; d = 0; save(); p1 = pop(); if (car(p1) === symbol(TAN)) { push(cadr(p1)); restore(); return; } if (isdouble(p1)) { errno = 0; d = Math.atan(p1.d); if (errno) { stop("arctan function error"); } push_double(d); restore(); return; } if (isZeroAtomOrTensor(p1)) { push(zero); restore(); return; } if (isnegative(p1)) { push(p1); negate(); arctan(); negate(); restore(); return; } // arctan(sin(a) / cos(a)) ? if (Find(p1, symbol(SIN)) && Find(p1, symbol(COS))) { push(p1); numerator(); p2 = pop(); push(p1); denominator(); p3 = pop(); if (car(p2) === symbol(SIN) && car(p3) === symbol(COS) && equal(cadr(p2), cadr(p3))) { push(cadr(p2)); restore(); 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) { push_double(Math.PI); } else { push(symbol(PI)); } multiply(); restore(); return; } // arctan(1) -> pi/4 if (equaln(p1, 1)) { push_rational(1, 4); if (evaluatingAsFloats) { push_double(Math.PI); } else { push(symbol(PI)); } multiply(); 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) { push_double(Math.PI); } else { push(symbol(PI)); } multiply(); restore(); return; } push_symbol(ARCTAN); push(p1); list(2); return restore(); }; Eval_arctanh = function() { push(cadr(p1)); Eval(); return arctanh(); }; arctanh = function() { var d; d = 0.0; save(); p1 = pop(); if (car(p1) === symbol(TANH)) { push(cadr(p1)); restore(); return; } if (isdouble(p1)) { d = p1.d; if (d < -1.0 || d > 1.0) { stop("arctanh function argument is not in the interval [-1,1]"); } d = Math.log((1.0 + d) / (1.0 - d)) / 2.0; push_double(d); restore(); return; } if (isZeroAtomOrTensor(p1)) { push(zero); restore(); return; } push_symbol(ARCTANH); push(p1); list(2); return restore(); }; DEBUG_ARG = false; Eval_arg = function() { push(cadr(p1)); Eval(); return arg(); }; arg = function() { save(); p1 = pop(); push(p1); numerator(); yyarg(); push(p1); denominator(); yyarg(); subtract(); 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); } else { push_integer(0); } } else if (isnegativenumber(p1)) { if (isdouble(p1) || evaluatingAsFloats) { push_double(Math.PI); } else { 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 { push(symbol(PI)); } 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); } if (DEBUG_ARG) { debugger; } push(cadr(p1)); arg(); if (DEBUG_ARG) { console.log(" = 1/2 * " + stack[tos - 1]); } push(caddr(p1)); multiply(); } else if (car(p1) === symbol(MULTIPLY)) { // product of factors push_integer(0); p1 = cdr(p1); while (iscons(p1)) { push(car(p1)); arg(); add(); p1 = cdr(p1); } } else if (car(p1) === symbol(ADD)) { // sum of terms push(p1); rect(); p1 = pop(); push(p1); real(); p2 = pop(); push(p1); imag(); p3 = pop(); if (isZeroAtomOrTensor(p2)) { if (evaluatingAsFloats) { push_double(Math.PI); } else { push(symbol(PI)); } if (isnegative(p3)) { negate(); } } else { push(p3); push(p2); divide(); arctan(); if (isnegative(p2)) { if (evaluatingAsFloats) { push_double(Math.PI); } else { push_symbol(PI); } if (isnegative(p3)) { subtract(); // quadrant 1 -> 3 } else { 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); } } return restore(); }; // pretty print bake = function() { var h, s, t, x, y, z; h = 0; s = 0; t = 0; x = 0; y = 0; z = 0; expanding++; save(); p1 = pop(); s = ispolyexpandedform(p1, symbol(SYMBOL_S)); t = ispolyexpandedform(p1, symbol(SYMBOL_T)); x = ispolyexpandedform(p1, symbol(SYMBOL_X)); y = ispolyexpandedform(p1, symbol(SYMBOL_Y)); z = ispolyexpandedform(p1, symbol(SYMBOL_Z)); if (s === 1 && t === 0 && x === 0 && y === 0 && z === 0) { p2 = symbol(SYMBOL_S); bake_poly(); } else if (s === 0 && t === 1 && x === 0 && y === 0 && z === 0) { p2 = symbol(SYMBOL_T); bake_poly(); } else if (s === 0 && t === 0 && x === 1 && y === 0 && z === 0) { p2 = symbol(SYMBOL_X); bake_poly(); } else if (s === 0 && t === 0 && x === 0 && y === 1 && z === 0) { p2 = symbol(SYMBOL_Y); bake_poly(); } 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)); p1 = cdr(p1); while (iscons(p1)) { push(car(p1)); bake(); p1 = cdr(p1); } list(tos - h); } else { push(p1); } restore(); return expanding--; }; polyform = function() { var h; h = 0; save(); p2 = pop(); p1 = pop(); if (ispolyexpandedform(p1, p2)) { bake_poly(); } else if (iscons(p1)) { h = tos; push(car(p1)); p1 = cdr(p1); while (iscons(p1)) { push(car(p1)); push(p2); polyform(); p1 = cdr(p1); } list(tos - h); } else { push(p1); } return restore(); }; bake_poly = function() { var a, h, i, k, n, o, ref; h = 0; i = 0; k = 0; n = 0; //U **a a = tos; k = coeff(p2, p1); h = tos; for (i = o = ref = k - 1; o >= 0; i = o += -1) { p1 = stack[a + i]; bake_poly_term(i); } n = tos - h; if (n > 1) { list(n); push(symbol(ADD)); swap(); cons(); } p1 = pop(); moveTos(tos - k); return push(p1); }; // p1 points to coefficient of p2 ^ k // k is an int bake_poly_term = function(k) { var h, n; h = 0; n = 0; if (isZeroAtomOrTensor(p1)) { return; } // constant term? if (k === 0) { if (car(p1) === symbol(ADD)) { p1 = cdr(p1); while (iscons(p1)) { push(car(p1)); p1 = cdr(p1); } } else { push(p1); } return; } h = tos; // coefficient if (car(p1) === symbol(MULTIPLY)) { p1 = cdr(p1); while (iscons(p1)) { push(car(p1)); p1 = cdr(p1); } } else if (!equaln(p1, 1)) { push(p1); } // x ^ k if (k === 1) { push(p2); } else { push(symbol(POWER)); push(p2); push_integer(k); list(3); } n = tos - h; if (n > 1) { list(n); push(symbol(MULTIPLY)); swap(); return cons(); } }; Eval_besselj = function() { push(cadr(p1)); Eval(); push(caddr(p1)); Eval(); return besselj(); }; besselj = function() { save(); yybesselj(); return restore(); }; //define X p1 //define N p2 //define SGN p3 yybesselj = function() { var d, n; d = 0.0; n = 0; p2 = pop(); 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); } else { push_integer(2); push_symbol(PI); divide(); } push(p1); divide(); push_rational(1, 2); power(); push(p1); sine(); multiply(); return; } // n = -1/2 if (MEQUAL(p2.q.a, -1)) { if (evaluatingAsFloats) { push_double(2.0 / Math.PI); } else { push_integer(2); push_symbol(PI); divide(); } push(p1); divide(); push_rational(1, 2); power(); push(p1); cosine(); 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); push(p1); divide(); push(p2); push(p3); subtract(); multiply(); push(p1); push(p2); push(p3); subtract(); besselj(); multiply(); push(p1); push(p2); push_integer(2); push(p3); multiply(); subtract(); besselj(); subtract(); return; } //if 0 # test cases needed if (isnegativeterm(p1)) { push(p1); negate(); push(p2); power(); push(p1); push(p2); negate(); power(); multiply(); push_symbol(BESSELJ); push(p1); negate(); push(p2); list(3); multiply(); return; } if (isnegativeterm(p2)) { push_integer(-1); push(p2); power(); push_symbol(BESSELJ); push(p1); push(p2); negate(); list(3); multiply(); return; } //endif push(symbol(BESSELJ)); push(p1); push(p2); return list(3); }; Eval_bessely = function() { push(cadr(p1)); Eval(); push(caddr(p1)); Eval(); return bessely(); }; bessely = function() { save(); yybessely(); return restore(); }; //define X p1 //define N p2 yybessely = function() { var d, n; d = 0.0; n = 0; p2 = pop(); p1 = pop(); push(p2); n = pop_integer(); if (isdouble(p1) && !isNaN(n)) { d = yn(n, p1.d); push_double(d); return; } if (isnegativeterm(p2)) { push_integer(-1); push(p2); power(); push_symbol(BESSELY); push(p1); push(p2); negate(); list(3); multiply(); return; } push_symbol(BESSELY); push(p1); push(p2); 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); }; isSmall = function(a) { 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)); } } return a; }; makeSignSameAs = function(a, b) { if (a.isPositive()) { if (b.isNegative()) { return a.multiply(bigInt(-1)); } } else { // a is negative if (b.isPositive()) { return a.multiply(bigInt(-1)); } } return a; }; makePositive = function(a) { if (a.isNegative()) { return a.multiply(bigInt(-1)); } 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 if (n == MP_MIN_SIZE && mfreecount) p = free_stack[--mfreecount] else p = [] #(unsigned int *) malloc((n + 3) * sizeof (int)) #if (p == 0) * 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] if (array[p] == MP_MIN_SIZE && mfreecount < MP_MAX_FREE) 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 fu = true else * !!! this is FU #MSIGN(p) = 1 fu = true * !!! 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 #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 * just use the bigNum's ge implementation * leaving it here just in case I decide to backport to C * * a >= b ? * and and b arrays of ints, len is an int ge = (a, b, len) -> i = 0 for i in [0...len] if (a[i] == b[i]) continue else break if (a[i] >= b[i]) return 1 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; } save(); p2 = pop(); p1 = pop(); if (isdouble(p1)) { a = p1.d; } else { a = convert_rational_to_double(p1); } if (isdouble(p2)) { b = p2.d; } else { b = convert_rational_to_double(p2); } theResult = a + b; push_double(theResult); return restore(); }; subtract_numbers = function() { var a, b; a = 0.0; b = 0.0; if (isrational(stack[tos - 1]) && isrational(stack[tos - 2])) { qsub(); return; } save(); p2 = pop(); p1 = pop(); if (isdouble(p1)) { a = p1.d; } else { a = convert_rational_to_double(p1); } if (isdouble(p2)) { b = p2.d; } else { b = convert_rational_to_double(p2); } push_double(a - b); return restore(); }; multiply_numbers = function() { var a, b; a = 0.0; b = 0.0; if (isrational(stack[tos - 1]) && isrational(stack[tos - 2])) { qmul(); return; } save(); p2 = pop(); p1 = pop(); if (isdouble(p1)) { a = p1.d; } else { a = convert_rational_to_double(p1); } if (isdouble(p2)) { b = p2.d; } else { b = convert_rational_to_double(p2); } push_double(a * b); return restore(); }; divide_numbers = function() { var a, b; a = 0.0; b = 0.0; if (isrational(stack[tos - 1]) && isrational(stack[tos - 2])) { qdiv(); return; } save(); p2 = pop(); p1 = pop(); if (isZeroAtomOrTensor(p2)) { stop("divide by zero"); } if (isdouble(p1)) { a = p1.d; } else { a = convert_rational_to_double(p1); } if (isdouble(p2)) { b = p2.d; } else { b = convert_rational_to_double(p2); } push_double(a / b); return restore(); }; invert_number = function() { var a, b; //unsigned int *a, *b save(); p1 = pop(); if (isZeroAtomOrTensor(p1)) { stop("divide by zero"); } if (isdouble(p1)) { push_double(1 / p1.d); restore(); return; } a = bigInt(p1.q.a); b = bigInt(p1.q.b); b = makeSignSameAs(b, a); a = setSignTo(a, 1); p1 = new U(); p1.k = NUM; p1.q.a = b; p1.q.b = a; push(p1); 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; y = 0.0; if (isrational(a) && isrational(b)) { return compare_rationals(a, b); } if (isdouble(a)) { x = a.d; } else { x = convert_rational_to_double(a); } if (isdouble(b)) { y = b.d; } else { y = convert_rational_to_double(b); } if (x < y) { return -1; } if (x > y) { return 1; } return 0; }; negate_number = function() { save(); p1 = pop(); if (isZeroAtomOrTensor(p1)) { push(p1); restore(); return; } switch (p1.k) { case NUM: p2 = new U(); p2.k = NUM; p2.q.a = bigInt(p1.q.a.multiply(bigInt.minusOne)); p2.q.b = bigInt(p1.q.b); push(p2); break; case DOUBLE: push_double(-p1.d); break; default: stop("bug caught in mp_negate_number"); } return restore(); }; bignum_truncate = function() { var a; //unsigned int *a save(); p1 = pop(); a = mdiv(p1.q.a, p1.q.b); p1 = new U(); p1.k = NUM; p1.q.a = a; p1.q.b = bigInt(1); push(p1); return restore(); }; mp_numerator = function() { save(); p1 = pop(); if (p1.k !== NUM) { push(one); restore(); return; } p2 = new U(); p2.k = NUM; p2.q.a = bigInt(p1.q.a); p2.q.b = bigInt(1); push(p2); return restore(); }; mp_denominator = function() { save(); p1 = pop(); if (p1.k !== NUM) { push(one); restore(); return; } p2 = new U(); p2.k = NUM; p2.q.a = bigInt(p1.q.b); p2.q.b = bigInt(1); push(p2); 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; a = makeSignSameAs(a, b); b = setSignTo(b, 1); } p1 = new U(); p1.k = NUM; p1.q.a = a; p1.q.b = b; push(p1); 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) { debugger; } quotientAndRemainder = p.q.a.divmod(p.q.b); result = quotientAndRemainder.quotient + quotientAndRemainder.remainder / p.q.b.toJSNumber(); return result; }; // n an integer new_integer = function(n) { var theNewInteger; theNewInteger = new U(); theNewInteger.k = NUM; theNewInteger.q.a = bigInt(n); theNewInteger.q.b = bigInt(1); return theNewInteger; }; // n an integer push_integer = function(n) { if (DEBUG) { console.log("pushing integer " + n); } return push(new_integer(n)); }; // d a double push_double = function(d) { save(); p1 = new U(); p1.k = DOUBLE; p1.d = d; push(p1); return restore(); }; // a,b parts of a rational push_rational = function(a, b) { /* save() p1 = new U() p1.k = NUM p1.q.a = bigInt(a) p1.q.b = bigInt(b) ## FIXME -- normalize ## push(p1) restore() */ var p; p = new U(); p.k = NUM; p.q.a = bigInt(a); p.q.b = bigInt(b); return push(p); }; pop_integer = function() { var n; n = 0/0; save(); p1 = pop(); switch (p1.k) { case NUM: if (isinteger(p1) && isSmall(p1.q.a)) { n = p1.q.a.toJSNumber(); } break; case DOUBLE: if (DEBUG) { console.log("popping integer but double is found"); } if (Math.floor(p1.d) === p1.d) { if (DEBUG) { console.log("...altough it's an integer"); } n = p1.d; } } restore(); return n; }; // p is a U, flag is an int print_double = function(p, flag) { var accumulator, buf; accumulator = ""; buf = doubleToReasonableString(p.d); if (flag === 1 && buf === '-') { accumulator += print_str(buf + 1); } else { accumulator += print_str(buf); } 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; p1.q.a = a; p1.q.b = bigInt(1); push(p1); if (sign_ === '-') { negate(); } 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 = ""; denominatorString = ""; buf = ""; switch (p.k) { case NUM: aAsString = p.q.a.toString(); if (!signed) { if (aAsString[0] === "-") { aAsString = aAsString.substring(1); } } if (printMode === PRINTMODE_LATEX && isfraction(p)) { aAsString = "\\frac{" + aAsString + "}{"; } accumulator += aAsString; if (isfraction(p)) { if (printMode !== PRINTMODE_LATEX) { accumulator += "/"; } denominatorString = p.q.b.toString(); if (printMode === PRINTMODE_LATEX) { denominatorString += "}"; } accumulator += denominatorString; } break; case DOUBLE: aAsString = doubleToReasonableString(p.d); if (!signed) { if (aAsString[0] === "-") { aAsString = aAsString.substring(1); } } accumulator += aAsString; } return accumulator; }; gcd_numbers = function() { 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); p3.q.b = mgcd(p1.q.b, p2.q.b); p3.q.a = setSignTo(p3.q.a, 1); push(p3); return restore(); }; pop_double = function() { var d; d = 0.0; save(); p1 = pop(); switch (p1.k) { case NUM: d = convert_rational_to_double(p1); break; case DOUBLE: d = p1.d; break; default: d = 0.0; } restore(); return d; }; bignum_float = function() { var d; d = 0.0; d = convert_rational_to_double(pop()); return push_double(d); }; //static unsigned int *__factorial(int) // n is an int bignum_factorial = function(n) { save(); p1 = new U(); p1.k = NUM; p1.q.a = __factorial(n); p1.q.b = bigInt(1); push(p1); 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; } 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) { b = bigInt(i); t = mmul(a, b); a = t; } } return a; }; 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(); push(caddr(p1)); Eval(); return binomial(); }; binomial = function() { save(); ybinomial(); return restore(); }; //define N p1 //define K p2 ybinomial = function() { p2 = pop(); p1 = pop(); if (BINOM_check_args() === 0) { push(zero); return; } push(p1); factorial(); push(p2); factorial(); divide(); push(p1); push(p2); subtract(); factorial(); return divide(); }; BINOM_check_args = function() { if (isNumericAtom(p1) && lessp(p1, zero)) { return 0; } else if (isNumericAtom(p2) && lessp(p2, zero)) { return 0; } else if (isNumericAtom(p1) && isNumericAtom(p2) && lessp(p1, p2)) { return 0; } else { return 1; } }; Eval_ceiling = function() { push(cadr(p1)); Eval(); return ceiling(); }; ceiling = function() { save(); yyceiling(); return restore(); }; yyceiling = function() { var d, doNothing; d = 0.0; p1 = pop(); if (!isNumericAtom(p1)) { push_symbol(CEILING); push(p1); list(2); return; } if (isdouble(p1)) { d = Math.ceil(p1.d); push_double(d); return; } if (isinteger(p1)) { push(p1); return; } p3 = new U(); p3.k = NUM; p3.q.a = mdiv(p1.q.a, p1.q.b); p3.q.b = mint(1); push(p3); if (isnegativenumber(p1)) { return doNothing = 1; } else { push_integer(1); return add(); } }; Eval_choose = function() { push(cadr(p1)); Eval(); push(caddr(p1)); Eval(); 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(); p1 = pop(); if (choose_check_args() === 0) { push_integer(0); restore(); return; } push(p1); factorial(); push(p2); factorial(); divide(); push(p1); push(p2); subtract(); factorial(); divide(); return restore(); }; choose_check_args = function() { if (isNumericAtom(p1) && lessp(p1, zero)) { return 0; } else if (isNumericAtom(p2) && lessp(p2, zero)) { return 0; } else if (isNumericAtom(p1) && isNumericAtom(p2) && lessp(p1, p2)) { return 0; } else { return 1; } }; Eval_circexp = function() { push(cadr(p1)); Eval(); circexp(); // normalize return Eval(); }; circexp = function() { var h, i, o, ref; i = 0; h = 0; save(); p1 = pop(); if (car(p1) === symbol(COS)) { push(cadr(p1)); expcos(); restore(); return; } if (car(p1) === symbol(SIN)) { push(cadr(p1)); expsin(); restore(); return; } if (car(p1) === symbol(TAN)) { p1 = cadr(p1); push(imaginaryunit); push(p1); multiply(); exponential(); p2 = pop(); push(imaginaryunit); push(p1); multiply(); negate(); exponential(); p3 = pop(); push(p3); push(p2); subtract(); push(imaginaryunit); multiply(); push(p2); push(p3); add(); divide(); restore(); return; } if (car(p1) === symbol(COSH)) { p1 = cadr(p1); push(p1); exponential(); push(p1); negate(); exponential(); add(); push_rational(1, 2); multiply(); restore(); return; } if (car(p1) === symbol(SINH)) { p1 = cadr(p1); push(p1); exponential(); push(p1); negate(); exponential(); subtract(); push_rational(1, 2); multiply(); restore(); return; } if (car(p1) === symbol(TANH)) { p1 = cadr(p1); push(p1); push_integer(2); multiply(); exponential(); p1 = pop(); push(p1); push_integer(1); subtract(); push(p1); push_integer(1); add(); divide(); restore(); return; } if (iscons(p1)) { h = tos; while (iscons(p1)) { push(car(p1)); circexp(); p1 = cdr(p1); } list(tos - h); restore(); return; } if (p1.k === TENSOR) { 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) { push(p1.tensor.elem[i]); circexp(); p1.tensor.elem[i] = pop(); } push(p1); restore(); return; } push(p1); return restore(); }; Eval_clearall = function() { do_clearall(); return push(symbol(NIL)); }; do_clearall = function() { if (test_flag === 0) { clear_term(); } do_clearPatterns(); clear_symbols(); defn(); 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) { 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]; results.push(isSymbolReclaimable[i] = true); } else { results.push(void 0); } } return results; }; 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 = ""; binding[indexFound] = symtab[indexFound]; isSymbolReclaimable[indexFound] = true; p2 = cdr(p2); } 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) */ // 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() { push(cadr(p1)); Eval(); return clockform(); }; 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); arg(); if (DEBUG_CLOCKFORM) { console.log("clockform: arg of " + p1 + " : " + stack[tos - 1]); } if (evaluatingAsFloats) { push_double(Math.PI); } else { push(symbol(PI)); } divide(); if (DEBUG_CLOCKFORM) { console.log("clockform: divide : " + stack[tos - 1]); } list(3); if (DEBUG_CLOCKFORM) { console.log("clockform: power : " + stack[tos - 1]); } multiply(); if (DEBUG_CLOCKFORM) { console.log("clockform: multiply : " + stack[tos - 1]); } //else /* p1 = pop() push(p1) abs() push(symbol(E)) push(p1) arg() push(imaginaryunit) 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(); push(caddr(p1)); Eval(); push(cadddr(p1)); Eval(); p3 = pop(); p2 = pop(); p1 = pop(); if (p3 === symbol(NIL)) { // p3 is N # only 2 args? p3 = p2; p2 = symbol(SYMBOL_X); // p2 is X } push(p1); push(p2); push(p3); power(); divide(); push(p2); return filter(); }; //----------------------------------------------------------------------------- // Put polynomial coefficients on the stack // Input: as per params // Output: Returns number of coefficients on stack // tos-n Coefficient of x^0 // tos-1 Coefficient of x^(n-1) //----------------------------------------------------------------------------- coeff = function(variable, polynomial) { var coeffsCount, constant, polynomialWithoutConstant, prev_expanding; if (DEBUG) { console.log("coeff: " + variable + " " + polynomial); } // works like this: // 1) find the constant (by just evaluating the pol setting the variable to zero) // 2) set aside the found constant: it's one of the coefficients to return // 3) take the polynomial and remove the constant // 4) divide that by variable, lowering the degree by one // 5) go back to 1) until degree is zero coeffsCount = 0; while (true) { push(polynomial); push(variable); push(zero); subst(); Eval(); constant = pop(); // this will be a coefficient that will be returned push(constant); coeffsCount++; push(polynomial); push(constant); subtract(); polynomialWithoutConstant = pop(); if (equal(polynomialWithoutConstant, zero)) { if (DEBUG) { console.log("coeff: result: " + coeffsCount); } return coeffsCount; } push(polynomialWithoutConstant); push(variable); prev_expanding = expanding; expanding = 1; divide(); expanding = prev_expanding; //console.log("just divided: " + stack[tos-1].toString()) // this is now the new polynomial with degree decreased by 1 polynomial = pop(); } }; Eval_cofactor = function() { var doNothing, i, j, n; i = 0; j = 0; n = 0; push(cadr(p1)); Eval(); p2 = pop(); if (istensor(p2) && p2.tensor.ndim === 2 && p2.tensor.dim[0] === p2.tensor.dim[1]) { doNothing = 1; } else { stop("cofactor: 1st arg: square matrix expected"); } n = p2.tensor.dim[0]; push(caddr(p1)); Eval(); i = pop_integer(); if (i < 1 || i > n) { stop("cofactor: 2nd arg: row index expected"); } push(cadddr(p1)); Eval(); j = pop_integer(); if (j < 1 || j > n) { stop("cofactor: 3rd arg: column index expected"); } return cofactor(p2, n, i - 1, j - 1); }; cofactor = function(p, n, row, col) { 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) { if (i !== row && j !== col) { push(p.tensor.elem[n * i + j]); } } } determinant(n - 1); if ((row + col) % 2) { return negate(); } }; // Condense an expression by factoring common terms. Eval_condense = function() { push(cadr(p1)); Eval(); return Condense(); }; Condense = function() { var prev_expanding; prev_expanding = expanding; expanding = 0; save(); yycondense(); restore(); return expanding = prev_expanding; }; 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)); if (DEBUG) { console.log("calculating gcd between: " + stack[tos - 1] + " and " + stack[tos - 2]); } gcd(); if (DEBUG) { console.log("partial gcd: " + stack[tos - 1]); } p3 = cdr(p3); } if (DEBUG) { console.log("condense: this is the gcd of all the terms: " + stack[tos - 1]); } // divide each term by gcd inverse(); p2 = pop(); push(zero); p3 = cdr(p1); 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(); }; Eval_conj = function() { push(cadr(p1)); Eval(); p1 = pop(); push(p1); if (!Find(p1, imaginaryunit)) { // example: (-1)^(1/3) polar(); conjugate(); return clockform(); } else { return conjugate(); } }; // 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); negate(); subst(); return Eval(); }; // Cons two things on the stack. consCount = 0; cons = function() { var p; consCount++; 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(); if (p === p.cons.cdr) { debugger; 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); }; Eval_contract = function() { push(cadr(p1)); Eval(); if (cddr(p1) === symbol(NIL)) { push_integer(1); push_integer(2); } else { push(caddr(p1)); Eval(); push(cadddr(p1)); Eval(); } return contract(); }; contract = function() { save(); yycontract(); return restore(); }; yycontract = function() { var a, ai, an, b, h, i, i1, j, j1, k, l, l1, m, m1, n, n1, ndim, nelem, o, o1, ref, ref1, ref2, ref3, ref4, ref5, ref6; h = 0; i = 0; j = 0; k = 0; l = 0; m = 0; n = 0; ndim = 0; nelem = 0; ai = []; an = []; p3 = pop(); p2 = pop(); p1 = pop(); if (!istensor(p1)) { if (!isZeroAtomOrTensor(p1)) { stop("contract: tensor expected, 1st arg is not a tensor"); } push(zero); return; } push(p2); l = pop_integer(); push(p3); m = pop_integer(); ndim = p1.tensor.ndim; if (l < 1 || l > ndim || m < 1 || m > ndim || l === m || p1.tensor.dim[l - 1] !== p1.tensor.dim[m - 1]) { stop("contract: index out of range"); } 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) { 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) { if (i !== l && i !== m) { p2.tensor.dim[j++] = p1.tensor.dim[i]; } } a = p1.tensor.elem; b = p2.tensor.elem; //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) { push(zero); 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) { 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(); //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; } if (++ai[j] < an[j]) { break; } ai[j] = 0; } } if (nelem === 1) { return push(b[0]); } else { return push(p2); } }; //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(); return cosine(); }; cosine = function() { save(); p1 = pop(); if (car(p1) === symbol(ADD)) { cosine_of_angle_sum(); } else { cosine_of_angle(); } 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)) { // p4 is B push(p1); push(p4); subtract(); p3 = pop(); push(p3); cosine(); push(p4); cosine(); multiply(); push(p3); sine(); push(p4); sine(); multiply(); subtract(); return; } p2 = cdr(p2); } return cosine_of_angle(); }; cosine_of_angle = function() { var d, n; if (car(p1) === symbol(ARCCOS)) { push(cadr(p1)); return; } if (isdouble(p1)) { d = Math.cos(p1.d); if (Math.abs(d) < 1e-10) { d = 0.0; } 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)); push_integer(2); power(); add(); push_rational(-1, 2); 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(); if (evaluatingAsFloats) { push_double(Math.PI); } else { push_symbol(PI); } 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); list(2); return; } switch (n % 360) { case 90: case 270: return push_integer(0); case 60: case 300: return push_rational(1, 2); case 120: case 240: return push_rational(-1, 2); case 45: case 315: push_rational(1, 2); push_integer(2); push_rational(1, 2); power(); return multiply(); case 135: case 225: push_rational(-1, 2); push_integer(2); push_rational(1, 2); power(); return multiply(); case 30: case 330: push_rational(1, 2); push_integer(3); push_rational(1, 2); power(); return multiply(); case 150: case 210: push_rational(-1, 2); push_integer(3); push_rational(1, 2); power(); return multiply(); case 0: return push_integer(1); case 180: return push_integer(-1); default: push(symbol(COS)); push(p1); return list(2); } }; Eval_cosh = function() { push(cadr(p1)); Eval(); return ycosh(); }; ycosh = function() { save(); yycosh(); return restore(); }; yycosh = function() { var d; d = 0.0; p1 = pop(); if (car(p1) === symbol(ARCCOSH)) { push(cadr(p1)); return; } if (isdouble(p1)) { d = Math.cosh(p1.d); if (Math.abs(d) < 1e-10) { d = 0.0; } push_double(d); return; } if (isZeroAtomOrTensor(p1)) { push(one); return; } push_symbol(COSH); push(p1); 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(); console.log("Eval_decomp is being called!!!!!!!!!!!!!!!!!!!!"); h = tos; push(symbol(NIL)); push(cadr(p1)); Eval(); push(caddr(p1)); Eval(); p1 = pop(); if (p1 === symbol(NIL)) { guess(); } else { push(p1); } decomp(false); list(tos - h); return restore(); }; pushTryNotToDuplicate = function(toBePushed) { if (tos > 0) { if (DEBUG) { console.log("comparing " + toBePushed + " to: " + stack[tos - 1]); } if (equal(toBePushed, stack[tos - 1])) { if (DEBUG) { console.log("skipping " + toBePushed + " because it's already on stack "); } return; } } return push(toBePushed); }; // returns constant expressions on the stack decomp = function(generalTransform) { save(); p2 = pop(); p1 = pop(); if (DEBUG) { console.log("DECOMPOSING " + p1); } // is the entire expression constant? if (generalTransform) { if (!iscons(p1)) { if (DEBUG) { console.log(" ground thing: " + p1); } pushTryNotToDuplicate(p1); restore(); return; } } else { if (Find(p1, p2) === 0) { if (DEBUG) { 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"); } p3 = cdr(p1); if (DEBUG) { 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)); } if (DEBUG) { console.log("recursive decomposition"); } push(car(p3)); if (DEBUG) { console.log("car(p3): " + car(p3)); } push(p2); if (DEBUG) { console.log("p2: " + p2); } decomp(generalTransform); p3 = cdr(p3); } return restore(); }; decomp_sum = function(generalTransform) { var h; if (DEBUG) { console.log(" decomposing the sum "); } h = 0; // decomp terms involving x p3 = cdr(p1); while (iscons(p3)) { if (Find(car(p3), p2) || generalTransform) { push(car(p3)); push(p2); decomp(generalTransform); } p3 = cdr(p3); } // add together all constant terms h = tos; p3 = cdr(p1); while (iscons(p3)) { if (Find(car(p3), p2) === 0) { pushTryNotToDuplicate(car(p3)); } p3 = cdr(p3); } if (tos - h) { add_all(tos - h); p3 = pop(); pushTryNotToDuplicate(p3); push(p3); return negate(); // need both +a, -a for some integrals } }; decomp_product = function(generalTransform) { var h; if (DEBUG) { console.log(" decomposing the product "); } h = 0; // decomp factors involving x p3 = cdr(p1); while (iscons(p3)) { if (Find(car(p3), p2) || generalTransform) { push(car(p3)); push(p2); decomp(generalTransform); } p3 = cdr(p3); } // multiply together all constant factors h = tos; p3 = cdr(p1); while (iscons(p3)) { if (Find(car(p3), p2) === 0) { pushTryNotToDuplicate(car(p3)); } p3 = cdr(p3); } if (tos - h) { return multiply_all(tos - h); } }; //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)) { // p3 is F stop("function name?"); } // evaluate function body (maybe) if (car(p5) === symbol(EVAL)) { // p5 is B push(cadr(p5)); Eval(); 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); list(3); p5 = pop(); set_binding(p3, p5); return push_symbol(NIL); }; Eval_function_reference = function() { 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. The argument list can be extended for multiple integrals (or "iterated 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 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 is X push(car(p1)); p1 = cdr(p1); Eval(); p4 = pop(); // p4 is A push(car(p1)); p1 = cdr(p1); Eval(); 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(); // 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); }; Eval_degree = function() { push(cadr(p1)); Eval(); push(caddr(p1)); Eval(); p1 = pop(); if (p1 === symbol(NIL)) { guess(); } else { push(p1); } 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(); p1 = pop(); p3 = zero; yydegree(p1); push(p3); return restore(); }; yydegree = function(p) { var results; if (equal(p, p2)) { if (isZeroAtomOrTensor(p3)) { return p3 = one; } } else if (car(p) === symbol(POWER)) { if (equal(cadr(p), p2) && isNumericAtom(caddr(p)) && lessp(p3, caddr(p))) { return p3 = caddr(p); } } else if (iscons(p)) { p = cdr(p); results = []; while (iscons(p)) { yydegree(car(p)); results.push(p = cdr(p)); } return results; } }; Eval_denominator = function() { push(cadr(p1)); Eval(); return denominator(); }; denominator = function() { var h, theArgument; h = 0; theArgument = pop(); //console.trace "denominator of: " + theArgument if (car(theArgument) === symbol(ADD)) { push(theArgument); rationalize(); theArgument = pop(); } if (car(theArgument) === symbol(MULTIPLY) && !isplusone(car(cdr(theArgument)))) { h = tos; theArgument = cdr(theArgument); while (iscons(theArgument)) { push(car(theArgument)); denominator(); theArgument = cdr(theArgument); } return multiply_all(tos - h); } else if (isrational(theArgument)) { push(theArgument); return mp_denominator(); } else if (car(theArgument) === symbol(POWER) && isnegativeterm(caddr(theArgument))) { push(theArgument); return reciprocate(); } else { return push(one); } }; // 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(); p2 = pop(); if (p2 === symbol(NIL)) { guess(); push(symbol(NIL)); } else if (isNumericAtom(p2)) { guess(); push(p2); } else { push(p2); p1 = cdr(p1); push(car(p1)); Eval(); } p5 = pop(); p4 = pop(); p3 = pop(); while (1) { // p5 (N) might be a symbol instead of a number if (isNumericAtom(p5)) { // p5 is N push(p5); n = pop_integer(); if (isNaN(n)) { stop("nth derivative: check n"); } } else { n = 1; } push(p3); if (n >= 0) { 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) { push(p4); integral(); } } p3 = pop(); if (p5 === symbol(NIL)) { // p5 is N 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)) { // p5 is N p1 = cdr(p1); push(car(p1)); Eval(); p5 = pop(); if (p5 === symbol(NIL)) { // p5 is N break; // arglist exhausted } if (isNumericAtom(p5)) { // p5 is N doNothing = 1; // N = arg1 } else { p4 = p5; p1 = cdr(p1); push(car(p1)); Eval(); p5 = pop(); // p5 is N # N = arg2 } } else { p4 = p5; p1 = cdr(p1); push(car(p1)); Eval(); p5 = pop(); // p5 is N # N = arg1 } } return push(p3); // p3 is F # final result }; derivative = function() { save(); p2 = pop(); p1 = pop(); if (isNumericAtom(p2)) { stop("undefined function"); } if (istensor(p1)) { if (istensor(p2)) { d_tensor_tensor(); } else { d_tensor_scalar(); } } else { if (istensor(p2)) { d_scalar_tensor(); } else { d_scalar_scalar(); } } return restore(); }; d_scalar_scalar = function() { 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)); subst(); push(symbol(SECRETX)); derivative(); push(symbol(SECRETX)); push(p2); return subst(); // cos(X) -> cos(cos(x)) } }; d_scalar_scalar_1 = function() { // d(x,x)? if (equal(p1, p2)) { push(one); return; } if (!iscons(p1)) { push(zero); return; } if (isadd(p1)) { dsum(); return; } if (car(p1) === symbol(MULTIPLY)) { dproduct(); return; } if (car(p1) === symbol(POWER)) { dpower(); return; } if (car(p1) === symbol(DERIVATIVE)) { dd(); return; } if (car(p1) === symbol(LOG)) { dlog(); return; } if (car(p1) === symbol(SIN)) { dsin(); return; } if (car(p1) === symbol(COS)) { dcos(); return; } if (car(p1) === symbol(TAN)) { dtan(); return; } if (car(p1) === symbol(ARCSIN)) { darcsin(); return; } if (car(p1) === symbol(ARCCOS)) { darccos(); return; } if (car(p1) === symbol(ARCTAN)) { darctan(); return; } if (car(p1) === symbol(SINH)) { dsinh(); return; } if (car(p1) === symbol(COSH)) { dcosh(); return; } if (car(p1) === symbol(TANH)) { dtanh(); return; } if (car(p1) === symbol(ARCSINH)) { darcsinh(); return; } if (car(p1) === symbol(ARCCOSH)) { darccosh(); return; } if (car(p1) === symbol(ARCTANH)) { darctanh(); return; } if (car(p1) === symbol(ABS)) { dabs(); return; } if (car(p1) === symbol(SGN)) { dsgn(); return; } if (car(p1) === symbol(HERMITE)) { dhermite(); return; } if (car(p1) === symbol(ERF)) { derf(); return; } if (car(p1) === symbol(ERFC)) { derfc(); return; } if (car(p1) === symbol(BESSELJ)) { if (isZeroAtomOrTensor(caddr(p1))) { dbesselj0(); } else { dbesseljn(); } return; } if (car(p1) === symbol(BESSELY)) { if (isZeroAtomOrTensor(caddr(p1))) { dbessely0(); } else { dbesselyn(); } return; } if (car(p1) === symbol(INTEGRAL) && caddr(p1) === p2) { derivative_of_integral(); return; } return dfunction(); }; dsum = function() { var h; h = tos; p1 = cdr(p1); while (iscons(p1)) { push(car(p1)); push(p2); derivative(); p1 = cdr(p1); } return add_all(tos - h); }; dproduct = function() { var i, i1, j, n, o, ref, ref1; i = 0; 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) { p3 = cdr(p1); 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); derivative(); } p3 = cdr(p3); } multiply_all(n); } 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)); divide(); push(cadr(p1)); push(p2); derivative(); multiply(); push(cadr(p1)); logarithm(); push(caddr(p1)); push(p2); derivative(); multiply(); add(); push(p1); return multiply(); }; dlog = function() { push(cadr(p1)); push(p2); derivative(); push(cadr(p1)); 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)); if (lessp(caddr(p3), caddr(p1))) { push(caddr(p3)); list(3); push(caddr(p1)); } else { push(caddr(p1)); list(3); push(caddr(p3)); } return list(3); } else { push(p3); push(caddr(p1)); return derivative(); } }; // derivative of a generic function dfunction = function() { p3 = cdr(p1); if (p3 === symbol(NIL) || Find(p3, p2)) { push_symbol(DERIVATIVE); push(p1); push(p2); return list(3); } else { return push(zero); } }; dsin = function() { push(cadr(p1)); push(p2); derivative(); push(cadr(p1)); cosine(); return multiply(); }; dcos = function() { push(cadr(p1)); push(p2); derivative(); push(cadr(p1)); sine(); multiply(); return negate(); }; dtan = function() { push(cadr(p1)); push(p2); derivative(); push(cadr(p1)); cosine(); push_integer(-2); power(); return multiply(); }; darcsin = function() { push(cadr(p1)); push(p2); derivative(); push_integer(1); push(cadr(p1)); push_integer(2); power(); subtract(); push_rational(-1, 2); power(); return multiply(); }; darccos = function() { push(cadr(p1)); push(p2); derivative(); push_integer(1); push(cadr(p1)); push_integer(2); power(); subtract(); push_rational(-1, 2); power(); multiply(); 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); derivative(); push_integer(1); push(cadr(p1)); push_integer(2); power(); add(); inverse(); multiply(); return simplify(); }; dsinh = function() { push(cadr(p1)); push(p2); derivative(); push(cadr(p1)); ycosh(); return multiply(); }; dcosh = function() { push(cadr(p1)); push(p2); derivative(); push(cadr(p1)); ysinh(); return multiply(); }; dtanh = function() { push(cadr(p1)); push(p2); derivative(); push(cadr(p1)); ycosh(); push_integer(-2); power(); return multiply(); }; darcsinh = function() { push(cadr(p1)); push(p2); derivative(); push(cadr(p1)); push_integer(2); power(); push_integer(1); add(); push_rational(-1, 2); power(); return multiply(); }; darccosh = function() { push(cadr(p1)); push(p2); derivative(); push(cadr(p1)); push_integer(2); power(); push_integer(-1); add(); push_rational(-1, 2); power(); return multiply(); }; darctanh = function() { push(cadr(p1)); push(p2); derivative(); push_integer(1); push(cadr(p1)); push_integer(2); power(); subtract(); inverse(); return multiply(); }; dabs = function() { push(cadr(p1)); push(p2); derivative(); push(cadr(p1)); sgn(); return multiply(); }; dsgn = function() { push(cadr(p1)); push(p2); derivative(); push(cadr(p1)); dirac(); multiply(); push_integer(2); return multiply(); }; dhermite = function() { push(cadr(p1)); push(p2); derivative(); push_integer(2); push(caddr(p1)); multiply(); multiply(); push(cadr(p1)); push(caddr(p1)); push_integer(-1); add(); hermite(); return multiply(); }; derf = function() { push(cadr(p1)); push_integer(2); power(); push_integer(-1); multiply(); exponential(); if (evaluatingAsFloats) { push_double(Math.PI); } else { push_symbol(PI); } push_rational(-1, 2); power(); multiply(); push_integer(2); multiply(); push(cadr(p1)); push(p2); derivative(); return multiply(); }; derfc = function() { push(cadr(p1)); push_integer(2); power(); push_integer(-1); multiply(); exponential(); if (evaluatingAsFloats) { push_double(Math.PI); } else { push_symbol(PI); } push_rational(-1, 2); power(); multiply(); push_integer(-2); multiply(); push(cadr(p1)); push(p2); derivative(); return multiply(); }; dbesselj0 = function() { push(cadr(p1)); push(p2); derivative(); push(cadr(p1)); push_integer(1); besselj(); multiply(); push_integer(-1); return multiply(); }; dbesseljn = function() { push(cadr(p1)); push(p2); derivative(); push(cadr(p1)); push(caddr(p1)); push_integer(-1); add(); besselj(); push(caddr(p1)); push_integer(-1); multiply(); push(cadr(p1)); divide(); push(cadr(p1)); push(caddr(p1)); besselj(); multiply(); add(); return multiply(); }; dbessely0 = function() { push(cadr(p1)); push(p2); derivative(); push(cadr(p1)); push_integer(1); besselj(); multiply(); push_integer(-1); return multiply(); }; dbesselyn = function() { push(cadr(p1)); push(p2); derivative(); push(cadr(p1)); push(caddr(p1)); push_integer(-1); add(); bessely(); push(caddr(p1)); push_integer(-1); multiply(); push(cadr(p1)); divide(); push(cadr(p1)); push(caddr(p1)); bessely(); multiply(); add(); return multiply(); }; derivative_of_integral = function() { return push(cadr(p1)); }; DET_check_arg = function() { if (!istensor(p1)) { return 0; } else if (p1.tensor.ndim !== 2) { return 0; } else if (p1.tensor.dim[0] !== p1.tensor.dim[1]) { return 0; } else { return 1; } }; det = function() { var a, i, i1, n, o, ref, ref1; i = 0; n = 0; //U **a save(); p1 = pop(); if (DET_check_arg() === 0) { push_symbol(DET); push(p1); list(2); restore(); return; } 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) { if (!isNumericAtom(a[i])) { break; } } if (i === n) { yydetg(); } else { 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]); } 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; i = 0; j = 0; k = 0; q = 0; s = 0; sign_ = 0; t = 0; a = []; //int *a, *c, *d h = tos - n * n; //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; } sign_ = 1; push(zero); while (1) { if (sign_ === 1) { push_integer(1); } else { push_integer(-1); } 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(); // FIXME -- problem here } add(); // next permutation (Knuth's algorithm P) j = n - 1; s = 0; breakFromOutherWhile = false; while (1) { q = a[n + j] + a[n + n + j]; if (q < 0) { a[n + n + j] = -a[n + n + j]; j--; continue; } if (q === j + 1) { if (j === 0) { breakFromOutherWhile = true; break; } s++; a[n + n + j] = -a[n + n + j]; j--; continue; } break; } if (breakFromOutherWhile) { break; } t = a[j - a[n + j] + s]; a[j - a[n + j] + s] = a[j - q + s]; a[j - q + s] = t; a[n + j] = q; sign_ = -sign_; } stack[h] = stack[tos - 1]; 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(); if (DET_check_arg() === 0) { push_symbol(DET); push(p1); list(2); restore(); return; } yydetg(); return restore(); }; yydetg = function() { var i, n, o, ref; 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) { push(p1.tensor.elem[i]); } lu_decomp(n); moveTos(tos - n * n); 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]; }; setM = function(h, n, i, j, value) { return stack[h + n * i + j] = value; }; lu_decomp = function(n) { var d, h, i, i1, j, j1, l1, m1, o, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8; d = 0; h = 0; i = 0; 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) { // diagonal element zero? if (equal(M(h, n, d, d), zero)) { // 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; } } if (i === n) { p1 = zero; break; } // 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(); // 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) { push(M(h, n, d, j)); push(p2); multiply(); push(M(h, n, i, j)); add(); setM(h, n, i, j, pop()); } } } // 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(); return dirac(); }; dirac = function() { save(); ydirac(); return restore(); }; //define p1 p1 ydirac = function() { p1 = pop(); if (isdouble(p1)) { if (p1.d === 0) { push_integer(1); return; } else { push_integer(0); return; } } if (isrational(p1)) { if (MZERO(mmul(p1.q.a, p1.q.b))) { push_integer(1); return; } else { push_integer(0); return; } } if (car(p1) === symbol(POWER)) { push_symbol(DIRAC); push(cadr(p1)); list(2); return; } if (isnegativeterm(p1)) { push_symbol(DIRAC); push(p1); negate(); list(2); return; } if (isnegativeterm(p1) || (car(p1) === symbol(ADD) && isnegativeterm(cadr(p1)))) { push(p1); negate(); p1 = pop(); } push_symbol(DIRAC); push(p1); 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; h = 0; n = 0; save(); 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) { p1.tensor.elem[i] = stack[h + i]; } moveTos(h); push(p1); return restore(); }; divisors_onstack = function() { var h, i, k, n, o, ref; h = 0; i = 0; k = 0; n = 0; 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))) { push(car(p1)); factor_small_number(); p1 = cdr(p1); } while (iscons(p1)) { p2 = car(p1); if (car(p2) === symbol(POWER)) { push(cadr(p2)); push(caddr(p2)); } else { push(p2); push(one); } p1 = cdr(p1); } } else if (car(p1) === symbol(POWER)) { push(cadr(p1)); push(caddr(p1)); } else { push(p1); 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) { 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; i = 0; save(); p1 = pop(); if (h === k) { push(p1); restore(); return; } p2 = stack[h + 0]; p3 = stack[h + 1]; 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) { push(p1); push(p2); push_integer(sign(expo) * i); power(); multiply(); gen(h + 2, k); } } 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); while (iscons(p3)) { push(car(p3)); gcd(); p3 = cdr(p3); } // check gcd p2 = pop(); if (isplusone(p2)) { push(p1); push(one); restore(); return; } // push factored gcd if (isNumericAtom(p2)) { push(p2); factor_small_number(); } else if (car(p2) === symbol(MULTIPLY)) { p3 = cdr(p2); if (isNumericAtom(car(p3))) { push(car(p3)); factor_small_number(); } else { push(car(p3)); push(one); } p3 = cdr(p3); while (iscons(p3)) { push(car(p3)); push(one); p3 = cdr(p3); } } else { push(p2); push(one); } // divide each term by gcd push(p2); inverse(); p2 = pop(); push(zero); p3 = cdr(p1); while (iscons(p3)) { push(p2); push(car(p3)); multiply(); add(); p3 = cdr(p3); } push(one); return restore(); }; // power function for double precision floating point dpow = function() { var a, b, base, expo, result, theta; a = 0.0; b = 0.0; base = 0.0; expo = 0.0; result = 0.0; 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); return; } 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); } else { a = Math.cos(theta); b = Math.sin(theta); } push_double(a * result); push_double(b * result); push(imaginaryunit); multiply(); 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. The eigenval function returns a matrix with the eigenvalues along the diagonal. 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 = []; EIG_yyqq = []; Eval_eigen = function() { if (EIG_check_arg() === 0) { stop("eigen: argument is not a square matrix"); } eigen(EIGEN); p1 = usr_symbol("D"); set_binding(p1, p2); p1 = usr_symbol("Q"); set_binding(p1, p3); return push(symbol(NIL)); }; Eval_eigenval = function() { if (EIG_check_arg() === 0) { push_symbol(EIGENVAL); push(p1); list(2); return; } eigen(EIGENVAL); return push(p2); }; Eval_eigenvec = function() { if (EIG_check_arg() === 0) { push_symbol(EIGENVEC); push(p1); list(2); return; } eigen(EIGENVEC); return push(p3); }; EIG_check_arg = function() { var i, i1, j, j1, l1, o, ref, ref1, ref2, ref3, ref4; i = 0; j = 0; push(cadr(p1)); Eval(); yyfloat(); Eval(); p1 = pop(); if (!istensor(p1)) { return 0; } if (p1.tensor.ndim !== 2 || p1.tensor.dim[0] !== p1.tensor.dim[1]) { 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) { 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) { 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"); } } } 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; // 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; } //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; } //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) { 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; } } // 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) { 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; } } 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) { 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) { 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) { push_double(EIG_yyqq[EIG_N * i + j]); results1.push(p3.tensor.elem[EIG_N * i + j] = pop()); } return results1; })()); } return results; } }; // 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 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++; } } } return count; }; step2 = function(p, q) { var c, cc, i1, j1, k, o, ref, ref1, ref2, s, ss, t, theta; k = 0; t = 0.0; theta = 0.0; c = 0.0; 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) { t = -t; } c = 1.0 / Math.sqrt(t * t + 1.0); s = t * c; // 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; } // 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; } // 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; EIG_yyqq[EIG_N * q + k] = c * ss - s * cc; } EIG_yydd[EIG_N * p + q] = 0.0; return EIG_yydd[EIG_N * q + p] = 0.0; }; Eval_erf = function() { push(cadr(p1)); Eval(); return yerf(); }; yerf = function() { save(); yyerf(); return restore(); }; yyerf = function() { var d; d = 0.0; p1 = pop(); if (isdouble(p1)) { d = 1.0 - erfc(p1.d); push_double(d); return; } if (isZeroAtomOrTensor(p1)) { push(zero); return; } if (isnegativeterm(p1)) { push_symbol(ERF); push(p1); negate(); list(2); negate(); return; } push_symbol(ERF); push(p1); list(2); }; //----------------------------------------------------------------------------- // Author : philippe.billet@noos.fr // erfc(x) // GW Added erfc() from Numerical Recipes in C //----------------------------------------------------------------------------- Eval_erfc = function() { push(cadr(p1)); Eval(); return yerfc(); }; yerfc = function() { save(); yyerfc(); return restore(); }; yyerfc = function() { var d; d = 0.0; p1 = pop(); if (isdouble(p1)) { d = erfc(p1.d); push_double(d); return; } if (isZeroAtomOrTensor(p1)) { push(one); return; } push_symbol(ERFC); push(p1); list(2); }; // from Numerical Recipes in C erfc = function(x) { var ans, t, z; if (x === 0) { return 1.0; } t = 0.0; z = 0.0; ans = 0.0; z = Math.abs(x); t = 1.0 / (1.0 + 0.5 * z); ans = t * Math.exp(-z * z - 1.26551223 + t * (1.00002368 + t * (0.37409196 + t * (0.09678418 + t * (-0.18628806 + t * (0.27886807 + t * (-1.13520398 + t * (1.48851587 + t * (-0.82215223 + t * 0.17087277))))))))); if (x >= 0.0) { return ans; } else { return 2.0 - ans; } }; // Evaluate an expression, for example... // push(p1) // Eval() // p2 = pop() Eval = function() { var willEvaluateAsFloats; check_esc_flag(); save(); p1 = pop(); if (p1 == null) { debugger; } if (!evaluatingAsFloats && isfloating(p1)) { willEvaluateAsFloats = true; evaluatingAsFloats++; } switch (p1.k) { case CONS: Eval_cons(); break; case NUM: if (evaluatingAsFloats) { push_double(convert_rational_to_double(p1)); } else { push(p1); } break; case DOUBLE: case STR: push(p1); break; case TENSOR: Eval_tensor(); break; case SYM: Eval_sym(); break; default: stop("atom?"); } if (willEvaluateAsFloats) { evaluatingAsFloats--; } return restore(); }; 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)); list(2); Eval(); return; } else if (p1 === symbol(PI) && evaluatingAsFloats) { 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) { cycleString += chainOfUserSymbolsNotFunctionsBeingEvaluated[i].printname + " -> "; } cycleString += p1.printname; stop("recursive evaluation of symbols: " + cycleString); return; } chainOfUserSymbolsNotFunctionsBeingEvaluated.push(p1); Eval(); return chainOfUserSymbolsNotFunctionsBeingEvaluated.pop(); } }; 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; } if (!issymbol(cons_head)) { stop("cons?"); } switch (symnum(cons_head)) { case ABS: return Eval_abs(); case ADD: return Eval_add(); case ADJ: return Eval_adj(); case AND: return Eval_and(); case ARCCOS: return Eval_arccos(); case ARCCOSH: return Eval_arccosh(); case ARCSIN: return Eval_arcsin(); case ARCSINH: return Eval_arcsinh(); case ARCTAN: return Eval_arctan(); case ARCTANH: return Eval_arctanh(); case ARG: return Eval_arg(); case ATOMIZE: return Eval_atomize(); case BESSELJ: return Eval_besselj(); case BESSELY: return Eval_bessely(); case BINDING: return Eval_binding(); case BINOMIAL: return Eval_binomial(); case CEILING: return Eval_ceiling(); case CHECK: return Eval_check(); case CHOOSE: return Eval_choose(); case CIRCEXP: return Eval_circexp(); case CLEAR: return Eval_clear(); case CLEARALL: return Eval_clearall(); case CLEARPATTERNS: return Eval_clearpatterns(); case CLOCK: return Eval_clock(); case COEFF: return Eval_coeff(); case COFACTOR: return Eval_cofactor(); case CONDENSE: return Eval_condense(); case CONJ: return Eval_conj(); case CONTRACT: return Eval_contract(); case COS: return Eval_cos(); case COSH: return Eval_cosh(); case DECOMP: return Eval_decomp(); case DEGREE: return Eval_degree(); case DEFINT: return Eval_defint(); case DENOMINATOR: return Eval_denominator(); case DERIVATIVE: return Eval_derivative(); case DET: return Eval_det(); case DIM: return Eval_dim(); case DIRAC: return Eval_dirac(); case DIVISORS: return Eval_divisors(); case DO: return Eval_do(); case DOT: return Eval_inner(); case DRAW: return Eval_draw(); case DSOLVE: return Eval_dsolve(); case EIGEN: return Eval_eigen(); case EIGENVAL: return Eval_eigenval(); case EIGENVEC: return Eval_eigenvec(); case ERF: return Eval_erf(); case ERFC: return Eval_erfc(); case EVAL: return Eval_Eval(); case EXP: return Eval_exp(); case EXPAND: return Eval_expand(); case EXPCOS: return Eval_expcos(); case EXPSIN: return Eval_expsin(); case FACTOR: return Eval_factor(); case FACTORIAL: return Eval_factorial(); case FACTORPOLY: return Eval_factorpoly(); case FILTER: return Eval_filter(); case FLOATF: return Eval_float(); case APPROXRATIO: return Eval_approxratio(); case FLOOR: 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: return Eval_gamma(); case GCD: return Eval_gcd(); case HERMITE: return Eval_hermite(); case HILBERT: return Eval_hilbert(); case IMAG: return Eval_imag(); case INDEX: return Eval_index(); case INNER: return Eval_inner(); case INTEGRAL: return Eval_integral(); case INV: return Eval_inv(); case INVG: return Eval_invg(); case ISINTEGER: return Eval_isinteger(); case ISPRIME: return Eval_isprime(); case LAGUERRE: return Eval_laguerre(); // when LAPLACE then Eval_laplace() case LCM: return Eval_lcm(); case LEADING: return Eval_leading(); case LEGENDRE: return Eval_legendre(); case LOG: return Eval_log(); case LOOKUP: return Eval_lookup(); case MOD: return Eval_mod(); case MULTIPLY: return Eval_multiply(); case NOT: return Eval_not(); case NROOTS: return Eval_nroots(); case NUMBER: return Eval_number(); case NUMERATOR: return Eval_numerator(); case OPERATOR: return Eval_operator(); case OR: return Eval_or(); case OUTER: return Eval_outer(); case PATTERN: return Eval_pattern(); case PATTERNSINFO: return Eval_patternsinfo(); case POLAR: return Eval_polar(); case POWER: return Eval_power(); case PRIME: return Eval_prime(); case PRINT: return Eval_print(); case PRINT2DASCII: return Eval_print2dascii(); case PRINTFULL: return Eval_printcomputer(); case PRINTLATEX: return Eval_printlatex(); case PRINTLIST: return Eval_printlist(); case PRINTPLAIN: return Eval_printhuman(); case PRODUCT: return Eval_product(); case QUOTE: return Eval_quote(); case QUOTIENT: return Eval_quotient(); case RANK: return Eval_rank(); case RATIONALIZE: return Eval_rationalize(); case REAL: return Eval_real(); case ROUND: return Eval_round(); case YYRECT: return Eval_rect(); case ROOTS: return Eval_roots(); case SETQ: return Eval_setq(); case SGN: return Eval_sgn(); case SILENTPATTERN: return Eval_silentpattern(); case SIMPLIFY: return Eval_simplify(); case SIN: return Eval_sin(); case SINH: return Eval_sinh(); case SHAPE: return Eval_shape(); case SQRT: return Eval_sqrt(); case STOP: return Eval_stop(); case SUBST: return Eval_subst(); case SUM: return Eval_sum(); case SYMBOLSINFO: return Eval_symbolsinfo(); case TAN: return Eval_tan(); case TANH: return Eval_tanh(); case TAYLOR: return Eval_taylor(); case TEST: return Eval_test(); case TESTEQ: return Eval_testeq(); case TESTGE: return Eval_testge(); case TESTGT: return Eval_testgt(); case TESTLE: return Eval_testle(); case TESTLT: return Eval_testlt(); case TRANSPOSE: return Eval_transpose(); case UNIT: return Eval_unit(); case ZERO: return Eval_zero(); default: return Eval_user_function(); } }; Eval_binding = function() { return push(get_binding(cadr(p1))); }; 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); } }; Eval_det = function() { push(cadr(p1)); Eval(); return det(); }; Eval_dim = function() { var n; //int n push(cadr(p1)); Eval(); p2 = pop(); if (iscons(cddr(p1))) { push(caddr(p1)); Eval(); n = pop_integer(); } else { n = 1; } if (!istensor(p2)) { return push_integer(1); // dim of scalar is 1 } else if (n < 1 || n > p2.tensor.ndim) { return push(p1); } else { return push_integer(p2.tensor.dim[n - 1]); } }; Eval_divisors = function() { push(cadr(p1)); Eval(); return divisors(); }; Eval_do = function() { var results; push(car(p1)); p1 = cdr(p1); results = []; while (iscons(p1)) { pop(); push(car(p1)); Eval(); results.push(p1 = cdr(p1)); } return results; }; Eval_dsolve = function() { push(cadr(p1)); Eval(); push(caddr(p1)); Eval(); push(cadddr(p1)); Eval(); return dsolve(); }; // for example, Eval(f,x,2) Eval_Eval = function() { push(cadr(p1)); Eval(); p1 = cddr(p1); while (iscons(p1)) { push(car(p1)); Eval(); push(cadr(p1)); Eval(); subst(); p1 = cddr(p1); } return Eval(); }; // exp evaluation: it replaces itself with // a POWER(E,something) node and evals that one Eval_exp = function() { push(cadr(p1)); Eval(); return exponential(); }; Eval_factorial = function() { push(cadr(p1)); Eval(); return factorial(); }; Eval_factorpoly = function() { var results; p1 = cdr(p1); push(car(p1)); Eval(); p1 = cdr(p1); push(car(p1)); Eval(); factorpoly(); p1 = cdr(p1); results = []; while (iscons(p1)) { push(car(p1)); Eval(); factorpoly(); results.push(p1 = cdr(p1)); } return results; }; Eval_hermite = function() { push(cadr(p1)); Eval(); push(caddr(p1)); Eval(); return hermite(); }; Eval_hilbert = function() { push(cadr(p1)); Eval(); return hilbert(); }; Eval_index = function() { 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(); theTensor = stack[tos - 1]; if (isNumericAtom(theTensor)) { 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; } p1 = cdr(p1); } return index_function(tos - h); }; Eval_inv = function() { push(cadr(p1)); Eval(); return inv(); }; Eval_invg = function() { push(cadr(p1)); Eval(); return invg(); }; Eval_isinteger = function() { var n; push(cadr(p1)); Eval(); p1 = pop(); if (isrational(p1)) { if (isinteger(p1)) { push(one); } else { push(zero); } return; } if (isdouble(p1)) { n = Math.floor(p1.d); if (n === p1.d) { push(one); } else { push(zero); } return; } push_symbol(ISINTEGER); push(p1); return list(2); }; Eval_number = function() { push(cadr(p1)); Eval(); p1 = pop(); if (p1.k === NUM || p1.k === DOUBLE) { return push_integer(1); } else { return push_integer(0); } }; Eval_operator = function() { var h; h = tos; push_symbol(OPERATOR); p1 = cdr(p1); while (iscons(p1)) { push(car(p1)); Eval(); p1 = cdr(p1); } return list(tos - h); }; // quote definition Eval_quote = function() { return push(cadr(p1)); }; // rank definition Eval_rank = function() { push(cadr(p1)); Eval(); p1 = pop(); if (istensor(p1)) { return push_integer(p1.tensor.ndim); } else { return push(zero); } }; // 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; } if (!issymbol(cadr(p1))) { stop("symbol assignment: error in symbol"); } push(caddr(p1)); 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); 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; push(caddr(p1)); Eval(); p2 = cdadr(p1); while (iscons(p2)) { push(car(p2)); Eval(); p2 = cdr(p2); } set_component(tos - h); p3 = pop(); set_binding(p4, p3); return push(symbol(NIL)); }; Eval_sqrt = function() { push(cadr(p1)); Eval(); push_rational(1, 2); return power(); }; Eval_stop = function() { return stop("user stop"); }; Eval_subst = function() { push(cadddr(p1)); Eval(); push(caddr(p1)); Eval(); push(cadr(p1)); Eval(); subst(); 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; n = 0; push(cadr(p1)); Eval(); n = pop_integer(); if (isNaN(n)) { push(p1); return; } if (n < 1) { push(p1); return; } p1 = alloc_tensor(n * n); 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) { p1.tensor.elem[n * i + i] = one; } check_tensor_dimensions(p1); return push(p1); }; Eval_noexpand = function() { var prev_expanding; prev_expanding = expanding; expanding = 0; Eval(); 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)); push(caddr(p1)); list(3); } Eval(); 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(); if (p2 === symbol(NIL)) { guess(); } else { push(p2); } 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(); p9 = pop(); p5 = pop(); if (istensor(p5)) { expand_tensor(); 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); while (iscons(p1)) { push(car(p1)); push(p9); expand(); add(); p1 = cdr(p1); } 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(); if (istensor(p4)) { push(p4); prev_expanding = expanding; expanding = 1; inv(); expanding = prev_expanding; push(p3); inner(); push(p2); inner(); } else { push(p3); push(p4); prev_expanding = expanding; expanding = 1; divide(); expanding = prev_expanding; push(p2); multiply(); } push(p7); add(); return restore(); }; expand_tensor = function() { var i, o, ref; i = 0; 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) { push(p5.tensor.elem[i]); push(p9); expand(); p5.tensor.elem[i] = pop(); } return push(p5); }; remove_negative_exponents = function() { var h, i, j, k, n, o, ref; h = 0; i = 0; j = 0; k = 0; n = 0; h = tos; 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) { p1 = stack[h + i]; if (car(p1) !== symbol(POWER)) { continue; } if (cadr(p1) !== p9) { continue; } push(caddr(p1)); k = pop_integer(); if (isNaN(k)) { continue; } if (k < j) { j = k; } } moveTos(h); 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); power(); multiply(); 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); while (iscons(p1)) { p5 = car(p1); expand_get_CF(); p1 = cdr(p1); } } else { p5 = p2; expand_get_CF(); } n = tos - h; if (n === 1) { p4 = pop(); return; } p4 = alloc_tensor(n * n); p4.tensor.ndim = 2; 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) { push(stack[a + j]); push(p9); push_integer(i); power(); prev_expanding = expanding; expanding = 1; divide(); expanding = prev_expanding; push(p9); filter(); p4.tensor.elem[n * i + j] = pop(); } } 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; i = 0; j = 0; n = 0; if (!Find(p5, p9)) { return; } prev_expanding = expanding; expanding = 1; trivial_divide(); expanding = prev_expanding; if (car(p5) === symbol(POWER)) { push(caddr(p5)); n = pop_integer(); p6 = cadr(p5); } else { n = 1; p6 = p5; } push(p6); push(p9); degree(); d = pop_integer(); results = []; 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) { push(p8); push(p6); push_integer(i); power(); prev_expanding = expanding; expanding = 1; multiply(); expanding = prev_expanding; push(p9); push_integer(j); power(); prev_expanding = expanding; expanding = 1; multiply(); results1.push(expanding = prev_expanding); } return results1; })()); } return results; }; // Returns T = A/F where F is a factor of A. trivial_divide = function() { var h; h = 0; if (car(p2) === symbol(MULTIPLY)) { h = tos; p0 = cdr(p2); while (iscons(p0)) { if (!equal(car(p0), p5)) { push(car(p0)); Eval(); // force expansion of (x+1)^2, f.e. } p0 = cdr(p0); } multiply_all(tos - h); } else { push_integer(1); } return p8 = pop(); }; // Returns the expansion coefficient vector B. expand_get_B = function() { var i, n, o, prev_expanding, ref; i = 0; n = 0; if (!istensor(p4)) { return; } n = p4.tensor.dim[0]; 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) { push(p3); push(p9); push_integer(i); power(); prev_expanding = expanding; expanding = 1; divide(); expanding = prev_expanding; push(p9); filter(); p8.tensor.elem[i] = pop(); } return p3 = p8; }; // Returns the expansion fractions in A. expand_get_A = function() { var h, i, n, o, ref; h = 0; i = 0; n = 0; if (!istensor(p4)) { push(p2); reciprocate(); p2 = pop(); return; } h = tos; if (car(p2) === symbol(MULTIPLY)) { p8 = cdr(p2); while (iscons(p8)) { p5 = car(p8); expand_get_AF(); p8 = cdr(p8); } } else { p5 = p2; expand_get_AF(); } n = tos - h; 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) { p8.tensor.elem[i] = stack[h + i]; } moveTos(h); return p2 = p8; }; expand_get_AF = function() { var d, i, j, n, o, ref, results; d = 0; i = 0; j = 0; n = 1; if (!Find(p5, p9)) { return; } if (car(p5) === symbol(POWER)) { push(caddr(p5)); n = pop_integer(); p5 = cadr(p5); } push(p5); push(p9); degree(); d = pop_integer(); results = []; 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) { push(p5); push_integer(i); power(); reciprocate(); push(p9); push_integer(j); power(); results1.push(multiply()); } return results1; })()); } return results; }; // Do the exponential cosine function. Eval_expcos = function() { push(cadr(p1)); Eval(); return expcos(); }; expcos = function() { save(); p1 = pop(); push(imaginaryunit); push(p1); multiply(); exponential(); push_rational(1, 2); multiply(); push(imaginaryunit); negate(); push(p1); multiply(); exponential(); push_rational(1, 2); multiply(); add(); return restore(); }; // Do the exponential sine function. Eval_expsin = function() { push(cadr(p1)); Eval(); return expsin(); }; expsin = function() { save(); p1 = pop(); push(imaginaryunit); push(p1); multiply(); exponential(); push(imaginaryunit); divide(); push_rational(1, 2); multiply(); push(imaginaryunit); negate(); push(p1); multiply(); exponential(); push(imaginaryunit); divide(); push_rational(1, 2); multiply(); subtract(); return restore(); }; // factor a polynomial or integer Eval_factor = function() { var results; push(cadr(p1)); Eval(); push(caddr(p1)); Eval(); p2 = pop(); if (p2 === symbol(NIL)) { guess(); } else { push(p2); } factor(); // more factoring? p1 = cdddr(p1); results = []; while (iscons(p1)) { push(car(p1)); Eval(); factor_again(); results.push(p1 = cdr(p1)); } return results; }; factor_again = function() { var h, n; save(); p2 = pop(); p1 = pop(); h = tos; if (car(p1) === symbol(MULTIPLY)) { p1 = cdr(p1); while (iscons(p1)) { push(car(p1)); push(p2); factor_term(); p1 = cdr(p1); } } else { push(p1); push(p2); factor_term(); } n = tos - h; if (n > 1) { multiply_all_noexpand(n); } return restore(); }; factor_term = function() { save(); factorpoly(); p1 = pop(); if (car(p1) === symbol(MULTIPLY)) { p1 = cdr(p1); while (iscons(p1)) { push(car(p1)); p1 = cdr(p1); } } else { push(p1); } return restore(); }; factor = function() { save(); p2 = pop(); p1 = pop(); if (isinteger(p1)) { push(p1); factor_number(); // see pollard.cpp } else { push(p1); push(p2); factorpoly(); } return restore(); }; // for factoring small integers (2^32 or less) factor_small_number = function() { var d, expo, i, n, o, ref; i = 0; save(); n = pop_integer(); if (isNaN(n)) { stop("number too big to factor"); } if (n < 0) { n = -n; } 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; } expo = 0; while (n % d === 0) { n /= d; expo++; } if (expo) { push_integer(d); push_integer(expo); } } if (n > 1) { push_integer(n); push_integer(1); } return restore(); }; factorial = function() { var n; n = 0; save(); p1 = pop(); push(p1); n = pop_integer(); if (n < 0 || isNaN(n)) { push_symbol(FACTORIAL); push(p1); list(2); restore(); return; } bignum_factorial(n); 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; save(); x = expanding; expanding = 0; p1 = pop(); if (car(p1) === symbol(ADD)) { push(zero); p1 = cdr(p1); while (iscons(p1)) { push(car(p1)); simplifyfactorials(); add(); p1 = cdr(p1); } expanding = x; restore(); return; } if (car(p1) === symbol(MULTIPLY)) { sfac_product(); expanding = x; restore(); return; } push(p1); expanding = x; return restore(); }; sfac_product = function() { var i, i1, j, j1, n, o, ref, ref1, ref2, ref3, s; i = 0; j = 0; n = 0; s = tos; p1 = cdr(p1); n = 0; while (iscons(p1)) { push(car(p1)); p1 = cdr(p1); n++; } 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) { if (stack[s + j] === symbol(NIL)) { continue; } sfac_product_f(s, i, j); } } push(one); for (i = j1 = 0, ref3 = n; (0 <= ref3 ? j1 < ref3 : j1 > ref3); i = 0 <= ref3 ? ++j1 : --j1) { if (stack[s + i] === symbol(NIL)) { continue; } push(stack[s + i]); multiply(); } p1 = pop(); moveTos(tos - n); return push(p1); }; sfac_product_f = function(s, a, b) { var i, n, o, ref; i = 0; n = 0; p1 = stack[s + a]; p2 = stack[s + b]; if (ispower(p1)) { p3 = caddr(p1); p1 = cadr(p1); } else { p3 = one; } if (ispower(p2)) { p4 = caddr(p2); p2 = cadr(p2); } else { p4 = one; } if (isfactorial(p1) && isfactorial(p2)) { // Determine if the powers cancel. push(p3); push(p4); add(); yyexpand(); n = pop_integer(); 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(); yyexpand(); n = pop_integer(); if (n === 0 || isNaN(n)) { return; } if (n < 0) { n = -n; p5 = p1; p1 = p2; p2 = p5; p5 = p3; p3 = p4; p4 = p5; } push(one); for (i = o = 1, ref = n; (1 <= ref ? o <= ref : o >= ref); i = 1 <= ref ? ++o : --o) { push(cadr(p2)); push_integer(i); add(); push(p3); power(); multiply(); } stack[s + a] = pop(); return stack[s + b] = symbol(NIL); } }; // Factor a polynomial factorpoly = function() { var polynomial, variable; if (DEBUG) { console.log("factorpoly: " + stack[tos - 1].toString() + " " + stack[tos - 2].toString()); } save(); variable = pop(); polynomial = pop(); if (!Find(polynomial, variable) || !ispolyexpandedform(polynomial, variable) || !issymbol(variable)) { push(polynomial); } else { yyfactorpoly(variable, polynomial); } return restore(); }; //----------------------------------------------------------------------------- // Input: tos-2 true polynomial // tos-1 free variable // Output: factored polynomial on stack //----------------------------------------------------------------------------- yyfactorpoly = function(variable, polynomial) { var A, AxPlusB, B, checkingTheDivision, dividend, factpoly_expo, firstParam, foundComplexRoot, foundRealRoot, h, i, i1, j1, l1, o, partOfPolynomialFactoredSoFar, polycoeff, prev_expanding, previousFactorisation, ref, ref1, ref2, ref3, remainingPoly, secondDegreePloly, secondParam, whichRootsAreWeFinding; if (DEBUG) { firstParam = variable; secondParam = polynomial; console.log("yyfactorpoly: " + firstParam + " " + secondParam); } save(); h = tos; if (isfloating(polynomial)) { stop("floating point numbers in polynomial"); } polycoeff = tos; factpoly_expo = coeff(variable, polynomial) - 1; if (DEBUG) { console.log("yyfactorpoly: " + firstParam + " " + secondParam + " factpoly_expo before rationalize_coefficients: " + factpoly_expo); } partOfPolynomialFactoredSoFar = rationalize_coefficients(h); if (DEBUG) { console.log("yyfactorpoly: " + firstParam + " " + secondParam + " factpoly_expo after rationalize_coefficients: " + factpoly_expo); } // for univariate polynomials we could do factpoly_expo > 1 whichRootsAreWeFinding = "real"; remainingPoly = null; while (factpoly_expo > 0) { if (DEBUG) { console.log("yyfactorpoly: " + firstParam + " " + secondParam + " factpoly_expo inside while loop: " + factpoly_expo); } if (isZeroAtomOrTensor(stack[polycoeff + 0])) { if (DEBUG) { console.log("yyfactorpoly: " + firstParam + " " + secondParam + " isZeroAtomOrTensor"); } A = one; B = zero; } else { //console.log("trying to find a " + whichRootsAreWeFinding + " root") if (whichRootsAreWeFinding === "real") { [foundRealRoot, A, B] = get_factor_from_real_root(variable, factpoly_expo, polycoeff); } else if (whichRootsAreWeFinding === "complex") { [foundComplexRoot, A] = get_factor_from_complex_root(remainingPoly, factpoly_expo, polycoeff); } } if (whichRootsAreWeFinding === "real") { if (foundRealRoot === 0) { whichRootsAreWeFinding = "complex"; continue; } else { // build the 1-degree polynomial out of the // real solution that was just found. push(A); // A push(variable); // x multiply(); push(B); // B add(); AxPlusB = pop(); if (DEBUG) { console.log("yyfactorpoly: " + firstParam + " " + secondParam + " success\nFACTOR=" + AxPlusB); } // factor out negative sign (not req'd because A > 1) //if 0 /* if (isnegativeterm(A)) push(AxPlusB) negate() AxPlusB = pop() push(partOfPolynomialFactoredSoFar) negate_noexpand() partOfPolynomialFactoredSoFar = pop() */ //endif // partOfPolynomialFactoredSoFar 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(partOfPolynomialFactoredSoFar); push(AxPlusB); multiply_noexpand(); partOfPolynomialFactoredSoFar = 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(factpoly_expo, polycoeff, A, B); 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) { push(stack[polycoeff + i]); push(variable); // the free variable push_integer(i); power(); multiply(); add(); } remainingPoly = pop(); } //console.log("real branch remainingPoly: " + remainingPoly) } else if (whichRootsAreWeFinding === "complex") { if (foundComplexRoot === 0) { break; } else { // build the 2-degree polynomial out of the // real solution that was just found. push(A); // A push(variable); // x subtract(); //console.log("first factor: " + stack[tos-1].toString()) push(A); // A conjugate(); push(variable); // x subtract(); //console.log("second factor: " + stack[tos-1].toString()) multiply(); //if (factpoly_expo > 0 && isnegativeterm(stack[polycoeff+factpoly_expo])) // negate() // negate_noexpand() secondDegreePloly = pop(); if (DEBUG) { console.log("yyfactorpoly: " + firstParam + " " + secondParam + " success\nFACTOR=" + secondDegreePloly); } // factor out negative sign (not req'd because A > 1) //if 0 /* if (isnegativeterm(A)) push(secondDegreePloly) negate() secondDegreePloly = pop() push(partOfPolynomialFactoredSoFar) negate_noexpand() partOfPolynomialFactoredSoFar = pop() */ //endif // partOfPolynomialFactoredSoFar 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(partOfPolynomialFactoredSoFar); previousFactorisation = pop(); //console.log("previousFactorisation: " + previousFactorisation) push(partOfPolynomialFactoredSoFar); push(secondDegreePloly); multiply_noexpand(); partOfPolynomialFactoredSoFar = pop(); if (remainingPoly == null) { push(zero); for (i = i1 = 0, ref1 = factpoly_expo; (0 <= ref1 ? i1 <= ref1 : i1 >= ref1); i = 0 <= ref1 ? ++i1 : --i1) { push(stack[polycoeff + i]); push(variable); // the free variable push_integer(i); power(); multiply(); add(); } remainingPoly = pop(); } //console.log("original polynomial (dividend): " + remainingPoly) dividend = remainingPoly; //push(dividend) //degree() //startingDegree = pop() push(dividend); //console.log("dividing " + stack[tos-1].toString() + " by " + secondDegreePloly) push(secondDegreePloly); // divisor push(variable); // X divpoly(); remainingPoly = pop(); push(remainingPoly); push(secondDegreePloly); // 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"); } if (DEBUG) { console.log("so just returning previousFactorisation times dividend: " + previousFactorisation + " * " + dividend); } push(previousFactorisation); push(dividend); prev_expanding = expanding; expanding = 0; yycondense(); expanding = prev_expanding; multiply_noexpand(); partOfPolynomialFactoredSoFar = pop(); stack[h] = partOfPolynomialFactoredSoFar; moveTos(h + 1); restore(); return; } //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(); } coeff(variable, remainingPoly); factpoly_expo -= 2; } } } //console.log("factpoly_expo: " + factpoly_expo) if (DEBUG) { console.log("yyfactorpoly: " + firstParam + " " + secondParam + " building 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) { push(stack[polycoeff + i]); push(variable); // the free variable push_integer(i); power(); multiply(); add(); } polynomial = pop(); if (DEBUG) { console.log("yyfactorpoly: " + firstParam + " " + secondParam + " remaining unfactored part of the polynomial: " + polynomial.toString()); } push(polynomial); prev_expanding = expanding; expanding = 0; yycondense(); expanding = prev_expanding; polynomial = pop(); if (DEBUG) { console.log("yyfactorpoly: " + firstParam + " " + secondParam + " new poly with extracted common factor: " + polynomial.toString()); } //debugger // factor out negative sign if (factpoly_expo > 0 && isnegativeterm(stack[polycoeff + factpoly_expo])) { push(polynomial); //prev_expanding = expanding //expanding = 1 negate(); //expanding = prev_expanding polynomial = pop(); push(partOfPolynomialFactoredSoFar); negate_noexpand(); partOfPolynomialFactoredSoFar = pop(); } push(partOfPolynomialFactoredSoFar); push(polynomial); multiply_noexpand(); partOfPolynomialFactoredSoFar = pop(); if (DEBUG) { console.log("yyfactorpoly: " + firstParam + " " + secondParam + " result: " + partOfPolynomialFactoredSoFar); } stack[h] = partOfPolynomialFactoredSoFar; moveTos(h + 1); return restore(); }; rationalize_coefficients = function(h) { var i, i1, o, ratio, ratioInverse, ref, ref1, ref2, ref3; // LCM of all polynomial coefficients ratio = one; for (i = o = ref = h, ref1 = tos; (ref <= ref1 ? o < ref1 : o > ref1); i = ref <= ref1 ? ++o : --o) { push(stack[i]); denominator(); push(ratio); lcm(); ratio = pop(); } // multiply each coefficient by RESULT for (i = i1 = ref2 = h, ref3 = tos; (ref2 <= ref3 ? i1 < ref3 : i1 > ref3); i = ref2 <= ref3 ? ++i1 : --i1) { push(ratio); push(stack[i]); multiply(); stack[i] = pop(); } // reciprocate RESULT push(ratio); reciprocate(); ratioInverse = pop(); if (DEBUG) { console.log("rationalize_coefficients result: " + ratioInverse.toString()); } return ratioInverse; }; get_factor_from_real_root = function(variable, factpoly_expo, polycoeff) { var a0, an, evalPolyResult, h, i, i1, j, j1, l1, m1, na0, nan, o, polynomial, ref, ref1, ref2, ref3, ref4, rootsTries_i, rootsTries_j, testDenominator, testNumerator, testValue; if (DEBUG) { console.log("get_factor_from_real_root"); } i = 0; j = 0; h = 0; a0 = 0; an = 0; na0 = 0; nan = 0; if (DEBUG) { push(zero); for (i = o = 0, ref = factpoly_expo; (0 <= ref ? o <= ref : o >= ref); i = 0 <= ref ? ++o : --o) { push(stack[polycoeff + i]); push(variable); push_integer(i); power(); multiply(); add(); } polynomial = pop(); console.log("POLY=" + polynomial); } h = tos; an = tos; push(stack[polycoeff + factpoly_expo]); divisors_onstack(); nan = tos - an; a0 = tos; push(stack[polycoeff + 0]); divisors_onstack(); 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) { 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) { console.log(", " + stack[an + i]); } } // 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 testNumerator = stack[an + rootsTries_i]; testDenominator = stack[a0 + rootsTries_j]; push(testDenominator); push(testNumerator); divide(); negate(); testValue = pop(); evalPolyResult = Evalpoly(factpoly_expo, polycoeff, testValue); if (DEBUG) { console.log("try A=" + testNumerator); console.log(", B=" + testDenominator); console.log(", root " + variable); console.log("=-B/A=" + testValue); console.log(", POLY(" + testValue); console.log(")=" + evalPolyResult); } if (isZeroAtomOrTensor(evalPolyResult)) { moveTos(h); if (DEBUG) { console.log("get_factor_from_real_root returning 1"); } return [1, testNumerator, testDenominator]; } push(testDenominator); negate(); testDenominator = pop(); push(testValue); negate(); testValue = pop(); evalPolyResult = Evalpoly(factpoly_expo, polycoeff, testValue); if (DEBUG) { console.log("try A=" + testNumerator); console.log(", B=" + testDenominator); console.log(", root " + variable); console.log("=-B/A=" + testValue); console.log(", POLY(" + testValue); console.log(")=" + evalPolyResult); } if (isZeroAtomOrTensor(evalPolyResult)) { moveTos(h); if (DEBUG) { console.log("get_factor_from_real_root returning 1"); } return [1, testNumerator, testDenominator]; } } } moveTos(h); if (DEBUG) { console.log("get_factor_from_real_root returning"); } return [0, null, null]; }; get_factor_from_complex_root = function(remainingPoly, factpoly_expo, polycoeff) { var a0, an, evalPolyResult, h, i, i1, j, na0, nan, o, rootsTries_i, rootsTries_j, testValue; i = 0; j = 0; h = 0; a0 = 0; an = 0; na0 = 0; nan = 0; if (factpoly_expo <= 2) { if (DEBUG) { console.log("no more factoring via complex roots to be found in polynomial of degree <= 2"); } return [0, null]; } if (DEBUG) { console.log("complex root finding for POLY=" + remainingPoly); } 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(); rect(); testValue = pop(); if (DEBUG) { console.log("complex root finding: trying with " + testValue); } push(testValue); evalPolyResult = Evalpoly(factpoly_expo, polycoeff, testValue); if (DEBUG) { console.log("complex root finding result: " + evalPolyResult); } if (isZeroAtomOrTensor(evalPolyResult)) { moveTos(h); if (DEBUG) { console.log("get_factor_from_complex_root returning 1"); } return [1, testValue]; } // 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(); rect(); testValue = pop(); if (DEBUG) { console.log("complex root finding: trying with " + testValue); } push(testValue); evalPolyResult = Evalpoly(factpoly_expo, polycoeff, testValue); if (DEBUG) { console.log("complex root finding result: " + evalPolyResult); } if (isZeroAtomOrTensor(evalPolyResult)) { moveTos(h); if (DEBUG) { console.log("get_factor_from_complex_root returning 1"); } return [1, testValue]; } // 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); push_integer(rootsTries_j); push(imaginaryunit); multiply(); add(); rect(); testValue = pop(); if (DEBUG) { console.log("complex root finding: trying simple complex combination " + testValue); } push(testValue); evalPolyResult = Evalpoly(factpoly_expo, polycoeff, testValue); //console.log("complex root finding result: " + evalPolyResult) if (isZeroAtomOrTensor(evalPolyResult)) { moveTos(h); if (DEBUG) { console.log("found complex root: " + evalPolyResult); } return [1, testValue]; } } } moveTos(h); if (DEBUG) { console.log("get_factor_from_complex_root returning 0"); } return [0, null]; }; //----------------------------------------------------------------------------- // Divide a polynomial by Ax+B // Input: on stack: polycoeff Dividend coefficients // factpoly_expo as parameter // A as parameter // B as parameter // Output: on stack: polycoeff Contains quotient coefficients //----------------------------------------------------------------------------- yydivpoly = function(factpoly_expo, polycoeff, A, B) { var Q, i, o, ref; Q = zero; for (i = o = ref = factpoly_expo; (ref <= 0 ? o < 0 : o > 0); i = ref <= 0 ? ++o : --o) { push(stack[polycoeff + i]); stack[polycoeff + i] = Q; push(A); divide(); Q = pop(); push(stack[polycoeff + i - 1]); push(Q); push(B); multiply(); subtract(); stack[polycoeff + i - 1] = pop(); } stack[polycoeff + 0] = Q; if (DEBUG) { return console.log("yydivpoly Q: " + Q.toString()); } }; Evalpoly = function(factpoly_expo, polycoeff, evaluateAt) { var i, o, ref; push(zero); for (i = o = ref = factpoly_expo; (ref <= 0 ? o <= 0 : o >= 0); i = ref <= 0 ? ++o : --o) { push(evaluateAt); multiply(); push(stack[polycoeff + i]); //if DEBUG // console.log("Evalpoly top of stack:") // console.log stack[tos-i].toString() add(); } return 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; if (car(p) === symbol(ADD)) { p = cdr(p); while (iscons(p)) { push_term_factors(car(p)); p = cdr(p); } } else { push_term_factors(p); } 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); } }; Eval_filter = function() { var results; p1 = cdr(p1); push(car(p1)); Eval(); p1 = cdr(p1); results = []; while (iscons(p1)) { push(car(p1)); Eval(); filter(); results.push(p1 = cdr(p1)); } return results; }; filter = function() { save(); p2 = pop(); p1 = pop(); filter_main(); return restore(); }; filter_main = function() { if (car(p1) === symbol(ADD)) { return filter_sum(); } else if (istensor(p1)) { return filter_tensor(); } else if (Find(p1, p2)) { return push_integer(0); } else { return push(p1); } }; filter_sum = function() { var results; push_integer(0); p1 = cdr(p1); results = []; while (iscons(p1)) { push(car(p1)); push(p2); filter(); add(); results.push(p1 = cdr(p1)); } return results; }; filter_tensor = function() { var i, i1, n, o, ref, ref1; i = 0; n = 0; 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) { 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) { push(p1.tensor.elem[i]); push(p2); filter(); p3.tensor.elem[i] = pop(); } return push(p3); }; Eval_float = function() { evaluatingAsFloats++; push(cadr(p1)); Eval(); yyfloat(); Eval(); return evaluatingAsFloats--; }; checkFloatHasWorkedOutCompletely = function(nodeToCheck) { var numberOfEs, numberOfMults, numberOfPIs, numberOfPowers, numberOfSums; numberOfPowers = countOccurrencesOfSymbol(symbol(POWER), nodeToCheck); numberOfPIs = countOccurrencesOfSymbol(symbol(PI), nodeToCheck); numberOfEs = countOccurrencesOfSymbol(symbol(E), nodeToCheck); numberOfMults = countOccurrencesOfSymbol(symbol(MULTIPLY), nodeToCheck); numberOfSums = countOccurrencesOfSymbol(symbol(ADD), nodeToCheck); if (DEBUG) { console.log(" ... numberOfPowers: " + numberOfPowers); console.log(" ... numberOfPIs: " + numberOfPIs); console.log(" ... numberOfEs: " + numberOfEs); console.log(" ... numberOfMults: " + numberOfMults); console.log(" ... numberOfSums: " + numberOfSums); } if (numberOfPowers > 1 || numberOfPIs > 0 || numberOfEs > 0 || numberOfMults > 1 || numberOfSums > 1) { return stop("float: some unevalued parts in " + nodeToCheck); } }; zzfloat = function() { save(); evaluatingAsFloats++; //p1 = pop() //push(cadr(p1)) //push(p1) Eval(); yyfloat(); Eval(); evaluatingAsFloats--; 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; h = 0; evaluatingAsFloats++; save(); p1 = pop(); if (iscons(p1)) { h = tos; while (iscons(p1)) { push(car(p1)); yyfloat(); p1 = cdr(p1); } list(tos - h); } else if (p1.k === TENSOR) { 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) { push(p1.tensor.elem[i]); yyfloat(); p1.tensor.elem[i] = pop(); } push(p1); } else if (p1.k === NUM) { push(p1); bignum_float(); } else if (p1 === symbol(PI)) { push_double(Math.PI); } else if (p1 === symbol(E)) { push_double(Math.E); } else { push(p1); } restore(); return evaluatingAsFloats--; }; Eval_floor = function() { push(cadr(p1)); Eval(); return yfloor(); }; yfloor = function() { save(); yyfloor(); return restore(); }; yyfloor = function() { var d; d = 0.0; p1 = pop(); if (!isNumericAtom(p1)) { push_symbol(FLOOR); push(p1); list(2); return; } if (isdouble(p1)) { d = Math.floor(p1.d); push_double(d); return; } if (isinteger(p1)) { push(p1); return; } p3 = new U(); p3.k = NUM; p3.q.a = mdiv(p1.q.a, p1.q.b); p3.q.b = mint(1); push(p3); if (isnegativenumber(p1)) { push_integer(-1); return add(); } }; // '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; j = 0; k = 0; loopingVariable = caddr(p1); if (!issymbol(loopingVariable)) { stop("for: 2nd arg should be the variable to loop over"); } push(cadddr(p1)); Eval(); j = pop_integer(); if (isNaN(j)) { push(p1); return; } push(caddddr(p1)); Eval(); k = pop_integer(); if (isNaN(k)) { 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) { push_integer(i); p5 = pop(); set_binding(loopingVariable, p5); push(cadr(p1)); 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(); return gamma(); }; gamma = function() { save(); gammaf(); return restore(); }; gammaf = function() { // double d p1 = pop(); if (isrational(p1) && MEQUAL(p1.q.a, 1) && MEQUAL(p1.q.b, 2)) { if (evaluatingAsFloats) { push_double(Math.PI); } else { push_symbol(PI); } push_rational(1, 2); power(); return; } if (isrational(p1) && MEQUAL(p1.q.a, 3) && MEQUAL(p1.q.b, 2)) { if (evaluatingAsFloats) { push_double(Math.PI); } else { push_symbol(PI); } push_rational(1, 2); power(); push_rational(1, 2); multiply(); return; } // if (p1->k == DOUBLE) { // d = exp(lgamma(p1.d)) // push_double(d) // return // } if (isnegativeterm(p1)) { if (evaluatingAsFloats) { push_double(Math.PI); } else { push_symbol(PI); } push_integer(-1); multiply(); if (evaluatingAsFloats) { push_double(Math.PI); } else { push_symbol(PI); } push(p1); multiply(); sine(); push(p1); multiply(); push(p1); negate(); gamma(); multiply(); divide(); return; } if (car(p1) === symbol(ADD)) { gamma_of_sum(); return; } push_symbol(GAMMA); push(p1); list(2); }; gamma_of_sum = function() { p3 = cdr(p1); if (isrational(car(p3)) && MEQUAL(car(p3).q.a, 1) && MEQUAL(car(p3).q.b, 1)) { push(cadr(p3)); push(cadr(p3)); gamma(); return multiply(); } else { if (isrational(car(p3)) && MEQUAL(car(p3).q.a, -1) && MEQUAL(car(p3).q.b, 1)) { push(cadr(p3)); gamma(); push(cadr(p3)); push_integer(-1); add(); return divide(); } else { push_symbol(GAMMA); push(p1); list(2); } } }; // 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); push(car(p1)); Eval(); p1 = cdr(p1); results = []; while (iscons(p1)) { push(car(p1)); Eval(); gcd(); results.push(p1 = cdr(p1)); } return results; }; gcd = function() { var prev_expanding; prev_expanding = expanding; save(); gcd_main(); restore(); return expanding = prev_expanding; }; gcd_main = function() { var polyVar; expanding = 1; p2 = pop(); p1 = pop(); if (DEBUG) { console.log("gcd_main: p1: " + p1 + " p2: " + p2); } if (equal(p1, p2)) { push(p1); return; } if (isrational(p1) && isrational(p2)) { push(p1); push(p2); gcd_numbers(); return; } if ((polyVar = areunivarpolysfactoredorexpandedform(p1, p2))) { gcd_polys(polyVar); return; } if (car(p1) === symbol(ADD) && car(p2) === symbol(ADD)) { gcd_sum_sum(); return; } if (car(p1) === symbol(ADD)) { gcd_sum(p1); p1 = pop(); } if (car(p2) === symbol(ADD)) { gcd_sum(p2); p2 = pop(); } if (car(p1) === symbol(MULTIPLY)) { gcd_sum_product(); return; } if (car(p2) === symbol(MULTIPLY)) { gcd_product_sum(); return; } if (car(p1) === symbol(MULTIPLY) && car(p2) === symbol(MULTIPLY)) { gcd_product_product(); return; } return gcd_powers_with_same_base(); }; areunivarpolysfactoredorexpandedform = function(p1, p2) { var polyVar; if (DEBUG) { console.log("areunivarpolysfactoredorexpandedform: p1: " + p1 + " p2: " + p2); } if (polyVar = isunivarpolyfactoredorexpandedform(p1)) { if (isunivarpolyfactoredorexpandedform(p2, polyVar)) { return polyVar; } } return false; }; gcd_polys = function(polyVar) { if (DEBUG) { console.log("gcd_polys: p1: " + p1 + " polyVar: " + polyVar); } // gcd of factors push(p1); push(polyVar); factorpoly(); p1 = pop(); push(p2); push(polyVar); factorpoly(); p2 = pop(); if (DEBUG) { console.log("GCD: factored polys:"); } if (DEBUG) { console.log(" p1:" + p1.toString()); } 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); push(one); list(3); p1 = pop(); } if (car(p2) !== symbol(MULTIPLY)) { push_symbol(MULTIPLY); push(p2); push(one); list(3); p2 = pop(); } } if (car(p1) === symbol(MULTIPLY) && car(p2) === symbol(MULTIPLY)) { gcd_product_product(); return; } gcd_powers_with_same_base(); return true; }; gcd_product_product = function() { var results; push(one); p3 = cdr(p1); results = []; while (iscons(p3)) { p4 = cdr(p2); while (iscons(p4)) { push(car(p3)); push(car(p4)); gcd(); multiply(); p4 = cdr(p4); } results.push(p3 = cdr(p3)); } return results; }; gcd_powers_with_same_base = function() { if (car(p1) === symbol(POWER)) { p3 = caddr(p1); // exponent p1 = cadr(p1); // base } else { p3 = one; } if (car(p2) === symbol(POWER)) { p4 = caddr(p2); // exponent p2 = cadr(p2); // base } else { p4 = one; } if (!equal(p1, p2)) { push(one); return; } // are both exponents numerical? if (isNumericAtom(p3) && isNumericAtom(p4)) { push(p1); if (lessp(p3, p4)) { push(p3); } else { push(p4); } 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 { p5 = one; } if (car(p4) === symbol(MULTIPLY) && isNumericAtom(cadr(p4))) { p6 = cadr(p4); } else { p6 = one; } if (lessp(p5, p6)) { push(p3); } else { push(p4); } power(); return; } push(p3); push(p4); subtract(); p5 = pop(); if (!isNumericAtom(p5)) { push(one); return; } // can't be equal because of test near beginning push(p1); if (isnegativenumber(p5)) { push(p3); } else { push(p4); } 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); return; } p3 = cdr(p1); push(car(p3)); p3 = cdr(p3); while (iscons(p3)) { push(car(p3)); gcd(); p3 = cdr(p3); } p3 = pop(); p4 = cdr(p2); push(car(p4)); p4 = cdr(p4); while (iscons(p4)) { push(car(p4)); gcd(); p4 = cdr(p4); } p4 = pop(); push(p1); push(p3); divide(); p5 = pop(); push(p2); push(p4); divide(); p6 = pop(); if (equal(p5, p6)) { push(p5); push(p3); push(p4); gcd(); return multiply(); } else { return push(one); } }; gcd_sum = function(p) { var results; p = cdr(p); push(car(p)); p = cdr(p); results = []; while (iscons(p)) { push(car(p)); gcd(); results.push(p = cdr(p)); } return results; }; gcd_sum_product = function() { var results; push(one); p3 = cdr(p1); results = []; while (iscons(p3)) { push(car(p3)); push(p2); gcd(); multiply(); results.push(p3 = cdr(p3)); } return results; }; gcd_product_sum = function() { var results; push(one); p4 = cdr(p2); results = []; while (iscons(p4)) { push(p1); push(car(p4)); gcd(); multiply(); results.push(p4 = cdr(p4)); } return results; }; // Guess which symbol to use for derivative, integral, etc. guess = function() { var p; p = pop(); push(p); if (Find(p, symbol(SYMBOL_X))) { return push_symbol(SYMBOL_X); } else if (Find(p, symbol(SYMBOL_Y))) { return push_symbol(SYMBOL_Y); } else if (Find(p, symbol(SYMBOL_Z))) { return push_symbol(SYMBOL_Z); } else if (Find(p, symbol(SYMBOL_T))) { return push_symbol(SYMBOL_T); } else if (Find(p, symbol(SYMBOL_S))) { return push_symbol(SYMBOL_S); } else { return push_symbol(SYMBOL_X); } }; //----------------------------------------------------------------------------- // 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; p2 = pop(); p1 = pop(); push(p2); n = pop_integer(); if (n < 0 || isNaN(n)) { push_symbol(HERMITE); push(p1); push(p2); list(3); return; } if (issymbol(p1)) { return yyhermite2(n); } else { p3 = p1; p1 = symbol(SECRETX); yyhermite2(n); p1 = p3; push(symbol(SECRETX)); push(p1); subst(); return Eval(); } }; yyhermite2 = function(n) { var i, o, ref, results; i = 0; push_integer(1); push_integer(0); p4 = pop(); results = []; for (i = o = 0, ref = n; (0 <= ref ? o < ref : o > ref); i = 0 <= ref ? ++o : --o) { p5 = p4; p4 = pop(); push(p1); push(p4); multiply(); push_integer(i); push(p5); multiply(); subtract(); push_integer(2); results.push(multiply()); } 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; j = 0; n = 0; save(); p2 = pop(); push(p2); n = pop_integer(); if (n < 2) { push_symbol(HILBERT); push(p2); list(2); restore(); return; } 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) { push_integer(i + j + 1); inverse(); p1.tensor.elem[i * n + j] = pop(); } } push(p1); return restore(); }; DEBUG_IMAG = false; Eval_imag = function() { push(cadr(p1)); Eval(); return imag(); }; imag = function() { save(); rect(); p1 = pop(); if (DEBUG_IMAG) { console.log("IMAGE of " + p1); } push(p1); push(p1); conjugate(); if (DEBUG_IMAG) { console.log(" image: conjugate result: " + stack[tos - 1]); } subtract(); push_integer(2); divide(); if (DEBUG_IMAG) { console.log(" image: 1st divide result: " + stack[tos - 1]); } push(imaginaryunit); divide(); if (DEBUG_IMAG) { console.log(" image: 2nd divide result: " + stack[tos - 1]); } 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; k = 0; m = 0; ndim = 0; nelem = 0; t = 0; save(); s = tos - n; p1 = stack[s]; ndim = p1.tensor.ndim; m = n - 1; if (m > ndim) { 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) { push(stack[s + i + 1]); t = pop_integer(); if (t < 1 || t > p1.tensor.dim[i]) { stop("index out of range"); } k = k * p1.tensor.dim[i] + t - 1; } if (ndim === m) { moveTos(tos - n); push(p1.tensor.elem[k]); restore(); return; } 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) { 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) { 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) { p2.tensor.elem[i] = p1.tensor.elem[k + i]; } check_tensor_dimensions(p1); check_tensor_dimensions(p2); moveTos(tos - n); push(p2); 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; k = 0; m = 0; ndim = 0; t = 0; save(); if (n < 3) { stop("error in indexed assign"); } s = tos - n; p2 = stack[s]; p1 = stack[s + 1]; if (!istensor(p1)) { // p1 is LVALUE stop("error in indexed assign: assigning to something that is not a tensor"); } ndim = p1.tensor.ndim; m = n - 2; if (m > ndim) { stop("error in indexed assign"); } k = 0; 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]) { stop("error in indexed assign\n"); } 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) { 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) { 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) { p3.tensor.elem[i] = p1.tensor.elem[i]; } check_tensor_dimensions(p1); check_tensor_dimensions(p3); p1 = p3; if (ndim === m) { if (istensor(p2)) { // p2 is RVALUE stop("error in indexed assign"); } p1.tensor.elem[k] = p2; check_tensor_dimensions(p1); moveTos(tos - n); push(p1); restore(); return; } if (!istensor(p2)) { // p2 is RVALUE stop("error in indexed assign"); } if (ndim - m !== p2.tensor.ndim) { // p2 is RVALUE stop("error in indexed assign"); } // 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"); } } // 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); check_tensor_dimensions(p2); moveTos(tos - n); push(p1); return restore(); }; 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))); if (secondArgument === symbol(NIL)) { stop("pattern needs at least a template and a transformed version"); } moretheArguments = cdr(cdr(p1)); while (moretheArguments !== symbol(NIL)) { 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) { push_symbol(INNER); swap(); push(theArguments[theArguments.length - i - 1]); swap(); list(3); } p1 = pop(); 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 = []; // 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 { refinedOperands.push(operands[i]); } } 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) { //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]); Eval(); inv(); push(operands[i + shift + 1]); Eval(); subtract(); difference = pop(); //console.log "result: " + difference if (isZeroAtomOrTensor(difference)) { shift += 1; } else { refinedOperands.push(operands[i + shift]); } } else { refinedOperands.push(operands[i + shift]); } } 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) { break; } } 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) { //console.log "pushing " + operands[i] push(operands[i]); } } else { pop(); push(symbol(SYMBOL_IDENTITY_MATRIX)); return; } //console.log "list(operands.length): " + (operands.length+1) list(operands.length + 1); p1 = pop(); p1 = cdr(p1); push(car(p1)); Eval(); p1 = cdr(p1); results = []; while (iscons(p1)) { push(car(p1)); Eval(); inner(); results.push(p1 = cdr(p1)); } 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(); p2 = pop(); push(p1); 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)); //a arg2 = car(cdr(cdr(p1))); //b arg3 = p2; p1 = arg1; push(arg2); push(arg3); 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(); return; } else if (p2 === symbol(SYMBOL_IDENTITY_MATRIX)) { push(p1); restore(); return; } if (istensor(p1) && istensor(p2)) { inner_f(); } else { if (!(isNumericAtomOrTensor(p1) || isNumericAtomOrTensor(p2))) { push(p1); push(p2); inv(); subtract(); subtractionResult = pop(); if (isZeroAtomOrTensor(subtractionResult)) { push_symbol(SYMBOL_IDENTITY_MATRIX); restore(); return; } } // if either 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)); push(p2); inner(); add(); p1 = cdr(p1); } restore(); return; } if (expanding && isadd(p2)) { p2 = cdr(p2); push(zero); while (iscons(p2)) { push(p1); push(car(p2)); inner(); add(); p2 = cdr(p2); } restore(); return; } 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); push(p1); push(p2); list(3); restore(); return; } } } 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; n = p1.tensor.dim[p1.tensor.ndim - 1]; if (n !== p2.tensor.dim[0]) { debugger; stop("inner: tensor dimension check"); } ndim = p1.tensor.ndim + p2.tensor.ndim - 2; if (ndim > MAXDIM) { stop("inner: rank of result exceeds maximum"); } 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) { 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) { bk *= p2.tensor.dim[i]; } p3 = alloc_tensor(ak * bk); c = p3.tensor.elem; // 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) { push(a[i * n + j]); push(b[j * bk + k]); multiply(); push(c[i * bk + k]); add(); c[i * bk + k] = pop(); } } } //--------------------------------------------------------------------- // 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) { 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) { 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); return; } if (cdr(tree) === symbol(NIL)) { tree = get_innerprod_factors(car(tree), factors_accumulator); 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; } return add_factor_to_accumulator(tree, factors_accumulator); }; add_factor_to_accumulator = function(tree, factors_accumulator) { if (tree !== symbol(NIL)) { // console.log ">> adding to factors_accumulator: " + tree return factors_accumulator.push(tree); } }; 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(); p2 = pop(); if (p2 === symbol(NIL)) { guess(); push(symbol(NIL)); } else if (isNumericAtom(p2)) { guess(); push(p2); } else { push(p2); p1 = cdr(p1); push(car(p1)); Eval(); } p5 = pop(); p4 = pop(); p3 = pop(); while (1) { // N might be a symbol instead of a number if (isNumericAtom(p5)) { push(p5); n = pop_integer(); if (isNaN(n)) { stop("nth integral: check n"); } } else { n = 1; } push(p3); if (n >= 0) { 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) { 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; // arglist exhausted } if (isNumericAtom(p5)) { doNothing = 1; // N = arg1 } else { p4 = p5; p1 = cdr(p1); push(car(p1)); Eval(); p5 = pop(); // N = arg2 } } else { p4 = p5; p1 = cdr(p1); push(car(p1)); Eval(); p5 = pop(); // N = arg1 } } return push(p3); // final result }; integral = function() { save(); p2 = pop(); p1 = pop(); if (car(p1) === symbol(ADD)) { integral_of_sum(); } else if (car(p1) === symbol(MULTIPLY)) { integral_of_product(); } else { integral_of_form(); } p1 = pop(); if (Find(p1, symbol(INTEGRAL))) { stop("integral: sorry, could not find a solution"); } push(p1); simplify(); Eval(); return restore(); }; integral_of_sum = function() { var results; p1 = cdr(p1); push(car(p1)); push(p2); integral(); p1 = cdr(p1); results = []; while (iscons(p1)) { push(car(p1)); push(p2); integral(); add(); results.push(p1 = cdr(p1)); } return results; }; integral_of_product = function() { push(p1); push(p2); partition(); p1 = pop(); integral_of_form(); return multiply(); // multiply constant part }; integral_of_form = function() { var hc, tab; 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); // free variable push(p2); // input expression transform(tab, false); p3 = pop(); if (p3 === symbol(NIL)) { push_symbol(INTEGRAL); push(p1); push(p2); return list(3); } else { return push(p3); } }; // 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, 'constant': 1.14416593629414332, 'constbase': 1.20364122304218824, 'sin': 1.73305482518303221, 'arcsin': 1.6483368529465804, 'cos': 1.058672123686340116, 'arccos': 1.8405225918106694, 'tan': 1.12249437762925064, 'arctan': 1.1297397925394962, 'sinh': 1.8176164926060078, 'cosh': 1.9404934661708022, 'tanh': 1.6421307715103121, 'log': 1.47744370135492387, 'erf': 1.0825269225702916 }; italu_hashcode = function(u, x) { var half; if (issymbol(u)) { if (equal(u, x)) { return hashcode_values.x; } else { return hashcode_values.constant; } } else if (iscons(u)) { switch (symnum(car(u))) { case ADD: return hash_addition(cdr(u), x); case MULTIPLY: return hash_multiplication(cdr(u), x); case POWER: return hash_power(cadr(u), caddr(u), x); case EXP: return hash_power(symbol(E), cadr(u), x); case SQRT: push_double(0.5); half = pop(); return hash_power(cadr(u), half, x); default: return hash_function(u, x); } } return hashcode_values.constant; }; hash_function = function(u, x) { var arg_hash, base, name; if (!Find(cadr(u), x)) { return hashcode_values.constant; } name = car(u); arg_hash = italu_hashcode(cadr(u), x); base = hashcode_values[name.printname]; if (!base) { throw new Error('Unsupported function ' + name.printname); } return Math.pow(base, arg_hash); }; hash_addition = function(terms, x) { var k, sum, term, term_hash, term_set, v; term_set = {}; while (iscons(terms)) { term = car(terms); terms = cdr(terms); term_hash = 0; 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; } sum = 0; for (k in term_set) { if (!hasProp.call(term_set, k)) continue; v = term_set[k]; sum = sum + parseFloat(k, 10); } return sum; }; hash_multiplication = function(terms, x) { var product, term; product = 1; while (iscons(terms)) { term = car(terms); terms = cdr(terms); if (Find(term, x)) { product = product * italu_hashcode(term, x); } } return product; }; hash_power = function(base, power, x) { var base_hash, exp_hash; base_hash = hashcode_values.constant; exp_hash = hashcode_values.constexp; if (Find(base, x)) { base_hash = italu_hashcode(base, x); } 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; } if (isminusone(power)) { exp_hash = -1; } else if (isoneovertwo(power)) { exp_hash = 0.5; } else if (isminusoneovertwo(power)) { exp_hash = -0.5; } else if (equalq(power, 2, 1)) { exp_hash = 2; } else if (equalq(power, -2, 1)) { exp_hash = -2; } } return Math.pow(base_hash, exp_hash); }; make_hashed_itab = function() { var f, h, key, len, o, s, tab, u; tab = {}; for (o = 0, len = itab.length; o < len; o++) { s = itab[o]; if (!s) { break; } scan_meta(s); f = pop(); u = cadr(f); h = italu_hashcode(u, symbol(METAX)); key = h.toFixed(6); if (!tab[key]) { tab[key] = []; } tab[key].push(s); } console.log('hashed_itab = ' + JSON.stringify(tab, null, 2)); return tab; }; $.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))"], "0.936400": ["f(x^a,x^(a+1)/(a+1))"], "1.095727": ["f(x^(-2),-x^(-1))"], "1.023118": ["f(x^(-1/2),2*x^(1/2))"], "0.977405": ["f(x^(1/2),2/3*x^(3/2))"], "0.955320": ["f(x,x^2/2)"], "0.912636": ["f(x^2,x^3/3)"], "1.137302": ["f(exp(a*x),1/a*exp(a*x))", "f(a^x,a^x/log(a),or(not(number(a)),a>0))"], "1.326774": ["f(exp(a*x+b),1/a*exp(a*x+b))"], "1.080259": ["f(x*exp(a*x^2),exp(a*x^2)/(2*a))"], "1.260228": ["f(x*exp(a*x^2+b),exp(a*x^2+b)/(2*a))"], "1.451902": ["f(log(a*x),x*log(a*x)-x)"], "0.486192": ["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/(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))"], "0.697274": ["f(1/sqrt(a-x^2),arcsin(x/(sqrt(a))))", "f(1/sqrt(a+x^2),log(x+sqrt(a+x^2)))", "f(1/sqrt(x^2+a),log(x+sqrt(x^2+a)))"], "0.476307": ["f(1/(a+b*x),1/b*log(a+b*x))"], "0.226868": ["f(1/(a+b*x)^2,-1/(b*(a+b*x)))"], "2.904531": ["f(1/(a+b*x)^3,-1/(2*b)*1/(a+b*x)^2)"], "0.455026": ["f(x/(a+b*x),x/b-a*log(a+b*x)/b/b)"], "0.216732": ["f(x/(a+b*x)^2,1/b^2*(log(a+b*x)+a/(a+b*x)))"], "0.434695": ["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)))"], "0.207048": ["f(x^2/(a+b*x)^2,1/b^3*(a+b*x-2*a*log(a+b*x)-a^2/(a+b*x)))"], "2.650781": ["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))"], "0.498584": ["f(1/x*1/(a+b*x),-1/a*log((a+b*x)/x))"], "0.237479": ["f(1/x*1/(a+b*x)^2,1/a*1/(a+b*x)-1/a^2*log((a+b*x)/x))"], "3.040375": ["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))))"], "0.521902": ["f(1/x^2*1/(a+b*x),-1/(a*x)+b/a^2*log((a+b*x)/x))"], "0.446014": ["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)))"], "0.248586": ["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))"], "0.464469": ["f(x/(a+b*x^2),1/2*1/b*log(a+b*x^2))"], "0.443716": ["f(x^2/(a+b*x^2),x/b-a/b*integral(1/(a+b*x^2),x))"], "0.236382": ["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))"], "0.508931": ["f(1/x*1/(a+b*x^2),1/2*1/a*log(x^2/(a+b*x^2)))"], "0.532733": ["f(1/x^2*1/(a+b*x^2),-1/(a*x)-b/a*integral(1/(a+b*x^2),x))"], "0.480638": ["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))))"], "0.438648": ["f(x^2/(a+b*x^3),1/3*1/b*log(a+b*x^3))"], "0.459164": ["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))"], "0.450070": ["f(x^3/(a+b*x^4),1/4*1/b*log(a+b*x^4))"], "1.448960": ["f(sqrt(a+b*x),2/3*1/b*sqrt((a+b*x)^3))"], "1.384221": ["f(x*sqrt(a+b*x),-2*(2*a-3*b*x)*sqrt((a+b*x)^3)/15/b^2)"], "1.322374": ["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)"], "1.516728": ["f(sqrt(a+b*x)/x,2*sqrt(a+b*x)+a*integral(1/x*1/sqrt(a+b*x),x))"], "1.587665": ["f(sqrt(a+b*x)/x^2,-sqrt(a+b*x)/x+b/2*integral(1/x*1/sqrt(a+b*x),x))"], "0.690150": ["f(1/sqrt(a+b*x),2*sqrt(a+b*x)/b)"], "0.659314": ["f(x/sqrt(a+b*x),-2/3*(2*a-b*x)*sqrt(a+b*x)/b^2)"], "0.629856": ["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)"], "0.722428": ["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))"], "0.756216": ["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))"], "1.434156": ["f(sqrt(x^2+a),1/2*(x*sqrt(x^2+a)+a*log(x+sqrt(x^2+a))))", "f(sqrt(a-x^2),1/2*(x*sqrt(a-x^2)+a*arcsin(x/sqrt(abs(a)))))", "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))"], "0.729886": ["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(1/x*1/sqrt(a-x^2),-1/sqrt(a)*log((sqrt(a)+sqrt(a-x^2))/x),or(not(number(a)),a>0))"], "1.501230": ["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(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))"], "0.666120": ["f(x/sqrt(x^2+a),sqrt(x^2+a))", "f(x/sqrt(a-x^2),-sqrt(a-x^2))"], "1.370077": ["f(x*sqrt(x^2+a),1/3*sqrt((x^2+a)^3))", "f(x*sqrt(a-x^2),-1/3*sqrt((a-x^2)^3))"], "1.730087": ["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)))))"], "0.578006": ["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)))"], "0.552180": ["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)))"], "1.652787": ["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))"], "1.308862": ["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^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))"], "1.342944": ["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^3*sqrt(a-x^2),(-1/5*x^2-2/15*a)*sqrt((a-x^2)^3),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))"], "0.636358": ["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^2/sqrt(a-x^2),-x/2*sqrt(a-x^2)+a/2*arcsin(x/sqrt(a)),or(not(number(a)),a>0))"], "0.652928": ["f(x^3/sqrt(x^2+a),1/3*sqrt((x^2+a)^3)-a*sqrt(x^2+a))", "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))"], "0.764022": ["f(1/x^2*1/sqrt(x^2+a),-sqrt(x^2+a)/a/x)", "f(1/x^2*1/sqrt(a-x^2),-sqrt(a-x^2)/a/x,or(not(number(a)),a>0))"], "1.578940": ["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))"], "1.620055": ["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))"], "0.332117": ["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))"], "1.571443": ["f(sqrt(a-x^2)/x^2,-sqrt(a-x^2)/x-arcsin(x/sqrt(a)),or(not(number(a)),a>0))"], "1.690994": ["f(sin(a*x),-cos(a*x)/a)"], "1.055979": ["f(cos(a*x),sin(a*x)/a)"], "1.116714": ["f(tan(a*x),-log(cos(a*x))/a)"], "0.895484": ["f(1/tan(a*x),log(sin(a*x))/a)"], "0.946989": ["f(1/cos(a*x),log(tan(pi/4+a*x/2))/a)"], "0.591368": ["f(1/sin(a*x),log(tan(a*x/2))/a)"], "2.859462": ["f(sin(a*x)^2,x/2-sin(2*a*x)/(4*a))"], "2.128050": ["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))"], "1.115091": ["f(cos(a*x)^2,x/2+sin(2*a*x)/(4*a))"], "1.081452": ["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))"], "0.349716": ["f(1/sin(a*x)^2,-1/(a*tan(a*x)))"], "0.896788": ["f(1/cos(a*x)^2,tan(a*x)/a)"], "1.785654": ["f(sin(a*x)*cos(a*x),sin(a*x)^2/(2*a))"], "3.188560": ["f(sin(a*x)^2*cos(a*x)^2,-sin(4*a*x)/(32*a)+x/8)"], "1.516463": ["f(sin(a*x)/cos(a*x)^2,1/(a*cos(a*x)))"], "2.707879": ["f(sin(a*x)^2/cos(a*x),(log(tan(pi/4+a*x/2))-sin(a*x))/a)"], "0.369293": ["f(cos(a*x)/sin(a*x)^2,-1/(a*sin(a*x)))"], "0.560019": ["f(1/(sin(a*x)*cos(a*x)),log(tan(a*x))/a)"], "0.530332": ["f(1/(sin(a*x)*cos(a*x)^2),(1/cos(a*x)+log(tan(a*x/2)))/a)"], "0.331177": ["f(1/(sin(a*x)^2*cos(a*x)),(log(tan(pi/4+a*x/2))-1/sin(a*x))/a)"], "0.313621": ["f(1/(sin(a*x)^2*cos(a*x)^2),-2/(a*tan(2*a*x)))"], "3.172365": ["f(sin(a+b*x),-cos(a+b*x)/b)"], "1.127162": ["f(cos(a+b*x),sin(a+b*x)/b)"], "0.352714": ["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/(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)"], "0.454515": ["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*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)"], "1.615441": ["f(x*sin(a*x),sin(a*x)/a^2-x*cos(a*x)/a)"], "1.543263": ["f(x^2*sin(a*x),2*x*sin(a*x)/a^2-(a^2*x^2-2)*cos(a*x)/a^3)"], "1.008798": ["f(x*cos(a*x),cos(a*x)/a^2+x*sin(a*x)/a)"], "0.963724": ["f(x^2*cos(a*x),2*x*cos(a*x)/a^2+(a^2*x^2-2)*sin(a*x)/a^3)"], "1.611938": ["f(arcsin(a*x),x*arcsin(a*x)+sqrt(1-a^2*x^2)/a)"], "1.791033": ["f(arccos(a*x),x*arccos(a*x)-sqrt(1-a^2*x^2)/a)"], "1.123599": ["f(arctan(a*x),x*arctan(a*x)-1/2*log(1+a^2*x^2)/a)"], "1.387031": ["f(x*log(a*x),x^2*log(a*x)/2-x^2/4)"], "1.325058": ["f(x^2*log(a*x),x^3*log(a*x)/3-1/9*x^3)"], "2.108018": ["f(log(x)^2,x*log(x)^2-2*x*log(x)+2*x)"], "0.403214": ["f(1/x*1/(a+log(x)),log(a+log(x)))"], "2.269268": ["f(log(a*x+b),(a*x+b)*log(a*x+b)/a-x)"], "2.486498": ["f(log(a*x+b)/x^2,a/b*log(x)-(a*x+b)*log(a*x+b)/b/x)"], "1.769733": ["f(sinh(x),cosh(x))"], "1.883858": ["f(cosh(x),sinh(x))"], "1.606140": ["f(tanh(x),log(cosh(x)))"], "1.690661": ["f(x*sinh(x),x*cosh(x)-sinh(x))"], "1.799688": ["f(x*cosh(x),x*sinh(x)-cosh(x))"], "3.131954": ["f(sinh(x)^2,sinh(2*x)/4-x/2)"], "2.579685": ["f(tanh(x)^2,x-tanh(x))"], "3.548923": ["f(cosh(x)^2,sinh(2*x)/4+x/2)"], "1.058866": ["f(x^3*exp(a*x^2),exp(a*x^2)*(x^2/a-1/(a^2))/2)"], "1.235270": ["f(x^3*exp(a*x^2+b),exp(a*x^2)*exp(b)*(x^2/a-1/(a^2))/2)"], "1.130783": ["f(exp(a*x^2),-i*sqrt(pi)*erf(i*sqrt(a)*x)/sqrt(a)/2)"], "1.078698": ["f(erf(a*x),x*erf(a*x)+exp(-a^2*x^2)/a/sqrt(pi))"], "2.573650": ["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)"], "2.640666": ["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)"], "1.086487": ["f(x*exp(a*x),exp(a*x)*(a*x-1)/(a^2))"], "1.267493": ["f(x*exp(a*x+b),exp(a*x+b)*(a*x-1)/(a^2))"], "1.037943": ["f(x^2*exp(a*x),exp(a*x)*(a^2*x^2-2*a*x+2)/(a^3))"], "1.210862": ["f(x^2*exp(a*x+b),exp(a*x+b)*(a^2*x^2-2*a*x+2)/(a^3))"], "1.064970": ["f(x^3*exp(a*x),exp(a*x)*x^3/a-3/a*integral(x^2*exp(a*x),x))"], "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; } else if (p1.tensor.ndim !== 2) { return 0; } else if (p1.tensor.dim[0] !== p1.tensor.dim[1]) { return 0; } else { return 1; } }; inv = function() { 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 = []; while (iscons(p1)) { 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) { push(accumulator[eachEntry]); inv(); if (eachEntry !== accumulator.length - 1) { inner(); } } restore(); return; } if (INV_check_arg() === 0) { push_symbol(INV); push(p1); list(2); restore(); return; } if (isNumericAtomOrTensor(p1)) { yyinvg(); } else { push(p1); adj(); push(p1); det(); p2 = pop(); if (isZeroAtomOrTensor(p2)) { stop("inverse of singular matrix"); } push(p2); divide(); } return restore(); }; invg = function() { save(); p1 = pop(); if (INV_check_arg() === 0) { push_symbol(INVG); push(p1); list(2); restore(); return; } yyinvg(); return restore(); }; // inverse using gaussian elimination yyinvg = function() { var h, i, i1, j, j1, l1, n, o, ref, ref1, ref2, ref3; h = 0; i = 0; j = 0; 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) { if (i === j) { push(one); } else { push(zero); } } } 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); p1 = alloc_tensor(n * n); 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) { 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; d = 0; i = 0; j = 0; u = 0; 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) { // diagonal element zero? if (equal(stack[a + n * d + d], zero)) { // 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; } } if (i === n) { stop("inverse of singular matrix"); } // 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; p2 = stack[u + n * d + j]; stack[u + n * d + j] = stack[u + n * i + j]; 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) { if (j > d) { push(stack[a + n * d + j]); push(p2); divide(); stack[a + n * d + j] = pop(); } push(stack[u + n * d + j]); push(p2); divide(); stack[u + n * d + j] = pop(); } 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) { 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) { if (j > d) { push(stack[a + n * i + j]); push(stack[a + n * d + j]); push(p2); multiply(); subtract(); stack[a + n * i + j] = pop(); } push(stack[u + n * i + j]); push(stack[u + n * d + j]); push(p2); multiply(); subtract(); results2.push(stack[u + n * i + j] = pop()); } return results2; })()); } return results1; })()); } return results; }; DEBUG_IS = false; // jsBoolToToInt = (p) -> // if p then 1 else 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. isZeroAtom = function(p) { switch (p.k) { case NUM: return MZERO(p.q.a); case DOUBLE: return p.d === 0.0; default: return 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. 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) { if (!isZeroAtomOrTensor(p.tensor.elem[i])) { return 0; } } 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; } if (isNumericAtomOrTensor(evalledArgument)) { 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: if (MSIGN(p.q.a) === -1) { return 1; } break; case DOUBLE: if (p.d < 0.0) { return 1; } } return 0; }; // p is a U ispositivenumber = function(p) { switch (p.k) { case NUM: if (MSIGN(p.q.a) === 1) { return 1; } break; case DOUBLE: if (p.d > 0.0) { return 1; } } return 0; }; // p is a U isplustwo = function(p) { switch (p.k) { case NUM: if (MEQUAL(p.q.a, 2) && MEQUAL(p.q.b, 1)) { return 1; } break; case DOUBLE: if (p.d === 2.0) { return 1; } } return 0; }; // p is a U isplusone = function(p) { switch (p.k) { case NUM: if (MEQUAL(p.q.a, 1) && MEQUAL(p.q.b, 1)) { return 1; } break; case DOUBLE: if (p.d === 1.0) { return 1; } } return 0; }; isminusone = function(p) { switch (p.k) { case NUM: if (MEQUAL(p.q.a, -1) && MEQUAL(p.q.b, 1)) { return 1; } break; case DOUBLE: if (p.d === -1.0) { return 1; } } return 0; }; isone = function(p) { return isplusone(p) || isminusone(p); }; isinteger = function(p) { if (p.k === NUM && MEQUAL(p.q.b, 1)) { return 1; } else { return 0; } }; isintegerorintegerfloat = function(p) { if (p.k === DOUBLE) { if (p.d === Math.round(p.d)) { return 1; } return 0; } return isinteger(p); }; isnonnegativeinteger = function(p) { if (isrational(p) && MEQUAL(p.q.b, 1) && MSIGN(p.q.a) === 1) { return 1; } else { return 0; } }; isposint = function(p) { if (isinteger(p) && MSIGN(p.q.a) === 1) { return 1; } else { return 0; } }; // -------------------------------------- isunivarpolyfactoredorexpandedform = function(p, x) { if (DEBUG) { console.log("isunivarpolyfactoredorexpandedform: p: " + p + " x: " + x); } if (x == null) { push(p); guess(); x = pop(); pop(); } if (ispolyfactoredorexpandedform(p, x) && (Find(p, symbol(SYMBOL_X)) + Find(p, symbol(SYMBOL_Y)) + Find(p, symbol(SYMBOL_Z)) === 1)) { return x; } else { return 0; } }; // -------------------------------------- // 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); }; ispolyfactoredorexpandedform_factor = function(p, x) { if (car(p) === symbol(MULTIPLY)) { p = cdr(p); while (iscons(p)) { if (DEBUG) { console.log("ispolyfactoredorexpandedform_factor testing " + car(p)); } if (!ispolyfactoredorexpandedform_power(car(p), x)) { if (DEBUG) { console.log("... tested negative:" + car(p)); } return 0; } p = cdr(p); } return 1; } else { return ispolyfactoredorexpandedform_power(p, x); } }; ispolyfactoredorexpandedform_power = function(p, x) { if (car(p) === symbol(POWER)) { if (DEBUG) { console.log("ispolyfactoredorexpandedform_power (isposint(caddr(p)) " + (isposint(caddr(p)), DEBUG ? console.log("ispolyfactoredorexpandedform_power ispolyexpandedform_expr(cadr(p), x)) " + ispolyexpandedform_expr(cadr(p), x)) : void 0)); } return isposint(caddr(p)) && ispolyexpandedform_expr(cadr(p), x); } else { if (DEBUG) { console.log("ispolyfactoredorexpandedform_power not a power, testing if this is exp form: " + p); } return ispolyexpandedform_expr(p, x); } }; // -------------------------------------- ispolyexpandedform = function(p, x) { if (Find(p, x)) { return ispolyexpandedform_expr(p, x); } else { return 0; } }; ispolyexpandedform_expr = function(p, x) { if (car(p) === symbol(ADD)) { p = cdr(p); while (iscons(p)) { if (!ispolyexpandedform_term(car(p), x)) { return 0; } p = cdr(p); } return 1; } else { return ispolyexpandedform_term(p, x); } }; ispolyexpandedform_term = function(p, x) { if (car(p) === symbol(MULTIPLY)) { p = cdr(p); while (iscons(p)) { if (!ispolyexpandedform_factor(car(p), x)) { return 0; } p = cdr(p); } return 1; } else { return ispolyexpandedform_factor(p, x); } }; ispolyexpandedform_factor = function(p, x) { if (equal(p, x)) { return 1; } if (car(p) === symbol(POWER) && equal(cadr(p), x)) { if (isposint(caddr(p))) { return 1; } else { return 0; } } if (Find(p, x)) { return 0; } else { return 1; } }; // -------------------------------------- isnegativeterm = function(p) { if (isnegativenumber(p)) { return 1; } else if (car(p) === symbol(MULTIPLY) && isnegativenumber(cadr(p))) { return 1; } else { return 0; } }; hasNegativeRationalExponent = function(p) { if (car(p) === symbol(POWER) && isrational(car(cdr(cdr(p)))) && isnegativenumber(car(cdr(p)))) { if (DEBUG_IS) { console.log("hasNegativeRationalExponent: " + p.toString() + " has imaginary component"); } return 1; } else { if (DEBUG_IS) { console.log("hasNegativeRationalExponent: " + p.toString() + " has NO imaginary component"); } return 0; } }; isimaginarynumberdouble = function(p) { if ((car(p) === symbol(MULTIPLY) && length(p) === 3 && isdouble(cadr(p)) && hasNegativeRationalExponent(caddr(p))) || equal(p, imaginaryunit)) { return 1; } else { return 0; } }; isimaginarynumber = function(p) { if ((car(p) === symbol(MULTIPLY) && length(p) === 3 && isNumericAtom(cadr(p)) && equal(caddr(p), imaginaryunit)) || equal(p, imaginaryunit) || hasNegativeRationalExponent(caddr(p))) { if (DEBUG_IS) { console.log("isimaginarynumber: " + p.toString() + " is imaginary number"); } return 1; } else { if (DEBUG_IS) { console.log("isimaginarynumber: " + p.toString() + " isn't an imaginary number"); } return 0; } }; iscomplexnumberdouble = function(p) { if ((car(p) === symbol(ADD) && length(p) === 3 && isdouble(cadr(p)) && isimaginarynumberdouble(caddr(p))) || isimaginarynumberdouble(p)) { return 1; } else { return 0; } }; iscomplexnumber = function(p) { if (DEBUG_IS) { debugger; } if ((car(p) === symbol(ADD) && length(p) === 3 && isNumericAtom(cadr(p)) && isimaginarynumber(caddr(p))) || isimaginarynumber(p)) { if (DEBUG) { console.log("iscomplexnumber: " + p.toString() + " is imaginary number"); } return 1; } else { if (DEBUG) { console.log("iscomplexnumber: " + p.toString() + " is imaginary number"); } return 0; } }; iseveninteger = function(p) { if (isinteger(p) && p.q.a.isEven()) { return 1; } else { return 0; } }; isnegative = function(p) { if (car(p) === symbol(ADD) && isnegativeterm(cadr(p))) { return 1; } else if (isnegativeterm(p)) { return 1; } else { return 0; } }; // returns 1 if there's a symbol somewhere. // not used anywhere. // NOTE: 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; } else { while (iscons(p)) { if (issymbolic(car(p))) { return 1; } p = cdr(p); } return 0; } }; // i.e. 2, 2^3, etc. isintegerfactor = function(p) { return isinteger(p) || car(p) === symbol(POWER) && isinteger(cadr(p)) && isinteger(caddr(p)); }; isNumberOneOverSomething = function(p) { return isfraction(p) && MEQUAL(p.q.a.abs(), 1); }; isoneover = function(p) { return car(p) === symbol(POWER) && isminusone(caddr(p)); }; isfraction = function(p) { return p.k === NUM && !MEQUAL(p.q.b, 1); }; // p is a U, n an int equaln = function(p, n) { switch (p.k) { case NUM: return MEQUAL(p.q.a, n) && MEQUAL(p.q.b, 1); case DOUBLE: return p.d === n; default: return false; } }; // p is a U, a and b ints equalq = function(p, a, b) { switch (p.k) { case NUM: return MEQUAL(p.q.a, a) && MEQUAL(p.q.b, b); case DOUBLE: return p.d === a / b; default: return false; } }; // 1/2 ? isoneovertwo = function(p) { return equalq(p, 1, 2); }; // -1/2 ? isminusoneovertwo = function(p) { return equalq(p, -1, 2); }; // 1/sqrt(2) ? isoneoversqrttwo = function(p) { return car(p) === symbol(POWER) && equaln(cadr(p), 2) && equalq(caddr(p), -1, 2); }; // -1/sqrt(2) ? isminusoneoversqrttwo = function(p) { return car(p) === symbol(MULTIPLY) && equaln(cadr(p), -1) && isoneoversqrttwo(caddr(p)) && length(p) === 3; }; // sqrt(3)/2 ? issqrtthreeovertwo = function(p) { return car(p) === symbol(MULTIPLY) && isoneovertwo(cadr(p)) && issqrtthree(caddr(p)) && length(p) === 3; }; // -sqrt(3)/2 ? isminussqrtthreeovertwo = function(p) { return car(p) === symbol(MULTIPLY) && isminusoneovertwo(cadr(p)) && issqrtthree(caddr(p)) && length(p) === 3; }; // p == sqrt(3) ? issqrtthree = function(p) { return car(p) === symbol(POWER) && equaln(cadr(p), 3) && isoneovertwo(caddr(p)); }; isfloating = function(p) { if (p.k === DOUBLE || p === symbol(FLOATF)) { return 1; } while (iscons(p)) { if (isfloating(car(p))) { return 1; } p = cdr(p); } return 0; }; isimaginaryunit = function(p) { if (equal(p, imaginaryunit)) { return 1; } else { return 0; } }; // n/2 * i * pi ? // return value: // 0 no // 1 1 // 2 -1 // 3 i // 4 -i isquarterturn = function(p) { var minussign, n; n = 0; minussign = 0; if (car(p) !== symbol(MULTIPLY)) { return 0; } if (equal(cadr(p), imaginaryunit)) { if (caddr(p) !== symbol(PI)) { return 0; } if (length(p) !== 3) { return 0; } return 2; } if (!isNumericAtom(cadr(p))) { return 0; } if (!equal(caddr(p), imaginaryunit)) { return 0; } if (cadddr(p) !== symbol(PI)) { return 0; } if (length(p) !== 4) { return 0; } push(cadr(p)); push_integer(2); multiply(); n = pop_integer(); if (isNaN(n)) { return 0; } if (n < 1) { minussign = 1; n = -n; } switch (n % 4) { case 0: n = 1; break; case 1: if (minussign) { n = 4; } else { n = 3; } break; case 2: n = 2; break; case 3: if (minussign) { n = 3; } else { n = 4; } } 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; if (p === symbol(PI)) { return 2; } if (car(p) === symbol(MULTIPLY) && isNumericAtom(cadr(p)) && caddr(p) === symbol(PI) && length(p) === 3) { doNothing = 0; } else { return 0; } push(cadr(p)); push_integer(2); multiply(); n = pop_integer(); if (isNaN(n)) { return 0; } if (n < 0) { n = 4 - (-n) % 4; } else { n = 1 + (n - 1) % 4; } return n; }; $.isZeroAtomOrTensor = isZeroAtomOrTensor; $.isnegativenumber = isnegativenumber; $.isplusone = isplusone; $.isminusone = isminusone; $.isinteger = isinteger; $.isnonnegativeinteger = isnonnegativeinteger; $.isposint = isposint; $.isnegativeterm = isnegativeterm; $.isimaginarynumber = isimaginarynumber; $.iscomplexnumber = iscomplexnumber; $.iseveninteger = iseveninteger; $.isnegative = isnegative; $.issymbolic = issymbolic; $.isintegerfactor = isintegerfactor; $.isoneover = isoneover; $.isfraction = isfraction; $.isoneoversqrttwo = isoneoversqrttwo; $.isminusoneoversqrttwo = isminusoneoversqrttwo; $.isfloating = isfloating; $.isimaginaryunit = isimaginaryunit; $.isquarterturn = isquarterturn; $.isnpi = isnpi; Eval_isprime = function() { push(cadr(p1)); Eval(); p1 = pop(); if (isnonnegativeinteger(p1) && mprime(p1.q.a)) { return push_integer(1); } else { return push_integer(0); } }; Eval_laguerre = function() { // 1st arg push(cadr(p1)); Eval(); // 2nd arg push(caddr(p1)); Eval(); // 3rd arg push(cadddr(p1)); Eval(); p2 = pop(); if (p2 === symbol(NIL)) { push_integer(0); } else { push(p2); } 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; save(); p3 = pop(); p2 = pop(); p1 = pop(); push(p2); n = pop_integer(); if (n < 0 || isNaN(n)) { push_symbol(LAGUERRE); push(p1); push(p2); push(p3); list(4); restore(); return; } if (issymbol(p1)) { laguerre2(n); } else { p4 = p1; p1 = symbol(SECRETX); laguerre2(n); p1 = p4; push(symbol(SECRETX)); push(p1); subst(); Eval(); } return restore(); }; laguerre2 = function(n) { var i, o, ref, results; i = 0; push_integer(1); push_integer(0); p6 = pop(); results = []; 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); push(p1); subtract(); push(p3); add(); push(p6); multiply(); push_integer(i); push(p3); add(); push(p5); multiply(); subtract(); push_integer(i + 1); results.push(divide()); } return results; }; // Find the least common multiple of two expressions. Eval_lcm = function() { var results; p1 = cdr(p1); push(car(p1)); Eval(); p1 = cdr(p1); results = []; while (iscons(p1)) { push(car(p1)); Eval(); lcm(); results.push(p1 = cdr(p1)); } return results; }; lcm = function() { var prev_expanding; prev_expanding = expanding; save(); yylcm(); restore(); return expanding = prev_expanding; }; yylcm = function() { expanding = 1; p2 = pop(); p1 = pop(); push(p1); push(p2); gcd(); push(p1); divide(); push(p2); divide(); return inverse(); }; Eval_leading = function() { push(cadr(p1)); Eval(); push(caddr(p1)); Eval(); p1 = pop(); if (p1 === symbol(NIL)) { guess(); } else { push(p1); } return leading(); }; //define P p1 //define X p2 //define N p3 leading = function() { save(); p2 = pop(); p1 = pop(); push(p1); // N = degree of P push(p2); degree(); p3 = pop(); push(p1); // divide through by X ^ N push(p2); push(p3); power(); divide(); push(p2); // remove terms that depend on X filter(); return restore(); }; Eval_legendre = function() { // 1st arg push(cadr(p1)); Eval(); // 2nd arg push(caddr(p1)); Eval(); // 3rd arg (optional) push(cadddr(p1)); Eval(); p2 = pop(); if (p2 === symbol(NIL)) { push_integer(0); } else { push(p2); } return legendre(); }; //define X p1 //define N p2 //define M p3 //define Y p4 //define Y0 p5 //define Y1 p6 legendre = function() { save(); __legendre(); return restore(); }; __legendre = function() { var m, n; m = 0; n = 0; p3 = pop(); p2 = pop(); p1 = pop(); push(p2); n = pop_integer(); push(p3); m = pop_integer(); if (n < 0 || isNaN(n) || m < 0 || isNaN(m)) { push_symbol(LEGENDRE); push(p1); push(p2); push(p3); list(4); return; } if (issymbol(p1)) { __legendre2(n, m); } else { p4 = p1; p1 = symbol(SECRETX); __legendre2(n, m); p1 = p4; push(symbol(SECRETX)); push(p1); subst(); Eval(); } return __legendre3(m); }; __legendre2 = function(n, m) { var i, i1, o, ref, ref1, results; i = 0; push_integer(1); push_integer(0); p6 = pop(); // 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); push(p1); multiply(); push(p6); multiply(); push_integer(i); push(p5); multiply(); subtract(); push_integer(i + 1); divide(); } results = []; 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; } if (car(p1) === symbol(COS)) { push(cadr(p1)); sine(); square(); } else if (car(p1) === symbol(SIN)) { push(cadr(p1)); cosine(); square(); } else { push_integer(1); push(p1); square(); subtract(); } push_integer(m); push_rational(1, 2); multiply(); power(); multiply(); if (m % 2) { return negate(); } }; // 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) { 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(); return logarithm(); }; logarithm = function() { save(); yylog(); return restore(); }; yylog = function() { var d; d = 0.0; p1 = pop(); if (p1 === symbol(E)) { push_integer(1); return; } if (equaln(p1, 1)) { push_integer(0); return; } if (isnegativenumber(p1)) { push(p1); negate(); logarithm(); push(imaginaryunit); if (evaluatingAsFloats) { push_double(Math.PI); } else { push_symbol(PI); } multiply(); add(); return; } if (isdouble(p1)) { d = Math.log(p1.d); push_double(d); return; } // rational number and not an integer? if (isfraction(p1)) { push(p1); numerator(); logarithm(); push(p1); denominator(); logarithm(); subtract(); return; } // log(a ^ b) --> b log(a) if (car(p1) === symbol(POWER)) { push(caddr(p1)); push(cadr(p1)); logarithm(); multiply(); return; } // log(a * b) --> log(a) + log(b) if (car(p1) === symbol(MULTIPLY)) { push_integer(0); p1 = cdr(p1); while (iscons(p1)) { push(car(p1)); logarithm(); add(); p1 = cdr(p1); } return; } push_symbol(LOG); push(p1); 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); // 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); }; msub = function(a, b) { return a.subtract(b); }; addf = function(a, b) { return a.add(b); }; subf = function(a, b) { 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) { var theNewString; theNewString = new U(); theNewString.k = STR; theNewString.str = s; return theNewString; }; out_of_memory = function() { return stop("out of memory"); }; // both ints push_zero_matrix = function(i, j) { push(alloc_tensor(i * j)); stack[tos - 1].tensor.ndim = 2; stack[tos - 1].tensor.dim[0] = i; return stack[tos - 1].tensor.dim[1] = j; }; push_identity_matrix = function(n) { var i, o, ref; push_zero_matrix(n, n); 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]); }; push_cars = function(p) { var results; results = []; while (iscons(p)) { push(car(p)); results.push(p = cdr(p)); } return results; }; // 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; } else { return 0; } }; lessp = function(p1, p2) { if (cmp_expr(p1, p2) < 0) { return 1; } else { return 0; } }; sign = function(n) { if (n < 0) { return -1; } else if (n > 0) { return 1; } else { return 0; } }; // 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; if (p1 === p2) { return 0; } if (p1 === symbol(NIL)) { return -1; } if (p2 === symbol(NIL)) { return 1; } if (isNumericAtom(p1) && isNumericAtom(p2)) { return sign(compare_numbers(p1, p2)); } if (isNumericAtom(p1)) { return -1; } if (isNumericAtom(p2)) { return 1; } if (isstr(p1) && isstr(p2)) { return sign(strcmp(p1.str, p2.str)); } if (isstr(p1)) { return -1; } if (isstr(p2)) { return 1; } if (issymbol(p1) && issymbol(p2)) { return sign(strcmp(get_printname(p1), get_printname(p2))); } if (issymbol(p1)) { return -1; } if (issymbol(p2)) { return 1; } if (istensor(p1) && istensor(p2)) { return compare_tensors(p1, p2); } if (istensor(p1)) { return -1; } if (istensor(p2)) { return 1; } // recursion here while (iscons(p1) && iscons(p2)) { n = cmp_expr(car(p1), car(p2)); if (n !== 0) { return n; } p1 = cdr(p1); p2 = cdr(p2); } if (iscons(p2)) { return -1; } if (iscons(p1)) { return 1; } return 0; }; length = function(p) { var n; n = 0; while (iscons(p)) { p = cdr(p); n++; } return n; }; unique = function(p) { save(); p1 = symbol(NIL); p2 = symbol(NIL); unique_f(p); if (p2 !== symbol(NIL)) { p1 = symbol(NIL); } p = p1; restore(); return p; }; unique_f = function(p) { if (isstr(p)) { if (p1 === symbol(NIL)) { p1 = p; } else if (p !== p1) { p2 = p; } return; } while (iscons(p)) { unique_f(car(p)); if (p2 !== symbol(NIL)) { return; } p = cdr(p); } }; ssqrt = function() { push_rational(1, 2); return power(); }; yyexpand = function() { var prev_expanding; prev_expanding = expanding; expanding = 1; Eval(); return expanding = prev_expanding; }; exponential = function() { push_symbol(E); swap(); return power(); }; square = function() { push_integer(2); 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); return stack = stack.slice(0, h).concat(subsetOfStack).concat(stack.slice(h + n)); }; $.equal = equal; $.length = length; // Bignum multiplication and division mmul = function(a, b) { return a.multiply(b); }; mdiv = function(a, b) { return a.divide(b); }; // a = a + b /* static void addf(unsigned int *a, unsigned int *b, int len) { int i long long t = 0; # can be signed or unsigned for (i = 0; i < len; i++) { t += (long long) a[i] + b[i] a[i] = (unsigned int) t t >>= 32 } } // a = a - b static void subf(unsigned int *a, unsigned int *b, int len) { int i long long t = 0; # must be signed for (i = 0; i < len; i++) { t += (long long) a[i] - b[i] a[i] = (unsigned int) t t >>= 32 } } // a = b * c // 0xffffffff + 0xffffffff * 0xffffffff == 0xffffffff00000000 static void mulf(unsigned int *a, unsigned int *b, int len, unsigned int c) { int i unsigned long long t = 0; # must be unsigned for (i = 0; i < len; i++) { t += (unsigned long long) b[i] * c a[i] = (unsigned int) t t >>= 32 } 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(); push(caddr(p1)); Eval(); return mod(); }; mod = function() { var n; n = 0; save(); p2 = pop(); p1 = pop(); if (isZeroAtomOrTensor(p2)) { stop("mod function: divide by zero"); } if (!isNumericAtom(p1) || !isNumericAtom(p2)) { push_symbol(MOD); push(p1); push(p2); list(3); restore(); return; } if (isdouble(p1)) { push(p1); n = pop_integer(); if (isNaN(n)) { stop("mod function: cannot convert float value to integer"); } push_integer(n); p1 = pop(); } if (isdouble(p2)) { push(p2); n = pop_integer(); if (isNaN(n)) { stop("mod function: cannot convert float value to integer"); } push_integer(n); p2 = pop(); } if (!isinteger(p1) || !isinteger(p2)) { stop("mod function: integer arguments expected"); } p3 = new U(); p3.k = NUM; p3.q.a = mmod(p1.q.a, p2.q.a); p3.q.b = mint(1); push(p3); 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; k = 0; if (index === 0) { stop("root index is zero"); } // count number of bits k = 0; while (n.shiftRight(k) > 0) { k++; } 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) { // 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--; } 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) DEBUG_MULTIPLY = false; Eval_multiply = function() { var results; push(cadr(p1)); Eval(); p1 = cddr(p1); results = []; while (iscons(p1)) { push(car(p1)); Eval(); multiply(); results.push(p1 = cdr(p1)); } 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"); } if (isNumericAtom(stack[tos - 2]) && isNumericAtom(stack[tos - 1])) { return multiply_numbers(); } else { save(); yymultiply(); return restore(); } }; yymultiply = function() { var h, i, n, o, ref, ref1; 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); } else { push(zero); } return; } // is either operand a sum? //console.log("yymultiply: expanding: " + expanding) if (expanding && isadd(p1)) { p1 = cdr(p1); if (evaluatingAsFloats) { push_double(0.0); } else { push(zero); } while (iscons(p1)) { push(car(p1)); push(p2); multiply(); add(); p1 = cdr(p1); } return; } if (expanding && isadd(p2)) { p2 = cdr(p2); if (evaluatingAsFloats) { push_double(0.0); } else { push(zero); } while (iscons(p2)) { push(p1); push(car(p2)); multiply(); add(); p2 = cdr(p2); } return; } if (!istensor(p1) && istensor(p2)) { push(p1); push(p2); 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 { push(p1); list(1); p1 = pop(); } if (car(p2) === symbol(MULTIPLY)) { p2 = cdr(p2); } else { push(p2); list(1); p2 = pop(); } // handle numerical coefficients if (isNumericAtom(car(p1)) && isNumericAtom(car(p2))) { push(car(p1)); push(car(p2)); multiply_numbers(); p1 = cdr(p1); p2 = cdr(p2); } else if (isNumericAtom(car(p1))) { push(car(p1)); p1 = cdr(p1); } else if (isNumericAtom(car(p2))) { push(car(p2)); p2 = cdr(p2); } else { if (evaluatingAsFloats) { push_double(1.0); } else { push(one); } } 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)); push(cdar(p2)); append(); cons(); p1 = cdr(p1); p2 = cdr(p2); parse_p1(); parse_p2(); continue; } switch (cmp_expr(p3, p4)) { case -1: push(car(p1)); p1 = cdr(p1); parse_p1(); break; case 1: push(car(p2)); p2 = cdr(p2); parse_p2(); break; case 0: combine_factors(h); p1 = cdr(p1); p2 = cdr(p2); parse_p1(); parse_p2(); break; default: stop("internal error 2"); } } // push remaining factors, if any while (iscons(p1)) { push(car(p1)); p1 = cdr(p1); } while (iscons(p2)) { 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) { 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(); pop(); push(p7); } else { stack[h] = symbol(MULTIPLY); list(n); } return; } list(n); p7 = pop(); push_symbol(MULTIPLY); push(p7); 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; if (car(p3) === symbol(POWER)) { p5 = caddr(p3); return p3 = cadr(p3); } }; // 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; if (car(p4) === symbol(POWER)) { p6 = caddr(p4); return p4 = cadr(p4); } }; // h an integer combine_factors = function(h) { push(p4); push(p5); push(p6); add(); power(); p7 = pop(); if (isNumericAtom(p7)) { push(stack[h]); push(p7); 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)); multiply_numbers(); stack[h] = pop(); return push(caddr(p7)); } else { return push(p7); } } else { return push(p7); } }; 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)]; if (n < 0) { n = -n; push(stack[h]); negate(); stack[h] = pop(); } if (n > 1) { return push(_gamma[n]); } }; // 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; expanding = 0; multiply(); return expanding = prev_expanding; }; // multiply n factors on stack // n an integer multiply_all = function(n) { var h, i, o, ref; i = 0; if (n === 1) { return; } if (n === 0) { push(evaluatingAsFloats ? one_as_double : one); return; } h = tos - n; push(stack[h]); for (i = o = 1, ref = n; (1 <= ref ? o < ref : o > ref); i = 1 <= ref ? ++o : --o) { push(stack[h + i]); multiply(); } stack[h] = pop(); return moveTos(h + 1); }; // n an integer multiply_all_noexpand = function(n) { var prev_expanding; prev_expanding = expanding; expanding = 0; multiply_all(n); 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(); } else { inverse(); return multiply(); } }; // this is different from inverse of a matrix (inv)! inverse = function() { if (isNumericAtom(stack[tos - 1])) { return invert_number(); } else { push_integer(-1); return power(); } }; reciprocate = function() { return inverse(); }; negate = function() { if (isNumericAtom(stack[tos - 1])) { return negate_number(); } else { if (evaluatingAsFloats) { push_double(-1.0); } else { push_integer(-1); } return multiply(); } }; negate_expand = function() { var prev_expanding; prev_expanding = expanding; expanding = 1; negate(); return expanding = prev_expanding; }; negate_noexpand = function() { var prev_expanding; prev_expanding = expanding; expanding = 0; negate(); 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; } // 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; } } if (i === tos) { return; } // ok, try to simplify save(); // numerator push(stack[h]); mp_numerator(); if (DEBUG_MULTIPLY) { 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)) { // p1 is A break; } if (!__is_radical_number(stack[i])) { continue; } p3 = cadr(stack[i]); p4 = caddr(stack[i]); if (!isnegativenumber(p4)) { //p4 is EXPO continue; } // numerator divisible by p3 (base)? push(p1); push(p3); divide(); p5 = pop(); if (!isinteger(p5)) { //p5 is TMP continue; } // reduce numerator p1 = p5; push_symbol(POWER); push(p3); push(evaluatingAsFloats ? one_as_double : one); push(p4); add(); list(3); stack[i] = pop(); } // denominator push(stack[h]); mp_denominator(); if (DEBUG_MULTIPLY) { 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)) { // p2 is B break; } if (!__is_radical_number(stack[i])) { continue; } p3 = cadr(stack[i]); p4 = caddr(stack[i]); if (isnegativenumber(p4)) { //p4 is EXPO continue; } // denominator divisible by p3? #p3 is BASE push(p2); push(p3); divide(); p5 = pop(); if (!isinteger(p5)) { //p5 is TMP continue; } if (DEBUG_MULTIPLY) { console.log("__new radical p5: " + p5.toString()); } if (DEBUG_MULTIPLY) { console.log("__new radical top stack: " + stack[tos - 1]); } // reduce denominator p2 = p5; push_symbol(POWER); push(p3); push(p4); if (DEBUG_MULTIPLY) { console.log("__new radical p3: " + p3.toString()); } if (DEBUG_MULTIPLY) { console.log("__new radical p4: " + p4.toString()); } push(one); 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(); push(p1); push(p3); divide(); p1 = pop(); break; } } if (DEBUG_MULTIPLY) { console.log("__new radical exponent: " + stack[tos - 1]); } list(3); stack[i] = pop(); } // reconstitute the coefficient push(p1); push(p2); divide(); stack[h] = pop(); return restore(); }; // don't include i // p is a U // TODO should this be in is.coffee ? __is_radical_number = function(p) { // don't use i return car(p) === symbol(POWER) && isNumericAtom(cadr(p)) && isfraction(caddr(p)) && !isminusone(cadr(p)); }; //----------------------------------------------------------------------------- // > 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; NROOTS_EPSILON = 1.0e-9; NROOTS_ABS = function(z) { 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() { class numericRootOfPolynomial {}; numericRootOfPolynomial.prototype.r = 0.0; numericRootOfPolynomial.prototype.i = 0.0; return numericRootOfPolynomial; }).call(this); nroots_a = new numericRootOfPolynomial(); nroots_b = new numericRootOfPolynomial(); nroots_x = new numericRootOfPolynomial(); nroots_y = new numericRootOfPolynomial(); nroots_fa = new numericRootOfPolynomial(); nroots_fb = new numericRootOfPolynomial(); nroots_dx = new numericRootOfPolynomial(); nroots_df = new numericRootOfPolynomial(); nroots_c = []; for (initNRoots = o = 0, ref = NROOTS_YMAX; (0 <= ref ? o < ref : o > ref); initNRoots = 0 <= ref ? ++o : --o) { nroots_c[initNRoots] = new numericRootOfPolynomial(); } Eval_nroots = function() { var h, i, i1, j1, k, l1, n, ref1, ref2, ref3; h = 0; i = 0; k = 0; n = 0; push(cadr(p1)); Eval(); push(caddr(p1)); Eval(); p2 = pop(); if (p2 === symbol(NIL)) { guess(); } else { push(p2); } p2 = pop(); p1 = pop(); if (!ispolyexpandedform(p1, p2)) { stop("nroots: polynomial?"); } // mark the stack h = tos; // get the coefficients n = coeff(p2, p1); if (n > NROOTS_YMAX) { stop("nroots: degree?"); } // 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(); Eval(); p1 = pop(); push(stack[h + i]); imag(); yyfloat(); Eval(); p2 = pop(); if (!isdouble(p1) || !isdouble(p2)) { stop("nroots: coefficients?"); } 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); if (Math.abs(nroots_a.r) < NROOTS_DELTA) { nroots_a.r = 0.0; } if (Math.abs(nroots_a.i) < NROOTS_DELTA) { nroots_a.i = 0.0; } push_double(nroots_a.r); push_double(nroots_a.i); push(imaginaryunit); multiply(); 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) { p1.tensor.elem[i] = stack[h + i]; } moveTos(h); return push(p1); } }; // divide the polynomial by its leading coefficient monic = function(n) { var i1, k, ref1, t; k = 0; t = 0.0; 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) { 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; } nroots_c[n - 1].r = 1.0; return nroots_c[n - 1].i = 0.0; }; // uses the secant method findroot = function(n) { var i1, j, j1, k, nrabs, t; j = 0; k = 0; t = 0.0; if (NROOTS_ABS(nroots_c[0]) < NROOTS_DELTA) { nroots_a.r = 0.0; nroots_a.i = 0.0; return; } for (j = i1 = 0; i1 < 100; j = ++i1) { nroots_a.r = NROOTS_RANDOM(); nroots_a.i = NROOTS_RANDOM(); compute_fa(n); nroots_b.r = nroots_a.r; nroots_b.i = nroots_a.i; nroots_fb.r = nroots_fa.r; nroots_fb.i = nroots_fa.i; nroots_a.r = NROOTS_RANDOM(); nroots_a.i = NROOTS_RANDOM(); for (k = j1 = 0; j1 < 1000; k = ++j1) { compute_fa(n); nrabs = NROOTS_ABS(nroots_fa); if (DEBUG) { console.log("nrabs: " + nrabs); } if (nrabs < NROOTS_EPSILON) { return; } if (NROOTS_ABS(nroots_fa) < NROOTS_ABS(nroots_fb)) { nroots_x.r = nroots_a.r; nroots_x.i = nroots_a.i; nroots_a.r = nroots_b.r; nroots_a.i = nroots_b.i; nroots_b.r = nroots_x.r; nroots_b.i = nroots_x.i; nroots_x.r = nroots_fa.r; nroots_x.i = nroots_fa.i; nroots_fa.r = nroots_fb.r; nroots_fa.i = nroots_fb.i; 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); } } return stop("nroots: convergence error"); }; compute_fa = function(n) { 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) { // 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) { 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; } if (NROOTS_ABS(nroots_c[0]) > NROOTS_DELTA) { stop("nroots: residual error"); } results = []; 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); } return results; }; Eval_numerator = function() { push(cadr(p1)); Eval(); return numerator(); }; numerator = function() { var h, theArgument; h = 0; 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(); theArgument = cdr(theArgument); } return multiply_all(tos - h); } else if (isrational(theArgument)) { push(theArgument); return mp_numerator(); } else if (car(theArgument) === symbol(POWER) && isnegativeterm(caddr(theArgument))) { return push(one); } else { return push(theArgument); } }; // Outer product of tensors Eval_outer = function() { var results; p1 = cdr(p1); push(car(p1)); Eval(); p1 = cdr(p1); results = []; while (iscons(p1)) { push(car(p1)); Eval(); outer(); results.push(p1 = cdr(p1)); } return results; }; outer = function() { save(); p2 = pop(); p1 = pop(); if (istensor(p1) && istensor(p2)) { yyouter(); } else { push(p1); push(p2); if (istensor(p1)) { tensor_times_scalar(); } else if (istensor(p2)) { scalar_times_tensor(); } else { multiply(); } } return restore(); }; yyouter = function() { var i, i1, j, j1, k, l1, m1, ndim, nelem, ref1, ref2, ref3, ref4; i = 0; j = 0; k = 0; ndim = 0; nelem = 0; ndim = p1.tensor.ndim + p2.tensor.ndim; if (ndim > MAXDIM) { stop("outer: rank of result exceeds maximum"); } 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) { 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) { 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) { push(p1.tensor.elem[i]); push(p2.tensor.elem[j]); multiply(); p3.tensor.elem[k++] = pop(); } } return push(p3); }; partition = function() { save(); p2 = pop(); p1 = pop(); push_integer(1); p3 = pop(); p4 = p3; p1 = cdr(p1); while (iscons(p1)) { if (Find(car(p1), p2)) { push(p4); push(car(p1)); multiply(); p4 = pop(); } else { push(p3); push(car(p1)); multiply(); p3 = pop(); } p1 = cdr(p1); } push(p3); push(p4); 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(); return push_symbol(NIL); }; Eval_pattern = function() { var firstArgument, patternPosition, secondArgument, stringKey, thirdArgument; if (!iscons(cdr(p1))) { stop("pattern needs at least a template and a transformed version"); } firstArgument = car(cdr(p1)); secondArgument = car(cdr(cdr(p1))); if (secondArgument === symbol(NIL)) { stop("pattern needs at least a template and a transformed version"); } if (!iscons(cdr(cdr(p1)))) { thirdArgument = symbol(NIL); } else { thirdArgument = car(cdr(cdr(cdr(p1)))); } 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 { if (DEBUG) { console.log("pattern already exists, replacing. " + cdr(p1)); } 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); }; 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); }; Eval_patternsinfo = function() { var patternsinfoToBePrinted; patternsinfoToBePrinted = patternsinfo(); if (patternsinfoToBePrinted !== "") { return push(new_string(patternsinfoToBePrinted)); } else { return push_symbol(NIL); } }; patternsinfo = function() { var i, i1, len, patternsinfoToBePrinted; patternsinfoToBePrinted = ""; for (i1 = 0, len = userSimplificationsInListForm.length; i1 < len; i1++) { i = userSimplificationsInListForm[i1]; patternsinfoToBePrinted += userSimplificationsInListForm + "\n"; } return patternsinfoToBePrinted; }; Eval_polar = function() { push(cadr(p1)); Eval(); return polar(); }; 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(); push(p1); abs(); push(imaginaryunit); push(p1); arg(); multiply(); exponential(); multiply(); evaluatingPolar--; return restore(); }; // Factor using the Pollard rho method n_factor_number = 0; factor_number = function() { var h; h = 0; save(); p1 = pop(); // 0 or 1? if (equaln(p1, 0) || equaln(p1, 1) || equaln(p1, -1)) { push(p1); restore(); return; } n_factor_number = p1.q.a; h = tos; factor_a(); if (tos - h > 1) { list(tos - h); push_symbol(MULTIPLY); swap(); cons(); } 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; if (n_factor_number.isNegative()) { n_factor_number = setSignTo(n_factor_number, 1); push_integer(-1); } 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; } } return factor_b(); }; try_kth_prime = function(k) { 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; } [q, r] = mdivrem(n_factor_number, d); // continue looping while remainder is zero if (r.isZero()) { count++; n_factor_number = q; } else { break; } } 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; l = 0; bigint_one = mint(1); x = mint(5); xprime = mint(2); k = 1; l = 1; while (1) { if (mprime(n_factor_number)) { push_factor(n_factor_number, 1); return 0; } while (1) { 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); if (MEQUAL(g, 1)) { if (--k === 0) { xprime = x; 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); x = t; continue; } push_factor(g, 1); 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; } } }; push_factor = function(d, count) { p1 = new U(); p1.k = NUM; p1.q.a = d; p1.q.b = mint(1); push(p1); if (count > 1) { push_symbol(POWER); swap(); p1 = new U(); p1.k = NUM; p1.q.a = mint(count); p1.q.b = mint(1); push(p1); return list(3); } }; DEBUG_POWER = false; Eval_power = function() { if (DEBUG_POWER) { debugger; } push(cadr(p1)); Eval(); push(caddr(p1)); Eval(); return power(); }; power = function() { save(); yypower(); return restore(); }; yypower = function() { var b_isEven_and_c_isItsInverse, hopefullySimplified, inputBase, inputExp, isThisOne, is_a_moreThanZero, n; if (DEBUG_POWER) { debugger; } n = 0; 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); } else { push(one); } if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } return; } // a ^ 1 -> a if (equal(p2, one)) { push(p1); if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } return; } // -1 ^ -1 -> -1 if (isminusone(p1) && isminusone(p2)) { if (evaluatingAsFloats) { push_double(1.0); } else { push(one); } negate(); if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } return; } // -1 ^ 1/2 -> i if (isminusone(p1) && (isoneovertwo(p2))) { push(imaginaryunit); if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } return; } // -1 ^ -1/2 -> -i if (isminusone(p1) && isminusoneovertwo(p2)) { push(imaginaryunit); negate(); if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } return; } // -1 ^ rational if (isminusone(p1) && !isdouble(p1) && isrational(p2) && !isinteger(p2) && ispositivenumber(p2) && !evaluatingAsFloats) { if (DEBUG_POWER) { console.log(" power: -1 ^ rational"); } if (DEBUG_POWER) { console.log(" trick: p2.q.a , p2.q.b " + p2.q.a + " , " + p2.q.b); } if (p2.q.a < p2.q.b) { push_symbol(POWER); push(p1); push(p2); list(3); } else { push_symbol(MULTIPLY); push(p1); push_symbol(POWER); push(p1); push_rational(p2.q.a.mod(p2.q.b), p2.q.b); list(3); list(3); if (DEBUG_POWER) { 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)"); } push(p1); push(p2); qpow(); if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } 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 "); } if (DEBUG_POWER) { console.log("POWER - isNumericAtom(p1) && isNumericAtom(p2)"); } push(p1); push(p2); dpow(); if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } return; } if (istensor(p1)) { if (DEBUG_POWER) { console.log(" power: istensor(p1) "); } power_tensor(); if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } 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 "); } push(cadr(p1)); push(p2); power(); if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } return; } // e^log(...) if (p1 === symbol(E) && car(p2) === symbol(LOG)) { push(cadr(p2)); if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } return; } // e^some_float if (p1 === symbol(E) && isdouble(p2)) { if (DEBUG_POWER) { console.log(" power: p1 == symbol(E) && isdouble(p2) "); } push_double(Math.exp(p2.d)); if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } 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); push(p2); list(3); if (DEBUG_POWER) { console.log(" power: turning complex exponential to rect: " + stack[tos - 1]); } rect(); hopefullySimplified = pop(); if (Find(hopefullySimplified, symbol(PI)) === 0) { if (DEBUG_POWER) { console.log(" power: turned complex exponential to rect: " + hopefullySimplified); } push(hopefullySimplified); 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) "); } p1 = cdr(p1); push(car(p1)); push(p2); power(); p1 = cdr(p1); while (iscons(p1)) { push(car(p1)); push(p2); power(); multiply(); p1 = cdr(p1); } if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } 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)) { // when a is >= 0 push(cadr(p1)); push(caddr(p1)); push(p2); multiply(); power(); if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } return; } b_isEven_and_c_isItsInverse = false; if (iseveninteger(caddr(p1))) { push(caddr(p1)); push(p2); multiply(); isThisOne = pop(); if (isone(isThisOne)) { b_isEven_and_c_isItsInverse = true; } } if (car(p1) === symbol(POWER) && b_isEven_and_c_isItsInverse) { if (DEBUG_POWER) { console.log(" power: car(p1) == symbol(POWER) && b_isEven_and_c_isItsInverse "); } push(cadr(p1)); abs(); if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } return; } // when expanding, // (a + b) ^ n -> (a + b) * (a + b) ... if (expanding && isadd(p1) && isNumericAtom(p2)) { push(p2); n = pop_integer(); if (n > 1 && !isNaN(n)) { if (DEBUG_POWER) { console.log(" power: expanding && isadd(p1) && isNumericAtom(p2) "); } power_sum(n); if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } 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) "); } push_integer(1); push(cadr(p1)); cosine(); push_integer(2); power(); subtract(); push(p2); push_rational(1, 2); multiply(); power(); if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } 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) "); } push_integer(1); push(cadr(p1)); sine(); push_integer(2); power(); subtract(); push(p2); push_rational(1, 2); multiply(); power(); if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } 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(); divide(); if (!isone(p2)) { push(p2); negate(); power(); } if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } return; } // noninteger or floating power? if (isNumericAtom(p2)) { push(p1); abs(); push(p2); power(); push_integer(-1); push(p1); arg(); 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(); push_symbol(POWER); push(p1); push(p2); list(3); } } if (DEBUG_POWER) { console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } 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"); } return; } if (DEBUG_POWER) { console.log(" power: nothing can be done "); } push_symbol(POWER); push(p1); push(p2); list(3); if (DEBUG_POWER) { return console.log(" power of " + inputBase + " ^ " + inputExp + ": " + stack[tos - 1]); } }; //----------------------------------------------------------------------------- // 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) { push(car(p1)); push_integer(j); power(); stack[frame + i * (n + 1) + j] = pop(); } p1 = cdr(p1); } push_integer(n); factorial(); p1 = pop(); for (i = l1 = 0, ref3 = k; (0 <= ref3 ? l1 < ref3 : l1 > ref3); i = 0 <= ref3 ? ++l1 : --l1) { a[i] = 0; } push(zero); multinomial_sum(k, n, a, 0, n); 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) { 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) { push_integer(a[j]); factorial(); divide(); } // 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; n = isquarterturn(p2); switch (n) { case 0: doNothing = 1; break; case 1: push_integer(1); return 1; case 2: push_integer(-1); return 1; case 3: push(imaginaryunit); return 1; case 4: push(imaginaryunit); negate(); return 1; } if (car(p2) === symbol(ADD)) { p3 = cdr(p2); while (iscons(p3)) { n = isquarterturn(car(p3)); if (n) { break; } p3 = cdr(p3); } switch (n) { case 0: return 0; case 1: push_integer(1); break; case 2: push_integer(-1); break; case 3: push(imaginaryunit); break; case 4: push(imaginaryunit); negate(); } push(p2); push(car(p3)); subtract(); exponential(); multiply(); return 1; } 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(); return prime(); }; prime = function() { var n; n = 0; n = pop_integer(); if (n < 1 || n > MAXPRIMETAB) { stop("prime: Argument out of range."); } n = primetab[n - 1]; return push_integer(n); }; power_str = "^"; 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); test_flag = original_test_flag; return push(symbol(NIL)); }; // this is only invoked when user invokes // "printlist" explicitly Eval_printlist = function() { var beenPrinted; beenPrinted = _print(cdr(p1), PRINTMODE_LIST); stringsEmittedByUserPrintouts += beenPrinted; return push(symbol(NIL)); }; _print = function(p, passedPrintMode) { var accumulator, origPrintMode; accumulator = ""; while (iscons(p)) { push(car(p)); Eval(); p2 = pop(); origPrintMode = printMode; if (passedPrintMode === PRINTMODE_COMPUTER) { printMode = PRINTMODE_COMPUTER; accumulator = printline(p2); rememberPrint(accumulator, LAST_FULL_PRINT); } else if (passedPrintMode === PRINTMODE_HUMAN) { printMode = PRINTMODE_HUMAN; accumulator = printline(p2); rememberPrint(accumulator, LAST_PLAIN_PRINT); } else if (passedPrintMode === PRINTMODE_2DASCII) { printMode = PRINTMODE_2DASCII; accumulator = print2dascii(p2); rememberPrint(accumulator, LAST_2DASCII_PRINT); } else if (passedPrintMode === PRINTMODE_LATEX) { printMode = PRINTMODE_LATEX; accumulator = printline(p2); rememberPrint(accumulator, LAST_LATEX_PRINT); } else if (passedPrintMode === PRINTMODE_LIST) { printMode = PRINTMODE_LIST; accumulator = print_list(p2); rememberPrint(accumulator, LAST_LIST_PRINT); } printMode = origPrintMode; p = cdr(p); } if (DEBUG) { console.log("emttedString from display: " + stringsEmittedByUserPrintouts); } return accumulator; }; rememberPrint = function(theString, theTypeOfPrint) { var parsedString; scan('"' + theString + '"'); parsedString = pop(); return set_binding(symbol(theTypeOfPrint), parsedString); }; print_str = function(s) { if (DEBUG) { console.log("emttedString from print_str: " + stringsEmittedByUserPrintouts); } return s; }; print_char = function(c) { return c; }; collectLatexStringFromReturnValue = function(p) { var origPrintMode, originalCodeGen, returnedString; origPrintMode = printMode; printMode = PRINTMODE_LATEX; originalCodeGen = codeGen; codeGen = false; returnedString = print_expr(p); // some variables might contain underscores, escape those returnedString = returnedString.replace(/_/g, "\\_"); printMode = origPrintMode; codeGen = originalCodeGen; if (DEBUG) { console.log("emttedString from collectLatexStringFromReturnValue: " + stringsEmittedByUserPrintouts); } return returnedString; }; printline = function(p) { var accumulator; accumulator = ""; accumulator += print_expr(p); return accumulator; }; 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)) { // p1 is BASE accumulator += print_char('('); accumulator += print_expr(p1); accumulator += print_char(')'); } else { accumulator += print_expr(p1); // p1 is BASE } return accumulator; }; print_expo_of_denom = function(p2) { var accumulator; accumulator = ""; 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); // 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)) { // p2 is EXPO accumulator += print_base_of_denom(p1); restore(); return accumulator; } 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(); accumulator += print_power(p1, p2); if (d === 1) { accumulator += print_char(')'); } restore(); return accumulator; }; //define A p3 //define B p4 print_a_over_b = function(p) { var accumulator, d, doNothing, n; accumulator = ""; flag = 0; n = 0; d = 0; save(); // count numerators and denominators n = 0; d = 0; p1 = cdr(p); p2 = car(p1); if (isrational(p2)) { push(p2); mp_numerator(); absval(); p3 = pop(); push(p2); mp_denominator(); p4 = pop(); if (!isplusone(p3)) { // p3 is A n++; } if (!isplusone(p4)) { // p4 is B d++; } p1 = cdr(p1); } else { p3 = one; p4 = one; // p4 is B } while (iscons(p1)) { p2 = car(p1); if (is_denominator(p2)) { d++; } else { n++; } p1 = cdr(p1); } //debugger if (printMode === PRINTMODE_LATEX) { accumulator += print_str('\\frac{'); } if (n === 0) { accumulator += print_char('1'); } else { flag = 0; p1 = cdr(p); if (isrational(car(p1))) { p1 = cdr(p1); } if (!isplusone(p3)) { // p3 is A accumulator += print_factor(p3); flag = 1; } while (iscons(p1)) { p2 = car(p1); if (is_denominator(p2)) { doNothing = 1; } else { if (flag) { accumulator += print_multiply_sign(); } accumulator += print_factor(p2); flag = 1; } p1 = cdr(p1); } } if (printMode === PRINTMODE_LATEX) { accumulator += print_str('}{'); } else if (printMode === PRINTMODE_HUMAN && !test_flag) { accumulator += print_str(" / "); } else { accumulator += print_str("/"); } if (d > 1 && printMode !== PRINTMODE_LATEX) { accumulator += print_char('('); } flag = 0; p1 = cdr(p); if (isrational(car(p1))) { p1 = cdr(p1); } if (!isplusone(p4)) { // p4 is B accumulator += print_factor(p4); flag = 1; } while (iscons(p1)) { p2 = car(p1); if (is_denominator(p2)) { if (flag) { accumulator += print_multiply_sign(); } accumulator += print_denom(p2, d); flag = 1; } p1 = cdr(p1); } if (d > 1 && printMode !== PRINTMODE_LATEX) { accumulator += print_char(')'); } if (printMode === PRINTMODE_LATEX) { accumulator += print_str('}'); } restore(); return accumulator; }; print_expr = function(p) { var accumulator; accumulator = ""; if (isadd(p)) { p = cdr(p); if (sign_of_term(car(p)) === '-') { accumulator += print_str("-"); } accumulator += print_term(car(p)); p = cdr(p); while (iscons(p)) { if (sign_of_term(car(p)) === '+') { if (printMode === PRINTMODE_HUMAN && !test_flag) { accumulator += print_str(" + "); } else { accumulator += print_str("+"); } } else { if (printMode === PRINTMODE_HUMAN && !test_flag) { accumulator += print_str(" - "); } else { accumulator += print_str("-"); } } accumulator += print_term(car(p)); p = cdr(p); } } else { if (sign_of_term(p) === '-') { accumulator += print_str("-"); } accumulator += print_term(p); } return accumulator; }; sign_of_term = function(p) { var accumulator; accumulator = ""; if (car(p) === symbol(MULTIPLY) && isNumericAtom(cadr(p)) && lessp(cadr(p), zero)) { accumulator += '-'; } else if (isNumericAtom(p) && lessp(p, zero)) { accumulator += '-'; } else { accumulator += '+'; } return accumulator; }; print_term = function(p) { var accumulator, denom, numberOneOverSomething, origAccumulator, previousFactorWasANumber; accumulator = ""; if (car(p) === symbol(MULTIPLY) && any_denominators(p)) { accumulator += print_a_over_b(p); return accumulator; } 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; denom = car(p).q.b.toString(); } if (numberOneOverSomething) { origAccumulator = accumulator; accumulator = ""; } else { 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)))))) { accumulator += " \\cdot "; } } } } } accumulator += print_multiply_sign(); accumulator += print_factor(car(p), false, true); previousFactorWasANumber = false; if (isNumericAtom(car(p))) { previousFactorWasANumber = true; } p = cdr(p); } if (numberOneOverSomething) { accumulator = origAccumulator + "\\frac{" + accumulator + "}{" + denom + "}"; } } else { accumulator += print_factor(p); } return accumulator; }; print_subexpr = function(p) { var accumulator; accumulator = ""; accumulator += print_char('('); accumulator += print_expr(p); accumulator += print_char(')'); return accumulator; }; print_factorial_function = function(p) { var accumulator; accumulator = ""; p = cadr(p); if (isfraction(p) || car(p) === symbol(ADD) || car(p) === symbol(MULTIPLY) || car(p) === symbol(POWER) || car(p) === symbol(FACTORIAL)) { accumulator += print_subexpr(p); } else { accumulator += print_expr(p); } accumulator += print_char('!'); return accumulator; }; print_ABS_latex = function(p) { var accumulator; accumulator = ""; accumulator += print_str("\\left |"); accumulator += print_expr(cadr(p)); accumulator += print_str(" \\right |"); return accumulator; }; print_BINOMIAL_latex = function(p) { var accumulator; accumulator = ""; accumulator += print_str("\\binom{"); accumulator += print_expr(cadr(p)); accumulator += print_str("}{"); accumulator += print_expr(caddr(p)); accumulator += print_str("} "); return accumulator; }; print_DOT_latex = function(p) { var accumulator; accumulator = ""; accumulator += print_expr(cadr(p)); accumulator += print_str(" \\cdot "); accumulator += print_expr(caddr(p)); return accumulator; }; print_DOT_codegen = function(p) { var accumulator; accumulator = "dot("; accumulator += print_expr(cadr(p)); accumulator += ", "; accumulator += print_expr(caddr(p)); accumulator += ")"; return accumulator; }; print_SIN_codegen = function(p) { var accumulator; accumulator = "Math.sin("; accumulator += print_expr(cadr(p)); accumulator += ")"; return accumulator; }; print_COS_codegen = function(p) { var accumulator; accumulator = "Math.cos("; accumulator += print_expr(cadr(p)); accumulator += ")"; return accumulator; }; print_TAN_codegen = function(p) { var accumulator; accumulator = "Math.tan("; accumulator += print_expr(cadr(p)); accumulator += ")"; return accumulator; }; print_ARCSIN_codegen = function(p) { var accumulator; accumulator = "Math.asin("; accumulator += print_expr(cadr(p)); accumulator += ")"; return accumulator; }; print_ARCCOS_codegen = function(p) { var accumulator; accumulator = "Math.acos("; accumulator += print_expr(cadr(p)); accumulator += ")"; return accumulator; }; print_ARCTAN_codegen = function(p) { var accumulator; accumulator = "Math.atan("; accumulator += print_expr(cadr(p)); accumulator += ")"; return accumulator; }; print_SQRT_latex = function(p) { var accumulator; accumulator = ""; accumulator += print_str("\\sqrt{"); accumulator += print_expr(cadr(p)); accumulator += print_str("} "); return accumulator; }; print_TRANSPOSE_latex = function(p) { var accumulator; accumulator = ""; accumulator += print_str("{"); if (iscons(cadr(p))) { accumulator += print_str('('); } accumulator += print_expr(cadr(p)); if (iscons(cadr(p))) { accumulator += print_str(')'); } accumulator += print_str("}"); accumulator += print_str("^T"); return accumulator; }; print_TRANSPOSE_codegen = function(p) { var accumulator; accumulator = ""; accumulator += print_str("transpose("); accumulator += print_expr(cadr(p)); accumulator += print_str(')'); return accumulator; }; print_UNIT_codegen = function(p) { var accumulator; accumulator = ""; accumulator += print_str("identity("); accumulator += print_expr(cadr(p)); accumulator += print_str(')'); return accumulator; }; print_INV_latex = function(p) { var accumulator; accumulator = ""; accumulator += print_str("{"); if (iscons(cadr(p))) { accumulator += print_str('('); } accumulator += print_expr(cadr(p)); if (iscons(cadr(p))) { accumulator += print_str(')'); } accumulator += print_str("}"); accumulator += print_str("^{-1}"); return accumulator; }; print_INV_codegen = function(p) { var accumulator; accumulator = ""; accumulator += print_str("inv("); accumulator += print_expr(cadr(p)); accumulator += print_str(')'); return accumulator; }; print_DEFINT_latex = function(p) { var accumulator, functionBody, i, i1, numberOfIntegrals, originalIntegral, ref1, theIntegral, theVariable; accumulator = ""; functionBody = car(cdr(p)); p = cdr(p); originalIntegral = p; numberOfIntegrals = 0; while (iscons(cdr(cdr(p)))) { numberOfIntegrals++; theIntegral = cdr(cdr(p)); accumulator += print_str("\\int^{"); accumulator += print_expr(car(cdr(theIntegral))); accumulator += print_str("}_{"); accumulator += print_expr(car(theIntegral)); accumulator += print_str("} \\! "); p = cdr(theIntegral); } 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) { theVariable = cdr(p); accumulator += print_str(" \\mathrm{d} "); accumulator += print_expr(car(theVariable)); if (i < numberOfIntegrals) { accumulator += print_str(" \\, "); } p = cdr(cdr(theVariable)); } return accumulator; }; print_tensor = function(p) { var accumulator; accumulator = ""; accumulator += print_tensor_inner(p, 0, 0)[1]; 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, 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) { [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 { // 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(","); } k++; } } accumulator += print_str("]"); return [k, accumulator]; }; print_tensor_latex = function(p) { var accumulator; accumulator = ""; if (p.tensor.ndim <= 2) { accumulator += print_tensor_inner_latex(true, p, 0, 0)[1]; } 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, 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) { [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 { // 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}"; } return [k, accumulator]; }; print_SUM_latex = function(p) { var accumulator; accumulator = "\\sum_{"; accumulator += print_expr(caddr(p)); accumulator += "="; accumulator += print_expr(cadddr(p)); accumulator += "}^{"; accumulator += print_expr(caddddr(p)); accumulator += "}{"; accumulator += print_expr(cadr(p)); accumulator += "}"; return accumulator; }; print_SUM_codegen = function(p) { var accumulator, body, lowerlimit, upperlimit, variable; body = cadr(p); variable = caddr(p); lowerlimit = cadddr(p); upperlimit = caddddr(p); accumulator = "(function(){" + " var " + variable + "; " + " var holderSum = 0; " + " var lowerlimit = " + print_expr(lowerlimit) + "; " + " var upperlimit = " + print_expr(upperlimit) + "; " + " for (" + variable + " = lowerlimit; " + variable + " < upperlimit; " + variable + "++) { " + " holderSum += " + print_expr(body) + ";" + " } " + " return holderSum;" + "})()"; return accumulator; }; print_TEST_latex = function(p) { var accumulator; 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)); accumulator += "} & otherwise "; accumulator += " \\\\\\\\"; break; } accumulator += "{"; accumulator += print_expr(cadr(p)); 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); return accumulator += "\\end{array} \\right."; }; print_TEST_codegen = function(p) { var accumulator, howManyIfs; accumulator = "(function(){"; 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)) + ");"; accumulator += "}"; break; } if (howManyIfs) { accumulator += " else "; } 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); } accumulator += "})()"; return accumulator; }; print_TESTLT_latex = function(p) { var accumulator; accumulator = "{"; accumulator += print_expr(cadr(p)); accumulator += "}"; accumulator += " < "; accumulator += "{"; accumulator += print_expr(caddr(p)); return accumulator += "}"; }; print_TESTLE_latex = function(p) { var accumulator; accumulator = "{"; accumulator += print_expr(cadr(p)); accumulator += "}"; accumulator += " \\leq "; accumulator += "{"; accumulator += print_expr(caddr(p)); return accumulator += "}"; }; print_TESTGT_latex = function(p) { var accumulator; accumulator = "{"; accumulator += print_expr(cadr(p)); accumulator += "}"; accumulator += " > "; accumulator += "{"; accumulator += print_expr(caddr(p)); return accumulator += "}"; }; print_TESTGE_latex = function(p) { var accumulator; accumulator = "{"; accumulator += print_expr(cadr(p)); accumulator += "}"; accumulator += " \\geq "; accumulator += "{"; accumulator += print_expr(caddr(p)); return accumulator += "}"; }; print_TESTEQ_latex = function(p) { var accumulator; accumulator = "{"; accumulator += print_expr(cadr(p)); accumulator += "}"; accumulator += " = "; accumulator += "{"; accumulator += print_expr(caddr(p)); return accumulator += "}"; }; print_FOR_codegen = function(p) { var accumulator, body, lowerlimit, upperlimit, variable; body = cadr(p); variable = caddr(p); lowerlimit = cadddr(p); upperlimit = caddddr(p); accumulator = "(function(){" + " var " + variable + "; " + " var lowerlimit = " + print_expr(lowerlimit) + "; " + " var upperlimit = " + print_expr(upperlimit) + "; " + " for (" + variable + " = lowerlimit; " + variable + " < upperlimit; " + variable + "++) { " + " " + print_expr(body) + " } " + "})()"; return accumulator; }; print_DO_codegen = function(p) { var accumulator; accumulator = ""; p = cdr(p); while (iscons(p)) { accumulator += print_expr(car(p)); p = cdr(p); } return accumulator; }; print_SETQ_codegen = function(p) { var accumulator; accumulator = ""; accumulator += print_expr(cadr(p)); accumulator += " = "; accumulator += print_expr(caddr(p)); accumulator += "; "; return accumulator; }; print_PRODUCT_latex = function(p) { var accumulator; accumulator = "\\prod_{"; accumulator += print_expr(caddr(p)); accumulator += "="; accumulator += print_expr(cadddr(p)); accumulator += "}^{"; accumulator += print_expr(caddddr(p)); accumulator += "}{"; accumulator += print_expr(cadr(p)); accumulator += "}"; return accumulator; }; print_PRODUCT_codegen = function(p) { var accumulator, body, lowerlimit, upperlimit, variable; body = cadr(p); variable = caddr(p); lowerlimit = cadddr(p); upperlimit = caddddr(p); accumulator = "(function(){" + " var " + variable + "; " + " var holderProduct = 1; " + " var lowerlimit = " + print_expr(lowerlimit) + "; " + " var upperlimit = " + print_expr(upperlimit) + "; " + " for (" + variable + " = lowerlimit; " + variable + " < upperlimit; " + variable + "++) { " + " holderProduct *= " + print_expr(body) + ";" + " } " + " return holderProduct;" + "})()"; return accumulator; }; print_base = function(p) { var accumulator; accumulator = ""; if (isadd(cadr(p)) || caadr(p) === symbol(MULTIPLY) || caadr(p) === symbol(POWER) || isnegativenumber(cadr(p))) { accumulator += print_str('('); accumulator += print_expr(cadr(p)); accumulator += print_str(')'); } else if (isNumericAtom(cadr(p)) && (lessp(cadr(p), zero) || isfraction(cadr(p)))) { accumulator += print_str('('); accumulator += print_factor(cadr(p)); accumulator += print_str(')'); } else { accumulator += print_factor(cadr(p)); } return accumulator; }; print_exponent = function(p) { var accumulator; accumulator = ""; if (iscons(caddr(p)) || isfraction(caddr(p)) || (isNumericAtom(caddr(p)) && lessp(caddr(p), zero))) { accumulator += print_str('('); accumulator += print_expr(caddr(p)); accumulator += print_str(')'); } else { accumulator += print_factor(caddr(p)); } return accumulator; }; 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) { accumulator += print_str("Math.SQRT2"); return accumulator; } } else { if (printMode === PRINTMODE_LATEX) { accumulator += print_str("\\sqrt{"); accumulator += print_expr(base); accumulator += print_str("}"); return accumulator; } else if (codeGen) { accumulator += print_str("Math.sqrt("); accumulator += print_expr(base); accumulator += print_str(')'); return accumulator; } } } if ((equaln(get_binding(symbol(PRINT_LEAVE_E_ALONE)), 1)) && base === symbol(E)) { if (codeGen) { accumulator += print_str("Math.exp("); accumulator += print_expo_of_denom(exponent); accumulator += print_str(')'); return accumulator; } if (printMode === PRINTMODE_LATEX) { accumulator += print_str("e^{"); accumulator += print_expr(exponent); accumulator += print_str("}"); } else { accumulator += print_str("exp("); accumulator += print_expr(exponent); accumulator += print_str(')'); } return accumulator; } if (codeGen) { accumulator += print_str("Math.pow("); accumulator += print_base_of_denom(base); accumulator += print_str(", "); accumulator += print_expo_of_denom(exponent); accumulator += print_str(')'); 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) { accumulator += print_str("\\frac{1}{"); } else if (printMode === PRINTMODE_HUMAN && !test_flag) { accumulator += print_str("1 / "); } else { accumulator += print_str("1/"); } if (iscons(base) && printMode !== PRINTMODE_LATEX) { accumulator += print_str('('); accumulator += print_expr(base); accumulator += print_str(')'); } else { accumulator += print_expr(base); } if (printMode === PRINTMODE_LATEX) { accumulator += print_str("}"); } return accumulator; } if (isnegativeterm(exponent)) { if (printMode === PRINTMODE_LATEX) { accumulator += print_str("\\frac{1}{"); } else if (printMode === PRINTMODE_HUMAN && !test_flag) { accumulator += print_str("1 / "); } else { accumulator += print_str("1/"); } push(exponent); push_integer(-1); multiply(); newExponent = pop(); if (iscons(base) && printMode !== PRINTMODE_LATEX) { accumulator += print_str('('); accumulator += print_power(base, newExponent); accumulator += print_str(')'); } else { accumulator += print_power(base, newExponent); } if (printMode === PRINTMODE_LATEX) { accumulator += print_str("}"); } return accumulator; } } if (isfraction(exponent) && printMode === PRINTMODE_LATEX) { accumulator += print_str("\\sqrt"); push(exponent); denominator(); denomExponent = pop(); if (!isplustwo(denomExponent)) { accumulator += print_str("["); accumulator += print_expr(denomExponent); accumulator += print_str("]"); } accumulator += print_str("{"); push(exponent); numerator(); numExponent = pop(); exponent = numExponent; accumulator += print_power(base, exponent); accumulator += print_str("}"); return accumulator; } } 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); accumulator += print_str(')'); } else if (car(base) === symbol(MULTIPLY) || car(base) === symbol(POWER)) { if (printMode !== PRINTMODE_LATEX) { accumulator += print_str('('); } accumulator += print_factor(base, true); if (printMode !== PRINTMODE_LATEX) { accumulator += print_str(')'); } } else if (isNumericAtom(base) && (lessp(base, zero) || isfraction(base))) { accumulator += print_str('('); accumulator += print_factor(base); accumulator += print_str(')'); } 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); accumulator += print_str("}"); } else { accumulator += print_expr(exponent); } } else if (iscons(exponent) || isfraction(exponent) || (isNumericAtom(exponent) && lessp(exponent, zero))) { accumulator += print_str('('); accumulator += print_expr(exponent); accumulator += print_str(')'); } else { accumulator += print_factor(exponent); } } return accumulator; }; print_index_function = function(p) { var accumulator; accumulator = ""; p = cdr(p); if (caar(p) === symbol(ADD) || caar(p) === symbol(MULTIPLY) || caar(p) === symbol(POWER) || caar(p) === symbol(FACTORIAL)) { accumulator += print_subexpr(car(p)); } else { accumulator += print_expr(car(p)); } accumulator += print_str('['); p = cdr(p); if (iscons(p)) { accumulator += print_expr(car(p)); p = cdr(p); while (iscons(p)) { accumulator += print_str(','); accumulator += print_expr(car(p)); p = cdr(p); } } accumulator += print_str(']'); return accumulator; }; print_factor = function(p, omitParens, pastFirstFactor) { var accumulator, base, exponent, fbody, parameters, returned; // debugger accumulator = ""; if (isNumericAtom(p)) { // in an evaluated term, all the numeric parts // are at the beginning of the term. // When printing the EXPRESSION, // we peek into the first factor of the term and we // look at whether it's a number less then zero. // if it is, we print the "-" as the "leading" part of the // print of the EXPRESSION, and then we proceed printint the factors // of the term. This means that when we come here, we must // skip printing the minus if the number is negative, // because it's already been printed. if (pastFirstFactor && lessp(p, zero)) { accumulator += '('; } accumulator += print_number(p, pastFirstFactor); if (pastFirstFactor && lessp(p, zero)) { accumulator += ')'; } return accumulator; } if (isstr(p)) { accumulator += print_str("\""); accumulator += print_str(p.str); accumulator += print_str("\""); return accumulator; } if (istensor(p)) { if (printMode === PRINTMODE_LATEX) { accumulator += print_tensor_latex(p); } else { accumulator += print_tensor(p); } return accumulator; } if (car(p) === symbol(MULTIPLY)) { if (!omitParens) { if (sign_of_term(p) === '-' || printMode !== PRINTMODE_LATEX) { if (printMode === PRINTMODE_LATEX) { accumulator += print_str(" \\left ("); } else { accumulator += print_str('('); } } } accumulator += print_expr(p); if (!omitParens) { if (sign_of_term(p) === '-' || printMode !== PRINTMODE_LATEX) { if (printMode === PRINTMODE_LATEX) { accumulator += print_str(" \\right ) "); } else { accumulator += print_str(')'); } } } return accumulator; } else if (isadd(p)) { if (!omitParens) { accumulator += print_str('('); } accumulator += print_expr(p); if (!omitParens) { accumulator += print_str(')'); } return accumulator; } if (car(p) === symbol(POWER)) { base = cadr(p); exponent = caddr(p); 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) { parameters = caddr(p); accumulator += print_str("function "); if (DEBUG) { console.log("emittedString from print_factor " + stringsEmittedByUserPrintouts); } returned = print_list(parameters); accumulator += returned; accumulator += print_str(" -> "); } accumulator += print_expr(fbody); return accumulator; } if (car(p) === symbol(PATTERN)) { accumulator += print_expr(caadr(p)); if (printMode === PRINTMODE_LATEX) { accumulator += print_str(" \\rightarrow "); } else { if (printMode === PRINTMODE_HUMAN && !test_flag) { accumulator += print_str(" -> "); } else { accumulator += print_str("->"); } } accumulator += print_expr(car(cdr(cadr(p)))); return accumulator; } if (car(p) === symbol(INDEX) && issymbol(cadr(p))) { accumulator += print_index_function(p); return accumulator; } if (car(p) === symbol(FACTORIAL)) { accumulator += print_factorial_function(p); return accumulator; } else if (car(p) === symbol(ABS) && printMode === PRINTMODE_LATEX) { 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)) { if (printMode === PRINTMODE_LATEX) { accumulator += print_TRANSPOSE_latex(p); return accumulator; } else if (codeGen) { accumulator += print_TRANSPOSE_codegen(p); return accumulator; } } else if (car(p) === symbol(UNIT)) { if (codeGen) { accumulator += print_UNIT_codegen(p); return accumulator; } } else if (car(p) === symbol(INV)) { if (printMode === PRINTMODE_LATEX) { accumulator += print_INV_latex(p); return accumulator; } else if (codeGen) { accumulator += print_INV_codegen(p); return accumulator; } } else if (car(p) === symbol(BINOMIAL) && printMode === PRINTMODE_LATEX) { accumulator += print_BINOMIAL_latex(p); return accumulator; } else if (car(p) === symbol(DEFINT) && printMode === PRINTMODE_LATEX) { accumulator += print_DEFINT_latex(p); return accumulator; } else if (isinnerordot(p)) { if (printMode === PRINTMODE_LATEX) { accumulator += print_DOT_latex(p); return accumulator; } else if (codeGen) { accumulator += print_DOT_codegen(p); return accumulator; } } else if (car(p) === symbol(SIN)) { if (codeGen) { accumulator += print_SIN_codegen(p); return accumulator; } } else if (car(p) === symbol(COS)) { if (codeGen) { accumulator += print_COS_codegen(p); return accumulator; } } else if (car(p) === symbol(TAN)) { if (codeGen) { accumulator += print_TAN_codegen(p); return accumulator; } } else if (car(p) === symbol(ARCSIN)) { if (codeGen) { accumulator += print_ARCSIN_codegen(p); return accumulator; } } else if (car(p) === symbol(ARCCOS)) { if (codeGen) { accumulator += print_ARCCOS_codegen(p); return accumulator; } } else if (car(p) === symbol(ARCTAN)) { if (codeGen) { accumulator += print_ARCTAN_codegen(p); return accumulator; } } else if (car(p) === symbol(SUM)) { if (printMode === PRINTMODE_LATEX) { accumulator += print_SUM_latex(p); return accumulator; } else if (codeGen) { 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); return accumulator; } else if (codeGen) { accumulator += print_PRODUCT_codegen(p); return accumulator; } } else if (car(p) === symbol(FOR)) { if (codeGen) { accumulator += print_FOR_codegen(p); return accumulator; } } else if (car(p) === symbol(DO)) { if (codeGen) { accumulator += print_DO_codegen(p); return accumulator; } } else if (car(p) === symbol(TEST)) { if (codeGen) { accumulator += print_TEST_codegen(p); return accumulator; } if (printMode === PRINTMODE_LATEX) { accumulator += print_TEST_latex(p); return accumulator; } } else if (car(p) === symbol(TESTLT)) { if (codeGen) { accumulator += "((" + print_expr(cadr(p)) + ") < (" + print_expr(caddr(p)) + "))"; return accumulator; } if (printMode === PRINTMODE_LATEX) { accumulator += print_TESTLT_latex(p); return accumulator; } } else if (car(p) === symbol(TESTLE)) { if (codeGen) { accumulator += "((" + print_expr(cadr(p)) + ") <= (" + print_expr(caddr(p)) + "))"; return accumulator; } if (printMode === PRINTMODE_LATEX) { accumulator += print_TESTLE_latex(p); return accumulator; } } else if (car(p) === symbol(TESTGT)) { if (codeGen) { accumulator += "((" + print_expr(cadr(p)) + ") > (" + print_expr(caddr(p)) + "))"; return accumulator; } if (printMode === PRINTMODE_LATEX) { accumulator += print_TESTGT_latex(p); return accumulator; } } else if (car(p) === symbol(TESTGE)) { if (codeGen) { accumulator += "((" + print_expr(cadr(p)) + ") >= (" + print_expr(caddr(p)) + "))"; return accumulator; } if (printMode === PRINTMODE_LATEX) { accumulator += print_TESTGE_latex(p); return accumulator; } } else if (car(p) === symbol(TESTEQ)) { if (codeGen) { accumulator += "((" + print_expr(cadr(p)) + ") === (" + print_expr(caddr(p)) + "))"; return accumulator; } if (printMode === PRINTMODE_LATEX) { accumulator += print_TESTEQ_latex(p); return accumulator; } } else if (car(p) === symbol(FLOOR)) { if (codeGen) { accumulator += "Math.floor(" + print_expr(cadr(p)) + ")"; return accumulator; } if (printMode === PRINTMODE_LATEX) { accumulator += " \\lfloor {" + print_expr(cadr(p)) + "} \\rfloor "; return accumulator; } } else if (car(p) === symbol(CEILING)) { if (codeGen) { accumulator += "Math.ceiling(" + print_expr(cadr(p)) + ")"; return accumulator; } if (printMode === PRINTMODE_LATEX) { accumulator += " \\lceil {" + print_expr(cadr(p)) + "} \\rceil "; return accumulator; } } else if (car(p) === symbol(ROUND)) { if (codeGen) { accumulator += "Math.round(" + print_expr(cadr(p)) + ")"; return accumulator; } } else if (car(p) === symbol(SETQ)) { if (codeGen) { accumulator += print_SETQ_codegen(p); return accumulator; } else { accumulator += print_expr(cadr(p)); accumulator += print_str("="); accumulator += print_expr(caddr(p)); return accumulator; } } 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) { accumulator += print_str('('); } if (iscons(p)) { accumulator += print_expr(car(p)); p = cdr(p); while (iscons(p)) { accumulator += print_str(","); accumulator += print_expr(car(p)); p = cdr(p); } } if (!omitParens) { accumulator += print_str(')'); } return accumulator; } if (p === symbol(DERIVATIVE)) { accumulator += print_char('d'); } else if (p === symbol(E)) { if (codeGen) { accumulator += print_str("Math.E"); } else { accumulator += print_str("e"); } } else if (p === symbol(PI)) { if (printMode === PRINTMODE_LATEX) { accumulator += print_str("\\pi"); } else { accumulator += print_str("pi"); } } else { accumulator += print_str(get_printname(p)); } return accumulator; }; print_list = function(p) { var accumulator; accumulator = ""; switch (p.k) { case CONS: accumulator += '('; accumulator += print_list(car(p)); if (p === cdr(p) && p !== symbol(NIL)) { console.log("oh no recursive!"); debugger; } p = cdr(p); while (iscons(p)) { accumulator += " "; accumulator += print_list(car(p)); p = cdr(p); if (p === cdr(p) && p !== symbol(NIL)) { console.log("oh no recursive!"); debugger; } } if (p !== symbol(NIL)) { accumulator += " . "; accumulator += print_list(p); } accumulator += ')'; break; case STR: //print_str("\"") accumulator += p.str; break; //print_str("\"") case NUM: case DOUBLE: accumulator += print_number(p, true); break; case SYM: accumulator += get_printname(p); break; default: accumulator += ""; } return accumulator; }; print_multiply_sign = function() { var accumulator; accumulator = ""; if (printMode === PRINTMODE_LATEX) { if (printMode === PRINTMODE_HUMAN && !test_flag) { accumulator += print_str(" "); } else { return accumulator; } } if (printMode === PRINTMODE_HUMAN && !test_flag && !codeGen) { accumulator += print_str(" "); } else { accumulator += print_str("*"); } return accumulator; }; is_denominator = function(p) { if (car(p) === symbol(POWER) && cadr(p) !== symbol(E) && isnegativeterm(caddr(p))) { return 1; } else { return 0; } }; // 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)) { return 1; } p = cdr(p); } 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() { class glyph {}; glyph.prototype.c = 0; glyph.prototype.x = 0; glyph.prototype.y = 0; 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) { chartab[charTabIndex] = new glyph(); } yindex = 0; level = 0; emit_x = 0; expr_level = 0; 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 = ""; accumulator += character; return accumulator; }; printchar = function(character) { return printchar_nowrap(character); }; print2dascii = function(p) { var beenPrinted, h, w, y; h = 0; w = 0; y = 0; save(); yindex = 0; level = 0; emit_x = 0; emit_top_expr(p); // if too wide then print flat [h, w, y] = get_size(0, yindex); if (w > 100) { printline(p); restore(); return; } beenPrinted = print_glyphs(); restore(); return beenPrinted; }; emit_top_expr = function(p) { if (car(p) === symbol(SETQ)) { emit_expr(cadr(p)); __emit_str(" = "); emit_expr(caddr(p)); return; } if (istensor(p)) { return emit_tensor(p); } else { return emit_expr(p); } }; will_be_displayed_as_fraction = function(p) { if (level > 0) { return 0; } if (isfraction(p)) { return 1; } if (car(p) !== symbol(MULTIPLY)) { return 0; } if (isfraction(cadr(p))) { return 1; } while (iscons(p)) { if (isdenominator(car(p))) { return 1; } p = cdr(p); } return 0; }; emit_expr = function(p) { // if (level > 0) { // printexpr(p) // return // } expr_level++; if (car(p) === symbol(ADD)) { p = cdr(p); if (__is_negative(car(p))) { __emit_char('-'); if (will_be_displayed_as_fraction(car(p))) { __emit_char(' '); } } emit_term(car(p)); p = cdr(p); while (iscons(p)) { if (__is_negative(car(p))) { __emit_char(' '); __emit_char('-'); __emit_char(' '); } else { __emit_char(' '); __emit_char('+'); __emit_char(' '); } emit_term(car(p)); p = cdr(p); } } else { if (__is_negative(p)) { __emit_char('-'); if (will_be_displayed_as_fraction(p)) { __emit_char(' '); } } emit_term(p); } return expr_level--; }; emit_unsigned_expr = function(p) { 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 = []; while (iscons(p)) { if (__is_negative(car(p))) { __emit_char(' '); __emit_char('-'); __emit_char(' '); } else { __emit_char(' '); __emit_char('+'); __emit_char(' '); } emit_term(car(p)); results.push(p = cdr(p)); } return results; } else { // if (__is_negative(p)) // __emit_char('-') return emit_term(p); } }; __is_negative = function(p) { if (isnegativenumber(p)) { return 1; } if (car(p) === symbol(MULTIPLY) && isnegativenumber(cadr(p))) { return 1; } return 0; }; emit_term = function(p) { var n; if (car(p) === symbol(MULTIPLY)) { n = count_denominators(p); if (n && level === 0) { return emit_fraction(p, n); } else { return emit_multiply(p, n); } } else { return emit_factor(p); } }; isdenominator = function(p) { if (car(p) === symbol(POWER) && cadr(p) !== symbol(E) && __is_negative(caddr(p))) { return 1; } else { return 0; } }; count_denominators = function(p) { 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)) { count++; } p = cdr(p); } 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) { p = cdr(p); if (isplusone(car(p)) || isminusone(car(p))) { p = cdr(p); } emit_factor(car(p)); p = cdr(p); results = []; while (iscons(p)) { __emit_char(' '); emit_factor(car(p)); results.push(p = cdr(p)); } return results; } else { emit_numerators(p); __emit_char('/'); // need grouping if more than one denominator if (n > 1 || isfraction(cadr(p))) { __emit_char('('); emit_denominators(p); return __emit_char(')'); } else { return emit_denominators(p); } } }; //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; k1 = 0; k2 = 0; n = 0; x = 0; save(); p3 = one; p4 = one; if (isrational(cadr(p))) { push(cadr(p)); mp_numerator(); absval(); p3 = pop(); push(cadr(p)); mp_denominator(); p4 = pop(); // p4 is B } if (isdouble(cadr(p))) { push(cadr(p)); absval(); p3 = pop(); // p3 is A } // count numerators if (isplusone(p3)) { // p3 is A n = 0; } else { n = 1; } p1 = cdr(p); if (isNumericAtom(car(p1))) { p1 = cdr(p1); } while (iscons(p1)) { p2 = car(p1); if (isdenominator(p2)) { doNothing = 1; } else { n++; } p1 = cdr(p1); } // emit numerators x = emit_x; k1 = yindex; count = 0; 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); } while (iscons(p1)) { p2 = car(p1); if (isdenominator(p2)) { doNothing = 1; } else { if (count > 0) { __emit_char(' '); } if (n === 1) { emit_expr(p2); } else { emit_factor(p2); } count++; } p1 = cdr(p1); } if (count === 0) { __emit_char('1'); } // emit denominators k2 = yindex; count = 0; if (!isplusone(p4)) { // p4 is B emit_number(p4, 0); count++; d++; } p1 = cdr(p); if (isrational(car(p1))) { p1 = cdr(p1); } while (iscons(p1)) { p2 = car(p1); if (isdenominator(p2)) { if (count > 0) { __emit_char(' '); } emit_denominator(p2, d); count++; } p1 = cdr(p1); } fixup_fraction(x, k1, k2); return restore(); }; // p points to a multiply emit_numerators = function(p) { var doNothing, n; save(); n = 0; p1 = one; p = cdr(p); if (isrational(car(p))) { push(car(p)); mp_numerator(); absval(); p1 = pop(); p = cdr(p); } else if (isdouble(car(p))) { push(car(p)); absval(); p1 = pop(); p = cdr(p); } n = 0; if (!isplusone(p1)) { emit_number(p1, 0); n++; } while (iscons(p)) { if (isdenominator(car(p))) { doNothing = 1; } else { if (n > 0) { __emit_char(' '); } emit_factor(car(p)); n++; } p = cdr(p); } if (n === 0) { __emit_char('1'); } return restore(); }; // p points to a multiply emit_denominators = function(p) { var n; save(); n = 0; p = cdr(p); if (isfraction(car(p))) { push(car(p)); mp_denominator(); p1 = pop(); emit_number(p1, 0); n++; p = cdr(p); } while (iscons(p)) { if (isdenominator(car(p))) { if (n > 0) { __emit_char(' '); } emit_denominator(car(p), 0); n++; } p = cdr(p); } return restore(); }; emit_factor = function(p) { if (istensor(p)) { if (level === 0) { //emit_tensor(p) emit_flat_tensor(p); } else { emit_flat_tensor(p); } return; } if (isdouble(p)) { emit_number(p, 0); return; } if (car(p) === symbol(ADD) || car(p) === symbol(MULTIPLY)) { emit_subexpr(p); return; } if (car(p) === symbol(POWER)) { emit_power(p); return; } if (iscons(p)) { //if (car(p) == symbol(FORMAL) && cadr(p).k == SYM) // emit_symbol(cadr(p)) //else emit_function(p); return; } if (isNumericAtom(p)) { if (level === 0) { emit_numerical_fraction(p); } else { emit_number(p, 0); } return; } if (issymbol(p)) { emit_symbol(p); return; } if (isstr(p)) { emit_string(p); } }; emit_numerical_fraction = function(p) { var k1, k2, x; k1 = 0; k2 = 0; x = 0; save(); push(p); mp_numerator(); absval(); p3 = pop(); push(p); mp_denominator(); p4 = pop(); if (isplusone(p4)) { // p4 is B emit_number(p3, 0); restore(); return; } x = emit_x; k1 = yindex; emit_number(p3, 0); k2 = yindex; 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; } if (issymbol(p)) { return 1; } if (isfraction(p)) { return 0; } if (isnegativenumber(p)) { return 0; } if (isNumericAtom(p)) { return 1; } return 0; }; emit_power = function(p) { var k1, k2, x; k1 = 0; k2 = 0; x = 0; if (cadr(p) === symbol(E)) { __emit_str("exp("); emit_expr(caddr(p)); __emit_char(')'); return; } if (level > 0) { if (isminusone(caddr(p))) { __emit_char('1'); __emit_char('/'); if (isfactor(cadr(p))) { emit_factor(cadr(p)); } else { emit_subexpr(cadr(p)); } } else { if (isfactor(cadr(p))) { emit_factor(cadr(p)); } else { emit_subexpr(cadr(p)); } __emit_char('^'); if (isfactor(caddr(p))) { emit_factor(caddr(p)); } else { emit_subexpr(caddr(p)); } } 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; } k1 = yindex; if (isfactor(cadr(p))) { emit_factor(cadr(p)); } else { emit_subexpr(cadr(p)); } k2 = yindex; level++; emit_expr(caddr(p)); level--; 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)); } else { emit_factor(cadr(p)); } 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--; return fixup_power(k1, k2); }; emit_function = function(p) { if (car(p) === symbol(INDEX) && issymbol(cadr(p))) { emit_index_function(p); return; } if (car(p) === symbol(FACTORIAL)) { emit_factorial_function(p); return; } if (car(p) === symbol(DERIVATIVE)) { __emit_char('d'); } else { emit_symbol(car(p)); } __emit_char('('); p = cdr(p); if (iscons(p)) { emit_expr(car(p)); p = cdr(p); while (iscons(p)) { __emit_char(','); //__emit_char(' ') emit_expr(car(p)); p = cdr(p); } } return __emit_char(')'); }; emit_index_function = function(p) { p = cdr(p); if (caar(p) === symbol(ADD) || caar(p) === symbol(MULTIPLY) || caar(p) === symbol(POWER) || caar(p) === symbol(FACTORIAL)) { emit_subexpr(car(p)); } else { emit_expr(car(p)); } __emit_char('['); p = cdr(p); if (iscons(p)) { emit_expr(car(p)); p = cdr(p); while (iscons(p)) { __emit_char(','); emit_expr(car(p)); p = cdr(p); } } return __emit_char(']'); }; emit_factorial_function = function(p) { p = cadr(p); if (isfraction(p) || car(p) === symbol(ADD) || car(p) === symbol(MULTIPLY) || car(p) === symbol(POWER) || car(p) === symbol(FACTORIAL)) { emit_subexpr(p); } else { emit_expr(p); } return __emit_char('!'); }; emit_subexpr = function(p) { __emit_char('('); emit_expr(p); return __emit_char(')'); }; emit_symbol = function(p) { var i, j1, pPrintName, ref2, results; i = 0; if (p === symbol(E)) { __emit_str("exp(1)"); return; } pPrintName = get_printname(p); results = []; 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; }; emit_string = function(p) { var i, j1, pString, ref2; 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) { __emit_char(pString[i]); } return __emit_char('"'); }; fixup_fraction = function(x, k1, k2) { var dx, dy, h1, h2, i, j1, ref2, results, w, w1, w2, y, y1, y2; dx = 0; dy = 0; i = 0; w = 0; y = 0; h1 = 0; w1 = 0; y1 = 0; h2 = 0; w2 = 0; y2 = 0; [h1, w1, y1] = get_size(k1, k2); [h2, w2, y2] = get_size(k2, yindex); if (w2 > w1) { 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); if (w2 > w1) { dx = -w1; } else { dx = -w1 + (w1 - w2) / 2; } dx++; dy = -y2 + 1; move(k2, yindex, dx, dy); if (w2 > w1) { w = w2; } else { w = w1; } w += 2; emit_x = x; results = []; 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, w1, w2, y1, y2; dy = 0; h1 = 0; w1 = 0; y1 = 0; h2 = 0; w2 = 0; y2 = 0; [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); }; move = function(j, k, dx, dy) { 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) { 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; min_x = chartab[j].x; 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) { if (chartab[i].x < min_x) { min_x = chartab[i].x; } if (chartab[i].x > max_x) { max_x = chartab[i].x; } if (chartab[i].y < min_y) { min_y = chartab[i].y; } if (chartab[i].y > max_y) { max_y = chartab[i].y; } } h = max_y - min_y + 1; w = max_x - min_x + 1; y = min_y; return [h, w, y]; }; displaychar = function(c) { return __emit_char(c); }; __emit_char = function(c) { if (yindex === YMAX) { return; } if (chartab[yindex] == null) { debugger; } chartab[yindex].c = c; chartab[yindex].x = emit_x; chartab[yindex].y = 0; yindex++; return emit_x++; }; __emit_str = function(s) { 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) { results.push(__emit_char(s[i])); } return results; }; emit_number = function(p, emit_sign) { var i, j1, l1, m1, ref2, ref3, ref4, results, results1, tmpString; tmpString = ""; i = 0; switch (p.k) { case NUM: tmpString = p.q.a.toString(); 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) { __emit_char(tmpString[i]); } tmpString = p.q.b.toString(); if (tmpString === "1") { break; } __emit_char('/'); results = []; 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; break; case DOUBLE: tmpString = doubleToReasonableString(p.d); if (tmpString[0] === '-' && emit_sign === 0) { tmpString = tmpString.substring(1); } results1 = []; 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; } if (a.y > b.y) { return 1; } if (a.x < b.x) { return -1; } if (a.x > b.x) { return 1; } return 0; }; print_glyphs = function() { 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) { while (chartab[i].y > y) { accumulator += printchar('\n'); x = 0; y++; } while (chartab[i].x > x) { accumulator += printchar_nowrap(' '); x++; } accumulator += printchar_nowrap(chartab[i].c); x++; } return accumulator; }; buffer = ""; getdisplaystr = function() { yindex = 0; level = 0; emit_x = 0; emit_expr(pop()); fill_buf(); return buffer; }; fill_buf = function() { var i, j1, ref2, sIndex, subsetOfStack, tmpBuffer, x, y; 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) { while (chartab[i].y > y) { tmpBuffer[sIndex++] = '\n'; x = 0; y++; } while (chartab[i].x > x) { tmpBuffer[sIndex++] = ' '; x++; } tmpBuffer[sIndex++] = chartab[i].c; x++; } return tmpBuffer[sIndex++] = '\n'; }; N = 100; oneElement = (function() { class oneElement {}; oneElement.prototype.x = 0; oneElement.prototype.y = 0; oneElement.prototype.h = 0; oneElement.prototype.w = 0; oneElement.prototype.index = 0; oneElement.prototype.count = 0; return oneElement; }).call(this); elem = []; for (elelmIndex = j1 = 0; j1 < 10000; elelmIndex = ++j1) { elem[elelmIndex] = new oneElement(); } SPACE_BETWEEN_COLUMNS = 3; 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, row, w, x, y; i = 0; n = 0; nrow = 0; ncol = 0; x = 0; y = 0; h = 0; w = 0; dx = 0; dy = 0; eh = 0; ew = 0; row = 0; col = 0; if (p.tensor.ndim > 2) { emit_flat_tensor(p); return; } nrow = p.tensor.dim[0]; if (p.tensor.ndim === 2) { ncol = p.tensor.dim[1]; } else { ncol = 1; } n = nrow * ncol; if (n > N) { emit_flat_tensor(p); return; } // horizontal coordinate of the matrix //if 0 //emit_x += 2; # make space for left paren //endif x = emit_x; // 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; [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, ref3 = n; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { if (elem[i].h > eh) { eh = elem[i].h; } if (elem[i].w > ew) { 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); // 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); } // 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); } } return emit_x = x + w; }; emit_flat_tensor = function(p) { return emit_tensor_inner(p, 0, 0); }; emit_tensor_inner = function(p, j, k) { 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) { if (j + 1 === p.tensor.ndim) { emit_expr(p.tensor.elem[k]); k = k + 1; } else { k = emit_tensor_inner(p, j + 1, k); } if (i + 1 < p.tensor.dim[j]) { __emit_char(','); } } __emit_char(')'); 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(); if (isNaN(j)) { push(p1); return; } // 4th arg (upper limit) push(caddddr(p1)); Eval(); k = pop_integer(); if (isNaN(k)) { 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) { push_integer(i); p5 = pop(); set_binding(indexVariable, p5); push(body); Eval(); if (DEBUG) { console.log("product - factor 1: " + stack[tos - 1].toString()); console.log("product - factor 2: " + stack[tos - 2].toString()); } multiply(); if (DEBUG) { 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"); } if (MZERO(p1.q.a)) { push(zero); restore(); return; } aa = mmul(p1.q.a, p2.q.b); bb = mmul(p1.q.b, p2.q.a); c = mgcd(aa, bb); c = makeSignSameAs(c, bb); p1 = new U(); p1.k = NUM; p1.q.a = mdiv(aa, c); p1.q.b = mdiv(bb, c); push(p1); 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(); return; } aa = mmul(p1.q.a, p2.q.a); bb = mmul(p1.q.b, p2.q.b); c = mgcd(aa, bb); c = makeSignSameAs(c, bb); p1 = new U(); 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)) { // p1 is BASE # p2 is EXPO push_integer(1); return; } // if (-1)^(1/2) -> leave it as is if (isminusone(p1) && isoneovertwo(p2)) { // p1 is BASE # p2 is EXPO push(imaginaryunit); return; } // 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 exponent is 1 then return base if (isplusone(p2)) { // p2 is EXPO push(p1); return; } // 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); list(3); return; } x = mpow(p1.q.a, Math.abs(expo)); y = mpow(p1.q.b, Math.abs(expo)); if (expo < 0) { t = x; x = y; y = t; x = makeSignSameAs(x, y); y = makePositive(y); } p3 = new U(); p3.k = NUM; p3.q.a = x; p3.q.b = y; push(p3); return; } // 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 base is negative then (-N)^M -> N^M * (-1)^M if (isnegativenumber(p1)) { // p1 is BASE push(p1); negate(); push(p2); qpow(); push_integer(-1); push(p2); qpow(); multiply(); return; } if (!isinteger(p1)) { // p1 is BASE push(p1); mp_numerator(); push(p2); qpow(); push(p1); mp_denominator(); push(p2); negate(); qpow(); multiply(); return; } // 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)) { // p2 is EXPO push_symbol(POWER); push(p1); // p1 is BASE push(p2); list(3); return; } a = p2.q.a; b = p2.q.b; x = mroot(p1.q.a, b); if (x === 0) { push_symbol(POWER); push(p1); push(p2); list(3); return; } y = mpow(x, a); //mfree(x) p3 = new U(); p3.k = NUM; if (p2.q.a.isNegative()) { // p2 is EXPO p3.q.a = bigInt(1); p3.q.b = y; } else { p3.q.a = y; p3.q.b = bigInt(1); } 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)) { // p1 is A if (p1.q.a.isOdd()) { // p1 is A push_integer(-1); // odd exponent } else { push_integer(1); // even exponent } restore(); return; } // floor push(p1); bignum_truncate(); p2 = pop(); if (isnegativenumber(p1)) { // p1 is A push(p2); // p2 is Q push_integer(-1); add(); p2 = pop(); // p2 is Q } // remainder (always positive) push(p1); push(p2); subtract(); p3 = pop(); push_symbol(POWER); push_integer(-1); push(p3); // p3 is R list(3); // negate if quotient is odd if (p2.q.a.isOdd()) { // p2 is Q negate(); } return restore(); }; is_small_integer = function(p) { 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; save(); p2 = pop(); p1 = pop(); h = tos; push(p1); factor_small_number(); n = tos - h; stackIndex = h; for (i = l1 = 0, ref2 = n; l1 < ref2; i = l1 += 2) { push(stack[stackIndex + i]); push(stack[stackIndex + i + 1]); push(p2); 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); push(p1); return restore(); }; // p1 (BASE) is a prime number so power is simpler quickpower = function() { var expo; expo = 0; save(); p2 = pop(); p1 = pop(); push(p2); bignum_truncate(); p3 = pop(); push(p2); push(p3); subtract(); p4 = pop(); if (!isZeroAtomOrTensor(p4)) { push_symbol(POWER); push(p1); push(p4); list(3); } push(p3); expo = pop_integer(); if (isNaN(expo)) { push_symbol(POWER); push(p1); push(p3); list(3); restore(); return; } if (expo === 0) { restore(); return; } push(p1); bignum_power_number(expo); return restore(); }; //if SELFTEST // Divide polynomials Eval_quotient = function() { push(cadr(p1)); Eval(); push(caddr(p1)); Eval(); push(cadddr(p1)); Eval(); p1 = pop(); if (p1 === symbol(NIL)) { p1 = symbol(SYMBOL_X); } push(p1); 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; i = 0; m = 0; n = 0; x = 0; //U **dividend, **divisor save(); p3 = pop(); p2 = pop(); p1 = pop(); h = tos; dividend = tos; m = coeff(p3, p1) - 1; divisor = tos; n = coeff(p3, p2) - 1; x = m - n; push_integer(0); p5 = pop(); while (x >= 0) { push(stack[dividend + m]); push(stack[divisor + n]); divide(); p4 = pop(); 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); multiply(); subtract(); stack[dividend + x + i] = pop(); } push(p5); push(p4); push(p3); push_integer(x); power(); multiply(); add(); p5 = pop(); m--; x--; } moveTos(h); push(p5); return restore(); }; DEBUG_RATIONALIZE = false; Eval_rationalize = function() { push(cadr(p1)); Eval(); return rationalize(); }; rationalize = function() { var x; x = expanding; yyrationalize(); return expanding = x; }; yyrationalize = function() { var commonDenominator, eachTerm, theArgument; theArgument = pop(); if (istensor(theArgument)) { __rationalize_tensor(theArgument); return; } expanding = 0; if (car(theArgument) !== symbol(ADD)) { push(theArgument); return; } if (DEBUG_RATIONALIZE) { console.log("rationalize: this is the input expr: " + theArgument); } // get new denominator push(one); multiply_denominators(theArgument); commonDenominator = pop(); if (DEBUG_RATIONALIZE) { console.log("rationalize: this is the new denominator: " + commonDenominator); } // multiply each term by new denominator push(zero); eachTerm = cdr(theArgument); while (iscons(eachTerm)) { if (DEBUG_RATIONALIZE) { console.log("term: " + car(eachTerm)); } push(commonDenominator); push(car(eachTerm)); multiply(); add(); eachTerm = cdr(eachTerm); } if (DEBUG_RATIONALIZE) { console.log("rationalize: original terms times new denominator: " + stack[tos - 1]); } // collect common factors Condense(); if (DEBUG_RATIONALIZE) { console.log("rationalize: after factoring: " + stack[tos - 1]); } // divide by common denominator push(commonDenominator); divide(); if (DEBUG_RATIONALIZE) { return console.log("rationalize: after dividing by new denom. (and we're done): " + stack[tos - 1]); } }; multiply_denominators = function(p) { var results; if (car(p) === symbol(ADD)) { p = cdr(p); results = []; while (iscons(p)) { multiply_denominators_term(car(p)); results.push(p = cdr(p)); } return results; } else { return multiply_denominators_term(p); } }; multiply_denominators_term = function(p) { var results; if (car(p) === symbol(MULTIPLY)) { p = cdr(p); results = []; while (iscons(p)) { multiply_denominators_factor(car(p)); results.push(p = cdr(p)); } return results; } else { return multiply_denominators_factor(p); } }; multiply_denominators_factor = function(p) { if (car(p) !== symbol(POWER)) { return; } 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(); }; __rationalize_tensor = function(theTensor) { var i, l1, n, ref2; i = 0; push(theTensor); Eval(); theTensor = pop(); 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) { push(theTensor.tensor.elem[i]); rationalize(); theTensor.tensor.elem[i] = pop(); } check_tensor_dimensions(theTensor); return push(theTensor); }; __lcm = function() { save(); p1 = pop(); p2 = pop(); push(p1); push(p2); multiply(); push(p1); push(p2); gcd(); divide(); return restore(); }; Eval_real = function() { push(cadr(p1)); Eval(); return real(); }; real = function() { save(); rect(); p1 = pop(); push(p1); push(p1); conjugate(); add(); push_integer(2); divide(); return restore(); }; DEBUG_RECT = false; Eval_rect = function() { push(cadr(p1)); Eval(); return rect(); }; rect = function() { var input; save(); p1 = pop(); input = p1; if (DEBUG_RECT) { console.log("RECT of " + input); } 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); } if (!isZeroAtomOrTensor(get_binding(symbol(ASSUME_REAL_VARIABLES)))) { push(p1); } else { push_symbol(YYRECT); 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))) { // 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 "); } push_integer(0); p1 = cdr(p1); while (iscons(p1)) { push(car(p1)); rect(); add(); 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 "); } push(p1); abs(); if (DEBUG_RECT) { console.log(" rect - " + input + " abs: " + stack[tos - 1].toString()); } push(p1); arg(); if (DEBUG_RECT) { console.log(" rect - " + input + " arg of " + p1 + " : " + stack[tos - 1].toString()); } p1 = pop(); push(p1); cosine(); if (DEBUG_RECT) { console.log(" rect - " + input + " cosine: " + stack[tos - 1].toString()); } push(imaginaryunit); push(p1); sine(); if (DEBUG_RECT) { console.log(" rect - " + input + " sine: " + stack[tos - 1].toString()); } multiply(); if (DEBUG_RECT) { console.log(" rect - " + input + " i * sine: " + stack[tos - 1].toString()); } add(); if (DEBUG_RECT) { console.log(" rect - " + input + " cos + i * sine: " + stack[tos - 1].toString()); } multiply(); } restore(); if (DEBUG_RECT) { return console.log("rect of " + input + " : " + stack[tos - 1]); } }; //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() { // this transforms simple "equation" forms into // something that can be processed. E.g., say, y = 3x - 2 // A == B -> A - B // A = B -> A - B p2 = cadr(p1); if (car(p2) === symbol(SETQ) || car(p2) === symbol(TESTEQ)) { push(cadr(p2)); Eval(); push(caddr(p2)); Eval(); subtract(); } else { push(p2); Eval(); p2 = pop(); if (car(p2) === symbol(SETQ) || car(p2) === symbol(TESTEQ)) { push(cadr(p2)); Eval(); push(caddr(p2)); Eval(); subtract(); } else { push(p2); } } // 2nd arg, x push(caddr(p1)); Eval(); p2 = pop(); if (p2 === symbol(NIL)) { guess(); } else { push(p2); } p2 = pop(); p1 = pop(); if (!ispolyexpandedform(p1, p2)) { stop("roots: 1st argument is not a polynomial in the variable " + p2); } push(p1); push(p2); return roots(); }; 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; } } return imaginaryCoefficients; }; 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; if (isZeroAtomOrTensor(stack[tos - k])) { isSimpleRootPolynomial = false; } for (i = l1 = ref2 = k - 1; l1 > 1; i = l1 += -1) { if (!isZeroAtomOrTensor(stack[tos - i])) { isSimpleRootPolynomial = false; break; } } } else { isSimpleRootPolynomial = false; } return isSimpleRootPolynomial; }; normalisedCoeff = function(variable, polynomial) { var divideBy, i, k, l1, m1, miniStack, ref2, ref3; k = coeff(variable, polynomial); //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) { miniStack.push(pop()); } //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; if (DEBUG) { console.log("roots: " + stack[tos - 1].toString() + " " + stack[tos - 2].toString()); } 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(); push(symbol(NIL)); restore(); return; } performing_roots = true; h = tos - 2; if (DEBUG) { console.log("roots checking if " + stack[tos - 1].toString() + " is a case of simple roots"); } p2 = pop(); p1 = pop(); push(p1); push(p2); k = normalisedCoeff(p2, p1); if (isSimpleRoot(k)) { if (DEBUG) { console.log("yes, " + stack[tos - 1].toString() + " is a case of simple roots"); } lastCoeff = stack[tos - k]; leadingCoeff = stack[tos - 1]; moveTos(tos - k); pop(); pop(); getSimpleRoots(k, leadingCoeff, lastCoeff); } else { moveTos(tos - k); roots2(); } n = tos - h; if (n === 0) { stop("roots: the polynomial is not factorable, try nroots"); } if (n === 1) { performing_roots = false; restore(); return; } sort_stack(n); 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) { p1.tensor.elem[i] = stack[h + i]; } moveTos(h); push(p1); restore(); 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); power(); push(leadingCoeff); push_rational(1, n); power(); divide(); commonPart = pop(); if (n % 2 === 0) { for (rootsOfOne = l1 = 1, ref2 = n; l1 <= ref2; rootsOfOne = l1 += 2) { push(commonPart); push_integer(-1); push_rational(rootsOfOne, n); power(); multiply(); aSol = pop(); push(aSol); push(aSol); negate(); } } else { 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); power(); multiply(); if (rootsOfOne % 2 === 0) { negate(); } } } return restore(); }; roots2 = function() { var k; save(); if (DEBUG) { console.log("roots2: " + stack[tos - 1].toString() + " " + stack[tos - 2].toString()); } p2 = pop(); // the polynomial variable p1 = pop(); // the polynomial push(p1); push(p2); k = normalisedCoeff(p2, p1); if (!hasImaginaryCoeff(k)) { moveTos(tos - k); factorpoly(); p1 = pop(); } else { moveTos(tos - k); pop(); pop(); } 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); roots3(); p1 = cdr(p1); } } else { push(p1); push(p2); roots3(); } return restore(); }; roots3 = function() { var n; save(); p2 = pop(); p1 = pop(); if (car(p1) === symbol(POWER) && ispolyexpandedform(cadr(p1), p2) && isposint(caddr(p1))) { n = normalisedCoeff(p2, cadr(p1)); mini_solve(n); } else if (ispolyexpandedform(p1, p2)) { n = normalisedCoeff(p2, p1); mini_solve(n); } 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); push(p3); divide(); negate(); restore(); return; } // AX^2 + BX + C, X = (-B +/- (B^2 - 4AC)^(1/2)) / (2A) if (n === 3) { //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(); // -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(); // A p4 = pop(); // B p5 = pop(); // C p6 = pop(); // D // C - only related calculations push(p5); push(p5); multiply(); R_c2 = pop(); push(R_c2); push(p5); multiply(); R_c3 = pop(); // B - only related calculations push(p4); push(p4); multiply(); R_b2 = pop(); push(R_b2); push(p4); multiply(); R_b3 = pop(); push(R_b3); push(p6); multiply(); R_b3_d = pop(); push(R_b3_d); push_integer(-4); multiply(); R_m4_b3_d = pop(); push(R_b3); push_integer(2); multiply(); R_2_b3 = pop(); // A - only related calculations push(p3); push(p3); multiply(); R_a2 = pop(); push(R_a2); push(p3); multiply(); R_a3 = pop(); push_integer(3); push(p3); multiply(); R_3_a = pop(); push(R_a2); push(p6); multiply(); R_a2_d = pop(); push(R_a2_d); push(p6); multiply(); R_a2_d2 = pop(); push(R_a2_d); push_integer(27); multiply(); R_27_a2_d = pop(); push(R_a2_d2); push_integer(-27); multiply(); R_m27_a2_d2 = pop(); push(R_3_a); push_integer(2); multiply(); R_6_a = pop(); // mixed calculations push(p3); push(p5); multiply(); R_a_c = pop(); push(R_a_c); push(p4); multiply(); R_a_b_c = pop(); push(R_a_b_c); push(p6); multiply(); R_a_b_c_d = pop(); push(R_a_c); push_integer(3); multiply(); R_3_a_c = pop(); push_integer(-4); push(p3); push(R_c3); multiply(); multiply(); R_m4_a_c3 = pop(); push(R_a_b_c); push_integer(9); multiply(); negate(); R_m9_a_b_c = pop(); push(R_a_b_c_d); push_integer(18); multiply(); R_18_a_b_c_d = pop(); push(R_b2); push(R_3_a_c); subtract(); R_DELTA0 = pop(); push(R_b2); push(R_c2); multiply(); R_b2_c2 = pop(); push(p4); negate(); push(R_3_a); divide(); R_m_b_over_3a = pop(); if (n === 4) { 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()); } push(R_DELTA0); push_integer(3); power(); push_integer(4); multiply(); R_4_DELTA03 = pop(); push(R_DELTA0); simplify(); absValFloat(); R_DELTA0_toBeCheckedIfZero = pop(); 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); push(R_m4_a_c3); push(R_m27_a2_d2); add(); add(); add(); add(); simplify(); absValFloat(); R_determinant = pop(); 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); add(); add(); R_DELTA1 = pop(); if (DEBUG) { console.log("cubic: D1: " + R_DELTA1.toString()); } // R_Q push(R_DELTA1); push_integer(2); power(); push(R_4_DELTA03); subtract(); push_rational(1, 2); power(); simplify(); R_Q = pop(); if (isZeroAtomOrTensor(R_determinant)) { if (isZeroAtomOrTensor(R_DELTA0_toBeCheckedIfZero)) { if (DEBUG) { console.log(" cubic: DETERMINANT IS ZERO and delta0 is zero"); } push(R_m_b_over_3a); // just same solution three times restore(); return; } else { if (DEBUG) { console.log(" cubic: DETERMINANT IS ZERO and delta0 is not zero"); } push(p3); push(p6); push_integer(9); multiply(); multiply(); push(p4); push(p5); multiply(); subtract(); push(R_DELTA0); push_integer(2); multiply(); divide(); // first solution root_solution = pop(); push(root_solution); // pushing two of them on the stack 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); push_integer(9); multiply(); 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; } } C_CHECKED_AS_NOT_ZERO = false; flipSignOFQSoCIsNotZero = false; while (!C_CHECKED_AS_NOT_ZERO) { // R_C push(R_Q); if (flipSignOFQSoCIsNotZero) { negate(); } push(R_DELTA1); add(); push_rational(1, 2); multiply(); push_rational(1, 3); power(); simplify(); R_C = pop(); if (DEBUG) { console.log("cubic: C: " + R_C.toString()); } push(R_C); simplify(); absValFloat(); R_C_simplified_toCheckIfZero = pop(); if (DEBUG) { console.log("cubic: C as absval and float: " + R_C_simplified_toCheckIfZero.toString()); } if (isZeroAtomOrTensor(R_C_simplified_toCheckIfZero)) { if (DEBUG) { console.log(" cubic: C IS ZERO flipping the sign"); } flipSignOFQSoCIsNotZero = true; } else { C_CHECKED_AS_NOT_ZERO = true; } } push(R_C); push(R_3_a); multiply(); R_3_a_C = pop(); push(R_3_a_C); push_integer(2); multiply(); R_6_a_C = pop(); // imaginary parts calculations push(imaginaryunit); push_integer(3); push_rational(1, 2); power(); multiply(); i_sqrt3 = pop(); push_integer(1); push(i_sqrt3); add(); one_plus_i_sqrt3 = pop(); push_integer(1); push(i_sqrt3); subtract(); one_minus_i_sqrt3 = pop(); push(R_C); push(R_3_a); divide(); R_C_over_3a = pop(); // first solution push(R_m_b_over_3a); // first term push(R_C_over_3a); negate(); // second term push(R_DELTA0); push(R_3_a_C); divide(); negate(); // third term // now add the three terms together add(); add(); simplify(); // 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(); // second term push(one_minus_i_sqrt3); push(R_DELTA0); multiply(); push(R_6_a_C); divide(); // third term // now add the three terms together add(); add(); simplify(); // 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(); // second term push(one_plus_i_sqrt3); push(R_DELTA0); multiply(); push(R_6_a_C); 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(); // E if (isZeroAtomOrTensor(p4) && isZeroAtomOrTensor(p6) && !isZeroAtomOrTensor(p5) && !isZeroAtomOrTensor(p7)) { if (DEBUG) { console.log("biquadratic case"); } push(p3); push(symbol(SECRETX)); push_integer(2); power(); multiply(); push(p5); push(symbol(SECRETX)); multiply(); push(p7); add(); add(); push(symbol(SECRETX)); roots(); biquadraticSolutions = pop(); ref2 = biquadraticSolutions.tensor.elem; for (l1 = 0, len = ref2.length; l1 < len; l1++) { eachSolution = ref2[l1]; push(eachSolution); push_rational(1, 2); power(); simplify(); push(eachSolution); push_rational(1, 2); power(); negate(); simplify(); } restore(); return; } // D - only related calculations push(p6); push(p6); multiply(); R_d2 = pop(); // E - only related calculations push(p7); push(p7); multiply(); R_e2 = pop(); push(R_e2); push(p7); multiply(); R_e3 = pop(); // DETERMINANT push_integer(256); push(R_a3); push(R_e3); multiply(); multiply(); // first term 256 a^3 e^3 push_integer(-192); push(R_a2_d); push(R_e2); push(p4); 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(); // third term -128 a^2 c^2 e^2 push_integer(144); push(R_a2_d2); push(p5); push(p7); multiply(); multiply(); multiply(); // fourth term 144 a^2 c d^2 e push(R_m27_a2_d2); push(R_d2); multiply(); // fifth term -27 a^2 d^4 push_integer(144); push(R_a_b_c); push(p4); push(R_e2); multiply(); multiply(); multiply(); // sixth term 144 a b^2 c e^2 push_integer(-6); push(p3); push(R_b2); push(R_d2); push(p7); 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(); // eigth term -80 a b c^2 d e push_integer(18); push(R_a_b_c_d); push(R_d2); 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(); // tenth term 16 a c^4 e push_integer(-4); push(R_a_c); push(R_c2); push(R_d2); 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(); // twelveth term -27 b^4 e^2 push_integer(18); push(R_b3_d); push(p5); push(p7); multiply(); multiply(); multiply(); // thirteenth term 18 b^3 c d e push(R_m4_b3_d); push(R_d2); multiply(); // fourteenth term -4 b^3 d^3 push_integer(-4); push(R_b2_c2); push(p5); push(p7); multiply(); multiply(); multiply(); // fifteenth term -4 b^2 c^3 e push(R_b2_c2); push(R_d2); multiply(); // sixteenth term b^2 c^2 d^2 // add together the sixteen terms by doing // fifteen adds add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); add(); R_determinant = pop(); if (DEBUG) { console.log("R_determinant: " + R_determinant.toString()); } // DELTA0 push(R_c2); // term one of DELTA0 push_integer(-3); push(p4); push(p6); multiply(); multiply(); // term two of DELTA0 push_integer(12); push(p3); push(p7); 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(); push_integer(-9); push(p4); push(p5); push(p6); multiply(); multiply(); multiply(); push_integer(27); push(R_b2); push(p7); multiply(); multiply(); push_integer(27); push(p3); push(R_d2); multiply(); multiply(); push_integer(-72); push(R_a_c); push(p7); multiply(); multiply(); // add the five terms together add(); add(); add(); add(); R_DELTA1 = pop(); if (DEBUG) { console.log("R_DELTA1: " + R_DELTA1.toString()); } // p push_integer(8); push(R_a_c); multiply(); push_integer(-3); push(R_b2); multiply(); add(); push_integer(8); push(R_a2); multiply(); divide(); R_p = pop(); if (DEBUG) { console.log("p: " + R_p.toString()); } // q push(R_b3); push_integer(-4); push(R_a_b_c); multiply(); push_integer(8); push(R_a2_d); multiply(); add(); add(); push_integer(8); push(R_a3); multiply(); divide(); R_q = pop(); if (DEBUG) { console.log("q: " + R_q.toString()); } if (DEBUG) { console.log("tos 1 " + tos); } if (!isZeroAtomOrTensor(p4)) { if (DEBUG) { console.log("tos 2 " + tos); } push_integer(8); push(p5); push(p3); multiply(); multiply(); push_integer(-3); push(p4); push_integer(2); power(); multiply(); add(); push_integer(8); push(p3); push_integer(2); power(); multiply(); divide(); R_p = pop(); if (DEBUG) { console.log("p for depressed quartic: " + R_p.toString()); } push(p4); push_integer(3); power(); push_integer(-4); push(p3); push(p4); push(p5); multiply(); multiply(); multiply(); push_integer(8); push(p6); push(p3); push_integer(2); power(); multiply(); multiply(); add(); add(); push_integer(8); push(p3); push_integer(3); power(); multiply(); divide(); R_q = pop(); if (DEBUG) { console.log("q for depressed quartic: " + R_q.toString()); } // convert to depressed quartic push(p4); push_integer(4); power(); push_integer(-3); multiply(); push_integer(256); push(R_a3); push(p7); multiply(); multiply(); push_integer(-64); push(R_a2_d); push(p4); multiply(); multiply(); push_integer(16); push(R_b2); push(p3); push(p5); multiply(); multiply(); multiply(); add(); add(); add(); push_integer(256); push(p3); push_integer(4); power(); multiply(); divide(); R_r = pop(); if (DEBUG) { console.log("r for depressed quartic: " + R_r.toString()); } if (DEBUG) { console.log("tos 4 " + tos); } push(symbol(SECRETX)); push_integer(4); power(); if (DEBUG) { console.log("4 * x^4: " + stack[tos - 1].toString()); } push(R_p); push(symbol(SECRETX)); push_integer(2); power(); multiply(); if (DEBUG) { console.log("R_p * x^2: " + stack[tos - 1].toString()); } push(R_q); push(symbol(SECRETX)); multiply(); if (DEBUG) { console.log("R_q * x: " + stack[tos - 1].toString()); } push(R_r); if (DEBUG) { console.log("R_r: " + stack[tos - 1].toString()); } add(); add(); add(); simplify(); if (DEBUG) { console.log("solving depressed quartic: " + stack[tos - 1].toString()); } push(symbol(SECRETX)); roots(); depressedSolutions = pop(); if (DEBUG) { console.log("depressedSolutions: " + depressedSolutions); } ref3 = depressedSolutions.tensor.elem; for (m1 = 0, len1 = ref3.length; m1 < len1; m1++) { eachSolution = ref3[m1]; push(eachSolution); push(p4); push_integer(4); push(p3); multiply(); divide(); subtract(); simplify(); if (DEBUG) { console.log("solution from depressed: " + stack[tos - 1].toString()); } } restore(); return; } else { 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) power() multiply() 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 console.log("examining solution: " + eachSolution) push(eachSolution) push_integer(2) 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) power() push(R_q) push(R_u) divide() add() add() push_integer(2) divide() R_t = pop() push(R_p) push(R_u) push_integer(2) power() push(R_q) push(R_u) divide() subtract() add() 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(); coeff2 = pop(); push_integer(2); push(R_p); push_integer(2); power(); multiply(); push(R_r); subtract(); coeff3 = pop(); push(R_p); push_integer(3); power(); push_integer(2); divide(); push_rational(-1, 2); push(R_p); push(R_r); multiply(); multiply(); push_rational(-1, 8); push(R_q); push_integer(2); power(); multiply(); add(); add(); coeff4 = pop(); 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(); if (DEBUG) { console.log("resolventCubic: " + stack[tos - 1].toString()); } push(symbol(SECRETX)); roots(); resolventCubicSolutions = pop(); if (DEBUG) { console.log("resolventCubicSolutions: " + resolventCubicSolutions); } 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) { console.log("examining solution: " + eachSolution); } push(eachSolution); push_integer(2); multiply(); push(R_p); add(); absValFloat(); toBeCheckedIFZero = pop(); if (DEBUG) { console.log("abs value is: " + eachSolution); } if (!isZeroAtomOrTensor(toBeCheckedIFZero)) { R_m = eachSolution; break; } } if (DEBUG) { console.log("chosen solution: " + R_m); } push(R_m); push_integer(2); multiply(); push(R_p); add(); push_rational(1, 2); power(); simplify(); sqrtPPlus2M = pop(); push(R_q); push_integer(2); multiply(); push(sqrtPPlus2M); divide(); simplify(); TwoQOversqrtPPlus2M = pop(); push(R_p); push_integer(3); multiply(); push(R_m); push_integer(2); multiply(); add(); ThreePPlus2M = pop(); // solution1 push(sqrtPPlus2M); push(ThreePPlus2M); push(TwoQOversqrtPPlus2M); add(); negate(); push_rational(1, 2); power(); simplify(); add(); push_integer(2); divide(); // solution2 push(sqrtPPlus2M); push(ThreePPlus2M); push(TwoQOversqrtPPlus2M); add(); negate(); push_rational(1, 2); power(); simplify(); subtract(); push_integer(2); divide(); // solution3 push(sqrtPPlus2M); negate(); push(ThreePPlus2M); push(TwoQOversqrtPPlus2M); subtract(); negate(); push_rational(1, 2); power(); simplify(); add(); push_integer(2); divide(); // solution4 push(sqrtPPlus2M); negate(); push(ThreePPlus2M); push(TwoQOversqrtPPlus2M); subtract(); negate(); push_rational(1, 2); power(); simplify(); subtract(); push_integer(2); divide(); restore(); return; } // Q --------------------------- push(R_determinant); simplify(); absValFloat(); R_determinant_simplified_toCheckIfZero = pop(); push(R_DELTA0); simplify(); absValFloat(); R_DELTA0_simplified_toCheckIfZero = pop(); S_CHECKED_AS_NOT_ZERO = false; choiceOfRadicalInQSoSIsNotZero = 0; while (!S_CHECKED_AS_NOT_ZERO) { 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(); R_principalCubicRoot = pop(); if (DEBUG) { console.log("principal cubic root: " + R_principalCubicRoot.toString()); } if (DEBUG) { console.log("tos : " + tos); } if (choiceOfRadicalInQSoSIsNotZero === 0) { if (DEBUG) { console.log("chosing principal cubic root"); } push(R_principalCubicRoot); } else if (choiceOfRadicalInQSoSIsNotZero === 1) { if (DEBUG) { console.log("chosing cubic root beyond principal"); } push(R_principalCubicRoot); push_rational(-1, 2); multiply(); push_integer(3); push_rational(1, 2); power(); push(imaginaryunit); multiply(); push_rational(-1, 2); multiply(); push(R_principalCubicRoot); multiply(); add(); } else if (choiceOfRadicalInQSoSIsNotZero === 1) { if (DEBUG) { console.log("chosing cubic root beyond beyond principal"); } push(R_principalCubicRoot); push_rational(-1, 2); multiply(); push_integer(3); push_rational(1, 2); power(); push(imaginaryunit); multiply(); push_rational(1, 2); multiply(); push(R_principalCubicRoot); multiply(); add(); } simplify(); R_Q = pop(); if (DEBUG) { console.log("Q " + R_Q.toString()); } if (DEBUG) { console.log("tos: " + tos); } push(R_Q); simplify(); absValFloat(); R_Q_simplified_toCheckIfZero = pop(); if (DEBUG) { console.log("Q simplified and abs" + R_Q_simplified_toCheckIfZero.toString()); } if (isZeroAtomOrTensor(R_Q_simplified_toCheckIfZero) && (!isZeroAtomOrTensor(R_determinant_simplified_toCheckIfZero) && isZeroAtomOrTensor(R_DELTA0_simplified_toCheckIfZero))) { if (DEBUG) { console.log(" *********************************** Q IS ZERO and it matters, flipping the sign"); } flipSignOFRadicalSoQIsNotZero = true; } else { Q_CHECKED_AS_NOT_ZERO = true; } if (DEBUG) { console.log("tos: " + tos); } } // S push_rational(-2, 3); push(R_p); multiply(); push(R_Q); push(R_DELTA0); push(R_Q); divide(); add(); //rationalize() //console.log("rationalised: " + stack[tos-1].toString()) //simplify() push(R_3_a); divide(); add(); push_rational(1, 2); power(); push_integer(2); divide(); show_power_debug = true; simplify(); R_S = pop(); if (DEBUG) { console.log("S " + R_S.toString()); } // now check if S is zero push(R_S); simplify(); absValFloat(); R_S_simplified_toCheckIfZero = pop(); if (DEBUG) { console.log("S " + R_S_simplified_toCheckIfZero.toString()); } if (isZeroAtomOrTensor(R_S_simplified_toCheckIfZero)) { if (DEBUG) { console.log(" *********************************** S IS ZERO chosing another cubic root"); } choiceOfRadicalInQSoSIsNotZero++; } else { S_CHECKED_AS_NOT_ZERO = true; } if (DEBUG) { console.log("tos: " + tos); } } // ---------------------------- if (DEBUG) { console.log("tos: " + tos); } push(p4); negate(); push(p3); push_integer(4); multiply(); divide(); R_minus_b_over_4a = pop(); push_integer(-4); push(R_S); push_integer(2); power(); multiply(); push_integer(2); push(R_p); multiply(); subtract(); R_minus_4S2_minus_2p = pop(); push(R_q); push(R_S); divide(); R_q_over_S = pop(); if (DEBUG) { console.log("tos before putting together the 4 solutions: " + tos); } // first solution push(R_minus_b_over_4a); // first term push(R_S); subtract(); push(R_minus_4S2_minus_2p); push(R_q_over_S); add(); push_rational(1, 2); power(); push_integer(2); divide(); add(); simplify(); // second solution push(R_minus_b_over_4a); // first term push(R_S); subtract(); push(R_minus_4S2_minus_2p); push(R_q_over_S); add(); push_rational(1, 2); power(); push_integer(2); divide(); subtract(); simplify(); // third solution push(R_minus_b_over_4a); // first term push(R_S); add(); push(R_minus_4S2_minus_2p); push(R_q_over_S); subtract(); push_rational(1, 2); power(); push_integer(2); divide(); add(); simplify(); // fourth solution push(R_minus_b_over_4a); // first term push(R_S); add(); push(R_minus_4S2_minus_2p); push(R_q_over_S); subtract(); push_rational(1, 2); power(); push_integer(2); divide(); subtract(); simplify(); restore(); return; } } moveTos(tos - n); return restore(); }; Eval_round = function() { push(cadr(p1)); Eval(); return yround(); }; yround = function() { save(); yyround(); return restore(); }; yyround = function() { var d; d = 0.0; p1 = pop(); if (!isNumericAtom(p1)) { push_symbol(ROUND); push(p1); list(2); return; } if (isdouble(p1)) { d = Math.round(p1.d); push_double(d); return; } if (isinteger(p1)) { push(p1); return; } push(p1); yyfloat(); p1 = pop(); 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; T_SYMBOL = 1003; T_FUNCTION = 1004; T_NEWLINE = 1006; T_STRING = 1007; T_GTEQ = 1008; T_LTEQ = 1009; T_EQ = 1010; T_NEQ = 1011; T_QUOTASSIGN = 1012; token = ""; newline_flag = 0; meta_mode = 0; input_str = 0; scan_str = 0; token_str = 0; token_buf = 0; lastFoundSymbol = null; symbolsRightOfAssignment = null; symbolsLeftOfAssignment = null; isSymbolLeftOfAssignment = null; scanningParameters = null; functionInvokationsScanningStack = null; skipRootVariableToBeSolved = false; 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 = []; isSymbolLeftOfAssignment = true; scanningParameters = []; functionInvokationsScanningStack = [""]; assignmentFound = false; scanned = s; meta_mode = 0; expanding++; input_str = 0; scan_str = 0; get_next_token(); if (token === "") { push(symbol(NIL)); expanding--; return 0; } scan_stmt(); expanding--; if (!assignmentFound) { symbolsInExpressionsWithoutAssignments = symbolsInExpressionsWithoutAssignments.concat(symbolsLeftOfAssignment); } return token_str - input_str; }; // takes a string scan_meta = function(s) { scanned = s; meta_mode = 1; expanding++; input_str = 0; scan_str = 0; get_next_token(); if (token === "") { push(symbol(NIL)); expanding--; return 0; } scan_stmt(); expanding--; return token_str - input_str; }; scan_stmt = function() { var assignmentIsOfQuotedType, existingDependencies, i, indexOfSymbolLeftOfAssignment, l1, len, len1, m1, symbolLeftOfAssignment; scan_relation(); assignmentIsOfQuotedType = false; if (token === T_QUOTASSIGN) { assignmentIsOfQuotedType = true; } if (token === T_QUOTASSIGN || token === '=') { symbolLeftOfAssignment = lastFoundSymbol; if (DEBUG) { console.log("assignment!"); } assignmentFound = true; isSymbolLeftOfAssignment = false; 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++) { i = symbolsRightOfAssignment[l1]; 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) { existingDependencies.push(i); } } return symbolsRightOfAssignment = []; } } }; scan_relation = function() { scan_expression(); switch (token) { case T_EQ: push_symbol(TESTEQ); swap(); get_next_token(); scan_expression(); return list(3); case T_NEQ: push_symbol(NOT); swap(); push_symbol(TESTEQ); swap(); get_next_token(); scan_expression(); list(3); return list(2); case T_LTEQ: push_symbol(TESTLE); swap(); get_next_token(); scan_expression(); return list(3); case T_GTEQ: push_symbol(TESTGE); swap(); get_next_token(); scan_expression(); return list(3); case '<': push_symbol(TESTLT); swap(); get_next_token(); scan_expression(); return list(3); case '>': push_symbol(TESTGT); swap(); get_next_token(); scan_expression(); return list(3); } }; scan_expression = function() { var h; h = tos; switch (token) { case '+': get_next_token(); scan_term(); break; case '-': get_next_token(); scan_term(); negate(); break; default: scan_term(); } while (newline_flag === 0 && (token === '+' || token === '-')) { if (token === '+') { get_next_token(); scan_term(); } else { get_next_token(); scan_term(); negate(); } } if (tos - h > 1) { list(tos - h); push_symbol(ADD); swap(); return cons(); } }; is_factor = function() { if ((typeof token.charCodeAt === "function" ? token.charCodeAt(0) : void 0) === dotprod_unicode) { return 1; } switch (token) { case '*': case '/': return 1; case '(': case T_SYMBOL: case T_FUNCTION: case T_INTEGER: case T_DOUBLE: case T_STRING: if (newline_flag) { // implicit mul can't cross line scan_str = token_str; // better error display return 0; } else { return 1; } } return 0; }; simplify_1_in_products = function(tos, h) { if (tos > h && isrational(stack[tos - 1]) && equaln(stack[tos - 1], 1)) { return pop(); } }; // calculate away consecutive constants multiply_consecutive_constants = function(tos, h) { if (tos > h + 1 && isNumericAtom(stack[tos - 2]) && isNumericAtom(stack[tos - 1])) { return multiply(); } }; scan_term = function() { var h; h = tos; scan_factor(); if (parse_time_simplifications) { simplify_1_in_products(tos, h); } while (is_factor()) { if (token === '*') { 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(); inverse(); } else if ((typeof token.charCodeAt === "function" ? token.charCodeAt(0) : void 0) === dotprod_unicode) { get_next_token(); push_symbol(INNER); swap(); scan_factor(); list(3); } else { scan_factor(); } if (parse_time_simplifications) { multiply_consecutive_constants(tos, h); simplify_1_in_products(tos, h); } } if (h === tos) { return push_integer(1); } else if (tos - h > 1) { list(tos - h); push_symbol(MULTIPLY); swap(); return cons(); } }; scan_power = function() { if (token === '^') { get_next_token(); push_symbol(POWER); swap(); scan_factor(); return list(3); } }; scan_index = function(h) { //console.log "[ as index" get_next_token(); push_symbol(INDEX); swap(); scan_expression(); while (token === ',') { get_next_token(); scan_expression(); } if (token !== ']') { scan_error("] expected"); } get_next_token(); return list(tos - h); }; scan_factor = function() { var firstFactorIsNumber, h; h = tos; //console.log "scan_factor token: " + token firstFactorIsNumber = false; if (token === '(') { scan_subexpr(); } else if (token === T_SYMBOL) { scan_symbol(); } 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; bignum_scan_integer(token_buf); get_next_token(); } else if (token === T_DOUBLE) { firstFactorIsNumber = true; bignum_scan_float(token_buf); get_next_token(); } else if (token === T_STRING) { scan_string(); } 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(); } } while (token === '!') { get_next_token(); push_symbol(FACTORIAL); swap(); list(2); } while ((typeof token.charCodeAt === "function" ? token.charCodeAt(0) : void 0) === transpose_unicode) { get_next_token(); push_symbol(TRANSPOSE); swap(); list(2); } return scan_power(); }; addSymbolRightOfAssignment = function(theSymbol) { var i, l1, prefixVar, ref2; if (predefinedSymbolsInGlobalScope_doNotTrackInDependencies.indexOf(theSymbol) === -1 && symbolsRightOfAssignment.indexOf(theSymbol) === -1 && symbolsRightOfAssignment.indexOf("'" + theSymbol) === -1 && !skipRootVariableToBeSolved) { if (DEBUG) { 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) { if (functionInvokationsScanningStack[i] !== "") { prefixVar += functionInvokationsScanningStack[i] + "_" + i + "_"; } } theSymbol = prefixVar + theSymbol; return symbolsRightOfAssignment.push(theSymbol); } }; addSymbolLeftOfAssignment = function(theSymbol) { var i, l1, prefixVar, ref2; if (predefinedSymbolsInGlobalScope_doNotTrackInDependencies.indexOf(theSymbol) === -1 && symbolsLeftOfAssignment.indexOf(theSymbol) === -1 && symbolsLeftOfAssignment.indexOf("'" + theSymbol) === -1 && !skipRootVariableToBeSolved) { if (DEBUG) { 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) { if (functionInvokationsScanningStack[i] !== "") { prefixVar += functionInvokationsScanningStack[i] + "_" + i + "_"; } } theSymbol = prefixVar + theSymbol; return symbolsLeftOfAssignment.push(theSymbol); } }; scan_symbol = function() { if (token !== T_SYMBOL) { scan_error("symbol expected"); } if (meta_mode && token_buf.length === 1) { switch (token_buf[0]) { case 'a': push(symbol(METAA)); break; case 'b': push(symbol(METAB)); break; case 'x': push(symbol(METAX)); break; default: push(usr_symbol(token_buf)); } } 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); } lastFoundSymbol = token_buf; if (isSymbolLeftOfAssignment) { addSymbolLeftOfAssignment(token_buf); } } else { if (DEBUG) { console.log("still scanning parameters, skipping " + token_buf); } if (isSymbolLeftOfAssignment) { addSymbolRightOfAssignment("'" + token_buf); } } if (DEBUG) { console.log("found symbol: " + token_buf + " left of assignment: " + isSymbolLeftOfAssignment); } if (!isSymbolLeftOfAssignment) { addSymbolRightOfAssignment(token_buf); } return get_next_token(); }; scan_string = function() { push(new_string(token_buf)); return get_next_token(); }; scan_function_call_with_function_name = function() { var functionName, i, l1, n, p, ref2; if (DEBUG) { console.log("-- scan_function_call_with_function_name start"); } n = 1; // the parameter number as we scan parameters p = new U(); p = usr_symbol(token_buf); push(p); functionName = token_buf; if (functionName === "roots" || functionName === "defint" || functionName === "sum" || functionName === "product" || functionName === "for") { functionInvokationsScanningStack.push(token_buf); } lastFoundSymbol = token_buf; if (!isSymbolLeftOfAssignment) { addSymbolRightOfAssignment(token_buf); } 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); }); skipRootVariableToBeSolved = true; } scan_stmt(); 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); }); } } scanningParameters.pop(); 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) + "_"), ""); } if (functionName === "defint") { symbolsRightOfAssignment[i] = symbolsRightOfAssignment[i].replace(new RegExp("defint_" + (functionInvokationsScanningStack.length - 1) + "_"), ""); } if (functionName === "sum") { symbolsRightOfAssignment[i] = symbolsRightOfAssignment[i].replace(new RegExp("sum_" + (functionInvokationsScanningStack.length - 1) + "_"), ""); } if (functionName === "product") { symbolsRightOfAssignment[i] = symbolsRightOfAssignment[i].replace(new RegExp("product_" + (functionInvokationsScanningStack.length - 1) + "_"), ""); } if (functionName === "for") { symbolsRightOfAssignment[i] = symbolsRightOfAssignment[i].replace(new RegExp("for_" + (functionInvokationsScanningStack.length - 1) + "_"), ""); } } } if (token !== ')') { scan_error(") expected"); } get_next_token(); list(n); if (functionName === "roots" || functionName === "defint" || functionName === "sum" || functionName === "product" || functionName === "for") { functionInvokationsScanningStack.pop(); } if (functionName === symbol(PATTERN).printname) { patternHasBeenFound = true; } if (DEBUG) { return console.log("-- scan_function_call_with_function_name end"); } }; scan_function_call_without_function_name = function() { var n; 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; // the parameter number as we scan parameters get_next_token(); // left paren scanningParameters.push(true); if (token !== ')') { scan_stmt(); n++; while (token === ',') { get_next_token(); scan_stmt(); n++; } } scanningParameters.pop(); if (token !== ')') { scan_error(") expected"); } get_next_token(); list(n); if (DEBUG) { return console.log("-- scan_function_call_without_function_name end: " + stack[tos - 1]); } }; // scan subexpression scan_subexpr = function() { var n; n = 0; if (token !== '(') { scan_error("( expected"); } get_next_token(); scan_stmt(); if (token !== ')') { scan_error(") expected"); } return get_next_token(); }; scan_tensor = function() { var n; n = 0; if (token !== '[') { scan_error("[ expected"); } get_next_token(); //console.log "scanning the next statement" scan_stmt(); n = 1; while (token === ',') { get_next_token(); scan_stmt(); n++; } //console.log "building tensor with elements number: " + n build_tensor(n); if (token !== ']') { scan_error("] expected"); } return get_next_token(); }; 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; } errorMessage += scanned[input_str++]; } errorMessage += " ? "; while (scanned[input_str] && (scanned[input_str] !== '\n' && scanned[input_str] !== '\r')) { errorMessage += scanned[input_str++]; } errorMessage += '\n'; 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) { p2.tensor.elem[i] = stack[tos - n + i]; } check_tensor_dimensions(p2); moveTos(tos - n); push(p2); return restore(); }; get_next_token = function() { newline_flag = 0; while (1) { get_token(); if (token !== T_NEWLINE) { break; } newline_flag = 1; } if (DEBUG) { return console.log("get_next_token token: " + token); } }; //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; scan_str++; return; } 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++; } if (scanned[scan_str] === '.') { scan_str++; while (isdigit(scanned[scan_str])) { scan_str++; } if (scanned[scan_str] === 'e' && (scanned[scan_str + 1] === '+' || scanned[scan_str + 1] === '-' || isdigit(scanned[scan_str + 1]))) { scan_str += 2; while (isdigit(scanned[scan_str])) { scan_str++; } } token = T_DOUBLE; } else { token = T_INTEGER; } update_token_buf(token_str, scan_str); return; } // symbol? if (isalpha(scanned[scan_str])) { while (isalnumorunderscore(scanned[scan_str])) { scan_str++; } if (scanned[scan_str] === '(') { token = T_FUNCTION; } else { token = T_SYMBOL; } 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"); scan_str--; } scan_str++; } scan_str++; token = T_STRING; 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++; } if (scanned[scan_str]) { scan_str++; } 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; return; } if (scanned[scan_str] === '<' && scanned[scan_str + 1] === '=') { scan_str += 2; token = T_LTEQ; return; } if (scanned[scan_str] === '>' && scanned[scan_str + 1] === '=') { scan_str += 2; 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(); return sgn(); }; sgn = function() { save(); yysgn(); return restore(); }; //define X p1 yysgn = function() { p1 = pop(); if (isdouble(p1)) { if (p1.d > 0) { push_integer(1); return; } else { if (p1.d === 0) { push_integer(1); return; } else { push_integer(-1); return; } } } if (isrational(p1)) { if (MSIGN(mmul(p1.q.a, p1.q.b)) === -1) { push_integer(-1); return; } else { if (MZERO(mmul(p1.q.a, p1.q.b))) { push_integer(0); return; } else { push_integer(1); return; } } } if (iscomplexnumber(p1)) { push_integer(-1); push(p1); absval(); power(); push(p1); multiply(); return; } if (isnegativeterm(p1)) { push_symbol(SGN); push(p1); negate(); list(2); push_integer(-1); multiply(); return; } /* push_integer(2) push(p1) heaviside() multiply() push_integer(-1) add() */ push_symbol(SGN); push(p1); return list(2); }; // shape of tensor Eval_shape = function() { push(cadr(p1)); Eval(); return shape(); }; shape = function() { var ai, an, i, l1, m1, ndim, ref2, ref3, t; i = 0; ndim = 0; t = 0; ai = []; an = []; 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)) { if (!isZeroAtomOrTensor(p1)) { stop("transpose: tensor expected, 1st arg is not a tensor"); } push(zero); restore(); return; } ndim = p1.tensor.ndim; 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) { push_integer(p1.tensor.dim[i]); p2.tensor.elem[i] = pop(); } push(p2); 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; save(); p1 = pop(); if (car(p1) === symbol(ADD)) { h = tos; p1 = cdr(p1); while (p1 !== symbol(NIL)) { push(car(p1)); simfac_term(); p1 = cdr(p1); } add_all(tos - h); } else { push(p1); simfac_term(); } return restore(); }; //else /* void simfac(void) { int h save() p1 = pop() if (car(p1) == symbol(ADD)) { h = tos p1 = cdr(p1) while (p1 != symbol(NIL)) { push(car(p1)) simfac_term() p1 = cdr(p1) } addk(tos - h) p1 = pop() if (find(p1, symbol(FACTORIAL))) { push(p1) if (car(p1) == symbol(ADD)) { Condense() simfac_term() } } } else { push(p1) simfac_term() } 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; } multiply_all_noexpand(tos - h); 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) { p1 = stack[i]; 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); subtract(); factorial(); stack[i] = pop(); 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); add(); factorial(); reciprocate(); stack[i] = pop(); stack[j] = one; return 1; } // (n + 1) n! -> (n + 1)! if (car(p2) === symbol(FACTORIAL)) { push(p1); push(cadr(p2)); subtract(); p3 = pop(); if (isplusone(p3)) { push(p1); factorial(); stack[i] = pop(); stack[j] = one; 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))); subtract(); p3 = pop(); if (isplusone(p3)) { push(cadr(p1)); factorial(); reciprocate(); stack[i] = pop(); stack[j] = one; 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))); subtract(); p3 = pop(); if (isplusone(p3)) { stack[i] = cadr(p1); stack[j] = one; return 1; } if (isminusone(p3)) { push(cadr(cadr(p2))); reciprocate(); stack[i] = pop(); stack[j] = one; return 1; } if (equaln(p3, 2)) { stack[i] = cadr(p1); push(cadr(p1)); push_integer(-1); add(); stack[j] = pop(); return 1; } if (equaln(p3, -2)) { push(cadr(cadr(p2))); reciprocate(); stack[i] = pop(); push(cadr(cadr(p2))); push_integer(-1); add(); reciprocate(); stack[j] = pop(); return 1; } } } } return 0; }; DEBUG_SIMPLIFY = false; Eval_simplify = function() { push(cadr(p1)); runUserDefinedSimplifications(); Eval(); return simplify(); }; 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; if (DEBUG_SIMPLIFY) { console.log("runUserDefinedSimplifications passed: " + stack[tos - 1].toString()); } Eval(); if (DEBUG_SIMPLIFY) { console.log("runUserDefinedSimplifications after eval no expanding: " + stack[tos - 1].toString()); } expanding = originalexpanding; p1 = stack[tos - 1]; if (DEBUG_SIMPLIFY) { console.log("patterns to be checked: "); } for (l1 = 0, len = userSimplificationsInListForm.length; l1 < len; l1++) { eachSimplification = userSimplificationsInListForm[l1]; if (DEBUG_SIMPLIFY) { console.log("..." + eachSimplification); } } atLeastOneSuccessInRouldOfRulesApplications = true; numberOfRulesApplications = 0; while (atLeastOneSuccessInRouldOfRulesApplications && numberOfRulesApplications < MAX_CONSECUTIVE_APPLICATIONS_OF_ALL_RULES) { atLeastOneSuccessInRouldOfRulesApplications = false; numberOfRulesApplications++; for (m1 = 0, len1 = userSimplificationsInListForm.length; m1 < len1; m1++) { eachSimplification = userSimplificationsInListForm[m1]; success = true; eachConsecutiveRuleApplication = 0; while (success && eachConsecutiveRuleApplication < MAX_CONSECUTIVE_APPLICATIONS_OF_SINGLE_RULE) { eachConsecutiveRuleApplication++; if (DEBUG_SIMPLIFY) { console.log("simplify - tos: " + tos + " checking pattern: " + eachSimplification + " on: " + p1); } push_symbol(NIL); success = transform(eachSimplification, true); if (success) { atLeastOneSuccessInRouldOfRulesApplications = true; } p1 = stack[tos - 1]; if (DEBUG_SIMPLIFY) { console.log("p1 at this stage of simplification: " + p1); } } if (eachConsecutiveRuleApplication === MAX_CONSECUTIVE_APPLICATIONS_OF_SINGLE_RULE) { stop("maximum application of single transformation rule exceeded: " + eachSimplification); } } } if (numberOfRulesApplications === MAX_CONSECUTIVE_APPLICATIONS_OF_ALL_RULES) { stop("maximum application of all transformation rules exceeded "); } if (DEBUG_SIMPLIFY) { console.log("METAX = " + get_binding(symbol(METAX))); } if (DEBUG_SIMPLIFY) { console.log("METAA = " + get_binding(symbol(METAA))); } if (DEBUG_SIMPLIFY) { return console.log("METAB = " + get_binding(symbol(METAB))); } } }; // ------------------------ 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(); }; simplify = function() { save(); simplify_main(); return restore(); }; 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); push(args); list(3); p1 = pop(); } if (istensor(p1)) { simplify_tensor(); return; } if (Find(p1, symbol(FACTORIAL))) { push(p1); simfac(); p2 = pop(); push(p1); rationalize(); simfac(); p3 = pop(); if (count(p2) < count(p3)) { p1 = p2; } else { p1 = p3; } } f10(); if (DEBUG_SIMPLIFY) { console.log("f10: " + p1.toString()); } f1(); if (DEBUG_SIMPLIFY) { console.log("f1: " + p1.toString()); } f2(); if (DEBUG_SIMPLIFY) { console.log("f2: " + p1.toString()); } f3(); if (DEBUG_SIMPLIFY) { console.log("f3: " + p1.toString()); } f4(); if (DEBUG_SIMPLIFY) { console.log("f4: " + p1.toString()); } f5(); if (DEBUG_SIMPLIFY) { console.log("f5: " + p1.toString()); } f9(); if (DEBUG_SIMPLIFY) { console.log("f9: " + p1.toString()); } 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_SIMPLIFY) { console.log("de-nesting successful into: " + p1.toString()); } push(p1); simplify(); return; } } simplify_rectToClock(); simplify_rational_expressions(); return push(p1); }; simplify_tensor = function() { var i, l1, m1, ref2, ref3; 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) { 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) { push(p1.tensor.elem[i]); simplify(); p2.tensor.elem[i] = pop(); } check_tensor_dimensions(p2); if (isZeroAtomOrTensor(p2)) { p2 = zero; // null tensor becomes scalar zero } return push(p2); }; // try rationalizing f1 = function() { if (car(p1) !== symbol(ADD)) { return; } push(p1); rationalize(); p2 = pop(); if (count(p2) < count(p1)) { return p1 = p2; } }; // try condensing f2 = function() { if (car(p1) !== symbol(ADD)) { return; } push(p1); Condense(); p2 = pop(); if (count(p2) <= count(p1)) { return p1 = p2; } }; // this simplifies forms like (A-B) / (B-A) f3 = function() { push(p1); rationalize(); negate(); rationalize(); negate(); rationalize(); p2 = pop(); if (count(p2) < count(p1)) { return p1 = p2; } }; f10 = function() { var a, b, carp1, miao, originalexpanding; 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_SIMPLIFY) { console.log("maybe collecting a transpose " + p1); } a = cadr(car(cdr(p1))); b = cadr(car(cdr(cdr(p1)))); if (carp1 === symbol(MULTIPLY)) { push(a); push(b); multiply(); } else if (isinnerordot(p1)) { push(b); push(a); inner(); } push_integer(1); push_integer(2); originalexpanding = expanding; expanding = false; transpose(); expanding = originalexpanding; p2 = pop(); if (count(p2) < count(p1)) { p1 = p2; } if (DEBUG_SIMPLIFY) { return console.log("collecting a transpose " + p2); } } } }; // try expanding denominators f4 = function() { if (isZeroAtomOrTensor(p1)) { return; } push(p1); rationalize(); inverse(); rationalize(); inverse(); rationalize(); p2 = pop(); if (count(p2) < count(p1)) { return p1 = p2; } }; // simplifies trig forms simplify_trig = function() { save(); p1 = pop(); f5(); push(p1); return restore(); }; f5 = function() { if (Find(p1, symbol(SIN)) === 0 && Find(p1, symbol(COS)) === 0) { return; } p2 = p1; trigmode = 1; push(p2); Eval(); p3 = pop(); trigmode = 2; push(p2); Eval(); p4 = pop(); trigmode = 0; if (count(p4) < count(p3) || nterms(p4) < nterms(p3)) { p3 = p4; } if (count(p3) < count(p1) || nterms(p3) < nterms(p1)) { return p1 = p3; } }; // if it's a sum then try to simplify each term f9 = function() { var oldp1, oldp2; if (car(p1) !== symbol(ADD)) { return; } push_integer(0); p2 = cdr(p1); while (iscons(p2)) { push(car(p2)); simplify(); add(); oldp1 = p1; oldp2 = p2; p1 = pop(); simplify_rational_expressions(); push(p1); p1 = oldp1; p2 = oldp2; p2 = cdr(p2); } p2 = pop(); if (count(p2) < count(p1)) { return p1 = p2; } }; simplify_rational_expressions = function() { var denom, num, polyVar, sasa, theGCD; push(p1); denominator(); denom = pop(); if (isone(denom)) { return; } push(p1); numerator(); num = pop(); if (isone(num)) { return; } if (!(polyVar = areunivarpolysfactoredorexpandedform(num, denom))) { return; } push(num); push(denom); gcd(); push(polyVar); factor(); theGCD = pop(); // if there are no common factors then // bail if (isone(theGCD)) { return; } push(num); push(polyVar); factor(); push(theGCD); //divide() inverse(); multiply_noexpand(); simplify(); sasa = stack[tos - 1].toString(); push(denom); 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(); if (count(p2) < count(p1)) { return p1 = p2; } }; // 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; } push(p1); Eval(); clockform(); p2 = pop(); if (DEBUG_SIMPLIFY) { console.log("before simplification clockform: " + p1 + " after: " + p2); } if (count(p2) < count(p1)) { return p1 = p2; } }; simplify_polarRect = function() { push(p1); polarRectAMinusOneBase(); Eval(); p2 = pop(); if (count(p2) < count(p1)) { return p1 = p2; } }; polarRectAMinusOneBase = function() { var h; save(); p1 = pop(); if (isimaginaryunit(p1)) { push(p1); restore(); 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); } else { push(p1); } restore(); }; nterms = function(p) { if (car(p) !== symbol(ADD)) { return 1; } else { return length(p) - 1; } }; simplify_nested_radicals = function() { var prev_expanding, simplificationWithCondense, simplificationWithoutCondense, somethingSimplified; if (recursionLevelNestedRadicalsRemoval > 0) { if (DEBUG_SIMPLIFY) { console.log("denesting bailing out because of too much recursion"); } return false; } 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; }; take_care_of_nested_radicals = function() { var A, B, C, SOLUTION, anyRadicalSimplificationWorked, base, checkSize, commonBases, commonInnerExponent, countingTerms, eachSolution, exponent, firstTerm, h, i, innerbase, innerexponent, l1, len, len1, len2, len3, lowercase_a, lowercase_b, m1, n1, numberOfTerms, o1, possibleNewExpression, possibleNewExpressionValue, possibleRationalSolutions, possibleSolutions, potentialPower, realOfpossibleRationalSolutions, ref2, secondTerm, secondTermFactor, termsThatAreNotPowers, whichRationalSolution; if (recursionLevelNestedRadicalsRemoval > 0) { if (DEBUG_SIMPLIFY) { console.log("denesting bailing out because of too much recursion"); } return false; } 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(); pop(); secondTerm = caddr(base); 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); innerexponent = caddr(potentialPower); if (equalq(innerexponent, 1, 2)) { if (commonInnerExponent == null) { commonInnerExponent = innerexponent; commonBases.push(innerbase); } else { if (equal(innerexponent, commonInnerExponent)) { //console.log("common base: " + innerbase.toString()) commonBases.push(innerbase); } else { } } } } else { //console.log("no common bases here ") //console.log("this one is a power base: " + innerbase + " , exponent: " + innerexponent) termsThatAreNotPowers.push(potentialPower); } secondTermFactor = cdr(secondTermFactor); } } } else if (car(secondTerm) === symbol(POWER)) { 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); } } if (commonBases.length === 0) { push(p1); restore(); 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(); // 4th coeff //console.log("constant coeff " + stack[tos-1].toString()) checkSize = pop(); push(checkSize); real(); yyfloat(); if (Math.abs(pop().d) > Math.pow(2, 32)) { push(p1); restore(); return false; } push(checkSize); push_integer(3); push(C); multiply(); // 3rd coeff //console.log("next coeff " + stack[tos-1].toString()) checkSize = pop(); push(checkSize); real(); yyfloat(); if (Math.abs(pop().d) > Math.pow(2, 32)) { pop(); push(p1); restore(); return false; } push(checkSize); push(symbol(SECRETX)); multiply(); push_integer(-3); push(A); multiply(); push(B); divide(); // 2nd coeff checkSize = pop(); push(checkSize); real(); yyfloat(); if (Math.abs(pop().d) > Math.pow(2, 32)) { pop(); pop(); push(p1); restore(); return false; } push(checkSize); //console.log("next coeff " + stack[tos-1].toString()) push(symbol(SECRETX)); push_integer(2); power(); multiply(); push_integer(1); // 1st coeff //console.log("next coeff " + stack[tos-1].toString()) push(symbol(SECRETX)); push_integer(3); power(); multiply(); add(); add(); add(); } else if (equalq(exponent, 1, 2)) { push(C); // 3th coeff checkSize = pop(); push(checkSize); real(); yyfloat(); if (Math.abs(pop().d) > Math.pow(2, 32)) { push(p1); restore(); return false; } push(checkSize); //console.log("constant coeff " + stack[tos-1].toString()) push_integer(-2); push(A); multiply(); push(B); divide(); // 2nd coeff checkSize = pop(); push(checkSize); real(); yyfloat(); if (Math.abs(pop().d) > Math.pow(2, 32)) { pop(); push(p1); restore(); return false; } push(checkSize); //console.log("next coeff " + stack[tos-1].toString()) push(symbol(SECRETX)); multiply(); push_integer(1); // 1st coeff //console.log("next coeff " + stack[tos-1].toString()) push(symbol(SECRETX)); push_integer(2); power(); multiply(); 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))) { if (DEBUG_SIMPLIFY) { console.log("roots bailed out because of too much recursion"); } pop(); push(p1); 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++) { eachSolution = ref2[n1]; if (!Find(eachSolution, symbol(POWER))) { possibleSolutions.push(eachSolution); } } pop(); // popping the tensor with the solutions //console.log("possible solutions: " + possibleSolutions.toString()) if (possibleSolutions.length === 0) { push(p1); restore(); return false; } 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); real(); yyfloat(); possibleRationalSolutions.push(i); realOfpossibleRationalSolutions.push(pop().d); } whichRationalSolution = realOfpossibleRationalSolutions.indexOf(Math.max.apply(Math, realOfpossibleRationalSolutions)); SOLUTION = possibleRationalSolutions[whichRationalSolution]; //console.log("picked solution: " + SOLUTION) /* #possibleNewExpressions = [] #realOfPossibleNewExpressions = [] * pick the solution which cubic root has no radicals lowercase_b = null for SOLUTION in possibleSolutions console.log("testing solution: " + SOLUTION.toString()) debugger if equalq(exponent,1,3) push(A) push(SOLUTION) push_integer(3) power() push_integer(3) push(C) multiply() push(SOLUTION) multiply() add() divide() console.log("argument of cubic root: " + stack[tos-1].toString()) push_rational(1,3) power() else if equalq(exponent,1,2) push(A) push(SOLUTION) push_integer(2) power() 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 !Find(lowercase_b, symbol(POWER)) break */ if (equalq(exponent, 1, 3)) { push(A); push(SOLUTION); push_integer(3); power(); push_integer(3); push(C); multiply(); push(SOLUTION); multiply(); add(); divide(); //console.log("argument of cubic root: " + stack[tos-1].toString()) push_rational(1, 3); power(); } else if (equalq(exponent, 1, 2)) { push(A); push(SOLUTION); push_integer(2); power(); 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); restore(); return false; } push(lowercase_b); push(SOLUTION); multiply(); if (equalq(exponent, 1, 3)) { //console.log("a is: " + stack[tos-1].toString()) lowercase_a = pop(); push(lowercase_b); push(C); push_rational(1, 2); power(); multiply(); push(lowercase_a); 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); push_rational(1, 2); power(); multiply(); push(lowercase_a); 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(); push(lowercase_a); negate(); lowercase_a = pop(); push(lowercase_b); push(C); push_rational(1, 2); power(); multiply(); push(lowercase_a); add(); 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; } else { push(p1); restore(); return false; } } else if (iscons(p1)) { 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); restore(); return anyRadicalSimplificationWorked; } else { push(p1); restore(); return false; } 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(); } 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)) { // p4 is B push(p1); push(p4); subtract(); p3 = pop(); push(p3); sine(); push(p4); cosine(); multiply(); push(p3); cosine(); push(p4); sine(); multiply(); 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)) { push(cadr(p1)); return; } if (isdouble(p1)) { d = Math.sin(p1.d); if (Math.abs(d) < 1e-10) { d = 0.0; } push_double(d); return; } // sine function is antisymmetric, sin(-x) = -sin(x) if (isnegative(p1)) { push(p1); negate(); sine(); 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); push(cadr(p1)); push_integer(2); power(); add(); push_rational(-1, 2); power(); 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(); if (evaluatingAsFloats) { push_double(Math.PI); } else { push_symbol(PI); } 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: return push_integer(0); case 30: case 150: return push_rational(1, 2); case 210: case 330: return push_rational(-1, 2); case 45: case 135: push_rational(1, 2); push_integer(2); push_rational(1, 2); power(); return multiply(); case 225: case 315: push_rational(-1, 2); push_integer(2); push_rational(1, 2); power(); return multiply(); case 60: case 120: push_rational(1, 2); push_integer(3); push_rational(1, 2); power(); return multiply(); case 240: case 300: push_rational(-1, 2); push_integer(3); push_rational(1, 2); power(); return multiply(); case 90: return push_integer(1); case 270: return push_integer(-1); default: push(symbol(SIN)); push(p1); return list(2); } }; // exp(x) - exp(-x) // sinh(x) = ---------------- // 2 Eval_sinh = function() { push(cadr(p1)); Eval(); return ysinh(); }; ysinh = function() { save(); yysinh(); return restore(); }; yysinh = function() { var d; d = 0.0; p1 = pop(); if (car(p1) === symbol(ARCSINH)) { push(cadr(p1)); return; } if (isdouble(p1)) { d = Math.sinh(p1.d); if (Math.abs(d) < 1e-10) { d = 0.0; } push_double(d); return; } if (isZeroAtomOrTensor(p1)) { push(zero); return; } push_symbol(SINH); push(p1); return list(2); }; subst = function() { var expr, i, l1, m1, newExpr, newTensor, oldExpr, ref2, ref3; save(); newExpr = pop(); oldExpr = pop(); if (oldExpr === symbol(NIL) || newExpr === symbol(NIL)) { restore(); return; } expr = pop(); if (istensor(expr)) { newTensor = alloc_tensor(expr.tensor.nelem); newTensor.tensor.ndim = expr.tensor.ndim; for (i = l1 = 0, ref2 = expr.tensor.ndim; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { newTensor.tensor.dim[i] = expr.tensor.dim[i]; } for (i = m1 = 0, ref3 = expr.tensor.nelem; (0 <= ref3 ? m1 < ref3 : m1 > ref3); i = 0 <= ref3 ? ++m1 : --m1) { push(expr.tensor.elem[i]); push(oldExpr); push(newExpr); subst(); newTensor.tensor.elem[i] = pop(); check_tensor_dimensions(newTensor); } push(newTensor); } else if (equal(expr, oldExpr)) { push(newExpr); } else if (iscons(expr)) { push(car(expr)); push(oldExpr); push(newExpr); subst(); push(cdr(expr)); push(oldExpr); push(newExpr); subst(); cons(); } else { push(expr); } 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(); if (isNaN(j)) { push(p1); return; } // 4th arg (upper limit) push(caddddr(p1)); Eval(); k = pop_integer(); if (isNaN(k)) { 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) { push_integer(i); p5 = pop(); set_binding(indexVariable, p5); push(body); 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(); return tangent(); }; tangent = function() { save(); yytangent(); return restore(); }; yytangent = function() { var d, n; n = 0; d = 0.0; p1 = pop(); if (car(p1) === symbol(ARCTAN)) { push(cadr(p1)); return; } if (isdouble(p1)) { d = Math.tan(p1.d); if (Math.abs(d) < 1e-10) { d = 0.0; } push_double(d); return; } // tan function is antisymmetric, tan(-x) = -tan(x) if (isnegative(p1)) { push(p1); negate(); tangent(); 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(); if (evaluatingAsFloats) { push_double(Math.PI); } else { push_symbol(PI); } 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); list(2); return; } switch (n % 360) { case 0: case 180: return push_integer(0); case 30: case 210: push_rational(1, 3); push_integer(3); push_rational(1, 2); power(); return multiply(); case 150: case 330: push_rational(-1, 3); push_integer(3); push_rational(1, 2); power(); return multiply(); case 45: case 225: return push_integer(1); case 135: case 315: return push_integer(-1); case 60: case 240: push_integer(3); push_rational(1, 2); return power(); case 120: case 300: push_integer(3); push_rational(1, 2); power(); return negate(); default: push(symbol(TAN)); push(p1); return list(2); } }; // exp(2 x) - 1 // tanh(x) = -------------- // exp(2 x) + 1 Eval_tanh = function() { var d; d = 0.0; push(cadr(p1)); Eval(); p1 = pop(); if (car(p1) === symbol(ARCTANH)) { push(cadr(p1)); return; } if (isdouble(p1)) { d = Math.tanh(p1.d); if (Math.abs(d) < 1e-10) { d = 0.0; } push_double(d); return; } if (isZeroAtomOrTensor(p1)) { push(zero); return; } push_symbol(TANH); push(p1); return list(2); }; Eval_taylor = function() { // 1st arg p1 = cdr(p1); push(car(p1)); Eval(); // 2nd arg p1 = cdr(p1); push(car(p1)); Eval(); p2 = pop(); if (p2 === symbol(NIL)) { guess(); } else { push(p2); } // 3rd arg p1 = cdr(p1); push(car(p1)); Eval(); p2 = pop(); if (p2 === symbol(NIL)) { 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); // 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; k = 0; save(); p4 = pop(); p3 = pop(); p2 = pop(); p1 = pop(); push(p3); k = pop_integer(); if (isNaN(k)) { push_symbol(TAYLOR); push(p1); push(p2); push(p3); push(p4); list(5); restore(); return; } push(p1); push(p2); push(p4); subst(); Eval(); push_integer(1); p5 = pop(); for (i = l1 = 1, ref2 = k; (1 <= ref2 ? l1 <= ref2 : l1 >= ref2); i = 1 <= ref2 ? ++l1 : --l1) { push(p1); push(p2); derivative(); p1 = pop(); if (isZeroAtomOrTensor(p1)) { break; } push(p5); push(p2); push(p4); subtract(); multiply(); p5 = pop(); push(p1); push(p2); push(p4); subst(); Eval(); push(p5); multiply(); push_integer(i); factorial(); divide(); add(); } 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: Start with a tensor of rank 1 with 2 elements (i.e. shape: 2) if you put in both its elements another 2 tensors of rank 1 with 2 elements (i.e. shape: 2) then the result is a tensor of rank 2 with shape 2,2 i.e. the dimension of a tensor at all times must be the number of nested tensors in it. Also, all tensors must be "uniform" i.e. they must be accessed uniformly, which means that all existing elements of a tensor must be contiguous and have the same shape. 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) { 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) { //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) { 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) { 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) { 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(); ndim = p1.tensor.ndim; 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) { 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) { push(a[i]); push(p2); multiply(); b[i] = pop(); } push(p3); return restore(); }; scalar_times_tensor = 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(); ndim = p2.tensor.ndim; 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) { 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) { push(p1); push(a[i]); multiply(); b[i] = pop(); } push(p3); return restore(); }; check_tensor_dimensions = function(p) { if (p.tensor.nelem !== p.tensor.elem.length) { console.log("something wrong in tensor dimensions"); debugger; } }; is_square_matrix = function(p) { if (istensor(p) && p.tensor.ndim === 2 && p.tensor.dim[0] === p.tensor.dim[1]) { return 1; } else { return 0; } }; //----------------------------------------------------------------------------- // 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) { push_symbol(DERIVATIVE); push(p1); push(p2); list(3); return; } 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) { 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) { push(a[i]); push(b[j]); derivative(); c[i * p2.tensor.nelem + j] = pop(); } } 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) { push(p1); push(a[i]); derivative(); b[i] = pop(); } 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) { 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) { push(a[i]); push(p2); derivative(); b[i] = pop(); } return push(p3); }; compare_tensors = function(p1, p2) { var i, l1, m1, ref2, ref3; i = 0; if (p1.tensor.ndim < p2.tensor.ndim) { return -1; } 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) { if (p1.tensor.dim[i] < p2.tensor.dim[i]) { return -1; } if (p1.tensor.dim[i] > p2.tensor.dim[i]) { return 1; } } 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; } if (lessp(p1.tensor.elem[i], p2.tensor.elem[i])) { return -1; } else { return 1; } } 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); push(p1); push(p2); list(3); return; } push(p2); n = pop_integer(); if (isNaN(n)) { push_symbol(POWER); push(p1); push(p2); list(3); return; } if (n === 0) { if (p1.tensor.ndim !== 2) { stop("power(tensor,0) with tensor rank not equal to 2"); } n = p1.tensor.dim[0]; p1 = alloc_tensor(n * n); 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) { p1.tensor.elem[n * i + i] = one; } check_tensor_dimensions(p1); push(p1); return; } if (n < 0) { n = -n; push(p1); inv(); p1 = pop(); } push(p1); results = []; 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])) { break; } else { results.push(void 0); } } return results; }; copy_tensor = function() { var i, l1, m1, ref2, ref3; i = 0; save(); 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) { 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) { p2.tensor.elem[i] = p1.tensor.elem[i]; } check_tensor_dimensions(p1); check_tensor_dimensions(p2); push(p2); 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; j = 0; k = 0; nelem = 0; ndim = 0; save(); p1 = pop(); if (!istensor(p1)) { push(p1); restore(); 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) { if (!compatible(p2, p1.tensor.elem[i])) { stop("Cannot promote tensor due to inconsistent tensor components."); } } if (!istensor(p2)) { push(p1); restore(); return; } ndim = p1.tensor.ndim + p2.tensor.ndim; if (ndim > MAXDIM) { stop("tensor rank > " + MAXDIM); } 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) { 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) { 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) { p2 = p1.tensor.elem[i]; 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]; } } check_tensor_dimensions(p2); check_tensor_dimensions(p3); push(p3); return restore(); }; compatible = function(p, q) { var i, l1, ref2; if (!istensor(p) && !istensor(q)) { return 1; } if (!istensor(p) || !istensor(q)) { return 0; } 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) { if (p.tensor.dim[i] !== q.tensor.dim[i]) { return 0; } } 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(); return; } 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(); push(caddr(p1)); 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); return; } else if ((checkResult != null) && !checkResult) { 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(); push(caddr(p1)); Eval(); simplify(); subtract(); subtractionResult = pop(); checkResult = isZeroLikeOrNonZeroLikeOrUndetermined(subtractionResult); if (checkResult) { push_integer(0); return; } else if ((checkResult != null) && !checkResult) { 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; comparison = cmp_args(); if (comparison == null) { push(orig); return; } if (comparison >= 0) { return push_integer(1); } else { return push_integer(0); } }; Eval_testgt = function() { var comparison, orig; orig = p1; comparison = cmp_args(); if (comparison == null) { push(orig); return; } if (comparison > 0) { return push_integer(1); } else { return push_integer(0); } }; Eval_testle = function() { var comparison, orig; orig = p1; comparison = cmp_args(); if (comparison == null) { push(orig); return; } if (comparison <= 0) { return push_integer(1); } else { return push_integer(0); } }; Eval_testlt = function() { var comparison, orig; orig = p1; comparison = cmp_args(); if (comparison == null) { push(orig); return; } if (comparison < 0) { return push_integer(1); } else { return push_integer(0); } }; // 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 { return push_integer(1); } }; // 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 { return push_integer(0); } }; // 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; push(cadr(p1)); Eval(); simplify(); push(caddr(p1)); Eval(); 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) { case NUM: if (MSIGN(p1.q.a) === -1) { t = -1; } else { t = 1; } break; case DOUBLE: //console.log "comparison p1.d: " + p1.d if (p1.d < 0.0) { t = -1; } else { t = 1; } 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(); // 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); push(p4); polyform(); push(p4); bookmarkTosToPrintDecomps = tos - 2; decomp(generalTransform); 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) { console.log(" decomposition element " + i + ": " + stack[tos - 1 - i]); } } transformationSuccessful = false; if (generalTransform) { if (!isNumericAtom(p3)) { theTransform = s; if (DEBUG) { console.log("applying transform: " + theTransform); } if (DEBUG) { 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(); p1 = pop(); p5 = car(p1); if (DEBUG) { console.log("template expression: " + p5); } p6 = cadr(p1); p7 = cddr(p1); /* p5 = p1.tensor.elem[0] p6 = p1.tensor.elem[1] for i in [2..(p1.tensor.elem.length-1)] 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); } transformedTerms = []; if (DEBUG) { console.log("car(p3): " + car(p3)); } restTerm = p3; if (iscons(restTerm)) { transformedTerms.push(car(p3)); restTerm = cdr(p3); } while (iscons(restTerm)) { secondTerm = car(restTerm); restTerm = cdr(restTerm); if (DEBUG) { console.log("tos before recursive transform: " + tos); } push(secondTerm); push_symbol(NIL); 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); } success = transform(s, generalTransform); transformationSuccessful = transformationSuccessful || success; transformedTerms.push(pop()); if (DEBUG) { 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(); // "integrals" mode } } } } else { for (n1 = 0, len1 = s.length; n1 < len1; n1++) { eachTransformEntry = s[n1]; if (DEBUG) { console.log("scanning table entry " + eachTransformEntry); if ((eachTransformEntry + "").indexOf("f(sqrt(a+b*x),2/3*1/b*sqrt((a+b*x)^3))") !== -1) { debugger; } } if (eachTransformEntry) { scan_meta(eachTransformEntry); p1 = pop(); p5 = cadr(p1); p6 = caddr(p1); p7 = cdddr(p1); /* p5 = p1.tensor.elem[0] p6 = p1.tensor.elem[1] for i in [2..(p1.tensor.elem.length-1)] 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; } } } } 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); } } restoreMetaBindings(); push(p1); restore(); return transformationSuccessful; }; saveMetaBindings = function() { push(get_binding(symbol(METAA))); push(get_binding(symbol(METAB))); return push(get_binding(symbol(METAX))); }; restoreMetaBindings = function() { set_binding(symbol(METAX), pop()); set_binding(symbol(METAB), pop()); 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) { 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) { 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)); Eval(); p2 = pop(); if (isZeroAtomOrTensor(p2)) { break; } p1 = cdr(p1); } if (iscons(p1)) { // conditions are not met, // skip to the next binding of metas continue; } push(p3); if (DEBUG) { console.log("about to evaluate template expression: " + p5 + " binding METAA to " + get_binding(symbol(METAA)) + " and binding METAB to " + get_binding(symbol(METAB)) + " and binding METAX to " + get_binding(symbol(METAX))); } push(p5); if (generalTransform) { originalexpanding = expanding; expanding = false; } Eval(); if (generalTransform) { expanding = originalexpanding; } if (DEBUG) { console.log(" comparing " + stack[tos - 1] + " to: " + stack[tos - 2]); } subtract(); p1 = pop(); if (isZeroAtomOrTensor(p1)) { if (DEBUG) { console.log("binding METAA to " + get_binding(symbol(METAA))); console.log("binding METAB to " + get_binding(symbol(METAB))); console.log("binding METAX to " + get_binding(symbol(METAX))); console.log("comparing " + p3 + " to: " + p5); } return 1; // yes } } } 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); } else { push(caddr(p1)); Eval(); push(cadddr(p1)); Eval(); } return transpose(); }; transpose = function() { var a, accumulator, ai, an, b, eachEntry, i, innerTranspSwitch1, innerTranspSwitch2, j, k, l, l1, m, m1, n1, ndim, nelem, o1, q1, r1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, s1, t; i = 0; j = 0; k = 0; l = 0; m = 0; ndim = 0; nelem = 0; t = 0; ai = []; an = []; 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(); // 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); restore(); 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)))); if ((equal(innerTranspSwitch1, p3) && equal(innerTranspSwitch2, p2)) || (equal(innerTranspSwitch2, p3) && equal(innerTranspSwitch1, p2)) || ((equal(innerTranspSwitch1, symbol(NIL)) && equal(innerTranspSwitch2, symbol(NIL))) && ((isplusone(p3) && isplustwo(p2)) || (isplusone(p2) && isplustwo(p3))))) { push(car(cdr(p1))); restore(); 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(); add(); p1 = cdr(p1); } 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(); multiply(); p1 = cdr(p1); } 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 = []; while (iscons(p1)) { 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) { push(accumulator[eachEntry][0]); push(accumulator[eachEntry][1]); push(accumulator[eachEntry][2]); transpose(); if (eachEntry !== accumulator.length - 1) { inner(); } } restore(); return; } 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))) { push(p2); push(p3); list(4); } else { list(2); } restore(); return; } push(zero); restore(); return; } 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(); return; } push(p2); l = pop_integer(); push(p3); m = pop_integer(); if (l < 1 || l > ndim || m < 1 || m > ndim) { stop("transpose: index out of range"); } l--; 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) { 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; // 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]; } // 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; t = an[l]; 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) { k = (k * an[j]) + ai[j]; } // swap indices back t = ai[l]; ai[l] = ai[m]; ai[m] = t; t = an[l]; an[l] = an[m]; an[m] = t; b[k] = a[i]; // 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; } ai[j] = 0; } } push(p2); 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)); } if (car(p1) === symbol(SYMBOL_D) && get_binding(symbol(SYMBOL_D)) === symbol(SYMBOL_D)) { 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."); } else if (istensor(bodyAndFormalArguments)) { stop("expected function invocation, found tensor product instead. Use 'dot/inner' explicitly."); } else if (isstr(bodyAndFormalArguments)) { stop("expected function, found string instead."); } 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); // 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; while (iscons(p1)) { push(car(p1)); Eval(); p1 = cdr(p1); } 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)) { // 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)) { n = rewrite_args_tensor(); restore(); return n; } 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)); push(p2); n += rewrite_args(); p1 = cdr(p1); } list(tos - h); restore(); return n; } if (!issymbol(p1)) { push(p1); 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)) { push(cadr(p3)); restore(); return 1; } p3 = cddr(p3); } // Get the symbol's content, if _that_ // matches then do the substitution p3 = get_binding(p1); push(p3); if (p1 !== p3) { push(p2); n = rewrite_args(); if (n === 0) { pop(); push(p1); // restore if not rewritten with arg } } restore(); return n; }; rewrite_args_tensor = function() { var i, l1, n, ref2; n = 0; i = 0; 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) { push(p1.tensor.elem[i]); push(p2); n += rewrite_args(); p1.tensor.elem[i] = pop(); } check_tensor_dimensions(p1); push(p1); return n; }; Eval_zero = function() { var i, k, l1, m, m1, n, ref2, ref3; i = 0; k = []; m = 0; n = 0; for (i = l1 = 0, ref2 = MAXDIM; (0 <= ref2 ? l1 < ref2 : l1 > ref2); i = 0 <= ref2 ? ++l1 : --l1) { k[i] = 0; } m = 1; n = 0; p2 = cdr(p1); while (iscons(p2)) { push(car(p2)); Eval(); i = pop_integer(); if (i < 1 || isNaN(i)) { // if the input is nonsensical // just return 0 push(zero); return; } m *= i; k[n++] = i; p2 = cdr(p2); } if (n === 0) { push(zero); return; } 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) { p1.tensor.dim[i] = k[i]; } return push(p1); }; allocatedId = 0; alloc_tensor = function(nelem) { var i, l1, p, ref2; i = 0; p = new U(); 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) { 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) untag(p3) untag(p4) untag(p5) untag(p6) 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++) { if (p[j].tag == 0) continue // still tagged so it's unused, put on free list switch (p[j].k) { case TENSOR: free(p[j].u.tensor) break case STR: free(p[j].u.str) break case NUM: mfree(p[j].u.q.a) mfree(p[j].u.q.b) break } p[j].k = CONS; // so no double free occurs above p[j].u.cons.cdr = free_list free_list = p + j free_count++ } } } void untag(U *p) { int i if (iscons(p)) { do { if (p->tag == 0) return p->tag = 0 untag(p->u.cons.car) p = p->u.cons.cdr } while (iscons(p)) untag(p) return } if (p->tag) { p->tag = 0 if (istensor(p)) { for (i = 0; i < p->u.tensor->nelem; i++) untag(p->u.tensor->elem[i]) } } } // get memory for 100,000 atoms void alloc_mem(void) { int i U *p if (mcount == M) return p = (U *) malloc(N * sizeof (struct U)) if (p == NULL) return mem[mcount++] = p for (i = 0; i < N; i++) { p[i].k = CONS; // so no free in gc p[i].u.cons.cdr = p + i + 1 } p[N - 1].u.cons.cdr = free_list 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; if (equal(p, q)) { return 1; } if (istensor(p)) { 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; } } return 0; } while (iscons(p)) { if (Find(car(p), q)) { return 1; } p = cdr(p); } 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; if (isimaginaryunit(p)) { return 0; } 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) { if (findPossibleClockForm(p.tensor.elem[i])) { return 1; } } return 0; } while (iscons(p)) { if (findPossibleClockForm(car(p))) { return 1; } p = cdr(p); } return 0; }; // find stuff like (e)^(i something) findPossibleExponentialForm = function(p) { var i, l1, ref2; i = 0; if (car(p) === symbol(POWER) && cadr(p) === symbol(E)) { 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) { if (findPossibleExponentialForm(p.tensor.elem[i])) { return 1; } } return 0; } while (iscons(p)) { if (findPossibleExponentialForm(car(p))) { return 1; } p = cdr(p); } return 0; }; $.Find = Find; init = function() { var i, l1, ref2; //debugger //console.log "DOING AN INIT ========================================================================" i = 0; flag = 0; reset_after_error(); chainOfUserSymbolsNotFunctionsBeingEvaluated = []; if (flag) { // already initted return; } flag = 1; // 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]; isSymbolReclaimable[i] = false; } return defn(); }; 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 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; p0 = symbol(NIL); p1 = symbol(NIL); p2 = symbol(NIL); p3 = symbol(NIL); p4 = symbol(NIL); p5 = symbol(NIL); p6 = symbol(NIL); p7 = symbol(NIL); p8 = symbol(NIL); p9 = symbol(NIL); std_symbol("abs", ABS); std_symbol("add", ADD); std_symbol("adj", ADJ); std_symbol("and", AND); std_symbol("approxratio", APPROXRATIO); std_symbol("arccos", ARCCOS); std_symbol("arccosh", ARCCOSH); std_symbol("arcsin", ARCSIN); std_symbol("arcsinh", ARCSINH); std_symbol("arctan", ARCTAN); std_symbol("arctanh", ARCTANH); std_symbol("arg", ARG); std_symbol("atomize", ATOMIZE); std_symbol("besselj", BESSELJ); std_symbol("bessely", BESSELY); std_symbol("binding", BINDING); std_symbol("binomial", BINOMIAL); std_symbol("ceiling", CEILING); std_symbol("check", CHECK); std_symbol("choose", CHOOSE); std_symbol("circexp", CIRCEXP); std_symbol("clear", CLEAR); std_symbol("clearall", CLEARALL); std_symbol("clearpatterns", CLEARPATTERNS); std_symbol("clock", CLOCK); std_symbol("coeff", COEFF); std_symbol("cofactor", COFACTOR); std_symbol("condense", CONDENSE); std_symbol("conj", CONJ); std_symbol("contract", CONTRACT); std_symbol("cos", COS); std_symbol("cosh", COSH); std_symbol("decomp", DECOMP); std_symbol("defint", DEFINT); std_symbol("deg", DEGREE); std_symbol("denominator", DENOMINATOR); std_symbol("det", DET); std_symbol("derivative", DERIVATIVE); std_symbol("dim", DIM); std_symbol("dirac", DIRAC); std_symbol("divisors", DIVISORS); std_symbol("do", DO); std_symbol("dot", DOT); std_symbol("draw", DRAW); std_symbol("dsolve", DSOLVE); std_symbol("erf", ERF); std_symbol("erfc", ERFC); std_symbol("eigen", EIGEN); std_symbol("eigenval", EIGENVAL); std_symbol("eigenvec", EIGENVEC); std_symbol("eval", EVAL); std_symbol("exp", EXP); std_symbol("expand", EXPAND); std_symbol("expcos", EXPCOS); std_symbol("expsin", EXPSIN); std_symbol("factor", FACTOR); std_symbol("factorial", FACTORIAL); std_symbol("factorpoly", FACTORPOLY); std_symbol("filter", FILTER); std_symbol("float", FLOATF); std_symbol("floor", FLOOR); std_symbol("for", FOR); std_symbol("function", FUNCTION); std_symbol("Gamma", GAMMA); std_symbol("gcd", GCD); std_symbol("hermite", HERMITE); std_symbol("hilbert", HILBERT); std_symbol("imag", IMAG); std_symbol("component", INDEX); std_symbol("inner", INNER); std_symbol("integral", INTEGRAL); std_symbol("inv", INV); std_symbol("invg", INVG); 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); std_symbol("log", LOG); std_symbol("lookup", LOOKUP); std_symbol("mod", MOD); std_symbol("multiply", MULTIPLY); std_symbol("not", NOT); std_symbol("nroots", NROOTS); std_symbol("number", NUMBER); std_symbol("numerator", NUMERATOR); std_symbol("operator", OPERATOR); std_symbol("or", OR); std_symbol("outer", OUTER); std_symbol("pattern", PATTERN); std_symbol("patternsinfo", PATTERNSINFO); std_symbol("polar", POLAR); std_symbol("power", POWER); std_symbol("prime", PRIME); std_symbol("print", PRINT); std_symbol("print2dascii", PRINT2DASCII); std_symbol("printcomputer", PRINTFULL); std_symbol("printlatex", PRINTLATEX); std_symbol("printlist", PRINTLIST); std_symbol("printhuman", PRINTPLAIN); std_symbol("printLeaveEAlone", PRINT_LEAVE_E_ALONE); std_symbol("printLeaveXAlone", PRINT_LEAVE_X_ALONE); std_symbol("product", PRODUCT); std_symbol("quote", QUOTE); std_symbol("quotient", QUOTIENT); std_symbol("rank", RANK); std_symbol("rationalize", RATIONALIZE); std_symbol("real", REAL); std_symbol("rect", YYRECT); std_symbol("roots", ROOTS); std_symbol("round", ROUND); std_symbol("equals", SETQ); std_symbol("sgn", SGN); std_symbol("silentpattern", SILENTPATTERN); std_symbol("simplify", SIMPLIFY); std_symbol("sin", SIN); std_symbol("sinh", SINH); std_symbol("shape", SHAPE); std_symbol("sqrt", SQRT); std_symbol("stop", STOP); std_symbol("subst", SUBST); std_symbol("sum", SUM); std_symbol("symbolsinfo", SYMBOLSINFO); std_symbol("tan", TAN); std_symbol("tanh", TANH); std_symbol("taylor", TAYLOR); std_symbol("test", TEST); std_symbol("testeq", TESTEQ); std_symbol("testge", TESTGE); std_symbol("testgt", TESTGT); std_symbol("testle", TESTLE); std_symbol("testlt", TESTLT); std_symbol("transpose", TRANSPOSE); std_symbol("unit", UNIT); std_symbol("zero", ZERO); std_symbol("nil", NIL); std_symbol("autoexpand", AUTOEXPAND); std_symbol("bake", BAKE); std_symbol("assumeRealVariables", ASSUME_REAL_VARIABLES); std_symbol("last", LAST); std_symbol("lastprint", LAST_PRINT); std_symbol("last2dasciiprint", LAST_2DASCII_PRINT); std_symbol("lastfullprint", LAST_FULL_PRINT); std_symbol("lastlatexprint", LAST_LATEX_PRINT); std_symbol("lastlistprint", LAST_LIST_PRINT); std_symbol("lastplainprint", LAST_PLAIN_PRINT); std_symbol("trace", TRACE); std_symbol("forceFixedPrintout", FORCE_FIXED_PRINTOUT); std_symbol("maxFixedPrintoutDigits", MAX_FIXED_PRINTOUT_DIGITS); 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); std_symbol("$SECRETX", SECRETX); std_symbol("version", VERSION); std_symbol("pi", PI); std_symbol("a", SYMBOL_A); std_symbol("b", SYMBOL_B); std_symbol("c", SYMBOL_C); std_symbol("d", SYMBOL_D); std_symbol("i", SYMBOL_I); std_symbol("j", SYMBOL_J); std_symbol("n", SYMBOL_N); std_symbol("r", SYMBOL_R); std_symbol("s", SYMBOL_S); std_symbol("t", SYMBOL_T); std_symbol("x", SYMBOL_X); std_symbol("y", SYMBOL_Y); std_symbol("z", SYMBOL_Z); std_symbol("I", SYMBOL_IDENTITY_MATRIX); std_symbol("a_", SYMBOL_A_UNDERSCORE); std_symbol("b_", SYMBOL_B_UNDERSCORE); std_symbol("x_", SYMBOL_X_UNDERSCORE); std_symbol("$C1", C1); std_symbol("$C2", C2); std_symbol("$C3", C3); std_symbol("$C4", C4); 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) { definitionOfInterest = defn_str[defn_i]; scan(definitionOfInterest); if (DEBUG) { console.log("... evaling " + definitionOfInterest); console.log("top of stack:"); console.log(print_list(stack[tos - 1])); } Eval(); pop(); } // restore the symbol dependencies as they were before. return codeGen = originalCodeGen; }; defineSomeHandyConstants = function() { zero = new_integer(0); one = new_integer(1); 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])); } push_integer(-1); if (DEBUG) { console.log(print_list(stack[tos - 1])); } push_rational(1, 2); if (DEBUG) { console.log(print_list(stack[tos - 1])); } list(3); if (DEBUG) { console.log(print_list(stack[tos - 1])); } 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); t = mcmp(a, b); return t; }; strcmp = function(str1, str2) { if (str1 === str2) { return 0; } else if (str1 > str2) { return 1; } else { return -1; } }; 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"; } if (parseFloat(stringRepresentation) !== d) { stringRepresentation = d.toFixed(maxFixedPrintoutDigits) + "..."; } } return stringRepresentation; }; // does nothing clear_term = function() {}; // s is a string here anyways isspace = function(s) { if (s == null) { return false; } return s === ' ' || s === '\t' || s === '\n' || s === '\v' || s === '\f' || s === '\r'; }; isdigit = function(str) { if (str == null) { return false; } return /^\d+$/.test(str); }; isalpha = function(str) { if (str == null) { return false; } //Check for non-alphabetic characters and space return str.search(/[^A-Za-z]/) === -1; }; isalphaOrUnderscore = function(str) { if (str == null) { return false; } //Check for non-alphabetic characters and space return str.search(/[^A-Za-z_]/) === -1; }; isunderscore = function(str) { if (str == null) { return false; } return str.search(/_/) === -1; }; isalnumorunderscore = function(str) { if (str == null) { return false; } return isalphaOrUnderscore(str) || isdigit(str); }; count = function(p) { var n; if (iscons(p)) { n = 0; while (iscons(p)) { n += count(car(p)) + 1; p = cdr(p); } } else { n = 1; } 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; if (iscons(p)) { while (iscons(p)) { n += countOccurrencesOfSymbol(needle, car(p)); p = cdr(p); } } else { if (equal(needle, p)) { n = 1; } } 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) { n += p.tensor.elem[i]; } } else if (iscons(p)) { while (iscons(p)) { n += count(car(p)) + 1; p = cdr(p); } } else { n = 1; } 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) { console.log("stringToBeParsed: " + stringToBeParsed); } timeStartFromAlgebra = new Date().getTime(); inited = true; codeGen = true; symbolsDependencies = {}; symbolsHavingReassignments = []; symbolsInExpressionsWithoutAssignments = []; patternHasBeenFound = false; indexOfPartRemainingToBeParsed = 0; 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 = ""; check_stack(); if (DEBUG) { console.log("findDependenciesInScript: scanning"); } n = scan(stringToBeParsed.substring(indexOfPartRemainingToBeParsed)); if (DEBUG) { console.log("scanned"); } pop(); check_stack(); } catch (error1) { error = error1; if (PRINTOUTRESULT) { console.log(error); } errorMessage = error + ""; //debugger reset_after_error(); break; } if (n === 0) { break; } indexOfPartRemainingToBeParsed += n; } testableString = ""; // print out all local dependencies as collected by this // parsing pass if (DEBUG) { console.log("all local dependencies ----------------"); } testableString += "All local dependencies: "; for (key in symbolsDependencies) { value = symbolsDependencies[key]; if (DEBUG) { console.log("variable " + key + " depends on: "); } dependencyInfo.affectsVariables.push(key); testableString += " variable " + key + " depends on: "; for (l1 = 0, len = value.length; l1 < len; l1++) { i = value[l1]; if (DEBUG) { console.log(" " + i); } if (i[0] !== "'") { dependencyInfo.affectedBy.push(i); } testableString += i + ", "; } testableString += "; "; } testableString += ". "; // print out the symbols with re-assignments: if (DEBUG) { console.log("Symbols with reassignments ----------------"); } testableString += "Symbols with reassignments: "; for (m1 = 0, len1 = symbolsHavingReassignments.length; m1 < len1; m1++) { key = symbolsHavingReassignments[m1]; if (dependencyInfo.affectedBy.indexOf(key) === -1) { dependencyInfo.affectedBy.push(key); testableString += key + ", "; } } testableString += ". "; // print out the symbols that appear in expressions without assignments if (DEBUG) { console.log("Symbols in expressions without assignments ----------------"); } testableString += "Symbols in expressions without assignments: "; for (n1 = 0, len2 = symbolsInExpressionsWithoutAssignments.length; n1 < len2; n1++) { key = symbolsInExpressionsWithoutAssignments[n1]; if (dependencyInfo.affectedBy.indexOf(key) === -1) { dependencyInfo.affectedBy.push(key); testableString += key + ", "; } } 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 ----------------"); } testableString += "All dependencies recursively: "; scriptEvaluation = ["", ""]; generatedCode = ""; readableSummaryOfGeneratedCode = ""; if (errorMessage === "" && !dontGenerateCode) { try { allReturnedPlainStrings = ""; allReturnedLatexStrings = ""; scriptEvaluation = run(stringToBeParsed, true); allReturnedPlainStrings = ""; allReturnedLatexStrings = ""; } catch (error1) { error = error1; if (PRINTOUTRESULT) { console.log(error); } errorMessage = error + ""; //debugger init(); } if (errorMessage === "") { for (key in symbolsDependencies) { codeGen = true; if (DEBUG) { console.log(" variable " + key + " is: " + get_binding(usr_symbol(key)).toString()); } codeGen = false; if (DEBUG) { console.log(" variable " + key + " depends on: "); } testableString += " variable " + key + " depends on: "; recursedDependencies = []; variablesWithCycles = []; cyclesDescriptions = []; recursiveDependencies(key, recursedDependencies, [], variablesWithCycles, [], cyclesDescriptions); for (o1 = 0, len3 = variablesWithCycles.length; o1 < len3; o1++) { i = variablesWithCycles[o1]; if (DEBUG) { console.log(" --> cycle through " + i); } } for (q1 = 0, len4 = recursedDependencies.length; q1 < len4; q1++) { i = recursedDependencies[q1]; if (DEBUG) { console.log(" " + i); } testableString += i + ", "; } testableString += "; "; for (r1 = 0, len5 = cyclesDescriptions.length; r1 < len5; r1++) { i = cyclesDescriptions[r1]; testableString += " " + i + ", "; } 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++) { eachDependency = recursedDependencies[s1]; if (eachDependency[0] === "'") { deQuotedDep = eachDependency.substring(1); originalUserSymbol = usr_symbol(deQuotedDep); newUserSymbol = usr_symbol("AVOID_BINDING_TO_EXTERNAL_SCOPE_VALUE" + deQuotedDep); replacementsFrom.push(originalUserSymbol); replacementsTo.push(newUserSymbol); push(originalUserSymbol); push(newUserSymbol); subst(); if (DEBUG) { console.log("after substitution: " + stack[tos - 1]); } } } try { simplifyForCodeGeneration(); } catch (error1) { error = error1; if (PRINTOUTRESULT) { 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) { //console.log "replacing back " + replacementsTo[indexOfEachReplacement] + " into: " + replacementsFrom[indexOfEachReplacement] push(replacementsTo[indexOfEachReplacement]); push(replacementsFrom[indexOfEachReplacement]); subst(); } 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 = ""; allReturnedLatexStrings = ""; codeGen = true; generatedBody = toBePrinted.toString(); codeGen = false; origPrintMode = printMode; printMode = PRINTMODE_LATEX; bodyForReadableSummaryOfGeneratedCode = toBePrinted.toString(); printMode = origPrintMode; if (variablesWithCycles.indexOf(key) !== -1) { 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 * are slightly less efficient * i.e. variables compare even if they are * simplified away. * In theory these signatures are more stable, but * in practice signatures vary quite a bit anyways * depending on previous assignments for example, * so it's unclear whether going for stability * is sensible at all.. if recursedDependencies.length != 0 parameters = "(" for i in recursedDependencies if i.indexOf("'") != 0 parameters += i + ", " else 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; }); if (userVariablesMentioned.length !== 0) { parameters = "("; for (u1 = 0, len7 = userVariablesMentioned.length; u1 < len7; u1++) { i = userVariablesMentioned[u1]; if (i.printname !== key) { parameters += i.printname + ", "; } } // eliminate the last ", " for printout clarity parameters = parameters.replace(/, $/gm, ""); parameters += ")"; generatedCode += key + " = function " + parameters + " { return ( " + generatedBody + " ); }"; readableSummaryOfGeneratedCode += key + parameters + " = " + bodyForReadableSummaryOfGeneratedCode; } else { generatedCode += key + " = " + generatedBody + ";"; readableSummaryOfGeneratedCode += key + " = " + bodyForReadableSummaryOfGeneratedCode; } } generatedCode += "\n"; readableSummaryOfGeneratedCode += "\n"; if (DEBUG) { console.log(" " + generatedCode); } } } } } // eliminate the last new line generatedCode = generatedCode.replace(/\n$/gm, ""); readableSummaryOfGeneratedCode = readableSummaryOfGeneratedCode.replace(/\n$/gm, ""); // cleanup symbolsDependencies = {}; symbolsHavingReassignments = []; patternHasBeenFound = false; symbolsInExpressionsWithoutAssignments = []; if (DEBUG) { console.log("testable string: " + testableString); } if (TIMING_DEBUGS) { console.log("findDependenciesInScript time for: " + stringToBeRun + " : " + ((new Date().getTime()) - timeStartFromAlgebra) + "ms"); } return [testableString, scriptEvaluation[0], generatedCode, readableSummaryOfGeneratedCode, scriptEvaluation[1], errorMessage, dependencyInfo]; }; 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) { console.log("can't keep following the chain of " + variableToBeChecked + " because it's actually a variable bound to a parameter"); } if (arrayWhereDependenciesWillBeAdded.indexOf("'" + variableToBeChecked) === -1 && arrayWhereDependenciesWillBeAdded.indexOf(variableToBeChecked) === -1) { arrayWhereDependenciesWillBeAdded.push(variableToBeChecked); } return arrayWhereDependenciesWillBeAdded; } } 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:"); } cyclesDescription = ""; for (m1 = 0, len1 = chainBeingChecked.length; m1 < len1; m1++) { k = chainBeingChecked[m1]; if (variablesWithCycles.indexOf(k) === -1) { variablesWithCycles.push(k); } if (DEBUG) { console.log(k + " --> "); } cyclesDescription += k + " --> "; } if (DEBUG) { console.log(" ... then " + i + " again"); } 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}!}"; turnErrorMessageToLatex = function(theErrorMessage) { theErrorMessage = theErrorMessage.replace(/\n/g, ""); theErrorMessage = theErrorMessage.replace(/_/g, "} \\_ \\text{"); theErrorMessage = theErrorMessage.replace(new RegExp(String.fromCharCode(transpose_unicode), 'g'), "}{}^{T}\\text{"); theErrorMessage = theErrorMessage.replace(new RegExp(String.fromCharCode(dotprod_unicode), 'g'), "}\\cdot \\text{"); theErrorMessage = theErrorMessage.replace("Stop:", "} \\quad \\text{Stop:"); 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)); stringToNormalise = stringToNormalise.replace(new RegExp(String.fromCharCode(12539), 'g'), String.fromCharCode(dotprod_unicode)); stringToNormalise = stringToNormalise.replace(new RegExp(String.fromCharCode(55296), 'g'), String.fromCharCode(dotprod_unicode)); stringToNormalise = stringToNormalise.replace(new RegExp(String.fromCharCode(65381), 'g'), String.fromCharCode(dotprod_unicode)); return stringToNormalise; }; TIMING_DEBUGS = false; run = function(stringToBeRun, generateLatex = false) { var allReturnedLatexStrings, allReturnedPlainStrings, collectedLatexResult, collectedPlainResult, error, errorWhileExecution, i, indexOfPartRemainingToBeParsed, n, stringToBeReturned, theErrorMessage, timeStart, timingDebugWrite; timeStart = new Date().getTime(); //stringToBeRun = stringToBeRun + "\n" stringToBeRun = normaliseDots(stringToBeRun); //console.log "run running: " + stringToBeRun if (stringToBeRun === "selftest") { selftest(); return; } if (!inited) { inited = true; init(); } i = 0; n = 0; indexOfPartRemainingToBeParsed = 0; allReturnedPlainStrings = ""; 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)); p1 = pop(); check_stack(); } catch (error1) { error = error1; if (PRINTOUTRESULT) { console.log(error); } //debugger allReturnedPlainStrings += error.message; if (generateLatex) { //debugger theErrorMessage = turnErrorMessageToLatex(error.message); allReturnedLatexStrings += theErrorMessage; } reset_after_error(); break; } 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)) { if (DEBUG) { console.log(p2.str); } if (DEBUG) { 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) { console.log("collectedLatexResult: " + collectedLatexResult); } } } allReturnedPlainStrings += collectedPlainResult; if (generateLatex) { allReturnedLatexStrings += collectedLatexResult; } if (PRINTOUTRESULT) { if (DEBUG) { console.log("printline"); } if (DEBUG) { console.log(collectedPlainResult); } } //alert collectedPlainResult if (PRINTOUTRESULT) { if (DEBUG) { console.log("display:"); } print2dascii(p2); } if (generateLatex) { allReturnedLatexStrings += "\n"; } } catch (error1) { error = error1; errorWhileExecution = true; collectedPlainResult = error.message; if (generateLatex) { collectedLatexResult = turnErrorMessageToLatex(error.message); } if (PRINTOUTRESULT) { console.log(collectedPlainResult); } allReturnedPlainStrings += collectedPlainResult; if (collectedPlainResult !== "") { allReturnedPlainStrings += "\n"; } if (generateLatex) { allReturnedLatexStrings += collectedLatexResult; allReturnedLatexStrings += "\n"; } init(); } } if (allReturnedPlainStrings[allReturnedPlainStrings.length - 1] === "\n") { allReturnedPlainStrings = allReturnedPlainStrings.substring(0, allReturnedPlainStrings.length - 1); } if (generateLatex) { if (allReturnedLatexStrings[allReturnedLatexStrings.length - 1] === "\n") { allReturnedLatexStrings = allReturnedLatexStrings.substring(0, allReturnedLatexStrings.length - 1); } } if (generateLatex) { if (DEBUG) { console.log("allReturnedLatexStrings: " + allReturnedLatexStrings); } stringToBeReturned = [allReturnedPlainStrings, allReturnedLatexStrings]; } else { stringToBeReturned = allReturnedPlainStrings; } if (TIMING_DEBUGS) { timingDebugWrite = "run time on: " + stringToBeRun + " : " + (new Date().getTime() - timeStart) + "ms"; console.log(timingDebugWrite); } allReturnedPlainStrings = ""; allReturnedLatexStrings = ""; return stringToBeReturned; }; check_stack = function() { if (tos !== 0) { debugger; stop("stack error"); } if (frame !== TOS) { debugger; stop("frame error"); } if (chainOfUserSymbolsNotFunctionsBeingEvaluated.length !== 0) { debugger; stop("symbols evaluation still ongoing?"); } if (evaluatingAsFloats !== 0) { debugger; stop("numeric evaluation still ongoing?"); } if (evaluatingPolar !== 0) { debugger; return stop("evaluation of polar still ongoing?"); } }; // cannot reference symbols yet // returns nil on stack if no result to print top_level_eval = function() { var evalledArgument, originalArgument, shouldAutoexpand; if (DEBUG) { console.log("#### top level eval"); } trigmode = 0; shouldAutoexpand = symbol(AUTOEXPAND); if (isZeroAtomOrTensor(get_binding(shouldAutoexpand))) { expanding = 0; } else { expanding = 1; } 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); return subst(); } else if (isimaginaryunit(get_binding(symbol(SYMBOL_I)))) { push(imaginaryunit); push_symbol(SYMBOL_I); return subst(); } }; check_esc_flag = function() { if (esc_flag) { return stop("esc key"); } }; // 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(); }; computeDependenciesFromAlgebra = function(codeFromAlgebraBlock) { var i, keepState, l1, len, len1, m1, originalcodeFromAlgebraBlock, userSimplificationsInProgramForm; 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(); codeFromAlgebraBlock = userSimplificationsInProgramForm + codeFromAlgebraBlock; if (DEBUG) { console.log("codeFromAlgebraBlock including patterns: " + codeFromAlgebraBlock); } } if (DEBUG) { console.log("computeDependenciesFromAlgebra: patterns in the list --------------- "); for (m1 = 0, len1 = userSimplificationsInListForm.length; m1 < len1; m1++) { i = userSimplificationsInListForm[m1]; console.log(car(i) + "," + cdr(i) + ")"); } console.log("...end of list --------------- "); } called_from_Algebra_block = false; return findDependenciesInScript(codeFromAlgebraBlock, true)[6]; }; computeResultsAndJavaScriptFromAlgebra = function(codeFromAlgebraBlock) { 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; timeStartFromAlgebra = new Date().getTime(); 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) { console.log("computeResultsAndJavaScriptFromAlgebra: patterns in the list --------------- "); for (l1 = 0, len = userSimplificationsInListForm.length; l1 < len; l1++) { i = userSimplificationsInListForm[l1]; console.log(car(i) + "," + cdr(i) + ")"); } console.log("...end of list --------------- "); } if (!keepState) { userSimplificationsInListForm = []; 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(); codeFromAlgebraBlock = userSimplificationsInProgramForm + codeFromAlgebraBlock; if (DEBUG) { console.log("codeFromAlgebraBlock including patterns: " + codeFromAlgebraBlock); } } //debugger [testableStringIsIgnoredHere, result, code, readableSummaryOfCode, latexResult, errorMessage, dependencyInfo] = findDependenciesInScript(codeFromAlgebraBlock); called_from_Algebra_block = false; if (readableSummaryOfCode !== "" || errorMessage !== "") { result += "\n" + readableSummaryOfCode; if (errorMessage !== "") { result += "\n" + errorMessage; } result = result.replace(/\n/g, "\n\n"); latexResult += "\n" + "$$" + readableSummaryOfCode + "$$"; if (errorMessage !== "") { latexResult += turnErrorMessageToLatex(errorMessage); } 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 }; }; (typeof exports !== "undefined" && exports !== null ? exports : this).run = run; (typeof exports !== "undefined" && exports !== null ? exports : this).findDependenciesInScript = findDependenciesInScript; (typeof exports !== "undefined" && exports !== null ? exports : this).computeDependenciesFromAlgebra = computeDependenciesFromAlgebra; (typeof exports !== "undefined" && exports !== null ? exports : this).computeResultsAndJavaScriptFromAlgebra = computeResultsAndJavaScriptFromAlgebra; (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) { if (p == null) { debugger; } 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--; } }; top = function() { return stack[tos - 1]; }; pop = function() { var elementToBeReturned; //popsNum++ //console.log "pop #" + popsNum if (tos === 0) { debugger; stop("stack underflow"); } if (stack[tos - 1] == null) { 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; frame -= n; if (frame < tos) { debugger; stop("frame overflow, circular reference?"); } results = []; 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) { return stop("frame underflow"); } }; save = function() { frame -= 10; if (frame < tos) { debugger; stop("frame overflow, circular reference?"); } stack[frame + 0] = p0; stack[frame + 1] = p1; stack[frame + 2] = p2; stack[frame + 3] = p3; stack[frame + 4] = p4; stack[frame + 5] = p5; stack[frame + 6] = p6; stack[frame + 7] = p7; stack[frame + 8] = p8; return stack[frame + 9] = p9; }; restore = function() { if (frame > TOS - 10) { stop("frame underflow"); } p0 = stack[frame + 0]; p1 = stack[frame + 1]; p2 = stack[frame + 2]; p3 = stack[frame + 3]; p4 = stack[frame + 4]; p5 = stack[frame + 5]; p6 = stack[frame + 6]; p7 = stack[frame + 7]; p8 = stack[frame + 8]; p9 = stack[frame + 9]; 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); }; $.dupl = dupl; $.swap = swap; $.restore = restore; $.save = save; $.push = push; $.pop = pop; // The symbol table is a simple array of struct U. // put symbol at index n Eval_symbolsinfo = function() { var symbolsinfoToBePrinted; symbolsinfoToBePrinted = symbolsinfo(); if (symbolsinfoToBePrinted !== "") { return push(new_string(symbolsinfoToBePrinted)); } else { return push_symbol(NIL); } }; 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) { if (symtab[i].printname === "") { if (isSymbolReclaimable[i] === false) { break; } else { continue; } } symtabi = symtab[i] + ""; bindingi = (binding[i] + "").substring(0, 4); symbolsinfoToBePrinted += "symbol: " + symtabi + " size: " + countsize(binding[i]) + " value: " + bindingi + "...\n"; } 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]; if (p == null) { debugger; } p.printname = s; if (latexPrint != null) { return p.latexPrint = latexPrint; } else { return p.latexPrint = s; } }; // 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) { 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; } } if (i === NSYM) { stop("symbol table overflow"); } 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"); } 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 for i in [0...symtab.length] if p.printname == symtab[i].printname 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; } if (DEBUG) { console.log("lookup >> set_binding lookup " + indexFound); } isSymbolReclaimable[indexFound] = false; 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 for i in [0...symtab.length] if p.printname == symtab[i].printname 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; } 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) { var indexFound; lookupsTotal++; if (p.k !== SYM) { stop("symbol error"); } indexFound = symtab.indexOf(p); if (symtab.indexOf(p, indexFound + 1) !== -1) { console.log("ops, more than one element!"); debugger; } 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) { // stop at the first empty // entry that is not reclaimable if (symtab[i].printname === "") { if (isSymbolReclaimable[i] === false) { break; } else { continue; } } symtab[i] = new U(); symtab[i].k = SYM; binding[i] = symtab[i]; results.push(isSymbolReclaimable[i] = false); } return results; }; //symtab[i].printname = "" //binding[i] = symtab[i] // collect all the variables in a tree collectUserSymbols = function(p, accumulator = []) { var i, l1, ref2; if (is_usr_symbol(p)) { if (accumulator.indexOf(p) === -1) { accumulator.push(p); return; } } if (istensor(p)) { 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; } while (iscons(p)) { collectUserSymbols(car(p), accumulator); p = cdr(p); } }; $.get_binding = get_binding; $.set_binding = set_binding; $.usr_symbol = usr_symbol; $.symbolsinfo = symbolsinfo; $.collectUserSymbols = collectUserSymbols; if (!inited) { inited = true; init(); } $.init = init; 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); } else { return push_double(argu); } } else if (argu instanceof U) { // hey look its a U return push(argu); } else { console.warn('unknown argument type', argu); return push(symbol(NIL)); } }; parse = function(argu) { var data, error; try { parse_internal(argu); data = pop(); check_stack(); } catch (error1) { error = error1; reset_after_error(); throw error; } return data; }; // 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); for (l1 = 0, len = argus.length; l1 < len; l1++) { argu = argus[l1]; parse_internal(argu); } list(1 + argus.length); p1 = pop(); push(p1); try { top_level_eval(); result = pop(); check_stack(); } catch (error1) { error = error1; reset_after_error(); throw error; } return result; }; $.exec = exec; $.parse = parse; (function() { var builtin_fns, fn, l1, len, results; builtin_fns = ["abs", "add", "adj", "and", "approxratio", "arccos", "arccosh", "arcsin", "arcsinh", "arctan", "arctanh", "arg", "atomize", "besselj", "bessely", "binding", "binomial", "ceiling", "check", "choose", "circexp", "clear", "clearall", "clearpatterns", "clock", "coeff", "cofactor", "condense", "conj", "contract", "cos", "cosh", "decomp", "defint", "deg", "denominator", "det", "derivative", "dim", "dirac", "divisors", "do", "dot", "draw", "dsolve", "eigen", "eigenval", "eigenvec", "erf", "erfc", "eval", "exp", "expand", "expcos", "expsin", "factor", "factorial", "factorpoly", "filter", "float", "floor", "for", "Gamma", "gcd", "hermite", "hilbert", "imag", "component", "inner", "integral", "inv", "invg", "isinteger", "isprime", "laguerre", "lcm", "leading", "legendre", "log", "mod", "multiply", "not", "nroots", "number", "numerator", "operator", "or", "outer", "pattern", "patternsinfo", "polar", "power", "prime", "print", "print2dascii", "printcomputer", "printlatex", "printlist", "printhuman", "product", "quote", "quotient", "rank", "rationalize", "real", "rect", "roots", "round", "equals", "shape", "sgn", "silentpattern", "simplify", "sin", "sinh", "sqrt", "stop", "subst", "sum", "symbolsinfo", "tan", "tanh", "taylor", "test", "testeq", "testge", "testgt", "testle", "testlt", "transpose", "unit", "zero"]; results = []; for (l1 = 0, len = builtin_fns.length; l1 < len; l1++) { fn = builtin_fns[l1]; results.push($[fn] = exec.bind(this, fn)); } return results; })(); freeze = function() { var frozenContents, frozenHash, frozenPatterns, frozenSymbols, i, l1, ref2; frozenSymbols = []; frozenContents = []; frozenPatterns = []; frozenHash = ""; 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, 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]; } return userSimplificationsInListForm = frozenPatterns.slice(0); }; compareState = function(previousHash) { var frozenHash; frozenHash = getStateHash(); if (frozenHash === previousHash) { return true; } else { return false; } }; 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) { if (symtab[i].printname === "") { if (isSymbolReclaimable[i] === false) { break; } else { continue; } } symtabi = print_list(symtab[i]); bindingi = print_list(binding[i]); frozenHash += " //" + symtabi + " : " + bindingi; } for (m1 = 0, len = userSimplificationsInListForm.length; m1 < len; m1++) { i = userSimplificationsInListForm[m1]; frozenHash += " pattern: " + i; } if (DEBUG) { console.log("frozenHash: " + frozenHash); } return frozenHash; }; }).call(this); },{"big-integer":2}],2:[function(require,module,exports){ var bigInt = (function (undefined) { "use strict"; var BASE = 1e7, LOG_BASE = 7, MAX_INT = 9007199254740992, MAX_INT_ARR = smallToArray(MAX_INT), DEFAULT_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz"; var supportsNativeBigInt = typeof BigInt === "function"; function Integer(v, radix, alphabet, caseSensitive) { if (typeof v === "undefined") return Integer[0]; if (typeof radix !== "undefined") return +radix === 10 && !alphabet ? parseValue(v) : parseBase(v, radix, alphabet, caseSensitive); return parseValue(v); } function BigInteger(value, sign) { this.value = value; this.sign = sign; this.isSmall = false; } BigInteger.prototype = Object.create(Integer.prototype); function SmallInteger(value) { this.value = value; this.sign = value < 0; this.isSmall = true; } SmallInteger.prototype = Object.create(Integer.prototype); function NativeBigInt(value) { this.value = value; } NativeBigInt.prototype = Object.create(Integer.prototype); function isPrecise(n) { return -MAX_INT < n && n < MAX_INT; } function smallToArray(n) { // For performance reasons doesn't reference BASE, need to change this function if BASE changes if (n < 1e7) return [n]; if (n < 1e14) return [n % 1e7, Math.floor(n / 1e7)]; return [n % 1e7, Math.floor(n / 1e7) % 1e7, Math.floor(n / 1e14)]; } function arrayToSmall(arr) { // If BASE changes this function may need to change trim(arr); var length = arr.length; if (length < 4 && compareAbs(arr, MAX_INT_ARR) < 0) { switch (length) { case 0: return 0; case 1: return arr[0]; case 2: return arr[0] + arr[1] * BASE; default: return arr[0] + (arr[1] + arr[2] * BASE) * BASE; } } return arr; } function trim(v) { var i = v.length; while (v[--i] === 0); v.length = i + 1; } function createArray(length) { // function shamelessly stolen from Yaffle's library https://github.com/Yaffle/BigInteger var x = new Array(length); var i = -1; while (++i < length) { x[i] = 0; } return x; } function truncate(n) { if (n > 0) return Math.floor(n); return Math.ceil(n); } function add(a, b) { // assumes a and b are arrays with a.length >= b.length var l_a = a.length, l_b = b.length, r = new Array(l_a), carry = 0, base = BASE, sum, i; for (i = 0; i < l_b; i++) { sum = a[i] + b[i] + carry; carry = sum >= base ? 1 : 0; r[i] = sum - carry * base; } while (i < l_a) { sum = a[i] + carry; carry = sum === base ? 1 : 0; r[i++] = sum - carry * base; } if (carry > 0) r.push(carry); return r; } function addAny(a, b) { if (a.length >= b.length) return add(a, b); return add(b, a); } function addSmall(a, carry) { // assumes a is array, carry is number with 0 <= carry < MAX_INT var l = a.length, r = new Array(l), base = BASE, sum, i; for (i = 0; i < l; i++) { sum = a[i] - base + carry; carry = Math.floor(sum / base); r[i] = sum - carry * base; carry += 1; } while (carry > 0) { r[i++] = carry % base; carry = Math.floor(carry / base); } return r; } BigInteger.prototype.add = function (v) { var n = parseValue(v); if (this.sign !== n.sign) { return this.subtract(n.negate()); } var a = this.value, b = n.value; if (n.isSmall) { return new BigInteger(addSmall(a, Math.abs(b)), this.sign); } return new BigInteger(addAny(a, b), this.sign); }; BigInteger.prototype.plus = BigInteger.prototype.add; SmallInteger.prototype.add = function (v) { var n = parseValue(v); var a = this.value; if (a < 0 !== n.sign) { return this.subtract(n.negate()); } var b = n.value; if (n.isSmall) { if (isPrecise(a + b)) return new SmallInteger(a + b); b = smallToArray(Math.abs(b)); } return new BigInteger(addSmall(b, Math.abs(a)), a < 0); }; SmallInteger.prototype.plus = SmallInteger.prototype.add; NativeBigInt.prototype.add = function (v) { return new NativeBigInt(this.value + parseValue(v).value); } NativeBigInt.prototype.plus = NativeBigInt.prototype.add; function subtract(a, b) { // assumes a and b are arrays with a >= b var a_l = a.length, b_l = b.length, r = new Array(a_l), borrow = 0, base = BASE, i, difference; for (i = 0; i < b_l; i++) { difference = a[i] - borrow - b[i]; if (difference < 0) { difference += base; borrow = 1; } else borrow = 0; r[i] = difference; } for (i = b_l; i < a_l; i++) { difference = a[i] - borrow; if (difference < 0) difference += base; else { r[i++] = difference; break; } r[i] = difference; } for (; i < a_l; i++) { r[i] = a[i]; } trim(r); return r; } function subtractAny(a, b, sign) { var value; if (compareAbs(a, b) >= 0) { value = subtract(a, b); } else { value = subtract(b, a); sign = !sign; } value = arrayToSmall(value); if (typeof value === "number") { if (sign) value = -value; return new SmallInteger(value); } return new BigInteger(value, sign); } function subtractSmall(a, b, sign) { // assumes a is array, b is number with 0 <= b < MAX_INT var l = a.length, r = new Array(l), carry = -b, base = BASE, i, difference; for (i = 0; i < l; i++) { difference = a[i] + carry; carry = Math.floor(difference / base); difference %= base; r[i] = difference < 0 ? difference + base : difference; } r = arrayToSmall(r); if (typeof r === "number") { if (sign) r = -r; return new SmallInteger(r); } return new BigInteger(r, sign); } BigInteger.prototype.subtract = function (v) { var n = parseValue(v); if (this.sign !== n.sign) { return this.add(n.negate()); } var a = this.value, b = n.value; if (n.isSmall) return subtractSmall(a, Math.abs(b), this.sign); return subtractAny(a, b, this.sign); }; BigInteger.prototype.minus = BigInteger.prototype.subtract; SmallInteger.prototype.subtract = function (v) { var n = parseValue(v); var a = this.value; if (a < 0 !== n.sign) { return this.add(n.negate()); } var b = n.value; if (n.isSmall) { return new SmallInteger(a - b); } return subtractSmall(b, Math.abs(a), a >= 0); }; SmallInteger.prototype.minus = SmallInteger.prototype.subtract; NativeBigInt.prototype.subtract = function (v) { return new NativeBigInt(this.value - parseValue(v).value); } NativeBigInt.prototype.minus = NativeBigInt.prototype.subtract; BigInteger.prototype.negate = function () { return new BigInteger(this.value, !this.sign); }; SmallInteger.prototype.negate = function () { var sign = this.sign; var small = new SmallInteger(-this.value); small.sign = !sign; return small; }; NativeBigInt.prototype.negate = function () { return new NativeBigInt(-this.value); } BigInteger.prototype.abs = function () { return new BigInteger(this.value, false); }; SmallInteger.prototype.abs = function () { return new SmallInteger(Math.abs(this.value)); }; NativeBigInt.prototype.abs = function () { return new NativeBigInt(this.value >= 0 ? this.value : -this.value); } function multiplyLong(a, b) { var a_l = a.length, b_l = b.length, l = a_l + b_l, r = createArray(l), base = BASE, product, carry, i, a_i, b_j; for (i = 0; i < a_l; ++i) { a_i = a[i]; for (var j = 0; j < b_l; ++j) { b_j = b[j]; product = a_i * b_j + r[i + j]; carry = Math.floor(product / base); r[i + j] = product - carry * base; r[i + j + 1] += carry; } } trim(r); return r; } function multiplySmall(a, b) { // assumes a is array, b is number with |b| < BASE var l = a.length, r = new Array(l), base = BASE, carry = 0, product, i; for (i = 0; i < l; i++) { product = a[i] * b + carry; carry = Math.floor(product / base); r[i] = product - carry * base; } while (carry > 0) { r[i++] = carry % base; carry = Math.floor(carry / base); } return r; } function shiftLeft(x, n) { var r = []; while (n-- > 0) r.push(0); return r.concat(x); } function multiplyKaratsuba(x, y) { var n = Math.max(x.length, y.length); if (n <= 30) return multiplyLong(x, y); n = Math.ceil(n / 2); var b = x.slice(n), a = x.slice(0, n), d = y.slice(n), c = y.slice(0, n); var ac = multiplyKaratsuba(a, c), bd = multiplyKaratsuba(b, d), abcd = multiplyKaratsuba(addAny(a, b), addAny(c, d)); var product = addAny(addAny(ac, shiftLeft(subtract(subtract(abcd, ac), bd), n)), shiftLeft(bd, 2 * n)); trim(product); return product; } // The following function is derived from a surface fit of a graph plotting the performance difference // between long multiplication and karatsuba multiplication versus the lengths of the two arrays. function useKaratsuba(l1, l2) { return -0.012 * l1 - 0.012 * l2 + 0.000015 * l1 * l2 > 0; } BigInteger.prototype.multiply = function (v) { var n = parseValue(v), a = this.value, b = n.value, sign = this.sign !== n.sign, abs; if (n.isSmall) { if (b === 0) return Integer[0]; if (b === 1) return this; if (b === -1) return this.negate(); abs = Math.abs(b); if (abs < BASE) { return new BigInteger(multiplySmall(a, abs), sign); } b = smallToArray(abs); } if (useKaratsuba(a.length, b.length)) // Karatsuba is only faster for certain array sizes return new BigInteger(multiplyKaratsuba(a, b), sign); return new BigInteger(multiplyLong(a, b), sign); }; BigInteger.prototype.times = BigInteger.prototype.multiply; function multiplySmallAndArray(a, b, sign) { // a >= 0 if (a < BASE) { return new BigInteger(multiplySmall(b, a), sign); } return new BigInteger(multiplyLong(b, smallToArray(a)), sign); } SmallInteger.prototype._multiplyBySmall = function (a) { if (isPrecise(a.value * this.value)) { return new SmallInteger(a.value * this.value); } return multiplySmallAndArray(Math.abs(a.value), smallToArray(Math.abs(this.value)), this.sign !== a.sign); }; BigInteger.prototype._multiplyBySmall = function (a) { if (a.value === 0) return Integer[0]; if (a.value === 1) return this; if (a.value === -1) return this.negate(); return multiplySmallAndArray(Math.abs(a.value), this.value, this.sign !== a.sign); }; SmallInteger.prototype.multiply = function (v) { return parseValue(v)._multiplyBySmall(this); }; SmallInteger.prototype.times = SmallInteger.prototype.multiply; NativeBigInt.prototype.multiply = function (v) { return new NativeBigInt(this.value * parseValue(v).value); } NativeBigInt.prototype.times = NativeBigInt.prototype.multiply; function square(a) { //console.assert(2 * BASE * BASE < MAX_INT); var l = a.length, r = createArray(l + l), base = BASE, product, carry, i, a_i, a_j; for (i = 0; i < l; i++) { a_i = a[i]; carry = 0 - a_i * a_i; for (var j = i; j < l; j++) { a_j = a[j]; product = 2 * (a_i * a_j) + r[i + j] + carry; carry = Math.floor(product / base); r[i + j] = product - carry * base; } r[i + l] = carry; } trim(r); return r; } BigInteger.prototype.square = function () { return new BigInteger(square(this.value), false); }; SmallInteger.prototype.square = function () { var value = this.value * this.value; if (isPrecise(value)) return new SmallInteger(value); return new BigInteger(square(smallToArray(Math.abs(this.value))), false); }; NativeBigInt.prototype.square = function (v) { return new NativeBigInt(this.value * this.value); } function divMod1(a, b) { // Left over from previous version. Performs faster than divMod2 on smaller input sizes. var a_l = a.length, b_l = b.length, base = BASE, result = createArray(b.length), divisorMostSignificantDigit = b[b_l - 1], // normalization lambda = Math.ceil(base / (2 * divisorMostSignificantDigit)), remainder = multiplySmall(a, lambda), divisor = multiplySmall(b, lambda), quotientDigit, shift, carry, borrow, i, l, q; if (remainder.length <= a_l) remainder.push(0); divisor.push(0); divisorMostSignificantDigit = divisor[b_l - 1]; for (shift = a_l - b_l; shift >= 0; shift--) { quotientDigit = base - 1; if (remainder[shift + b_l] !== divisorMostSignificantDigit) { quotientDigit = Math.floor((remainder[shift + b_l] * base + remainder[shift + b_l - 1]) / divisorMostSignificantDigit); } // quotientDigit <= base - 1 carry = 0; borrow = 0; l = divisor.length; for (i = 0; i < l; i++) { carry += quotientDigit * divisor[i]; q = Math.floor(carry / base); borrow += remainder[shift + i] - (carry - q * base); carry = q; if (borrow < 0) { remainder[shift + i] = borrow + base; borrow = -1; } else { remainder[shift + i] = borrow; borrow = 0; } } while (borrow !== 0) { quotientDigit -= 1; carry = 0; for (i = 0; i < l; i++) { carry += remainder[shift + i] - base + divisor[i]; if (carry < 0) { remainder[shift + i] = carry + base; carry = 0; } else { remainder[shift + i] = carry; carry = 1; } } borrow += carry; } result[shift] = quotientDigit; } // denormalization remainder = divModSmall(remainder, lambda)[0]; return [arrayToSmall(result), arrayToSmall(remainder)]; } function divMod2(a, b) { // Implementation idea shamelessly stolen from Silent Matt's library http://silentmatt.com/biginteger/ // Performs faster than divMod1 on larger input sizes. var a_l = a.length, b_l = b.length, result = [], part = [], base = BASE, guess, xlen, highx, highy, check; while (a_l) { part.unshift(a[--a_l]); trim(part); if (compareAbs(part, b) < 0) { result.push(0); continue; } xlen = part.length; highx = part[xlen - 1] * base + part[xlen - 2]; highy = b[b_l - 1] * base + b[b_l - 2]; if (xlen > b_l) { highx = (highx + 1) * base; } guess = Math.ceil(highx / highy); do { check = multiplySmall(b, guess); if (compareAbs(check, part) <= 0) break; guess--; } while (guess); result.push(guess); part = subtract(part, check); } result.reverse(); return [arrayToSmall(result), arrayToSmall(part)]; } function divModSmall(value, lambda) { var length = value.length, quotient = createArray(length), base = BASE, i, q, remainder, divisor; remainder = 0; for (i = length - 1; i >= 0; --i) { divisor = remainder * base + value[i]; q = truncate(divisor / lambda); remainder = divisor - q * lambda; quotient[i] = q | 0; } return [quotient, remainder | 0]; } function divModAny(self, v) { var value, n = parseValue(v); if (supportsNativeBigInt) { return [new NativeBigInt(self.value / n.value), new NativeBigInt(self.value % n.value)]; } var a = self.value, b = n.value; var quotient; if (b === 0) throw new Error("Cannot divide by zero"); if (self.isSmall) { if (n.isSmall) { return [new SmallInteger(truncate(a / b)), new SmallInteger(a % b)]; } return [Integer[0], self]; } if (n.isSmall) { if (b === 1) return [self, Integer[0]]; if (b == -1) return [self.negate(), Integer[0]]; var abs = Math.abs(b); if (abs < BASE) { value = divModSmall(a, abs); quotient = arrayToSmall(value[0]); var remainder = value[1]; if (self.sign) remainder = -remainder; if (typeof quotient === "number") { if (self.sign !== n.sign) quotient = -quotient; return [new SmallInteger(quotient), new SmallInteger(remainder)]; } return [new BigInteger(quotient, self.sign !== n.sign), new SmallInteger(remainder)]; } b = smallToArray(abs); } var comparison = compareAbs(a, b); if (comparison === -1) return [Integer[0], self]; if (comparison === 0) return [Integer[self.sign === n.sign ? 1 : -1], Integer[0]]; // divMod1 is faster on smaller input sizes if (a.length + b.length <= 200) value = divMod1(a, b); else value = divMod2(a, b); quotient = value[0]; var qSign = self.sign !== n.sign, mod = value[1], mSign = self.sign; if (typeof quotient === "number") { if (qSign) quotient = -quotient; quotient = new SmallInteger(quotient); } else quotient = new BigInteger(quotient, qSign); if (typeof mod === "number") { if (mSign) mod = -mod; mod = new SmallInteger(mod); } else mod = new BigInteger(mod, mSign); return [quotient, mod]; } BigInteger.prototype.divmod = function (v) { var result = divModAny(this, v); return { quotient: result[0], remainder: result[1] }; }; NativeBigInt.prototype.divmod = SmallInteger.prototype.divmod = BigInteger.prototype.divmod; BigInteger.prototype.divide = function (v) { return divModAny(this, v)[0]; }; NativeBigInt.prototype.over = NativeBigInt.prototype.divide = function (v) { return new NativeBigInt(this.value / parseValue(v).value); }; SmallInteger.prototype.over = SmallInteger.prototype.divide = BigInteger.prototype.over = BigInteger.prototype.divide; BigInteger.prototype.mod = function (v) { return divModAny(this, v)[1]; }; NativeBigInt.prototype.mod = NativeBigInt.prototype.remainder = function (v) { return new NativeBigInt(this.value % parseValue(v).value); }; SmallInteger.prototype.remainder = SmallInteger.prototype.mod = BigInteger.prototype.remainder = BigInteger.prototype.mod; BigInteger.prototype.pow = function (v) { var n = parseValue(v), a = this.value, b = n.value, value, x, y; if (b === 0) return Integer[1]; if (a === 0) return Integer[0]; if (a === 1) return Integer[1]; if (a === -1) return n.isEven() ? Integer[1] : Integer[-1]; if (n.sign) { return Integer[0]; } if (!n.isSmall) throw new Error("The exponent " + n.toString() + " is too large."); if (this.isSmall) { if (isPrecise(value = Math.pow(a, b))) return new SmallInteger(truncate(value)); } x = this; y = Integer[1]; while (true) { if (b & 1 === 1) { y = y.times(x); --b; } if (b === 0) break; b /= 2; x = x.square(); } return y; }; SmallInteger.prototype.pow = BigInteger.prototype.pow; NativeBigInt.prototype.pow = function (v) { var n = parseValue(v); var a = this.value, b = n.value; var _0 = BigInt(0), _1 = BigInt(1), _2 = BigInt(2); if (b === _0) return Integer[1]; if (a === _0) return Integer[0]; if (a === _1) return Integer[1]; if (a === BigInt(-1)) return n.isEven() ? Integer[1] : Integer[-1]; if (n.isNegative()) return new NativeBigInt(_0); var x = this; var y = Integer[1]; while (true) { if ((b & _1) === _1) { y = y.times(x); --b; } if (b === _0) break; b /= _2; x = x.square(); } return y; } BigInteger.prototype.modPow = function (exp, mod) { exp = parseValue(exp); mod = parseValue(mod); if (mod.isZero()) throw new Error("Cannot take modPow with modulus 0"); var r = Integer[1], base = this.mod(mod); while (exp.isPositive()) { if (base.isZero()) return Integer[0]; if (exp.isOdd()) r = r.multiply(base).mod(mod); exp = exp.divide(2); base = base.square().mod(mod); } return r; }; NativeBigInt.prototype.modPow = SmallInteger.prototype.modPow = BigInteger.prototype.modPow; function compareAbs(a, b) { if (a.length !== b.length) { return a.length > b.length ? 1 : -1; } for (var i = a.length - 1; i >= 0; i--) { if (a[i] !== b[i]) return a[i] > b[i] ? 1 : -1; } return 0; } BigInteger.prototype.compareAbs = function (v) { var n = parseValue(v), a = this.value, b = n.value; if (n.isSmall) return 1; return compareAbs(a, b); }; SmallInteger.prototype.compareAbs = function (v) { var n = parseValue(v), a = Math.abs(this.value), b = n.value; if (n.isSmall) { b = Math.abs(b); return a === b ? 0 : a > b ? 1 : -1; } return -1; }; NativeBigInt.prototype.compareAbs = function (v) { var a = this.value; var b = parseValue(v).value; a = a >= 0 ? a : -a; b = b >= 0 ? b : -b; return a === b ? 0 : a > b ? 1 : -1; } BigInteger.prototype.compare = function (v) { // See discussion about comparison with Infinity: // https://github.com/peterolson/BigInteger.js/issues/61 if (v === Infinity) { return -1; } if (v === -Infinity) { return 1; } var n = parseValue(v), a = this.value, b = n.value; if (this.sign !== n.sign) { return n.sign ? 1 : -1; } if (n.isSmall) { return this.sign ? -1 : 1; } return compareAbs(a, b) * (this.sign ? -1 : 1); }; BigInteger.prototype.compareTo = BigInteger.prototype.compare; SmallInteger.prototype.compare = function (v) { if (v === Infinity) { return -1; } if (v === -Infinity) { return 1; } var n = parseValue(v), a = this.value, b = n.value; if (n.isSmall) { return a == b ? 0 : a > b ? 1 : -1; } if (a < 0 !== n.sign) { return a < 0 ? -1 : 1; } return a < 0 ? 1 : -1; }; SmallInteger.prototype.compareTo = SmallInteger.prototype.compare; NativeBigInt.prototype.compare = function (v) { if (v === Infinity) { return -1; } if (v === -Infinity) { return 1; } var a = this.value; var b = parseValue(v).value; return a === b ? 0 : a > b ? 1 : -1; } NativeBigInt.prototype.compareTo = NativeBigInt.prototype.compare; BigInteger.prototype.equals = function (v) { return this.compare(v) === 0; }; NativeBigInt.prototype.eq = NativeBigInt.prototype.equals = SmallInteger.prototype.eq = SmallInteger.prototype.equals = BigInteger.prototype.eq = BigInteger.prototype.equals; BigInteger.prototype.notEquals = function (v) { return this.compare(v) !== 0; }; NativeBigInt.prototype.neq = NativeBigInt.prototype.notEquals = SmallInteger.prototype.neq = SmallInteger.prototype.notEquals = BigInteger.prototype.neq = BigInteger.prototype.notEquals; BigInteger.prototype.greater = function (v) { return this.compare(v) > 0; }; NativeBigInt.prototype.gt = NativeBigInt.prototype.greater = SmallInteger.prototype.gt = SmallInteger.prototype.greater = BigInteger.prototype.gt = BigInteger.prototype.greater; BigInteger.prototype.lesser = function (v) { return this.compare(v) < 0; }; NativeBigInt.prototype.lt = NativeBigInt.prototype.lesser = SmallInteger.prototype.lt = SmallInteger.prototype.lesser = BigInteger.prototype.lt = BigInteger.prototype.lesser; BigInteger.prototype.greaterOrEquals = function (v) { return this.compare(v) >= 0; }; NativeBigInt.prototype.geq = NativeBigInt.prototype.greaterOrEquals = SmallInteger.prototype.geq = SmallInteger.prototype.greaterOrEquals = BigInteger.prototype.geq = BigInteger.prototype.greaterOrEquals; BigInteger.prototype.lesserOrEquals = function (v) { return this.compare(v) <= 0; }; NativeBigInt.prototype.leq = NativeBigInt.prototype.lesserOrEquals = SmallInteger.prototype.leq = SmallInteger.prototype.lesserOrEquals = BigInteger.prototype.leq = BigInteger.prototype.lesserOrEquals; BigInteger.prototype.isEven = function () { return (this.value[0] & 1) === 0; }; SmallInteger.prototype.isEven = function () { return (this.value & 1) === 0; }; NativeBigInt.prototype.isEven = function () { return (this.value & BigInt(1)) === BigInt(0); } BigInteger.prototype.isOdd = function () { return (this.value[0] & 1) === 1; }; SmallInteger.prototype.isOdd = function () { return (this.value & 1) === 1; }; NativeBigInt.prototype.isOdd = function () { return (this.value & BigInt(1)) === BigInt(1); } BigInteger.prototype.isPositive = function () { return !this.sign; }; SmallInteger.prototype.isPositive = function () { return this.value > 0; }; NativeBigInt.prototype.isPositive = SmallInteger.prototype.isPositive; BigInteger.prototype.isNegative = function () { return this.sign; }; SmallInteger.prototype.isNegative = function () { return this.value < 0; }; NativeBigInt.prototype.isNegative = SmallInteger.prototype.isNegative; BigInteger.prototype.isUnit = function () { return false; }; SmallInteger.prototype.isUnit = function () { return Math.abs(this.value) === 1; }; NativeBigInt.prototype.isUnit = function () { return this.abs().value === BigInt(1); } BigInteger.prototype.isZero = function () { return false; }; SmallInteger.prototype.isZero = function () { return this.value === 0; }; NativeBigInt.prototype.isZero = function () { return this.value === BigInt(0); } BigInteger.prototype.isDivisibleBy = function (v) { var n = parseValue(v); if (n.isZero()) return false; if (n.isUnit()) return true; if (n.compareAbs(2) === 0) return this.isEven(); return this.mod(n).isZero(); }; NativeBigInt.prototype.isDivisibleBy = SmallInteger.prototype.isDivisibleBy = BigInteger.prototype.isDivisibleBy; function isBasicPrime(v) { var n = v.abs(); if (n.isUnit()) return false; if (n.equals(2) || n.equals(3) || n.equals(5)) return true; if (n.isEven() || n.isDivisibleBy(3) || n.isDivisibleBy(5)) return false; if (n.lesser(49)) return true; // we don't know if it's prime: let the other functions figure it out } function millerRabinTest(n, a) { var nPrev = n.prev(), b = nPrev, r = 0, d, t, i, x; while (b.isEven()) b = b.divide(2), r++; next: for (i = 0; i < a.length; i++) { if (n.lesser(a[i])) continue; x = bigInt(a[i]).modPow(b, n); if (x.isUnit() || x.equals(nPrev)) continue; for (d = r - 1; d != 0; d--) { x = x.square().mod(n); if (x.isUnit()) return false; if (x.equals(nPrev)) continue next; } return false; } return true; } // Set "strict" to true to force GRH-supported lower bound of 2*log(N)^2 BigInteger.prototype.isPrime = function (strict) { var isPrime = isBasicPrime(this); if (isPrime !== undefined) return isPrime; var n = this.abs(); var bits = n.bitLength(); if (bits <= 64) return millerRabinTest(n, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]); var logN = Math.log(2) * bits.toJSNumber(); var t = Math.ceil((strict === true) ? (2 * Math.pow(logN, 2)) : logN); for (var a = [], i = 0; i < t; i++) { a.push(bigInt(i + 2)); } return millerRabinTest(n, a); }; NativeBigInt.prototype.isPrime = SmallInteger.prototype.isPrime = BigInteger.prototype.isPrime; BigInteger.prototype.isProbablePrime = function (iterations) { var isPrime = isBasicPrime(this); if (isPrime !== undefined) return isPrime; var n = this.abs(); var t = iterations === undefined ? 5 : iterations; for (var a = [], i = 0; i < t; i++) { a.push(bigInt.randBetween(2, n.minus(2))); } return millerRabinTest(n, a); }; NativeBigInt.prototype.isProbablePrime = SmallInteger.prototype.isProbablePrime = BigInteger.prototype.isProbablePrime; BigInteger.prototype.modInv = function (n) { var t = bigInt.zero, newT = bigInt.one, r = parseValue(n), newR = this.abs(), q, lastT, lastR; while (!newR.isZero()) { q = r.divide(newR); lastT = t; lastR = r; t = newT; r = newR; newT = lastT.subtract(q.multiply(newT)); newR = lastR.subtract(q.multiply(newR)); } if (!r.isUnit()) throw new Error(this.toString() + " and " + n.toString() + " are not co-prime"); if (t.compare(0) === -1) { t = t.add(n); } if (this.isNegative()) { return t.negate(); } return t; }; NativeBigInt.prototype.modInv = SmallInteger.prototype.modInv = BigInteger.prototype.modInv; BigInteger.prototype.next = function () { var value = this.value; if (this.sign) { return subtractSmall(value, 1, this.sign); } return new BigInteger(addSmall(value, 1), this.sign); }; SmallInteger.prototype.next = function () { var value = this.value; if (value + 1 < MAX_INT) return new SmallInteger(value + 1); return new BigInteger(MAX_INT_ARR, false); }; NativeBigInt.prototype.next = function () { return new NativeBigInt(this.value + BigInt(1)); } BigInteger.prototype.prev = function () { var value = this.value; if (this.sign) { return new BigInteger(addSmall(value, 1), true); } return subtractSmall(value, 1, this.sign); }; SmallInteger.prototype.prev = function () { var value = this.value; if (value - 1 > -MAX_INT) return new SmallInteger(value - 1); return new BigInteger(MAX_INT_ARR, true); }; NativeBigInt.prototype.prev = function () { return new NativeBigInt(this.value - BigInt(1)); } var powersOfTwo = [1]; while (2 * powersOfTwo[powersOfTwo.length - 1] <= BASE) powersOfTwo.push(2 * powersOfTwo[powersOfTwo.length - 1]); var powers2Length = powersOfTwo.length, highestPower2 = powersOfTwo[powers2Length - 1]; function shift_isSmall(n) { return Math.abs(n) <= BASE; } BigInteger.prototype.shiftLeft = function (v) { var n = parseValue(v).toJSNumber(); if (!shift_isSmall(n)) { throw new Error(String(n) + " is too large for shifting."); } if (n < 0) return this.shiftRight(-n); var result = this; if (result.isZero()) return result; while (n >= powers2Length) { result = result.multiply(highestPower2); n -= powers2Length - 1; } return result.multiply(powersOfTwo[n]); }; NativeBigInt.prototype.shiftLeft = SmallInteger.prototype.shiftLeft = BigInteger.prototype.shiftLeft; BigInteger.prototype.shiftRight = function (v) { var remQuo; var n = parseValue(v).toJSNumber(); if (!shift_isSmall(n)) { throw new Error(String(n) + " is too large for shifting."); } if (n < 0) return this.shiftLeft(-n); var result = this; while (n >= powers2Length) { if (result.isZero() || (result.isNegative() && result.isUnit())) return result; remQuo = divModAny(result, highestPower2); result = remQuo[1].isNegative() ? remQuo[0].prev() : remQuo[0]; n -= powers2Length - 1; } remQuo = divModAny(result, powersOfTwo[n]); return remQuo[1].isNegative() ? remQuo[0].prev() : remQuo[0]; }; NativeBigInt.prototype.shiftRight = SmallInteger.prototype.shiftRight = BigInteger.prototype.shiftRight; function bitwise(x, y, fn) { y = parseValue(y); var xSign = x.isNegative(), ySign = y.isNegative(); var xRem = xSign ? x.not() : x, yRem = ySign ? y.not() : y; var xDigit = 0, yDigit = 0; var xDivMod = null, yDivMod = null; var result = []; while (!xRem.isZero() || !yRem.isZero()) { xDivMod = divModAny(xRem, highestPower2); xDigit = xDivMod[1].toJSNumber(); if (xSign) { xDigit = highestPower2 - 1 - xDigit; // two's complement for negative numbers } yDivMod = divModAny(yRem, highestPower2); yDigit = yDivMod[1].toJSNumber(); if (ySign) { yDigit = highestPower2 - 1 - yDigit; // two's complement for negative numbers } xRem = xDivMod[0]; yRem = yDivMod[0]; result.push(fn(xDigit, yDigit)); } var sum = fn(xSign ? 1 : 0, ySign ? 1 : 0) !== 0 ? bigInt(-1) : bigInt(0); for (var i = result.length - 1; i >= 0; i -= 1) { sum = sum.multiply(highestPower2).add(bigInt(result[i])); } return sum; } BigInteger.prototype.not = function () { return this.negate().prev(); }; NativeBigInt.prototype.not = SmallInteger.prototype.not = BigInteger.prototype.not; BigInteger.prototype.and = function (n) { return bitwise(this, n, function (a, b) { return a & b; }); }; NativeBigInt.prototype.and = SmallInteger.prototype.and = BigInteger.prototype.and; BigInteger.prototype.or = function (n) { return bitwise(this, n, function (a, b) { return a | b; }); }; NativeBigInt.prototype.or = SmallInteger.prototype.or = BigInteger.prototype.or; BigInteger.prototype.xor = function (n) { return bitwise(this, n, function (a, b) { return a ^ b; }); }; NativeBigInt.prototype.xor = SmallInteger.prototype.xor = BigInteger.prototype.xor; var LOBMASK_I = 1 << 30, LOBMASK_BI = (BASE & -BASE) * (BASE & -BASE) | LOBMASK_I; function roughLOB(n) { // get lowestOneBit (rough) // SmallInteger: return Min(lowestOneBit(n), 1 << 30) // BigInteger: return Min(lowestOneBit(n), 1 << 14) [BASE=1e7] var v = n.value, x = typeof v === "number" ? v | LOBMASK_I : typeof v === "bigint" ? v | BigInt(LOBMASK_I) : v[0] + v[1] * BASE | LOBMASK_BI; return x & -x; } function integerLogarithm(value, base) { if (base.compareTo(value) <= 0) { var tmp = integerLogarithm(value, base.square(base)); var p = tmp.p; var e = tmp.e; var t = p.multiply(base); return t.compareTo(value) <= 0 ? { p: t, e: e * 2 + 1 } : { p: p, e: e * 2 }; } return { p: bigInt(1), e: 0 }; } BigInteger.prototype.bitLength = function () { var n = this; if (n.compareTo(bigInt(0)) < 0) { n = n.negate().subtract(bigInt(1)); } if (n.compareTo(bigInt(0)) === 0) { return bigInt(0); } return bigInt(integerLogarithm(n, bigInt(2)).e).add(bigInt(1)); } NativeBigInt.prototype.bitLength = SmallInteger.prototype.bitLength = BigInteger.prototype.bitLength; function max(a, b) { a = parseValue(a); b = parseValue(b); return a.greater(b) ? a : b; } function min(a, b) { a = parseValue(a); b = parseValue(b); return a.lesser(b) ? a : b; } function gcd(a, b) { a = parseValue(a).abs(); b = parseValue(b).abs(); if (a.equals(b)) return a; if (a.isZero()) return b; if (b.isZero()) return a; var c = Integer[1], d, t; while (a.isEven() && b.isEven()) { d = min(roughLOB(a), roughLOB(b)); a = a.divide(d); b = b.divide(d); c = c.multiply(d); } while (a.isEven()) { a = a.divide(roughLOB(a)); } do { while (b.isEven()) { b = b.divide(roughLOB(b)); } if (a.greater(b)) { t = b; b = a; a = t; } b = b.subtract(a); } while (!b.isZero()); return c.isUnit() ? a : a.multiply(c); } function lcm(a, b) { a = parseValue(a).abs(); b = parseValue(b).abs(); return a.divide(gcd(a, b)).multiply(b); } function randBetween(a, b) { a = parseValue(a); b = parseValue(b); var low = min(a, b), high = max(a, b); var range = high.subtract(low).add(1); if (range.isSmall) return low.add(Math.floor(Math.random() * range)); var digits = toBase(range, BASE).value; var result = [], restricted = true; for (var i = 0; i < digits.length; i++) { var top = restricted ? digits[i] : BASE; var digit = truncate(Math.random() * top); result.push(digit); if (digit < top) restricted = false; } return low.add(Integer.fromArray(result, BASE, false)); } var parseBase = function (text, base, alphabet, caseSensitive) { alphabet = alphabet || DEFAULT_ALPHABET; text = String(text); if (!caseSensitive) { text = text.toLowerCase(); alphabet = alphabet.toLowerCase(); } var length = text.length; var i; var absBase = Math.abs(base); var alphabetValues = {}; for (i = 0; i < alphabet.length; i++) { alphabetValues[alphabet[i]] = i; } for (i = 0; i < length; i++) { var c = text[i]; if (c === "-") continue; if (c in alphabetValues) { if (alphabetValues[c] >= absBase) { if (c === "1" && absBase === 1) continue; throw new Error(c + " is not a valid digit in base " + base + "."); } } } base = parseValue(base); var digits = []; var isNegative = text[0] === "-"; for (i = isNegative ? 1 : 0; i < text.length; i++) { var c = text[i]; if (c in alphabetValues) digits.push(parseValue(alphabetValues[c])); else if (c === "<") { var start = i; do { i++; } while (text[i] !== ">" && i < text.length); digits.push(parseValue(text.slice(start + 1, i))); } else throw new Error(c + " is not a valid character"); } return parseBaseFromArray(digits, base, isNegative); }; function parseBaseFromArray(digits, base, isNegative) { var val = Integer[0], pow = Integer[1], i; for (i = digits.length - 1; i >= 0; i--) { val = val.add(digits[i].times(pow)); pow = pow.times(base); } return isNegative ? val.negate() : val; } function stringify(digit, alphabet) { alphabet = alphabet || DEFAULT_ALPHABET; if (digit < alphabet.length) { return alphabet[digit]; } return "<" + digit + ">"; } function toBase(n, base) { base = bigInt(base); if (base.isZero()) { if (n.isZero()) return { value: [0], isNegative: false }; throw new Error("Cannot convert nonzero numbers to base 0."); } if (base.equals(-1)) { if (n.isZero()) return { value: [0], isNegative: false }; if (n.isNegative()) return { value: [].concat.apply([], Array.apply(null, Array(-n.toJSNumber())) .map(Array.prototype.valueOf, [1, 0]) ), isNegative: false }; var arr = Array.apply(null, Array(n.toJSNumber() - 1)) .map(Array.prototype.valueOf, [0, 1]); arr.unshift([1]); return { value: [].concat.apply([], arr), isNegative: false }; } var neg = false; if (n.isNegative() && base.isPositive()) { neg = true; n = n.abs(); } if (base.isUnit()) { if (n.isZero()) return { value: [0], isNegative: false }; return { value: Array.apply(null, Array(n.toJSNumber())) .map(Number.prototype.valueOf, 1), isNegative: neg }; } var out = []; var left = n, divmod; while (left.isNegative() || left.compareAbs(base) >= 0) { divmod = left.divmod(base); left = divmod.quotient; var digit = divmod.remainder; if (digit.isNegative()) { digit = base.minus(digit).abs(); left = left.next(); } out.push(digit.toJSNumber()); } out.push(left.toJSNumber()); return { value: out.reverse(), isNegative: neg }; } function toBaseString(n, base, alphabet) { var arr = toBase(n, base); return (arr.isNegative ? "-" : "") + arr.value.map(function (x) { return stringify(x, alphabet); }).join(''); } BigInteger.prototype.toArray = function (radix) { return toBase(this, radix); }; SmallInteger.prototype.toArray = function (radix) { return toBase(this, radix); }; NativeBigInt.prototype.toArray = function (radix) { return toBase(this, radix); }; BigInteger.prototype.toString = function (radix, alphabet) { if (radix === undefined) radix = 10; if (radix !== 10) return toBaseString(this, radix, alphabet); var v = this.value, l = v.length, str = String(v[--l]), zeros = "0000000", digit; while (--l >= 0) { digit = String(v[l]); str += zeros.slice(digit.length) + digit; } var sign = this.sign ? "-" : ""; return sign + str; }; SmallInteger.prototype.toString = function (radix, alphabet) { if (radix === undefined) radix = 10; if (radix != 10) return toBaseString(this, radix, alphabet); return String(this.value); }; NativeBigInt.prototype.toString = SmallInteger.prototype.toString; NativeBigInt.prototype.toJSON = BigInteger.prototype.toJSON = SmallInteger.prototype.toJSON = function () { return this.toString(); } BigInteger.prototype.valueOf = function () { return parseInt(this.toString(), 10); }; BigInteger.prototype.toJSNumber = BigInteger.prototype.valueOf; SmallInteger.prototype.valueOf = function () { return this.value; }; SmallInteger.prototype.toJSNumber = SmallInteger.prototype.valueOf; NativeBigInt.prototype.valueOf = NativeBigInt.prototype.toJSNumber = function () { return parseInt(this.toString(), 10); } function parseStringValue(v) { if (isPrecise(+v)) { var x = +v; if (x === truncate(x)) return supportsNativeBigInt ? new NativeBigInt(BigInt(x)) : new SmallInteger(x); throw new Error("Invalid integer: " + v); } var sign = v[0] === "-"; if (sign) v = v.slice(1); var split = v.split(/e/i); if (split.length > 2) throw new Error("Invalid integer: " + split.join("e")); if (split.length === 2) { var exp = split[1]; if (exp[0] === "+") exp = exp.slice(1); exp = +exp; if (exp !== truncate(exp) || !isPrecise(exp)) throw new Error("Invalid integer: " + exp + " is not a valid exponent."); var text = split[0]; var decimalPlace = text.indexOf("."); if (decimalPlace >= 0) { exp -= text.length - decimalPlace - 1; text = text.slice(0, decimalPlace) + text.slice(decimalPlace + 1); } if (exp < 0) throw new Error("Cannot include negative exponent part for integers"); text += (new Array(exp + 1)).join("0"); v = text; } var isValid = /^([0-9][0-9]*)$/.test(v); if (!isValid) throw new Error("Invalid integer: " + v); if (supportsNativeBigInt) { return new NativeBigInt(BigInt(sign ? "-" + v : v)); } var r = [], max = v.length, l = LOG_BASE, min = max - l; while (max > 0) { r.push(+v.slice(min, max)); min -= l; if (min < 0) min = 0; max -= l; } trim(r); return new BigInteger(r, sign); } function parseNumberValue(v) { if (supportsNativeBigInt) { return new NativeBigInt(BigInt(v)); } if (isPrecise(v)) { if (v !== truncate(v)) throw new Error(v + " is not an integer."); return new SmallInteger(v); } return parseStringValue(v.toString()); } function parseValue(v) { if (typeof v === "number") { return parseNumberValue(v); } if (typeof v === "string") { return parseStringValue(v); } if (typeof v === "bigint") { return new NativeBigInt(v); } return v; } // Pre-define numbers in range [-999,999] for (var i = 0; i < 1000; i++) { Integer[i] = parseValue(i); if (i > 0) Integer[-i] = parseValue(-i); } // Backwards compatibility Integer.one = Integer[1]; Integer.zero = Integer[0]; Integer.minusOne = Integer[-1]; Integer.max = max; Integer.min = min; Integer.gcd = gcd; Integer.lcm = lcm; Integer.isInstance = function (x) { return x instanceof BigInteger || x instanceof SmallInteger || x instanceof NativeBigInt; }; Integer.randBetween = randBetween; Integer.fromArray = function (digits, base, isNegative) { return parseBaseFromArray(digits.map(parseValue), parseValue(base || 10), isNegative); }; return Integer; })(); // Node.js check if (typeof module !== "undefined" && module.hasOwnProperty("exports")) { module.exports = bigInt; } //amd check if (typeof define === "function" && define.amd) { define("big-integer", [], function () { return bigInt; }); } },{}],3:[function(require,module,exports){ window.Algebrite = require('../dist/algebrite') },{"../dist/algebrite":1}]},{},[3]);