=============== Reference Guide =============== Procedures ========== A procedure is a code block that will be inlined at the call-site. .. frm:statement:: proc name(args; localargs) { [statements] } :param args: arguments to the function :param localargs: variables local to the procedure. They will shadow existing variables. Define a code block that will be placed inline in the code when called with :frm:st:`call`. All variables in the arguments ``args`` are replaced. ``localargs`` will shadow arguments from the outer scope. The code block can contain :frm:st:`apply` statements. .. code-block:: reform proc derivative(x, n) { for $i in 1..(n+1) { id x^m? = m? * x^(m? - 1); } } expr F = u^5; apply { call derivative(u, 2); } yields .. code-block:: reform 20*u^3 User-defined functions ====================== Users can define their own functions in the global scope with the following statement: .. frm:statement:: fn name(args) = expression; :param name: Name of the function :param args: Arguments to the function :param expression: The resulting expression Replace the function ``name`` with ``expression`` where all occurences of the ``args`` are replaced by the given function arguments. .. note:: Functions in already existing expressions will not be automatically substitued when they are defined as custom functions at a later stage. Use ``id myfunc(?a) = myfunc(?a);`` to trigger the substitution. .. code-block:: reform fn factorial(n) = ifelse_(n > 0, n * factorial(n-1), 1); $a = factorial(10); print $a; yields .. code-block:: reform 3628800 Statements ========== .. frm:statement:: apply [name for F1,...,F2 exclude F3,...,] { [statements] }; :param name: Optional name of the module :param statements: A list of statements that will be applied Apply a list of statements (a module) to all active expressions. If ``for F1,...,F2`` is specified, the module is only applied to these expressions. It is also possible to apply the module to all expressions excluding some by using ``excluding F3,...``. The ``apply`` statement cannot be nested. For example: .. code-block:: reform expr F = f(5); apply { id f(x?) = f(x? + 1); id f(6) = f(3); } The statements will be processed term by term. .. frm:statement:: argument f1,f2,... { [statements] } :param f1,...: Functions the statements should be applied to. :param statements: Statement block to be executed on function arguments Execute a block of statements on the arguments of specific functions. .. code-block:: reform expr F = f(1+x,y*x); apply { argument f { id y = 5; } } yields .. code-block:: reform F = f(1+x,5*x) .. frm:statement:: assign x = expr; :param x: A variable :param expr: A reFORM expression Assign the expression to the variable ``x``. .. code-block:: reform $a = 1 + x; print $a; yields .. code-block:: reform 1 + x .. frm:statement:: attrib f = Linear + NonCommutative + Symmetric; :param f: A function name. Assign attributes to a function. At the moment the options are ``Linear``, ``NonCommutative``, and ``Symmetric``. Multiple options can be given with a ``+``. .. code-block:: reform expr F = f(x, y); attrib f = Linear; apply { id f(x1?,x2?) = f(x1?+2,x2?+5); } yields .. code-block:: reform +f(x,y) +f(x,5) +f(2,y) +f(2,5) .. frm:statement:: call proc(args); :param proc: A procedure :param args: Arguments to the procedure Call a procedure (see `Procedures`_) with arguments. .. code-block:: reform procedure derivative(x, n) { for $i in 1..(n+1) { id x^m? = m? * x^(m? - 1); } } expr F = u^5; apply { call derivative(u, 2); } yields .. code-block:: reform u^3*20 .. frm:statement:: collect fn; :param fn: A function name. If this statement is called `inside` a module, it will wrap the entire term in a function ``fn``. if this statement is called outside the module, it will wrap the entire expression in a function ``fn``. The latter is only possible if the expression fits in memory. .. note:: The collect statement must currently be placed before the apply block. This will be fixed in the future. .. code-block:: reform expr F = (1+x)^4; collect f; print; apply { expand; } yields .. code-block:: reform +f(x*4+x^2*6+x^3*4+x^4+1) .. frm:statement:: discard; Discard the current term. .. code-block:: reform expr F = x + y; apply { if match(x) { Discard; } } yields .. code-block:: reform y .. frm:statement:: expand; Expand all structures. For example, ```(1+x)^5```, and ```(1+x)*(1+y)``` will be completely written out. .. code-block:: reform expr F = (1+x)^2*(1+y); apply { expand; } yields .. code-block:: reform +x*y*2 +x*2 +x^2 +x^2*y +y +1 .. frm:statement:: expr name = expression; :param name: The name of a new expression :param expression: Any valid reFORM expression. Create a new `expression`. An expression is processed term-by-term and can be larger than memory. Use :frm:st:`apply` to operate on the terms of the expression. .. frm:statement:: extract $i x1,...,xn; :param $i: A reFORM variable. :param x1,...,xn: A list of algebraic variables. Construct a Horner scheme in the variables ``x1`` to ``xn`` for the expression in variable ``$i``. .. code-block:: reform $a = x + x*y + x*y*z + y*z + x^2 + x^2*y + 2; extract $a x,y; print $a; yields .. code-block:: reform (y+1)*x^2+y*z+2+((z+1)*y+1)*x .. frm:statement:: fn name(args) = expression; See `User-defined functions`_. .. frm:statement:: for i in lb..ub { [statements] }; for i in {s1,s2,...} { [statements] }; :param i: The loop variable. :param lb..ub: A numerical range. :param {s1,s2,...}: A list of expressions. Loop over a numerical range or over a list of expressions. Loops can be made both inside and outside of modules. .. code-block:: reform expr F = f(2); for $i in 1..4 { print; apply { id f($i) = f($i+1); } } yields .. code-block:: reform F = f(2); F = f(3); F = f(4); .. frm:statement:: id lhs = rhs; :param lhs: Any valid reFORM expression with `wildcards`. :param rhs: Any valid reFORM expression with `wildcards`. Apply the lhs to an active term (therefore an :frm:st:`id` statement needs to be in an :frm:st:`inside` or :frm:st:`apply` block (module). See :doc:`Pattern matching ` for the patterns that are allowed to match. For example: .. code-block:: reform expr F = f(5); apply { id f(x?) = f(x? + 1); } .. frm:statement:: if cond { [statements] } [else { [statements] } ] if match(expr) { [statements] } [else { [statements] } ] if defined(dollar) { [statements] } [else { [statements] } ] :param cond: A boolean condition :param match(expr): A test to see if an expression matches :param defined(dollar): A test to see if a dollar variable is defined :param statements: Statement block to be executed Only execute if a condition holds. If there is an ``else`` block, that will only be executed if ``cond`` does not hold. The condition can test if a pattern exists (see frm:st:`id`) using the ``match`` option. The condition can also be a comparison of two expressions, i.e., ``<=, >=, <, >, ==``. .. note:: Inequalities use reFORM's internal ordering which may give unexpected results. .. code-block:: reform expr F = f(1); apply { if match(f(1)) { id f(1) = f(2); } else { id f(x?) = f(1); } if defined($a) { Multiply $a; } if f(1) < f(2) { id f(2) = f(3); } print; } yields .. code-block:: reform f(3) .. frm:statement:: inside x1,x2,... { [statements] } :param x1,...: Variables the statements should be applied to. :param statements: Statement block to be executed on the terms in variables. Execute a block of statements on specific variables. .. code-block:: reform $x = 1 + x + y*x; inside $x { id x = 5; } print $x; yields .. code-block:: reform 6 + 5*y .. frm:statement:: matchassign pattern { [assigns] }; :param pattern: A pattern to match the current expression to. :param assigns: A list of :frm:st:`assign` statements. Match the current term and use the matched wildcards in the assignment of dollar variables. .. code-block:: reform expr F = f(x,1,2,3); $a = 0; $b = 0; apply { matchassign f(y?,?b) { $a = 2*y?*f(?b); $b = y?^5; } } print $a,$b; yields .. code-block:: reform 2*f(1,2,3)*x x^5 .. frm:statement:: maximum x; :param x: A variable Get the maximum of the variable ``x`` over all terms in the module. .. code-block:: reform $a = 0; apply { if match(f(1)) { $a = 2; } else { $a = 1; } maximum $a; } print $a; yields .. code-block:: reform 2 .. frm:statement:: multiply expr; :param expr: An expression to multiply. Multiply the expression into the current active term. ``Multiply`` can only be used in a module. .. code-block:: reform expr F = y; apply { Multiply 1 + x; } yields .. code-block:: reform y*(1+x) .. frm:statement:: print [format] [vars]; .. frm:statement:: print [format] format_string; :param format: Optional format for printing. It can either be ``Form`` or ``Mathematica``. :param vars: A list of variables to print. :param format_string: a list of variables to print Print objects or a formatted string to the screen. If the ``Print`` statement without arguments ``vars`` or ``format_string`` is used in a module, the current term is printed. If it is used outside a module without these arguments, it will print all active expressions. The ``format`` option can be used to format the terms in a way such that it is compatible with other software. The current supported options are ``Form`` (default) and ``Mathematica``. If a list of variables ``vars`` is specified, each variable will be printed on a new line. If a format string is specified, the formatted string is printed. Variables and special objects can be printed by putting them between ``{ }`` in the format string. Special objects are: - ``{data_}``: print the current date and time - ``{time_}``: print the current time - ``{term_}``: print the current term - ``{$a}``: print the value of ``$a`` .. note:: A print statement for expressions must currently be placed before the apply block. This will be fixed in the future. .. code-block:: reform $a = f(x); print mathematica $a; expr F = 1 + x; print; // print F at the end of the next module apply { print; // print the current term print "{date_}: current term={term_}, $a={$a}"; } .. frm:statement:: procedure name(args; localargs) { [statements] } See `Procedures`_. .. frm:statement:: repeat { [statements] } :param statements: Statement block to be repeated until no terms change anymore. Repeat a block of statements until the term does not change anymore. The code below does a naive Fibonacci series evaluation. The repeat block will continue until none of the three :frm:st:`id` statements match. .. code-block:: reform expr F = f(30); apply { repeat { id f(x?{>1}) = f(x? - 1) + f(x? - 2); id f(1) = 1; id f(0) = 0; } } yields .. code-block:: reform F = f(1,x,2*y) .. frm:statement:: replaceby expr; :param expr: An expression Replace the current term by ``expr``. .. code-block:: reform expr F = x*y + y; apply { if match(x) { ReplaceBy z; } } yields .. code-block:: reform y + z .. frm:statement:: splitarg fn; :param fn: A function Split a subexpression in a function argument into new function arguments. For example: .. code-block:: reform expr F = f(1+x+2*y); apply { splitarg f; } yields .. code-block:: reform F = f(1,x,2*y) .. frm:statement:: symmetrize fn; :param fn: A function name. Symmetrize the function arguments based on reFORM's internal ordering. .. code-block:: reform expr F = f(3,2,x,1+y,g(5)); apply { symmetrize f; } yields .. code-block:: reform f(g(5),y+1,x,2,3) Functions ========= .. frm:function:: delta_(x1) :param x1: A reFORM expression Returns 1 if ``x1`` is 0. If it is a number other than 0, it will return 0. If ``x1`` is not a number, nothing happens. .. code-block:: reform expr F = delta_(0)*x + delta_(1)*y + delta_(x); yields .. code-block:: reform x + delta_(x) .. frm:function:: gcd_(p1, p2) :param p1: A multivariate polynomial with integer numbers as coefficients :param p2: A multivariate polynomial with integer numbers as coefficients Compute the greatest common divisor of two multivariate polynomials with integer numbers as a coefficient. If the arguments are not valid polynomials, no replacement will be made. .. code-block:: reform expr F = gcd_(100+100*x-90*x^3-90*x^4+12*y+12*x*y+3*x^3*y^2+3*x^4*y^2, 100-100*x-90*x^3+90*x^4+12*y-12*x*y+3*x^3*y^2-3*x^4*y^2); yields .. code-block:: reform +x^3*y^2*3 +x^3*-90 +y*12 +100 .. frm:function:: ifelse_(cond, truebranch, falsebranch) :param cond: A comparison, i.e., ``$a < 2`` :param truebranch: An expression that will be the result of the function if the condition is true :param falsebranch: An expression that will be the result of the function if the condition is false Return ``truebranch`` if the condition ``cond`` is true and ``falsebranch`` if it is false. At the moment ``cond`` should be a comparison between expressions. If the expressions are both numbers, all both equality and inequality tests are evaluted. In all other cases, only an equality test will be evaluated. .. note:: The expressions in both branches are not normalized (simplified), since that will take extra work (only one of the branches should be executed) and could cause infinite loops. As a result, pattern matching on the arguments of ``ifelse_`` will likely not work. .. code-block:: reform expr F = f(5); apply { id f(n?) = ifelse_(n? <= 6, n? + 10, n?); } yields .. code-block:: reform 15 .. frm:function:: list_(i, lb, ub, expr) :param i: A variable used as a counter :param lb: A numeric lower bound for ``i`` :param ub: A numeric upper bound for ``i`` Return a list of ``expr`` with ``i`` going from ``lb`` to (and including) ``ub``. This function will only be replaced when it is a function argument. .. code-block:: reform expr F = f(1,2,list_($i,2,5,$i^2),3,4); yields .. code-block:: reform f(1,2,4,9,16,25,3,4) .. frm:function:: nargs_(a1,...,an) :param a1,...,an: A list of expressions Returns the number of arguments the function has. It is especially useful in combination with the :doc:`ranged wildcards `. .. code-block:: reform expr F = f(1,2,3,4,5); apply { id f(?a) = nargs_(?a); } yields .. code-block:: reform 5 .. frm:function:: prod_(i, lb, ub, expr) :param i: A variable used as a counter :param lb: A numeric lower bound for ``i`` :param ub: A numeric upper bound for ``i`` Return the product of ``expr`` with ``i`` going from ``lb`` to (and including) ``ub``. .. code-block:: reform expr F = prod_($i, 2, 5, $i^2); yields .. code-block:: reform 14400 .. frm:function:: rat_(num, den) :param num: A multivariate polynomial with integer numbers as coefficients :param den: A multivariate polynomial with integer numbers as coefficients The ``rat_`` function can be used to have a ratio of multivariate polynomials as a coefficient . It will compute multivariate gcds to make sure the fraction does not grow more than necessary. If the arguments are not valid polynomials, no replacement will be made. .. code-block:: reform expr F = rat_(x^2+2*x+1,1)*rat_(1,1+x)+rat_(2,1); yields .. code-block:: reform rat_(3+x,1) .. frm:function:: sum_(i, lb, ub, expr) :param i: A variable used as a counter :param lb: A numeric lower bound for ``i`` :param ub: A numeric upper bound for ``i`` Return the sum of ``expr`` with ``i`` going from ``lb`` to (and including) ``ub``. .. code-block:: reform expr F = sum_($i, 2, 5, $i^2); yields .. code-block:: reform 54 .. frm:function:: takearg_(k,a1,...,an) :param k: The index of the argument to take :param a1,...,an: Arguments Return the ``k`` th argument of the list ``a1,...,an`` . If the index is out of bounds, no substitution takes place. .. code-block:: reform expr F = takearg_(2, x1, x2, x3); yields .. code-block:: reform x2