diff --git a/README.md b/README.md
index 2e8c855..ae4945d 100644
--- a/README.md
+++ b/README.md
@@ -32,7 +32,9 @@ Interface for applying symbolic manipulation on [Julia expressions](https://docs
* interface link communicates and interprets via various reduce output modes using `rcall` method;
* high-level reduce-julia syntax parser-generator walks arbitrary expression to rewrite mathematical code;
* import operators from REDUCE using code generation to apply to arbitrary computational expressions;
-* interactive `reduce>` REPL within the Julia terminal window activated by `}` key.
+* interactive `reduce>` REPL within the Julia terminal window activated by `}` key;
+* extended arithmetic operators `+`,`-`,`*`,`^`,`/`,`//` compute on `Symbol` and `Expr` types;
+* provides over 330 internal and external methods each supporting many argument types.
## Setup
@@ -53,24 +55,27 @@ View the documentation [stable](https://chakravala.github.io/Reduce.jl/stable) /
Reduce expressions encapsulated into `RExpr` objects can be manipulated within julia using the standard syntax. Create an expression object either using the `RExpr("expression")` string constructor or `R"expression"`. Additionally, arbitrary julia expressions can also be parsed directly using the `RExpr(expr)` constructor. Internally `RExpr` objects are represented as an array that can be accessed by calling `*.str[n]` on the object.
-Sequences of reduce statements are automatically parsed into julia `quote` blocks using the `RExpr` constructor, which can `parse` back into a julia expression.
+When `Reduce` is used in Julia, all of the standard arithmetic operations are now extended to also work on `Symbol` and `Expr` types.
```Julia
-julia> :((x+im+π)^2; int(1/(1+x^3),x)) |> RExpr
-^(+(x,i,pi),2);
-int(/(1,+(1,^(x,3))),x);
+julia> 1-1/:n
+:((n - 1) // n)
-julia> rcall(ans,:expand) |> parse
-quote
- (((π + 2x) * π + x ^ 2) - 1) + 2 * (π + x) * im
- ((2 * sqrt(3) * atan((2x - 1) // sqrt(3)) - log((x ^ 2 - x) + 1)) + 2 * log(x + 1)) // 6
-end
-```
-Call `split(::RExpr)` to create a new `RExpr` object with all expressions split into separate array elements.
+julia> ans^-:n
+:(1 // ((n - 1) // n) ^ n)
+julia> limit(ans,:n,Inf)
+e = 2.7182818284590...
+```
+Julia abstract syntax trees are automatically converted into sequences of reduce statements that are in return parsed into julia `quote` blocks using the `RExpr` constructor.
The `rcall` method is used to evaluate any type of expression.
```Julia
julia> :(int(sin(im*x+pi)^2-1,x)) |> rcall
-:(-(((e ^ (4x) + 4 * e ^ (2x) * x) - 1)) // (8 * e ^ (2x)))
+:((1 - (e ^ (4x) + 4 * e ^ (2x) * x)) // (8 * e ^ (2x)))
+```
+However, there are often multiple equivalent ways of achieving the same result:
+```Julia
+julia> int(sin(im*:x+π)^2-1,:x)
+:((1 - (e ^ (4x) + 4 * e ^ (2x) * x)) // (8 * e ^ (2x)))
```
The output of `rcall` will be the same as its input type.
```Julia
@@ -78,6 +83,18 @@ julia> "int(sin(y)^2, y)" |> rcall
"( - cos(y)*sin(y) + y)/2"
```
Use `rcall(expr,switches...)` to evaluate `expr` using REDUCE mode `switches` like `:expand`, `:factor`, and `:latex`.
+```Julia
+julia> :((x+im+π)^2; int(1/(1+x^3),x)) |> RExpr
+^(+(x,i,pi),2);
+int(/(1,+(1,^(x,3))),x);
+
+julia> rcall(ans,:horner) |> parse
+quote
+ ((π + 2x) * π + 2 * (π + x) * im + x ^ 2) - 1
+ ((2 * sqrt(3) * atan((2x - 1) // sqrt(3)) - log((x ^ 2 - x) + 1)) + 2 * log(x + 1)) // 6
+end
+```
+Call `split(::RExpr)` to create a new `RExpr` object with all expressions internally split into separate array elements.
Mathematical operators and REDUCE modes can be applied directly to `Expr` and `RExpr` objects.
```Julia
@@ -91,11 +108,23 @@ Although not all language features have been implemented yet, it is possible to
julia> Expr(:for,:(i=2:34),:(product(i))) |> rcall
:(@big_str "295232799039604140847618609643520000000")
```
+The `squash` function provides a way to reduce full program blocks into simplified functions,
+```Julia
+julia> Expr(:function,:(example(a,b)),quote
+ z = 3
+ target = z * :a * :b
+ z -= 1
+ target += z*(1-:a)*(1-:b)
+ end) |> squash |> factor
+:(function example(a, b)
+ (5b - 2) * a - 2 * (b - 1)
+ end)
+```
### Output mode
Various output modes are supported. While in the REPL, the default `nat` output mode will be displayed for `RExpr` objects.
- ```Julia
- julia> :(sin(x*im) + cos(y*φ)) |> RExpr
+```Julia
+julia> :(sin(x*im) + cos(y*φ)) |> RExpr
(sqrt(5) + 1)*y
cos(-----------------) + sinh(x)*i
@@ -105,14 +134,14 @@ This same output can also be printed to the screen by calling `print(nat(r))` me
It is possible to direclty convert a julia expression object to LaTeX code using the `latex` method.
```Julia
-julia> print(@latex sin(x*im) + cos(y*φ))
+julia> print(@latex sin(x) + cos(y*φ))
\begin{displaymath}
-\cos \left(\left(\left(\sqrt {5}+1\right) y\right)/2\right)+\sinh \,x\: i
+\cos \left(\left(\left(\sqrt {5}+1\right) y\right)/2\right)+\sin \,x
\end{displaymath}
```
-Internally, this command essentially expands to `rcall(:(sin(x*im) + cos(y*φ)),:latex) |> print`, which is equivalent.
+Internally, this command essentially expands to `rcall(:(sin(x) + cos(y*φ)),:latex) |> print`, which is equivalent.
-![latex-equation](https://latex.codecogs.com/svg.latex?\cos&space;\left(\left(\left(\sqrt&space;{5}+1\right)&space;y\right)/2\right)+\sinh&space;x\:&space;i)
+![latex-equation](https://latex.codecogs.com/svg.latex?\cos&space;\left(\left(\left(\sqrt&space;{5}+1\right)&space;y\right)/2\right)+\sin&space;x)
In `IJulia` the display output of `RExpr` objects will be rendered LaTeX with the `rlfi` REDUCE package in `latex` mode.
@@ -145,6 +174,8 @@ Releases of `Reduce.jl` enable the general application of various REDUCE functio
If the `reduce>` REPL is not appearing when `}` is pressed or the `Reduce.PSL` pipe is broken, the session can be restored by simply calling `Reduce.Reset()`, without requiring a restart of `julia` or reloading the package. This kills the currently running `redpsl` session and then re-initializes it for new use.
+Otherwise, questions can be asked on gitter/discourse or submit your issue or pull-request if you require additional features or noticed some unusual edge-case behavior.
+
### OhMyREPL Compatibility
Reduce.jl is compatible with the [OhMyREPL.jl](https://github.com/KristofferC/OhMyREPL.jl) package.
diff --git a/docs/make.jl b/docs/make.jl
index 014153d..75b45fc 100644
--- a/docs/make.jl
+++ b/docs/make.jl
@@ -23,7 +23,7 @@ deploydocs(
branch = "gh-pages",
latest = "master",
osname = "linux",
- julia = "0.5",
+ julia = "0.6",
deps = nothing,
make = nothing,
)
diff --git a/docs/src/index.md b/docs/src/index.md
index c3e6aa3..aa5cd5e 100644
--- a/docs/src/index.md
+++ b/docs/src/index.md
@@ -36,6 +36,8 @@ Interface for applying symbolic manipulation on [Julia expressions](https://docs
* high-level reduce-julia syntax parser-generator walks arbitrary expression to rewrite mathematical code;
* import operators from REDUCE using code generation to apply to arbitrary computational expressions;
* interactive `reduce>` REPL within the Julia terminal window activated by `}` key.
+* extended arithmetic operators `+`,`-`,`*`,`^`,`/`,`//` compute on `Symbol` and `Expr` types;
+* provides over 330 internal and external methods each supporting many argument types.
## Setup
@@ -69,24 +71,27 @@ Releases of `Reduce.jl` enable the general application of various REDUCE functio
Reduce expressions encapsulated into `RExpr` objects can be manipulated within julia using the standard syntax. Create an expression object either using the `RExpr("expression")` string constructor or `R"expression"`. Additionally, arbitrary julia expressions can also be parsed directly using the `RExpr(expr)` constructor. Internally `RExpr` objects are represented as an array that can be accessed by calling `*.str[n]` on the object.
-Sequences of reduce statements are automatically parsed into julia `quote` blocks using the `RExpr` constructor, which can `parse` back into a julia expression.
+When `Reduce` is used in Julia, all of the standard arithmetic operations are now extended to also work on `Symbol` and `Expr` types.
```Julia
-julia> :((x+im+π)^2; int(1/(1+x^3),x)) |> RExpr
-^(+(x,i,pi),2);
-int(/(1,+(1,^(x,3))),x);
+julia> 1-1/:n
+:((n - 1) // n)
-julia> rcall(ans,:expand) |> parse
-quote
- (((π + 2x) * π + x ^ 2) - 1) + 2 * (π + x) * im
- ((2 * sqrt(3) * atan((2x - 1) // sqrt(3)) - log((x ^ 2 - x) + 1)) + 2 * log(x + 1)) // 6
-end
-```
-Call `split(::RExpr)` to create a new `RExpr` object with all expressions split into separate array elements.
+julia> ans^-:n
+:(1 // ((n - 1) // n) ^ n)
+julia> limit(ans,:n,Inf)
+e = 2.7182818284590...
+```
+Julia abstract syntax trees are automatically converted into sequences of reduce statements that are in return parsed into julia `quote` blocks using the `RExpr` constructor.
The `rcall` method is used to evaluate any type of expression.
```Julia
julia> :(int(sin(im*x+pi)^2-1,x)) |> rcall
-:(-(((e ^ (4x) + 4 * e ^ (2x) * x) - 1)) // (8 * e ^ (2x)))
+:((1 - (e ^ (4x) + 4 * e ^ (2x) * x)) // (8 * e ^ (2x)))
+```
+However, there are often multiple equivalent ways of achieving the same result:
+```Julia
+julia> int(sin(im*:x+π)^2-1,:x)
+:((1 - (e ^ (4x) + 4 * e ^ (2x) * x)) // (8 * e ^ (2x)))
```
The output of `rcall` will be the same as its input type.
```Julia
@@ -94,6 +99,18 @@ julia> "int(sin(y)^2, y)" |> rcall
"( - cos(y)*sin(y) + y)/2"
```
Use `rcall(expr,switches...)` to evaluate `expr` using REDUCE mode `switches` like `:expand`, `:factor`, and `:latex`.
+```Julia
+julia> :((x+im+π)^2; int(1/(1+x^3),x)) |> RExpr
+^(+(x,i,pi),2);
+int(/(1,+(1,^(x,3))),x);
+
+julia> rcall(ans,:horner) |> parse
+quote
+ ((π + 2x) * π + 2 * (π + x) * im + x ^ 2) - 1
+ ((2 * sqrt(3) * atan((2x - 1) // sqrt(3)) - log((x ^ 2 - x) + 1)) + 2 * log(x + 1)) // 6
+end
+```
+Call `split(::RExpr)` to create a new `RExpr` object with all expressions internally split into separate array elements.
Mathematical operators and REDUCE modes can be applied directly to `Expr` and `RExpr` objects.
```Julia
@@ -107,28 +124,40 @@ Although not all language features have been implemented yet, it is possible to
julia> Expr(:for,:(i=2:34),:(product(i))) |> rcall
:(@big_str "295232799039604140847618609643520000000")
```
+The `squash` function provides a way to reduce full program blocks into simplified functions,
+```Julia
+julia> Expr(:function,:(example(a,b)),quote
+ z = 3
+ target = z * :a * :b
+ z -= 1
+ target += z*(1-:a)*(1-:b)
+ end) |> squash |> factor
+:(function example(a, b)
+ (5b - 2) * a - 2 * (b - 1)
+ end)
+```
### Output mode
Various output modes are supported. While in the REPL, the default `nat` output mode will be displayed for `RExpr` objects.
```Julia
julia> :(sin(x*im) + cos(y*φ)) |> RExpr
- (sqrt(5) + 1)*y
- cos(-----------------) + sinh(x)*i
- 2
+ (sqrt(5) + 1)*y
+cos(-----------------) + sinh(x)*i
+ 2
```
This same output can also be printed to the screen by calling `print(nat(r))` method.
It is possible to direclty convert a julia expression object to LaTeX code using the `latex` method.
```Julia
-julia> print(@latex sin(x*im) + cos(y*φ))
+julia> print(@latex sin(x) + cos(y*φ))
\begin{displaymath}
-\cos \left(\left(\left(\sqrt {5}+1\right) y\right)/2\right)+\sinh \,x\: i
+\cos \left(\left(\left(\sqrt {5}+1\right) y\right)/2\right)+\sin \,x
\end{displaymath}
```
-Internally, this command essentially expands to `rcall(:(sin(x*im) + cos(y*φ)),:latex) |> print`, which is equivalent.
+Internally, this command essentially expands to `rcall(:(sin(x) + cos(y*φ)),:latex) |> print`, which is equivalent.
-![latex-equation](https://latex.codecogs.com/svg.latex?\cos&space;\left(\left(\left(\sqrt&space;{5}+1\right)&space;y\right)/2\right)+\sinh&space;x\:&space;i)
+![latex-equation](https://latex.codecogs.com/svg.latex?\cos&space;\left(\left(\left(\sqrt&space;{5}+1\right)&space;y\right)/2\right)+\sin&space;x)
In `IJulia` the display output of `RExpr` objects will be rendered LaTeX with the `rlfi` REDUCE package in `latex` mode.
@@ -137,17 +166,19 @@ Similar to ? help and ; shell modes in Julia, `Reduce` pro
```Julia
reduce> df(atan(golden_ratio*x),x);
- 2 2
- sqrt(5)*x + sqrt(5) - x + 1
- -------------------------------
- 4 2
- 2*(x + 3*x + 1)
+ 2 2
+ sqrt(5)*x + sqrt(5) - x + 1
+-------------------------------
+ 4 2
+ 2*(x + 3*x + 1)
```
## Troubleshooting
If the `reduce>` REPL is not appearing when `}` is pressed or the `Reduce.PSL` pipe is broken, the session can be restored by simply calling `Reduce.Reset()`, without requiring a restart of `julia` or reloading the package. This kills the currently running `redpsl` session and then re-initializes it for new use.
+Otherwise, questions can be asked on gitter/discourse or submit your issue or pull-request if you require additional features or noticed some unusual edge-case behavior.
+
### OhMyREPL Compatibility
Reduce.jl is compatible with the [OhMyREPL.jl](https://github.com/KristofferC/OhMyREPL.jl) package.
diff --git a/docs/src/library.md b/docs/src/library.md
index b8ce935..5f359d1 100644
--- a/docs/src/library.md
+++ b/docs/src/library.md
@@ -30,22 +30,30 @@ parse
load_package
```
+```@docs
+sub
+```
+
+```@docs
+squash
+```
+
## Imported Operators
Reduce `switch` modes callable as functions from Julia
-> :expand, :complex, :factor, :expandlog, :combinelog, :precise, :combineexpt, :rounded, :evallhseq, :nat, :latex
+> expand, complex, factor, horner, expandlog, combinelog, precise, combineexpt, rounded, evallhseq, nat, latex
-Calculus operators
-> :df, :int
+Reduce operators with multiple arguments
+> df, int, limit, sum, prod, +, -, ^, *, /, //
Unary operators
-> :abs, :conj, :factorial, :floor, :max, :min, :round, :sign, :acos, :acosh, :acot, :acoth, :acsc, :acsch, :asec, :asech, :asin, :asinh, :atan, :atanh, :atan2, :cos, :cosh, :cot, :coth, :csc, :csch, :exp, :hypot, :log, :log10, :sec, :sech, :sin, :sinh, :sqrt, :tan, :tanh, :gamma, :factorize
+> abs, conj, factorial, floor, max, min, round, sign, acos, acosh, acot, acoth, acsc, acsch, asec, asech, asin, asinh, atan, atanh, atan2, cos, cosh, cot, coth, csc, csch, exp, hypot, log, log10, sec, sech, sin, sinh, sqrt, tan, tanh, gamma, factorize
-> :beta, :besseli, :besselj, :besselk, :bessely, :polygamma, :zeta
+> beta, besseli, besselj, besselk, bessely, polygamma, zeta
-> :ibeta, :igamma, :ln, :psi, :bernoulli, :continued_fraction, :ci, :dilog, :ei, :si, :airy_ai, :airy_aiprime, :airy_bi, :airy_biprime, :hanekl1, :hankel2, :kummerm, :kummeru, :lommel1, :lommel2, :struveh, :struvel, :whittakerm, :whittakeru, :solidharmonicy, :sphericalharmonicy
+> ibeta, igamma, ln, psi, bernoulli, continued_fraction, ci, dilog, ei, si, airy_ai, airy_aiprime, airy_bi, airy_biprime, hanekl1, hankel2, kummerm, kummeru, lommel1, lommel2, struveh, struvel, whittakerm, whittakeru, solidharmonicy, sphericalharmonicy
-> :ceiling, :fix, :impart, :repart, :nextprime, :euler, :fibonacci, :motzkin, :random, :random_new_seed
+> ceiling, fix, impart, repart, nextprime, euler, fibonacci, motzkin, random, random_new_seed
## Tools & Options
@@ -53,6 +61,10 @@ Unary operators
Reduce.parsegen
```
+```@docs
+Reduce.unfoldgen
+```
+
```@docs
Reduce.linefilter
```
@@ -64,3 +76,19 @@ Reduce.Rational
```@docs
Reduce.SubCall
```
+
+```@docs
+Reduce.SubHold
+```
+
+```@docs
+Reduce.SubFail
+```
+
+```@docs
+Reduce.ColCheck
+```
+
+```@docs
+Reduce.DisplayLog
+```
diff --git a/src/parser.jl b/src/parser.jl
index 40ba109..8228994 100644
--- a/src/parser.jl
+++ b/src/parser.jl
@@ -706,7 +706,7 @@ end
"""
unfoldgen(::Symbol,::Symbol)
-Parser generator that outputs code to walk and manipulate REDUCE expressions
+Parser generator that outputs code to walk and manipulate Julia expressions
"""
function unfoldgen(fun::Symbol,mode::Symbol)
modefun = Symbol(:parse,"_",mode)
diff --git a/src/rexpr.jl b/src/rexpr.jl
index cb33f5b..781a925 100644
--- a/src/rexpr.jl
+++ b/src/rexpr.jl
@@ -164,13 +164,23 @@ const repjlr = jl_to_r_utf
const gexrjl = Regex("($(join(keys(r_to_jl),")|(")))")
const gexjlr = Regex("($(join(keys(jl_to_r),")|(")))")
+"""
+ sub(::Union{Dict,Pair},expr)
+
+Make variable substitutions using Reduce's native sub command
+"""
sub(syme::String,expr::RExpr) = "sub($syme,$expr)" |> rcall |> RExpr
sub(syme::String,expr::T) where T = convert(T,sub(syme,RExpr(expr)))
sub(s::Dict{String,String},expr) = sub(_syme(s),expr)
sub(s::Dict{<:Any,<:Any},expr) = sub(Dict([=>(string.(RExpr.([b[1],b[2]]))...) for b ∈ collect(s)]...),expr)
sub(s::Pair{<:Any,<:Any},expr) = sub(Dict(s),expr)
-sub(s::Array{Pair{<:Any,<:Any},1},expr) = sub(Dict(s...),expr)
+sub(s::Array{<:Pair{<:Any,<:Any},1},expr) = sub(Dict(s...),expr)
+
+"""
+ sub(T::DataType,expr::Expr)
+Make a substitution to conver numerical values to type T
+"""
function sub(T::DataType,ixpr)
if typeof(ixpr) == Expr
expr = deepcopy(ixpr)
@@ -465,6 +475,11 @@ end
end
end=#
+"""
+ squash(expr)
+
+Reduces an entire program statement block using symbolic rewriting
+"""
function squash(expr)
typeof(expr) == Expr && if expr.head == :block
return @eval $expr
diff --git a/test/runtests.jl b/test/runtests.jl
index 14b4f0c..509cf78 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -50,3 +50,19 @@ using Base.Test
@test :([1 2; 3 4]) |> RExpr |> Reduce.parse |> RExpr == [1 2; 3 4] |> RExpr
println()
#@test Reduce.repl_init(Base.active_repl)==nothing
+
+@test nextprime("3") == "5"
+@test expand("(x-2)^2") |> RExpr == R"(x-2)^2"
+@test nat("x+1") == "\nx + 1\n"
+@test macroexpand(@factor(:(x^2+2x+1))) == :((x+1)^2)
+@test :x^2 == :(x^2)
+@test NaN//NaN |> isnan
+@test join(split(R"x+1;x+2"))|> string == "x+1;\nx+2"
+@test sub(:x=>7,:x+7) == sub([:x=>7,:z=>21],:z-:x)
+@test sub(Float64,prod((:x-:n)^:n,:n,1,7)|>horner) |> typeof == Expr
+@test squash(Expr(:function,:(fun(x)),:(z=3;z+=:x))).args[2] == squash(:(y=:x;y+=3))
+@test Expr(:block,:(x+1)) |> RExpr == R"1+x"
+@test limit((1-1/:n)^-:n,:n,Inf) == e
+@test log(exp(:pi)) == π
+@test 2//Inf == 0
+@test Inf//2 == Inf