├── source ├── and_not.txt ├── and_true.txt ├── or_not.txt ├── and_false.txt ├── lagrangian │ ├── spring.poi │ ├── spring_goal.poi │ └── spring_motion.poi ├── rot.poi ├── trans4.poi ├── rot3_x.poi ├── rot3_y.poi ├── rot3_z.poi └── collatz │ └── script.poi ├── .gitignore ├── .github └── FUNDING.yml ├── examples ├── display.rs ├── or.rs ├── and.rs ├── parsing.rs ├── not_not.rs ├── asymmetric_paths.rs ├── binary_paths.rs ├── echo.rs ├── binary.rs └── poi.rs ├── assets ├── help │ ├── list.txt │ ├── deriv.txt │ ├── script.txt │ ├── integ.txt │ ├── symbol.txt │ ├── norm.txt │ ├── eqv.txt │ ├── rad.txt │ ├── dom.txt │ ├── eps.txt │ ├── sym.txt │ ├── goal.txt │ ├── imag.txt │ ├── triv.txt │ ├── ex.txt │ ├── catus.txt │ ├── asym.txt │ └── help.txt ├── syntax.txt └── std.md ├── src ├── op.rs ├── standard_library.rs ├── val.rs ├── knowledge.rs ├── arity.rs ├── matrix.rs ├── expr.rs ├── sym.rs └── parsing.rs ├── Cargo.toml ├── LICENSE-MIT ├── LICENSE-APACHE └── README.md /source/and_not.txt: -------------------------------------------------------------------------------- 1 | and[not] 2 | -------------------------------------------------------------------------------- /source/and_true.txt: -------------------------------------------------------------------------------- 1 | and(true) 2 | -------------------------------------------------------------------------------- /source/or_not.txt: -------------------------------------------------------------------------------- 1 | or[not] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /source/and_false.txt: -------------------------------------------------------------------------------- 1 | and(false) 2 | -------------------------------------------------------------------------------- /source/lagrangian/spring.poi: -------------------------------------------------------------------------------- 1 | 0.5 * m * (∂x/∂t)^2 - 0.5 * k * (x - s)^2 2 | -------------------------------------------------------------------------------- /source/lagrangian/spring_goal.poi: -------------------------------------------------------------------------------- 1 | m * (∂^2 * x / ∂t^2) = (-k) * (x - s) 2 | -------------------------------------------------------------------------------- /source/rot.poi: -------------------------------------------------------------------------------- 1 | [ 2 | [cos(α), -sin(α)], 3 | [sin(α), cos(α)] 4 | ] 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: bvssvni 4 | -------------------------------------------------------------------------------- /source/lagrangian/spring_motion.poi: -------------------------------------------------------------------------------- 1 | (∂ / ∂t) * (∂(spring.poi) / (∂ * (∂x/∂t))) = ∂(spring.poi) / ∂x 2 | -------------------------------------------------------------------------------- /source/trans4.poi: -------------------------------------------------------------------------------- 1 | [ 2 | [1, 0, 0, tx], 3 | [0, 1, 0, ty], 4 | [0, 0, 1, tz], 5 | [0, 0, 0, 1] 6 | ] 7 | -------------------------------------------------------------------------------- /source/rot3_x.poi: -------------------------------------------------------------------------------- 1 | [ 2 | [1, 0, 0], 3 | [0, cos(αx), -sin(αx)], 4 | [0, sin(αx), cos(αx)] 5 | ] 6 | -------------------------------------------------------------------------------- /source/rot3_y.poi: -------------------------------------------------------------------------------- 1 | [ 2 | [cos(αy), 0, -sin(αy)], 3 | [ 0, 1, 0], 4 | [sin(αy), 0, cos(αy)] 5 | ] 6 | -------------------------------------------------------------------------------- /source/rot3_z.poi: -------------------------------------------------------------------------------- 1 | [ 2 | [cos(αz), -sin(αz), 0], 3 | [sin(αz), cos(αz), 0], 4 | [ 0, 0, 1] 5 | ] 6 | -------------------------------------------------------------------------------- /examples/display.rs: -------------------------------------------------------------------------------- 1 | use poi::prelude::*; 2 | 3 | fn main() { 4 | let a = path("f", ("g0", "g1", "g2")); 5 | println!("{}", a); 6 | } 7 | -------------------------------------------------------------------------------- /assets/help/list.txt: -------------------------------------------------------------------------------- 1 | === List === 2 | 3 | A list is written in the form: 4 | 5 | [1, 2, 3, ...] 6 | 7 | Some common operations on lists: 8 | 9 | concat(a, b) a ++ b concatenates two lists 10 | item(i, a) looks up item by index `i` in list `a` 11 | -------------------------------------------------------------------------------- /examples/or.rs: -------------------------------------------------------------------------------- 1 | use poi::prelude::*; 2 | 3 | fn main() { 4 | let ref std = std(); 5 | 6 | // or(true, true) = true 7 | let a = app(Or, (true, true)).reduce_all(std); 8 | 9 | // or(true)(true) = true 10 | let a = a.inline(&Or, std).unwrap().reduce_all(std); 11 | assert_eq!(a, true.into()); 12 | } 13 | -------------------------------------------------------------------------------- /examples/and.rs: -------------------------------------------------------------------------------- 1 | use poi::prelude::*; 2 | 3 | fn main() { 4 | let ref std = std(); 5 | 6 | // and(true, true) = true 7 | let a = app(And, (true, true)).reduce_all(std); 8 | 9 | // and(true)(true) = true 10 | let a = a.inline(&And, std).unwrap().reduce_all(std); 11 | assert_eq!(a, true.into()); 12 | } 13 | -------------------------------------------------------------------------------- /assets/help/deriv.txt: -------------------------------------------------------------------------------- 1 | === Derivative === 2 | 3 | The derivative measures the sensitivity to change of a function 4 | output value with respect to its argument input value. 5 | 6 | For example: 7 | 8 | deriv(x, 2 * x) 9 | 10 | The function here is `2 * x` and the argument input value 11 | measuring the sensitivity for is `x`. 12 | -------------------------------------------------------------------------------- /assets/help/script.txt: -------------------------------------------------------------------------------- 1 | === Scripts === 2 | 3 | Poi-Reduce is designed to be used with Unix pipes. 4 | This means that commands to Poi-Reduce can be executed as a script. 5 | 6 | To run a file as a script, type the following in the Terminal/shell: 7 | 8 | cat | poireduce 9 | 10 | Comments start with `//` in Poi-Reduce scripts. 11 | -------------------------------------------------------------------------------- /assets/help/integ.txt: -------------------------------------------------------------------------------- 1 | === Integral === 2 | 3 | The integral of a function is the signed area bounded by its graph. 4 | 5 | For example: 6 | 7 | integ(x, c, 2 * x) 8 | 9 | The first argument `x` is the input variable of the function. 10 | The second argument `c` is a constant added to the solution. 11 | The third argument `2 * x` is the function. 12 | -------------------------------------------------------------------------------- /src/op.rs: -------------------------------------------------------------------------------- 1 | /// Binary operation on functions. 2 | #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Debug)] 3 | pub enum Op { 4 | /// Function composition `f . g` 5 | Compose, 6 | /// Path `f[g]` 7 | Path, 8 | /// Apply function to some argument. 9 | Apply, 10 | /// Constrain function input. 11 | Constrain, 12 | /// Type judgement. 13 | Type, 14 | } 15 | -------------------------------------------------------------------------------- /src/standard_library.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// Standard library knowledge base. 4 | pub fn std() -> Vec { 5 | match parse_data_str(include_str!("../assets/std.md"), &[]) { 6 | Ok(ParseData::Knowledge(k)) => k, 7 | Ok(ParseData::Expr(_)) => panic!("Expected knowledge, found expression"), 8 | Err(err) => panic!("ERROR:\n{}", err), 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/parsing.rs: -------------------------------------------------------------------------------- 1 | use poi::prelude::*; 2 | 3 | fn main() { 4 | test("source/and_not.txt", path(And, Not)); 5 | test("source/or_not.txt", path(Or, Not)); 6 | test("source/and_true.txt", app(And, true)); 7 | test("source/and_false.txt", app(And, false)); 8 | } 9 | 10 | fn test(file: &str, e: Expr) { 11 | let a: Expr = parse(file, &[]).map_err(|err| panic!("{}", err)).unwrap(); 12 | assert_eq!(a, e); 13 | } 14 | -------------------------------------------------------------------------------- /assets/help/symbol.txt: -------------------------------------------------------------------------------- 1 | === Symbols vs Variables === 2 | 3 | A symbol only matches against itself in a rule. 4 | A variable matches against any sub-expression in a rule. 5 | 6 | Standard symbols are recognized automatically. 7 | Custom symbols must start with two lowercase alphabetic letters. 8 | 9 | Examples: 10 | 11 | x variable 12 | x0 variable 13 | X variable 14 | xF variable 15 | Foo variable 16 | sin standard symbol 17 | foo custom symbol 18 | -------------------------------------------------------------------------------- /src/val.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// Value. 4 | #[derive(Clone, PartialEq, PartialOrd, Debug)] 5 | pub enum Value { 6 | /// A boolean value. 7 | Bool(bool), 8 | /// A f64 value. 9 | F64(f64), 10 | } 11 | 12 | impl fmt::Display for Value { 13 | fn fmt(&self, w: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> { 14 | use Value::*; 15 | 16 | match self { 17 | Bool(v) => write!(w, "{}", v)?, 18 | F64(v) => write!(w, "{}", v)?, 19 | } 20 | Ok(()) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "poi" 3 | version = "0.25.0" 4 | authors = ["Sven Nilsen "] 5 | edition = "2018" 6 | keywords = ["advancedresearch", "theorem", "proving", "assistant", "point-free"] 7 | description = "A pragmatic point-free theorem prover assistant" 8 | license = "MIT OR Apache-2.0" 9 | readme = "README.md" 10 | repository = "https://github.com/advancedresearch/poi.git" 11 | homepage = "https://github.com/advancedresearch/poi" 12 | exclude = ["source/*"] 13 | 14 | [dependencies] 15 | piston_meta = "2.0.0" 16 | 17 | [dev-dependencies] 18 | read_token = "1.0.0" 19 | levenshtein = "1.0.4" 20 | -------------------------------------------------------------------------------- /examples/not_not.rs: -------------------------------------------------------------------------------- 1 | use poi::*; 2 | use poi::prelude::*; 3 | 4 | fn main() { 5 | let ref std = std(); 6 | 7 | // (not . not)(false) = false 8 | let a = app(comp(Not, Not), false); 9 | let a = a.reduce(std).unwrap().0; 10 | let a = a.inline(&Idb, std).unwrap(); 11 | let a = a.reduce(std).unwrap().0; 12 | assert_eq!(a, false.into()); 13 | 14 | // (not . not)(true) = true 15 | let a = app(comp(Not, Not), true); 16 | let a = a.reduce(std).unwrap().0; 17 | let a = a.inline(&Idb, std).unwrap(); 18 | let a = a.reduce(std).unwrap().0; 19 | assert_eq!(a, true.into()); 20 | } 21 | -------------------------------------------------------------------------------- /examples/asymmetric_paths.rs: -------------------------------------------------------------------------------- 1 | use poi::prelude::*; 2 | 3 | fn main() { 4 | let ref std = std(); 5 | 6 | // `if(a, b)[not -> id]` 7 | let a = path(_if("a", "b"), (Not, Id)).reduce_all(std); 8 | assert_eq!(a, _if("b", "a")); 9 | 10 | let a = path("f", (Id, Not)).reduce_all(std); 11 | assert_eq!(a, comp(Not, "f")); 12 | 13 | // `f[not] => f[not -> id][id -> not]` 14 | let a = path("f", Not).reduce(std).unwrap().0; 15 | assert_eq!(a, path(path("f", (Not, Id)), (Id, Not))); 16 | 17 | let a = comp(Not, path(_if("a", "b"), Not)).reduce_all(std); 18 | assert_eq!(a, _if("b", "a")); 19 | } 20 | -------------------------------------------------------------------------------- /assets/help/norm.txt: -------------------------------------------------------------------------------- 1 | === Normal Paths === 2 | 3 | A normal path is the secret sauce of path semantics. There are two kinds: 4 | 5 | - symmetric paths e.g. `f[g]` 6 | - asymmetric paths e.g. `f[g0 x g1 -> g2]` 7 | 8 | A normal path is basically everything one can predict about a function. 9 | 10 | The properties `g0` and `g1` of the input of `f` predicts the property `g2` 11 | using the function `h`: 12 | 13 | f[g0 x g1 -> g2] <=> h 14 | 15 | In equational form: 16 | 17 | g2(f(a, b)) = h(g0(a), g1(a)) 18 | 19 | For more help, try: 20 | 21 | - help sym more help about symmetric paths 22 | - help asym more help about asymmetric paths 23 | -------------------------------------------------------------------------------- /assets/help/eqv.txt: -------------------------------------------------------------------------------- 1 | === Equivalent Expressions === 2 | 3 | When the expression can not be reduced further, 4 | a list of equivalent expressions are displayed. 5 | 6 | For example, type `(len . concat)(a, b)` and you will get the suggestion 7 | `<=> add((len · fst)(a)(b), (len · snd)(a)(b))` 8 | Copy-paste this as the new input and it will reduce to `add(len(a))(len(b))`. 9 | 10 | Equivalent expressions are used when there is not always clear which 11 | direction to reduce an expression. This is hard-coded into the knowledge rule. 12 | The decision of which rules should be reductions or equivalences is based on 13 | Poi user experience over time. You can help improving the knowledge base! 14 | -------------------------------------------------------------------------------- /examples/binary_paths.rs: -------------------------------------------------------------------------------- 1 | use poi::prelude::*; 2 | 3 | fn main() { 4 | let ref std = std(); 5 | 6 | assert_eq!(path(And, Not).reduce_all(std), Or.into()); 7 | assert_eq!(path(Or, Not).reduce_all(std), And.into()); 8 | assert_eq!(path(Xor, Not).reduce_all(std), Eqb.into()); 9 | assert_eq!(path(Eqb, Not).reduce_all(std), Xor.into()); 10 | assert_eq!(path(Nand, Not).reduce_all(std), Nor.into()); 11 | assert_eq!(path(Nor, Not).reduce_all(std), Nand.into()); 12 | 13 | assert_eq!(comp(Not, Nand).reduce_all(std), And.into()); 14 | assert_eq!(comp(Not, Nor).reduce_all(std), Or.into()); 15 | assert_eq!(comp(Not, And).reduce_all(std), Nand.into()); 16 | assert_eq!(comp(Not, Or).reduce_all(std), Nor.into()); 17 | } 18 | -------------------------------------------------------------------------------- /assets/help/rad.txt: -------------------------------------------------------------------------------- 1 | === Radians === 2 | 3 | In mathematics, it is common to use radians to describe angles. 4 | This is because radians give the arc length of a given angle on a unit circle. 5 | If the angle is `a`, then the arc length on a circle of radius `r` is `(a * r)`. 6 | 7 | The radians of a full circle with radius `r` is `(2 * pi * r)`. 8 | Many mathematicians use `(2 * pi)` to describe a full circle. 9 | However, it is more convenient to use `tau` or `τ` instead. 10 | 11 | For example, `0.25tau` or `(tau / 4)` is a quarter of a circle. 12 | You can also think about it as the circle circumference of a quarter radius. 13 | 14 | Poi understand expressions like `0.25tau` or `0.25τ`. 15 | 16 | This way, one can work with radians as the range `0τ` to `1τ` for a unit circle. 17 | -------------------------------------------------------------------------------- /assets/help/dom.txt: -------------------------------------------------------------------------------- 1 | === Domains and Partial Functions === 2 | 3 | A partial function is a function where the input is constrained in some way. 4 | According to Path Semantics, this changes the identity of the function. 5 | Therefore, one should think about partial functions as 'different' functions. 6 | 7 | For example: `and(a, a)`. Type it and see what happens. 8 | 9 | In `and(a, a)`, the input of `and` is constrained implicitly. 10 | Poi reduces this first to `and{eq}(a)(a)` by adding `eq` as domain constraint. 11 | This turns `and` into a partial function `and{eq}`. 12 | 13 | Now, the identity of the `and` function has changed into another function. 14 | Poi uses the rule `and{eq} => fstb` to reduce this expression further. 15 | In the end, the expression `and(a, a)` is reduced to just `a`. 16 | -------------------------------------------------------------------------------- /examples/echo.rs: -------------------------------------------------------------------------------- 1 | use poi::*; 2 | 3 | fn main() { 4 | loop { 5 | use std::io::{self, Write}; 6 | 7 | print!("> "); 8 | let mut input = String::new(); 9 | io::stdout().flush().unwrap(); 10 | match io::stdin().read_line(&mut input) { 11 | Ok(_) => {} 12 | Err(_) => { 13 | println!("ERROR: Could not read input"); 14 | continue; 15 | } 16 | }; 17 | 18 | match input.trim() { 19 | "bye" => break, 20 | _ => {} 21 | } 22 | 23 | let expr = match parse_str(&input, &[]) { 24 | Ok(expr) => expr, 25 | Err(err) => { 26 | println!("ERROR:\n{}", err); 27 | continue; 28 | } 29 | }; 30 | println!("{}", expr); 31 | println!("{:?}", expr); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/binary.rs: -------------------------------------------------------------------------------- 1 | use poi::prelude::*; 2 | 3 | fn main() { 4 | test_binary(And, |a, b| a && b); 5 | test_binary(Or, |a, b| a || b); 6 | test_binary(Eqb, |a, b| a == b); 7 | test_binary(Xor, |a, b| a ^ b); 8 | test_binary(Nand, |a, b| !(a && b)); 9 | test_binary(Nor, |a, b| !(a || b)); 10 | test_binary(Exc, |a, b| a && !b); 11 | test_binary(Imply, |a, b| !a || b); 12 | test_binary(Fstb, |a, _| a); 13 | test_binary(Sndb, |_, b| b); 14 | } 15 | 16 | pub fn test_binary(sym: Symbol, f: fn(bool, bool) -> bool) { 17 | let ref std = std(); 18 | let cases = &[ 19 | (false, false), 20 | (false, true), 21 | (true, false), 22 | (true, true) 23 | ]; 24 | for case in cases { 25 | let r = f(case.0, case.1); 26 | let a = app(sym.clone(), (case.0, case.1)).eval(std).unwrap(); 27 | assert_eq!(a, r.into()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /assets/help/eps.txt: -------------------------------------------------------------------------------- 1 | === Epsilon === 2 | 3 | In physics and machine learning, it is very convenient to use a constant that is 4 | a very small positive number, written in Poi as `eps` or `ε`. 5 | 6 | This number has the following property (for all practical purposes): 7 | 8 | ε^2 = 0 9 | 10 | In the form `(a + b * eps)`, this forms the theory of dual numbers. 11 | 12 | Dual numbers are very useful because it lets you "cheat" in ways that seems 13 | unreasonably rewarding and efficient at advanced problem solving. 14 | 15 | For example, in matrix algebra, if you have an equation of the form `f(X) = 0` 16 | where `f` is a function of a matrix `X`, you can transform with `det(f(X)) = 0`, 17 | insert dual numbers for coefficients of `X` and tease out matrix solutions. 18 | 19 | Poi understands e.g. `(2 + 3 eps)` or `(2 + 3ε)` as `(2 + 3 * ε)`. 20 | Remember to use space in `3 eps` because of `e` in scientific notation. 21 | -------------------------------------------------------------------------------- /assets/help/sym.txt: -------------------------------------------------------------------------------- 1 | === Symmetric Paths === 2 | 3 | A symmetric path is a convenient short hand notation, e.g.: 4 | 5 | and[not] 6 | 7 | This is equal to the same expression as an asymmetric path: 8 | 9 | and[not x not -> not] 10 | 11 | The benefit of symmetric paths is that the number of arguments do not matter. 12 | 13 | Composition rules of symmetric paths is the same as for normal composition: 14 | 15 | f[g][h] <=> f[h . g] 16 | 17 | Most of generic path semantics is first defined for symmetric paths, 18 | because the more general case of asymmetric paths is a simple modification. 19 | 20 | Many useful laws in mathematics are expressible as symmetric paths: 21 | 22 | and[not] <=> or or[not] <=> and DeMorgan's laws 23 | add[exp] <=> mul mul[ln] <=> add Exponentiation and logarithm 24 | mul_mat[det] <=> mul Matrices and determinants 25 | concat[len] <=> add Lists and lengths 26 | -------------------------------------------------------------------------------- /assets/help/goal.txt: -------------------------------------------------------------------------------- 1 | === Goal Oriented Theorem Proving === 2 | 3 | A goal is an expression you would like to find. 4 | 5 | For example, to prove that `1 + 1 = 2`, type `goal 2` and then `1+1`. 6 | You can also type `goal 1+1` and then `2`. 7 | 8 | When the goal is not found by reduction, 9 | it is searched for among equivalent expressions up to some depth. 10 | You can control the depth using `reset depth` and `inc depth`. 11 | 12 | For example: 13 | 14 | goal x+y+z 15 | z+y+x 16 | 17 | The equivalence `depth: 2 <=> (x + (z + y))` shows that the goal is found. 18 | It takes 3 more steps to complete the proof, where `depth: 0` is the last step. 19 | 20 | - `min depth` sets automated minimum depth toward goal 21 | - `pick depth` sets user chooses equivalence toward goal 22 | 23 | Use `reset goal` to go back to default mode of theorem proving. 24 | 25 | When the goal can not be reached automatically, 26 | the Levenshtein distance is shown as heuristic information, e.g. `20 lev`. 27 | -------------------------------------------------------------------------------- /assets/help/imag.txt: -------------------------------------------------------------------------------- 1 | === Imaginary Number === 2 | 3 | The imaginary number `sqrt(-1)` is written in Poi as `imag` or `𝐢`. 4 | In the form `(a + b * imag)` one gets the theory of complex numbers. 5 | 6 | In mathematics, complex numbers usually refers the form `(a + b * imag)`, 7 | but in general there are other bases and algebras, such as dual numbers. 8 | 9 | Complex numbers are very useful because they naturally represent circle-like 10 | operations and provides solutions to equations when there are no real solutions. 11 | One can use vectors and matrices instead of complex numbers, which is common, 12 | so it is a good idea to learn how to translate math between the two forms. 13 | 14 | When you multiply two complex numbers in vector form, you use the formula 15 | `mulc([a0, b0], [a1, b1])`, but in the form `(a + b * imag)` you can use 16 | normal multiplication `((a0 + b0 * imag) * (a1 + b1 * imag))`. 17 | 18 | Poi understands e.g. `(2 + 3imag)` or `(2 + 3𝐢)` as `(2 + 3 * imag)`. 19 | -------------------------------------------------------------------------------- /assets/help/triv.txt: -------------------------------------------------------------------------------- 1 | === Trivial Paths === 2 | 3 | Make sure you understand `help dom` before you read this. 4 | 5 | A trivial path is a function, returning a boolean, that describes whether 6 | some input is allowed in the domain constraint or not. 7 | 8 | For example, in `and{eqb}`, the trivial path is `eqb`. 9 | If you type `triv(and{eqb})`, Poi will reduce this to `eqb`. 10 | Alternative syntaxes are `∀(and{eqb})` or `dom(and{eqb})`. 11 | 12 | It is called "trivial" because its sibling "the existential path", is so hard 13 | to find that it is an undecidable problem in general. 14 | For all total functions, the trivial path is just `\true`. 15 | 16 | The name "trivial path" is to distinguish from other ambiguous uses of 17 | function domains. For example, a function domain can be described indirectly. 18 | However, in most cases, you can just think about it as the domain. 19 | 20 | If you worry about the name, don't worry. 21 | You can call it whatever you like, because the standard notation is `∀f`. 22 | The symbol `∀` is also used as for-all quantifier in first-order logic, 23 | but here is occurs without a body and can be parsed unambiguously. 24 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 AdvancedResearch 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /assets/help/ex.txt: -------------------------------------------------------------------------------- 1 | === Existential Paths === 2 | 3 | Make sure you understand `help dom` and `help triv` before you read this. 4 | 5 | An existential path is a function, returning a boolean, which tells you whether 6 | a function outputs some value. Existential paths can also be defined for other 7 | mathematical languages than functions, but this is irrelevant for using Poi. 8 | 9 | In most cases, one can think about the existential path as the function codomain. 10 | 11 | If you type `ex(and)`, then Poi will reduce this to `true1`. 12 | Alternative syntaxes are `∃(and)` or `codom(and)`. 13 | 14 | The `true1` function has the type `bool -> bool` and returns `true` for all inputs. 15 | When you ask it "does `and` return `false`?" it answers `true`. 16 | When you ask it "does `and` return `true`?" it answers `true`. 17 | 18 | Its sibling "the trivial path" changes the function identity, so in order to 19 | know what a function outputs, one must first define what the input means. 20 | The Halting Problem (https://en.wikipedia.org/wiki/Halting_problem) is 21 | undecidable because if you do not define exactly what the input is, 22 | then it is impossible to say what the function will return. 23 | Similarly, finding the existential path in general is undecidable. 24 | -------------------------------------------------------------------------------- /assets/help/catus.txt: -------------------------------------------------------------------------------- 1 | === Catuṣkoṭi Existential Lift === 2 | 3 | Link to paper: https://github.com/advancedresearch/path_semantics/blob/master/papers-wip2/catuskoti-existential-lift.pdf 4 | 5 | Catuṣkoṭi is 4-value logic that was developed in traditional Indian logic. 6 | 7 | lifted existential path 8 | - true => idb 9 | - false => not 10 | - both => true 11 | - neither => false 12 | 13 | A "truth value" in Catuṣkoṭi is 1 of these four statements. 14 | Each truth value maps to an existential path of type `bool -> bool`. 15 | 16 | Catuṣkoṭi Existential Lift means that equations of the form: 17 | 18 | f(x) = 19 | 20 | Is lifted to the following form: 21 | 22 | ∃(f{(= x)}) = 23 | 24 | Since existential paths might be hard to wrap your head around, 25 | one can use Catuṣkoṭi existential lift to simplify theorem proving 26 | when reasoning about indeterminacy. 27 | 28 | Indeterminacy happens under non-determinism, 29 | but also over normal paths that depends on the input, e.g.: 30 | 31 | collatz[even](true) = both 32 | 33 | It means that the normal path `collatz[even]` is not inhabited by 34 | the type `type -> type`. However, the existential path is still meaningful! 35 | -------------------------------------------------------------------------------- /assets/help/asym.txt: -------------------------------------------------------------------------------- 1 | === Asymmetric Paths === 2 | 3 | You can write asymmetric paths, e.g. `not . and[not x id -> id]`. 4 | This will reduce to `and[not ⨯ id → not]`. 5 | 6 | An asymmetric path consists of two parts: 7 | 8 | - The functions that are used to transform the input, e.g. `not x id` 9 | - The function that is used to transform the output, e.g. `id` 10 | 11 | The arrow `->` is used to distinguish between the two parts. 12 | 13 | When only `id` is used to transform the inputs, one gets function composition: 14 | 15 | f[id x id -> g] <=> g . f 16 | 17 | The most difficult thing to understand about asymmetric paths is when some 18 | function, that is not `id`, is used to transform the input. 19 | This means that some information is removed or added to the input. 20 | 21 | When some information is added, it happens inside the path: 22 | 23 | f[g(a) -> id] 24 | 25 | When there are no arguments inside the path and the function is not `id`, 26 | it means that some information is removed. 27 | If the normal path exists when mapping to `id`, then it is a proof that there 28 | was some information in the input was not needed to compute the output. 29 | 30 | The only way one can have a normal path that uses all the input information, 31 | is by mapping to some function that is not `id`: 32 | 33 | f[g0 -> g1] 34 | 35 | This contracts `f` along the path such that: 36 | 37 | f[g0 -> g1] . g0 <=> g1 . f 38 | 39 | Notice that `f[g0 -> g1]` does not need to be solved in order to be sound. 40 | It is like complex roots of polynomials, they are always defined. 41 | In order to convert it into a real program, one must find the solution. 42 | -------------------------------------------------------------------------------- /src/knowledge.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use super::*; 4 | 5 | /// Represents knowledge about symbols. 6 | #[derive(Clone, Debug)] 7 | pub enum Knowledge { 8 | /// A symbol has some definition. 9 | Def(Symbol, Expr), 10 | /// A reduction from a more complex expression into another by normalization. 11 | Red(Expr, Expr), 12 | /// Two expressions that are equivalent but neither normalizes the other. 13 | Eqv(Expr, Expr), 14 | /// Two expressions that are equivalent but evaluates from left to right. 15 | EqvEval(Expr, Expr), 16 | } 17 | 18 | impl fmt::Display for Knowledge { 19 | fn fmt(&self, w: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> { 20 | use Knowledge::*; 21 | 22 | let mut display = |op: &str, a: &Expr, b: &Expr| -> std::result::Result<(), fmt::Error> { 23 | let rule = true; 24 | let parens = false; 25 | a.display(w, parens, rule)?; 26 | write!(w, " {} ", op)?; 27 | b.display(w, parens, rule)?; 28 | Ok(()) 29 | }; 30 | match self { 31 | Def(a, b) => { 32 | let rule = true; 33 | let parens = false; 34 | a.display(w, rule)?; 35 | write!(w, " := ")?; 36 | b.display(w, parens, rule)?; 37 | } 38 | Red(a, b) => display("=>", a, b)?, 39 | Eqv(a, b) => display("<=>", a, b)?, 40 | EqvEval(a, b) => display("<=>>", a, b)?, 41 | } 42 | Ok(()) 43 | } 44 | } 45 | 46 | impl<'a> From<&'a str> for Knowledge { 47 | fn from(val: &'a str) -> Knowledge { 48 | match parse_data_str(val, &[]) { 49 | Ok(ParseData::Knowledge(k)) => k[0].clone(), 50 | Ok(ParseData::Expr(_)) => panic!("Expected knowledge, found expression"), 51 | Err(err) => panic!("ERROR:\n{}", err), 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /source/collatz/script.poi: -------------------------------------------------------------------------------- 1 | // # Collatz 2 | // 3 | // To run, type `cat source/collatz/script.poi | poi` in the Terminal. 4 | 5 | collatz := (if((/ 2))(inc · (* 3)) · even) · dup 6 | 7 | goal true 8 | collatz[even](true) = both 9 | inline collatz 10 | 11 | goal false 12 | collatz[even](true) = neither 13 | inline collatz 14 | 15 | goal false 16 | collatz[even](true) = true 17 | inline collatz 18 | subgoal ∃(((if((/ 2))(inc · (* 3)) · even) · dup)[even]{(= true)}) = idb 19 | repeat 20 | 21 | goal false 22 | collatz[even](true) = false 23 | inline collatz 24 | subgoal ∃(((if((/ 2))(inc · (* 3)) · even) · dup)[even]{(= true)}) = not 25 | repeat 26 | 27 | goal false 28 | collatz[even](false) = both 29 | inline collatz 30 | subgoal ∃(((if((/ 2))(inc · (* 3)) · even) · dup)[even]{(= false)}) = true 31 | subgoal ∃(((if((/ 2))(inc · (* 3)) · even) · dup)[even]{not}) = true 32 | subgoal ∃(even · (inc · (* 3)){not · even}) = true 33 | subgoal ∃(even · (inc · (* 3){odd})) = true 34 | subgoal ∃((even · inc) · (* 3){odd}) = true 35 | repeat 36 | 37 | goal false 38 | collatz[even](false) = neither 39 | inline collatz 40 | subgoal ∃(((if((/ 2))(inc · (* 3)) · even) · dup)[even]{(= false)}) = false 41 | subgoal ∃(((if((/ 2))(inc · (* 3)) · even) · dup)[even]{not}) = false 42 | subgoal ∃(even · (inc · (* 3)){not · even}) = false 43 | subgoal ∃(even · (inc · (* 3){odd})) = false 44 | subgoal ∃((even · inc) · (* 3){odd}) = false 45 | repeat 46 | 47 | goal true 48 | collatz[even](false) = true 49 | inline collatz 50 | subgoal ∃(((if((/ 2))(inc · (* 3)) · even) · dup)[even]{(= false)}) = idb 51 | subgoal ∃(((if((/ 2))(inc · (* 3)) · even) · dup)[even]{not}) = id 52 | subgoal ∃(even · (inc · (* 3)){not · even}) = id 53 | subgoal ∃(even · (inc · (* 3){odd})) = id 54 | subgoal ∃((even · inc) · (* 3){odd}) = id 55 | subgoal ∃((inc[even] · even) · (* 3){odd}) = id 56 | repeat 57 | 58 | goal false 59 | collatz[even](false) = false 60 | inline collatz 61 | subgoal ∃(((if((/ 2))(inc · (* 3)) · even) · dup)[even]{(= false)}) = not 62 | subgoal ∃(((if((/ 2))(inc · (* 3)) · even) · dup)[even]{not}) = not 63 | subgoal ∃(even · (inc · (* 3)){not · even}) = not 64 | subgoal ∃(even · (inc · (* 3){odd})) = not 65 | subgoal ∃((even · inc) · (* 3){odd}) = not 66 | repeat 67 | -------------------------------------------------------------------------------- /assets/help/help.txt: -------------------------------------------------------------------------------- 1 | === Poi Reduce Help === 2 | Made by Sven Nilsen, 2020 3 | 4 | Poi is based on the theory of path semantics: 5 | https://github.com/advancedresearch/path_semantics 6 | 7 | Special commands: 8 | - bye quits the program 9 | - inline inlines definition from previous expression 10 | - inline all inlines all definitions from previous expression 11 | - `` prints separator for readability 12 | - ` ` try again with last expression (alternative: `repeat`) 13 | - eval evaluates expression 14 | - echo prints out debug format of expression 15 | - goal set expression as new goal 16 | - reset goal reset goal 17 | - subgoal set expression as new subgoal 18 | - auto prove with automated minimum Levenshtein distance 19 | - lev pick minimum Levenshtein distance 20 | - auto lev automated minimum Levenshtein distance 21 | - inc depth increase search depth for goal 22 | - reset depth reset search depth 23 | - min depth automated minimum depth toward goal 24 | - pick depth user chooses equivalence toward goal 25 | - def lists definitions of symbol 26 | - std lists all rules in standard library 27 | - open "" add Poi directory 28 | - clear dir clear Poi directories 29 | - .poi load Poi file as expression 30 | - help goal more help about goal oriented theorem proving 31 | - help norm more help about normal paths 32 | - help sym more help about symmetric paths 33 | - help asym more help about asymmetric paths 34 | - help eqv more help about equivalent expressions 35 | - help dom more help about domains and partial functions 36 | - help triv more help about trivial paths 37 | - help ex more help about existential paths 38 | - help rad more help about radians (trigonometry) 39 | - help imag more help about imaginary number (complex numbers) 40 | - help eps more help about epsilon (dual numbers) 41 | - help deriv more help about derivative 42 | - help integ more help about integral 43 | - help list more help about lists 44 | - help symbol more help about symbols vs variables 45 | - help script more help about scripts 46 | - help catus more help about Catuṣkoṭi existential lift 47 | 48 | Type in an expression in path semantics, e.g. `and[not]` 49 | -------------------------------------------------------------------------------- /src/arity.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | impl Symbol { 4 | /// Returns the arity of a symbol. 5 | pub fn arity(&self) -> Option { 6 | match self { 7 | VecType | AnyType | RetType | Pi | Tau | Eps | Imag | Imag2 | Imag3 | 8 | BoolType | F64Type | QuatType | Inf => Some(0), 9 | 10 | Dup | Not | Idb | Id | Inv | Neg | Inc | Reci | Arity | 11 | False1 | True1 | Even | Odd | Abs | Conj | 12 | Norm | Sqnorm | Sqrt | Ln | Log2 | Log10 | Exp | Len | 13 | Sum | Min | Max | ArgMax | ArgMin | SoftMax | SoftMin | Prob | Probl | Probr | Probm | 14 | Det | Transpose | Dim | IsSquareMat | Sin | Asin | Cos | Acos | Tan | Atan | 15 | Re | Im | Ex | Triv | TypeOf | Pariv => Some(1), 16 | 17 | Eq | Eqb | And | Or | Nand | Nor | Xor | Exc | 18 | Add | Mul | Div | Rdiv | Sub | Rsub | Rem | False2 | True2 | 19 | Imply | Fstb | Sndb | Lt | Rlt | Le | Rle | Gt | Rgt | 20 | Ge | Rge | Mulc | Pow | Rpow | Concat | Min2 | Max2 | 21 | MulMat | Base | Fst | Snd | Atan2 | Dot | Push | PushFront | 22 | Item | Col | Neq | Deriv | Rty | VecUop | SoftArgMax | SoftArgMin => Some(2), 23 | 24 | Range | Rangel | Ranger | Rangem | El | If | VecOp | Integ | Subst => Some(3), 25 | 26 | Any | Var(_) | ArityVar(_, _) | ListVar(_) | 27 | Singleton(_) | HeadTailTup(_, _) | HeadTailList(_, _) | 28 | RetVar(_) | RetIntVar(_) | RetPosVar(_) | RetStrictPosVar(_) | RetNegVar(_) | 29 | NotRetVar(_) | NotInVarName(_, _) | 30 | BinopRetVar(_, _, _) | TernopRetVar(_, _, _, _) | UnopRetVar(_, _) | 31 | NoConstrVar(_) | NoSubstVar(_) => None, 32 | 33 | Custom(_) => None, 34 | 35 | Both | Neither => None, 36 | 37 | // _ => None 38 | } 39 | } 40 | } 41 | 42 | impl Expr { 43 | /// Returns the arity of an expression. 44 | /// 45 | /// Unfinished: This function requires analysis and unit testing. 46 | pub fn arity(&self) -> Option { 47 | match self { 48 | Sym(s) => s.arity(), 49 | EOp(Apply, x, y) => { 50 | match (&**x, &**y) { 51 | (Sym(Rty), Sym(VecType)) => Some(1), 52 | (Sym(s), Ret(_)) => { 53 | if let Some(arity) = s.arity() { 54 | if arity >= 1 {Some(arity - 1)} 55 | else {Some(0)} 56 | } else { 57 | None 58 | } 59 | } 60 | _ => None 61 | } 62 | } 63 | EOp(Compose, _, y) => y.arity(), 64 | _ => None, 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/matrix.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | pub fn transpose(a: &Vec) -> Result { 4 | let mut res = vec![]; 5 | let cols = if let Some(a) = a.get(0) { 6 | if let List(a) = a { 7 | a.len() 8 | } else { 9 | return Err(Error::InvalidComputation); 10 | } 11 | } else { 12 | return Err(Error::InvalidComputation); 13 | }; 14 | if a.len() != cols { 15 | return Err(Error::InvalidComputation); 16 | } 17 | for i in 0..a.len() { 18 | let mut row = vec![]; 19 | for j in 0..cols { 20 | if let Some(List(a)) = a.get(j) { 21 | if let Some(el) = a.get(i) { 22 | row.push(el.clone()); 23 | } else { 24 | return Err(Error::InvalidComputation); 25 | } 26 | } else { 27 | return Err(Error::InvalidComputation); 28 | } 29 | } 30 | res.push(List(row)); 31 | } 32 | Ok(List(res)) 33 | } 34 | 35 | pub fn is_square_mat(a: &Vec) -> Result { 36 | let n = a.len(); 37 | let mut sq = true; 38 | for i in 0..n { 39 | if let List(b) = &a[i] { 40 | if b.len() != n { 41 | sq = false; 42 | break; 43 | } 44 | } else { 45 | sq = false; 46 | break; 47 | } 48 | } 49 | Ok(Ret(Bool(sq))) 50 | } 51 | 52 | pub fn dim(a: &Vec) -> Result { 53 | let n = a.len(); 54 | let mut m: Option = None; 55 | for i in 0..n { 56 | if let List(b) = &a[i] { 57 | if let Some(m) = m { 58 | if b.len() != m { 59 | return Err(Error::InvalidComputation); 60 | } 61 | } else { 62 | m = Some(b.len()); 63 | } 64 | } else { 65 | return Err(Error::InvalidComputation); 66 | } 67 | } 68 | if let Some(m) = m { 69 | Ok(List(vec![(n as f64).into(), (m as f64).into()])) 70 | } else { 71 | return Err(Error::InvalidComputation); 72 | } 73 | } 74 | 75 | pub fn col(a: f64, b: &Vec) -> Result { 76 | let mut res = vec![]; 77 | for bi in b { 78 | if let List(bi) = bi { 79 | if let Some(bi) = bi.get(a as usize) { 80 | res.push(bi.clone()); 81 | } else { 82 | return Err(Error::InvalidComputation); 83 | } 84 | } else { 85 | return Err(Error::InvalidComputation); 86 | } 87 | } 88 | Ok(List(res)) 89 | } 90 | 91 | pub fn mul_mat(a: &Vec, b: &Vec) -> Result { 92 | let mut res = vec![]; 93 | let cols = if let Some(b) = b.get(0) { 94 | if let List(b) = b { 95 | b.len() 96 | } else { 97 | return Err(Error::InvalidComputation); 98 | } 99 | } else { 100 | return Err(Error::InvalidComputation); 101 | }; 102 | for i in 0..a.len() { 103 | let mut row = vec![]; 104 | for j in 0..cols { 105 | if let Some(a) = a.get(i) { 106 | row.push(app(Sum, 107 | app2(Mul, a.clone(), col(j as f64, b)?) 108 | )); 109 | } else { 110 | return Err(Error::InvalidComputation); 111 | } 112 | } 113 | res.push(List(row)); 114 | } 115 | Ok(List(res)) 116 | } 117 | -------------------------------------------------------------------------------- /assets/syntax.txt: -------------------------------------------------------------------------------- 1 | _seps: "[]()\\;:,.·⨯-→{}<>=!+-*/%^&|" 2 | 3 | 200 multi_line_comment = ["/*" ..."*/"? .r?({ 4 | [!"*/" "*" ..."*/"?] [multi_line_comment ..."*/"?] ["/" ..."*/"?] 5 | }) "*/"] 6 | 201 comment = {multi_line_comment ["//" ..."\n"?]} 7 | 8 | 33 not_in_var_name = [.._seps!:"x" "!>" .._seps!:"y"] 9 | 32 head_tail_list = ["[" .w? .._seps!:"head" .w? "," .w? .._seps!:"tail" ".." .w? "]"] 10 | 31 head_tail_tup = ["(" .w? .._seps!:"head" .w? "," .w? .._seps!:"tail" ".." .w? ")"] 11 | 30 compute = ["compute::" .._seps!:"fun" "(" .w? 12 | .s!([.w? "," .w?] .._seps!:"arg") .w? ")"] 13 | 29 arity = [.._seps!:"fun" ":[arity]" .$:"arg"] 14 | 28 seq_left = { 15 | arity:"arity" 16 | no_constr:"no_constr" 17 | no_subst:"no_subst" 18 | not_in_var_name:"not_in_var_name" 19 | var 20 | val 21 | } 22 | 27 no_subst = [.._seps!:"fun" ":!subst"] 23 | 26 no_constr = [.._seps!:"fun" ":!{}"] 24 | 23 knowledge = [top_expr:"left" {"<=>>":"eqveval" "<=>":"eqv" 25 | "=>":"red" ":=":"def"} top_expr:"right"] 26 | 22 top_expr = { 27 | [.w? raw_expr .w?] 28 | [.w? expr .w?] 29 | } 30 | 22 alg_prefix = [{ 31 | "-":"neg" 32 | ["∂":"pariv" !"(" !"^" !")"] 33 | ["!":"not" !["\\" !"true" !"false"]] 34 | "¬":"not" 35 | } !.w! !.$] 36 | 21 alg3 = [?alg_prefix expr:"alg_item" ?[.w? "^":"^" .w? expr:"alg_item"]] 37 | 20 alg2 = .s!([.w? {"*":"*" "/":"/" "%":"%" "&":"&" "∧":"&"} .w?] alg3:"alg") 38 | 19 alg1 = .s!([.w? {"++":"++" "+":"+" "-":"-" "|":"|" "∨":"|"} .w?] alg2:"alg") 39 | 18 alg = ["(" .w? alg_inner .w? ")"] 40 | 17 alg_inner = .s!([.w? {"<=":"<=" "<":"<" "==":"=" "=":"=" ">=":">=" 41 | ">":">"} .w!] alg1:"alg") 42 | 16 rapp = ["(" .w? { 43 | ":":"rty" 44 | "<=":"rle" 45 | "<":"rlt" 46 | "=":"eq" 47 | ">=":"rge" 48 | ">":"rgt" 49 | "*":"mul" 50 | "/":"rdiv" 51 | "+":"add" 52 | "-":"rsub" 53 | "^":"rpow" 54 | } .w! {raw_expr:"arg" expr:"arg"} .w? ")"] 55 | 15 list = ["[" .w? .s?([.w? "," .w?] {raw_expr:"item" expr:"item"}) .w? "]"] 56 | 14 tup_items_expr = tup_items:"tup" 57 | 13 tup_items = [.s!([.w? "," .w?] {raw_expr:"item" expr:"item"})] 58 | 12 tup_path_expr = tup_path:"tup" 59 | 11 tup_path = [.s!([.w? {"x" "⨯"} .w?] expr:"item") .w? {"->" "→"} .w? expr:"item"] 60 | 10 val = { 61 | [.._seps!:"poi" ".poi"] 62 | [?"\\" { 63 | ["true":"bool" !.$] 64 | ["false":!"bool" !.$] 65 | [.$_:"num_pi" .w? {"pi" "π"}] 66 | [.$_:"num_tau" .w? {"tau" "τ"}] 67 | [.$_:"num_eps" {[.w! "eps"] [.w? "ε"]}] 68 | [.$_:"num_imag3" {[.w? "imag3"] [.w? "𝐢₃"]}] 69 | [.$_:"num_imag2" {[.w? "imag2"] [.w? "𝐢₂"]}] 70 | [.$_:"num_imag" {[.w? "imag"] [.w? "𝐢"]}] 71 | .$_:"num" 72 | }] 73 | ["\\[" .._seps!:"singleton" "]"] 74 | ["[" .._seps!:"list_var" "..]"] 75 | ["!\\" .._seps!:"not_ret_var"] 76 | ["\\" .._seps!:"ret_int_var" ":int"] 77 | ["\\" .._seps!:"ret_pos_var" ":(>= 0)"] 78 | ["\\" .._seps!:"ret_neg_var" ":(< 0)"] 79 | ["\\" .._seps!:"ret_var"] 80 | } 81 | 9 var = [!.$ .._seps!:"var" ?[.w? ":" .w? "\\":"ret_ty"]] 82 | 8 path_right = [.w? "[" .w? {tup_path_expr expr} .w? "]"] 83 | 7 app_right = [.w? "(" .w? {tup_items_expr expr} .w? ")"] 84 | 6 comp_right = [.w? {"." "·"} .w! expr] 85 | 5 typ_right = [.w? ":" .w? {"\\":"ret_ty" expr}] 86 | 4 constr_right = [.w? "{" .w? {tup_items_expr expr} .w? "}"] 87 | 3 seq = [{ 88 | rapp:"rapp" 89 | ["(" .w? tup_items_expr:"left" .w? ")"] 90 | alg:"alg" 91 | list:"list" 92 | seq_left:"left" 93 | } .r!({ 94 | path_right:"path" 95 | app_right:"app" 96 | constr_right:"constr" 97 | comp_right:"comp" 98 | typ_right:"typ" 99 | })] 100 | 2 expr = { 101 | seq:"seq" 102 | rapp:"rapp" 103 | head_tail_tup:"head_tail_tup" 104 | head_tail_list:"head_tail_list" 105 | ["(" .w? tup_items_expr .w? ")"] 106 | alg:"alg" 107 | list:"list" 108 | compute:"compute" 109 | arity:"arity" 110 | no_constr:"no_constr" 111 | no_subst:"no_subst" 112 | not_in_var_name:"not_in_var_name" 113 | val 114 | var 115 | } 116 | 1 raw_expr = alg_inner:"alg" 117 | 0 doc = { 118 | ["#" .l([.l([!"```poi" ..."\n"?]) "```poi" .w? 119 | .s!.([.w? ";" .w?] {comment knowledge:"knowledge"}) .w? "```"])] 120 | .s!.([.w? ";" .w?] {comment knowledge:"knowledge"}) 121 | top_expr:"expr" 122 | } 123 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Poi 3 | a pragmatic point-free theorem prover assistant 4 | 5 | [Standard Library](./assets/std.md) 6 | 7 | ```text 8 | === Poi 0.24 === 9 | Type `help` for more information. 10 | > and[not] 11 | and[not] 12 | or 13 | ∵ and[not] => or 14 | <=> (not · and) · (not · fst, not · snd) 15 | ∵ (not · and) · (not · fst, not · snd) <=> or 16 | <=> not · nor 17 | ∵ not · nor <=> or 18 | ∴ or 19 | ``` 20 | 21 | Poi uses a mathematical knowledge base to do theorem proving 22 | automatically, but also shows alternatives to you. 23 | 24 | - `∵` means "because" 25 | - `∴` means "therefore" 26 | - `<=>` an alternative 27 | 28 | To run Poi Reduce from your Terminal, type: 29 | 30 | ```text 31 | cargo install --example poi poi 32 | ``` 33 | 34 | Then, to run: 35 | 36 | ```text 37 | poi 38 | ``` 39 | 40 | You can use `help` to learn more about commands in Poi and the theory behind it. 41 | 42 | Download and check it out! (Think about it as a tool to learn by doing) 43 | 44 | Also, check out the FAQ on Path Semantics: 45 | 46 | - [What is the overall problem?](https://github.com/advancedresearch/path_semantics/blob/master/faq.md#what-is-the-overall-problem) 47 | - [Why normal paths?](https://github.com/advancedresearch/path_semantics/blob/master/faq.md#why-normal-paths) 48 | 49 | ### Example: Length of concatenated lists 50 | 51 | Poi lets you specify a goal and automatically prove it. 52 | 53 | For example, when computing the length of two concatenated lists, 54 | there is a faster way, which is to compute the length of each list and add them together: 55 | 56 | ```text 57 | > goal len(a)+len(b) 58 | new goal: (len(a) + len(b)) 59 | > len(a++b) 60 | len((a ++ b)) 61 | depth: 1 <=> (len · concat)(a)(b) 62 | ∵ (f · g)(a)(b) <=> f(g(a)(b)) 63 | . 64 | (len · concat)(a)(b) 65 | (concat[len] · (len · fst, len · snd))(a)(b) 66 | ∵ len · concat => concat[len] · (len · fst, len · snd) 67 | (add · (len · fst, len · snd))(a)(b) 68 | ∵ concat[len] => add 69 | depth: 0 <=> ((len · fst)(a)(b) + (len · snd)(a)(b)) 70 | ∵ (f · (g0, g1))(a)(b) <=> f(g0(a)(b))(g1(a)(b)) 71 | ((len · fst)(a)(b) + (len · snd)(a)(b)) 72 | (len(a) + (len · snd)(a)(b)) 73 | ∵ (f · fst)(a)(_) => f(a) 74 | (len(a) + len(b)) 75 | ∵ (f · snd)(_)(a) => f(a) 76 | ∴ (len(a) + len(b)) 77 | Q.E.D. 78 | ``` 79 | 80 | The notation `concat[len]` is a "normal path", 81 | which lets you transform into a more efficient program. 82 | Normal paths are composable and point-free, 83 | unlike their equational representations. 84 | 85 | ### Example: Levenshtein proof search 86 | 87 | For deep automated theorem proving, Poi uses Levenshtein distance heuristic. 88 | This is simply the minimum single-character edit distance in text representation. 89 | 90 | Try the following: 91 | 92 | ```text 93 | > goal a + b + c + d 94 | > d + c + b + a 95 | > auto lev 96 | ``` 97 | 98 | The command `auto lev` tells Poi to automatically pick the equivalence with 99 | smallest Levenshtein distance found in any sub-proof. 100 | 101 | ### Introduction to Poi and Path Semantics 102 | 103 | In "point-free" or "tacit" programming, functions do not identify the arguments 104 | (or "points") on which they operate. See [Wikipedia article](https://en.wikipedia.org/wiki/Tacit_programming). 105 | 106 | Poi is an implementation of a small subset of [Path Semantics](https://github.com/advancedresearch/path_semantics). 107 | In order to explain how Poi works, one needs to explain a bit about Path Semantics. 108 | 109 | [Path Semantics](https://github.com/advancedresearch/path_semantics) is an extremely expressive language for mathematical programming, 110 | which has a "path-space" in addition to normal computation. 111 | If normal programming is 2D, then Path Semantics is 3D. 112 | Path Semantics is often used in combination with [Category Theory](https://en.wikipedia.org/wiki/Category_theory), [Logic](https://en.wikipedia.org/wiki/Logic), etc. 113 | 114 | A "path" (or "normal path") is a way of navigating between functions, for example: 115 | 116 | ```text 117 | and[not] <=> or 118 | ``` 119 | 120 | Translated into words, this sentence means: 121 | 122 | ```text 123 | If you flip the input and output bits of an `and` function, 124 | then you can predict the output directly from the input bits 125 | using the function `or`. 126 | ``` 127 | 128 | In normal programming, there is no way to express this idea directly, 129 | but you can represent the logical relationship as an equation: 130 | 131 | ```text 132 | not(and(a, b)) = or(not(a), not(b)) 133 | ``` 134 | 135 | This is known as one of [De Morgan's laws](https://en.wikipedia.org/wiki/De_Morgan's_laws). 136 | 137 | When represented as a commutative diagram, one can visualize the dimensions: 138 | 139 | ```text 140 | not x not 141 | o ---------> o o -------> path-space 142 | | | | x 143 | and | | or | x 144 | V V | x 145 | o ---------> o V x - Sets are points 146 | not computation 147 | ``` 148 | 149 | Computation and paths is like complex numbers 150 | where the "real" part is computation and 151 | the "imaginary" part is the path. 152 | 153 | This is written in asymmetric path notation: 154 | 155 | ```text 156 | and[not x not -> not] <=> or 157 | ``` 158 | 159 | In symmetric path notation: 160 | 161 | ```text 162 | and[not] <=> or 163 | ``` 164 | 165 | Both computation and path-space are directional, 166 | meaning that one can not always find the inverse. 167 | Composition in path-space is just function composition: 168 | 169 | ```text 170 | f[g][h] <=> f[h . g] 171 | ``` 172 | 173 | If one imagines `computation = 2D`, then `computation + path-space = 3D`. 174 | 175 | Path Semantics can be thought of as "point-free style" sub-set of equations. 176 | This sub-set of equations is particularly helpful in programming. 177 | 178 | ### Design of Poi 179 | 180 | Poi is designed to be used as a Rust library. 181 | 182 | It means that anybody can create their own tools on top of Poi, 183 | without needing a lot of dependencies. 184 | 185 | Poi uses primarily rewriting-rules for theorem proving. 186 | This means that the core design is "stupid" and will do dumb things like running 187 | in infinite loops when given the wrong rules. 188 | 189 | However, this design makes also Poi very flexible, because it can pattern match 190 | in any way, independent of computational direction. 191 | It is relatively easy to define such rules in Rust code. 192 | 193 | #### Syntax 194 | 195 | Poi uses [Piston-Meta](https://github.com/pistondevelopers/meta) to describe its syntax. Piston-Meta is a meta parsing language for human readable text documents. 196 | It makes it possible to easily make changes to Poi's grammar, 197 | and also preserve backward compatibility. 198 | 199 | Since Piston-Meta can describe its own grammar rules, it means that future 200 | versions of Piston-Meta can parse grammars of old versions of Poi. 201 | The old documents can then be transformed into new versions of Poi using synthesis. 202 | 203 | #### Core Design 204 | 205 | At the core of Poi, there is the `Expr` structure: 206 | 207 | ```rust(ignore) 208 | use poi::*; 209 | 210 | /// Function expression. 211 | #[derive(Clone, PartialEq, Debug)] 212 | pub enum Expr { 213 | /// A symbol that is used together with symbolic knowledge. 214 | Sym(Symbol), 215 | /// Some function that returns a value, ignoring the argument. 216 | /// 217 | /// This can also be used to store values, since zero arguments is a value. 218 | Ret(Value), 219 | /// A binary operation on functions. 220 | EOp(Op, Box, Box), 221 | /// A tuple for more than one argument. 222 | Tup(Vec), 223 | /// A list. 224 | List(Vec), 225 | } 226 | ``` 227 | 228 | The simplicity of the `Expr` structure is important and heavily based on 229 | advanced path semantical knowledge. 230 | 231 | A symbol contains every domain-specific symbol and generalisations of symbols. 232 | 233 | The `Ret` variant comes from the notation used in [Higher Order Operator Overloading](https://github.com/advancedresearch/path_semantics/blob/master/sequences.md#higher-order-operator-overloading). Instead of describing a value as value, 234 | it is thought of as a function of some unknown input type, which returns a known value. For example, if a function returns `2` for all inputs, this is written `\2`. 235 | This means that point-free transformations on functions sometimes can compute stuff, without explicitly needing to reference the concrete value directly. 236 | See paper [Higher Order Operator Overloading and Existential Path Equations](https://github.com/advancedresearch/path_semantics/blob/master/papers-wip/higher-order-operator-overloading-and-existential-path-equations.pdf) for more information. 237 | 238 | The `EOp` variant generalizes binary operators on functions, 239 | such as `Composition`, `Path` (normal path), 240 | `Apply` (call a function) and `Constrain` (partial functions). 241 | 242 | The `Tup` variant represents tuples of expressions, where a singleton (a tuple of one element) is 243 | "lifted up" one level. This is used e.g. to transition from `and[not x not -> not]` to `and[not]` without having to write rules for asymmetric cases. 244 | 245 | The `List` variant represents lists of expressions, e.g. `[1, 2, 3]`. 246 | This differs from `Tup` by the property that singletons are not "lifted up". 247 | 248 | #### Representing Knowledge 249 | 250 | In higher dimensions of functional programming, the definition of "normalization" 251 | depends on the domain specific use of a theory. Intuitively, since there are 252 | more directions, what counts as progression toward an answer is somewhat 253 | chosen arbitrarily. Therefore, the subjectivity of this choice must be 254 | reflected in the representation of knowledge. 255 | 256 | Poi's representation of knowledge is designed for multi-purposes. 257 | Unlike in normal programming, you do not want to always do e.g. evaluation. 258 | Instead, you design different tools for different purposes, using the same 259 | knowledge. 260 | 261 | The `Knowledge` struct represents mathematical knowledge in form of rules: 262 | 263 | ```rust(ignore) 264 | use poi::{Expr, Symbol}; 265 | 266 | /// Represents knowledge about symbols. 267 | pub enum Knowledge { 268 | /// A symbol has some definition. 269 | Def(Symbol, Expr), 270 | /// A reduction from a more complex expression into another by normalization. 271 | Red(Expr, Expr), 272 | /// Two expressions that are equivalent but neither normalizes the other. 273 | Eqv(Expr, Expr), 274 | /// Two expressions that are equivalent but evaluates from left to right. 275 | EqvEval(Expr, Expr), 276 | } 277 | ``` 278 | 279 | The `Def` variant represents a definition. 280 | A definition is inlined when evaluating an expression. 281 | 282 | The `Red` variant represents what counts as "normalization" in a domain specific theory. 283 | It can use computation in the sense of normal evaluation, or use path-space. 284 | This rule is directional, which means it pattern matches on the first expression 285 | and binds variables, which are synthesized using the second expression. 286 | 287 | The `Eqv` variant represents choices that one can make when traveling along a path. 288 | Going in one direction might be as good as another. 289 | This is used when it is not clear which direction one should go. 290 | This rule is bi-directional, which means one can treat it as a reduction both ways. 291 | 292 | The `EqvEval` variant is similar to `Eqv`, but when evaluating an expression, it 293 | reduces from left to right. This is used on e.g. `sin(τ / 8)`. 294 | You usually want the readability of `sin(τ / 8)` when doing theorem proving. 295 | For example, in Poi Reduce, the value of `sin(τ / 8)` is presented as a choice (equivalence). 296 | When evaluating an expression it is desirable to just replace it with the computed value. 297 | 298 | ### What Poi is not 299 | 300 | Some people hoped that Poi might be used to solve problems 301 | where dependent types are used, but in a more convenient way. 302 | 303 | Although Poi uses ideas from dependent types, it is not suitable for other applications 304 | of dependent types, e.g. verification of programs by applying it to some immediate representation of machine code. 305 | 306 | Normal paths might be used for such applications in the future, 307 | but this might require a different architecture. 308 | 309 | This implementation is designed for algebraic problems: 310 | 311 | - The object model is restricted to dynamical types 312 | - Reductions are balanced with equivalences 313 | 314 | This means that not everything is provable, 315 | because this makes automated theorem proving harder, 316 | something that is required for the necessary depth of algebraic solving. 317 | -------------------------------------------------------------------------------- /src/expr.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::ops::{Add, Sub, Mul}; 3 | 4 | use super::*; 5 | 6 | /// Function expression. 7 | #[derive(Clone, PartialEq, PartialOrd, Debug)] 8 | pub enum Expr { 9 | /// A symbol that is used together with symbolic knowledge. 10 | Sym(Symbol), 11 | /// Some function that returns a value, ignoring the argument. 12 | /// 13 | /// This can also be used to store values, since zero arguments is a value. 14 | Ret(Value), 15 | /// A binary operation on functions. 16 | EOp(Op, Box, Box), 17 | /// A tuple for more than one argument. 18 | Tup(Vec), 19 | /// A list. 20 | List(Vec), 21 | } 22 | 23 | impl Add for Expr { 24 | type Output = Expr; 25 | fn add(self, other: Expr) -> Expr {app2(Add, self, other)} 26 | } 27 | 28 | impl Sub for Expr { 29 | type Output = Expr; 30 | fn sub(self, other: Expr) -> Expr {app2(Sub, self, other)} 31 | } 32 | 33 | impl Mul for Expr { 34 | type Output = Expr; 35 | fn mul(self, other: Expr) -> Expr {app2(Mul, self, other)} 36 | } 37 | 38 | impl Expr { 39 | /// Used to display format with additional options. 40 | pub fn display( 41 | &self, 42 | w: &mut fmt::Formatter<'_>, 43 | parens: bool, 44 | rule: bool, 45 | ) -> std::result::Result<(), fmt::Error> { 46 | match self { 47 | Sym(s) => s.display(w, rule)?, 48 | Ret(v) => write!(w, "{}", v)?, 49 | EOp(Path, a, b) => { 50 | if let Tup(b) = &**b { 51 | let parens = true; 52 | a.display(w, parens, rule)?; 53 | write!(w, "[")?; 54 | for i in 0..b.len() { 55 | if i > 0 { 56 | if i + 1 < b.len() { 57 | write!(w, " ⨯ ")? 58 | } else { 59 | write!(w, " → ")? 60 | } 61 | } 62 | b[i].display(w, true, rule)?; 63 | } 64 | write!(w, "]")? 65 | } else { 66 | a.display(w, true, rule)?; 67 | write!(w, "[")?; 68 | b.display(w, false, rule)?; 69 | write!(w, "]")?; 70 | } 71 | } 72 | EOp(Apply, a, b) => { 73 | let mut r = |op: &str| -> std::result::Result<(), fmt::Error> { 74 | write!(w, "({} ", op)?; 75 | b.display(w, false, rule)?; 76 | write!(w, ")") 77 | }; 78 | if let Sym(Neg) = **a { 79 | if parens { 80 | write!(w, "(")?; 81 | } 82 | write!(w, "-")?; 83 | b.display(w, true, rule)?; 84 | if parens { 85 | write!(w, ")")?; 86 | } 87 | } else if let Sym(Not) = **a { 88 | if parens { 89 | write!(w, "(")?; 90 | } 91 | write!(w, "!")?; 92 | b.display(w, true, rule)?; 93 | if parens { 94 | write!(w, ")")?; 95 | } 96 | } else if let Sym(Rty) = **a { 97 | if let Sym(_) = **b { 98 | r(":")?; 99 | } 100 | } else if let Sym(Rlt) = **a { 101 | r("<")?; 102 | } else if let Sym(Rle) = **a { 103 | r("<=")?; 104 | } else if let Sym(Eq) = **a { 105 | r("=")?; 106 | } else if let Sym(Rgt) = **a { 107 | r(">")?; 108 | } else if let Sym(Rge) = **a { 109 | r(">=")?; 110 | } else if let Sym(Mul) = **a { 111 | r("*")?; 112 | } else if let Sym(Add) = **a { 113 | r("+")?; 114 | } else if let Sym(Rsub) = **a { 115 | r("-")?; 116 | } else if let Sym(Rdiv) = **a { 117 | r("/")?; 118 | } else if let Sym(Rpow) = **a { 119 | r("^")?; 120 | } else { 121 | if let (EOp(Apply, f, a), Sym(Pi)) = (&**a, &**b) { 122 | if let (Sym(Mul), Ret(F64(a))) = (&**f, &**a) { 123 | write!(w, "{}π", a)?; 124 | return Ok(()) 125 | } 126 | } 127 | if let (EOp(Apply, f, a), Sym(Tau)) = (&**a, &**b) { 128 | if let (Sym(Mul), Ret(F64(a))) = (&**f, &**a) { 129 | write!(w, "{}τ", a)?; 130 | return Ok(()) 131 | } 132 | } 133 | if let (EOp(Apply, f, a), Sym(Eps)) = (&**a, &**b) { 134 | if let (Sym(Mul), Ret(F64(a))) = (&**f, &**a) { 135 | write!(w, "{}ε", a)?; 136 | return Ok(()) 137 | } 138 | } 139 | if let (EOp(Apply, f, a), Sym(Imag)) = (&**a, &**b) { 140 | if let (Sym(Mul), Ret(F64(a))) = (&**f, &**a) { 141 | write!(w, "{}𝐢", a)?; 142 | return Ok(()) 143 | } 144 | } 145 | if let (EOp(Apply, f, a), Sym(Imag2)) = (&**a, &**b) { 146 | if let (Sym(Mul), Ret(F64(a))) = (&**f, &**a) { 147 | write!(w, "{}𝐢₂", a)?; 148 | return Ok(()) 149 | } 150 | } 151 | if let (EOp(Apply, f, a), Sym(Imag3)) = (&**a, &**b) { 152 | if let (Sym(Mul), Ret(F64(a))) = (&**f, &**a) { 153 | write!(w, "{}𝐢₃", a)?; 154 | return Ok(()) 155 | } 156 | } 157 | if let (EOp(Apply, f, b), Sym(Var(ref a))) = (&**a, &**b) { 158 | if let (Sym(Mul), Sym(Pariv)) = (&**f, &**b) { 159 | write!(w, "∂{}", a)?; 160 | return Ok(()) 161 | } 162 | } 163 | if let EOp(Apply, f, a) = &**a { 164 | let mut pr = | 165 | op_txt: &str, 166 | op_sym: &Symbol 167 | | -> std::result::Result<(), fmt::Error> { 168 | if parens {write!(w, "(")?}; 169 | let left = true; 170 | a.display(w, a.needs_parens(op_sym, left), rule)?; 171 | write!(w, " {} ", op_txt)?; 172 | let right = false; 173 | b.display(w, b.needs_parens(op_sym, right), rule)?; 174 | if parens {write!(w, ")")?}; 175 | Ok(()) 176 | }; 177 | 178 | match **f { 179 | Sym(Add) => { 180 | pr("+", &Add)?; 181 | return Ok(()) 182 | } 183 | Sym(Sub) => { 184 | pr("-", &Sub)?; 185 | return Ok(()) 186 | } 187 | Sym(Mul) => { 188 | pr("*", &Mul)?; 189 | return Ok(()) 190 | } 191 | Sym(Div) => { 192 | pr("/", &Div)?; 193 | return Ok(()) 194 | } 195 | Sym(Rem) => { 196 | pr("%", &Rem)?; 197 | return Ok(()) 198 | } 199 | Sym(Pow) => { 200 | pr("^", &Pow)?; 201 | return Ok(()) 202 | } 203 | Sym(And) => { 204 | pr("&", &And)?; 205 | return Ok(()) 206 | } 207 | Sym(Or) => { 208 | pr("|", &Or)?; 209 | return Ok(()) 210 | } 211 | Sym(Concat) => { 212 | pr("++", &Concat)?; 213 | return Ok(()) 214 | } 215 | Sym(Lt) => { 216 | pr("<", &Lt)?; 217 | return Ok(()) 218 | } 219 | Sym(Le) => { 220 | pr("<=", &Le)?; 221 | return Ok(()) 222 | } 223 | Sym(Eq) => { 224 | pr("=", &Eq)?; 225 | return Ok(()) 226 | } 227 | Sym(Gt) => { 228 | pr(">", &Gt)?; 229 | return Ok(()) 230 | } 231 | Sym(Ge) => { 232 | pr(">=", &Ge)?; 233 | return Ok(()) 234 | } 235 | _ => {} 236 | } 237 | } 238 | 239 | if let Ret(_) = **a { 240 | write!(w, "\\")?; 241 | } 242 | let parens = true; 243 | a.display(w, parens, rule)?; 244 | if let Tup(_) = &**b { 245 | b.display(w, parens, rule)?; 246 | } else { 247 | write!(w, "(")?; 248 | b.display(w, false, rule)?; 249 | write!(w, ")")?; 250 | } 251 | } 252 | } 253 | EOp(Constrain, a, b) => { 254 | if let Ret(_) = **a { 255 | write!(w, "\\")?; 256 | } 257 | a.display(w, true, rule)?; 258 | if let Tup(b) = &**b { 259 | write!(w, "{{")?; 260 | for i in 0..b.len() { 261 | if i > 0 {write!(w, ", ")?} 262 | b[i].display(w, false, rule)?; 263 | } 264 | write!(w, "}}")?; 265 | } else { 266 | write!(w, "{{")?; 267 | b.display(w, false, rule)?; 268 | write!(w, "}}")?; 269 | } 270 | } 271 | EOp(Compose, a, b) => { 272 | if parens { 273 | write!(w, "(")?; 274 | } 275 | a.display(w, true, rule)?; 276 | write!(w, " · ")?; 277 | b.display(w, true, rule)?; 278 | if parens { 279 | write!(w, ")")?; 280 | } 281 | } 282 | EOp(Type, a, b) => { 283 | if parens { 284 | write!(w, "(")?; 285 | } 286 | a.display(w, true, rule)?; 287 | write!(w, " : ")?; 288 | b.display(w, true, rule)?; 289 | if parens { 290 | write!(w, ")")?; 291 | } 292 | } 293 | Tup(b) => { 294 | write!(w, "(")?; 295 | for i in 0..b.len() { 296 | if i > 0 {write!(w, ", ")?} 297 | b[i].display(w, false, rule)?; 298 | } 299 | write!(w, ")")?; 300 | } 301 | List(b) => { 302 | write!(w, "[")?; 303 | for i in 0..b.len() { 304 | if i > 0 {write!(w, ", ")?} 305 | b[i].display(w, false, rule)?; 306 | } 307 | write!(w, "]")?; 308 | } 309 | // _ => write!(w, "{:?}", self)?, 310 | } 311 | Ok(()) 312 | } 313 | 314 | /// Returns `true` if the expression needs parentheses, given parent operation and side. 315 | pub fn needs_parens(&self, parent_op: &Symbol, left: bool) -> bool { 316 | if let EOp(Apply, f, _) = self { 317 | if let EOp(Apply, f, _) = &**f { 318 | match &**f { 319 | Sym(x) => { 320 | if let (Some(px), Some(py)) = (x.precedence(), parent_op.precedence()) { 321 | if left { 322 | (x == parent_op && parent_op == &Symbol::Pow) || px > py 323 | } else { 324 | px >= py 325 | } 326 | } else {true} 327 | } 328 | _ => true 329 | } 330 | } else { 331 | match &**f { 332 | Sym(x) => { 333 | if let (Some(x), Some(y)) = (x.precedence(), parent_op.precedence()) { 334 | if left {x > y} else {x >= y} 335 | } else {true} 336 | } 337 | _ => true 338 | } 339 | } 340 | } else { 341 | true 342 | } 343 | } 344 | } 345 | 346 | impl fmt::Display for Expr { 347 | fn fmt(&self, w: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> { 348 | let parens = false; 349 | let rule = false; 350 | self.display(w, parens, rule) 351 | } 352 | } 353 | 354 | #[cfg(test)] 355 | mod tests { 356 | use crate::*; 357 | use std::fmt; 358 | 359 | #[test] 360 | fn parens() { 361 | let expr = app2(Mul, app2(Mul, "a", "b"), "c"); 362 | assert_eq!(format!("{}", expr), "a * b * c"); 363 | let expr = app2(Mul, "a", app2(Mul, "b", "c")); 364 | assert_eq!(format!("{}", expr), "a * (b * c)"); 365 | let expr = app2(Add, "a", "b"); 366 | assert_eq!(format!("{}", expr), "a + b"); 367 | let expr = app2(Mul, app2(Add, "a", "b"), "c"); 368 | assert_eq!(format!("{}", expr), "(a + b) * c"); 369 | let expr = app2(Add, app2(Add, "a", "b"), "c"); 370 | assert_eq!(format!("{}", expr), "a + b + c"); 371 | let expr = app2(Add, "a", app2(Add, "b", "c")); 372 | assert_eq!(format!("{}", expr), "a + (b + c)"); 373 | let expr = app2(Pow, "a", 2.0); 374 | assert_eq!(format!("{}", expr), "a ^ 2"); 375 | let expr = app2(Add, "a", app2(Pow, "b", 2.0)); 376 | assert_eq!(format!("{}", expr), "a + b ^ 2"); 377 | let expr = app2(Add, app2(Pow, "a", 2.0), "b"); 378 | assert_eq!(format!("{}", expr), "a ^ 2 + b"); 379 | let expr = app2(Div, app2(Add, "a", "b"), "c"); 380 | assert_eq!(format!("{}", expr), "(a + b) / c"); 381 | let expr = app2(Sub, "a", "b"); 382 | assert_eq!(format!("{}", expr), "a - b"); 383 | let expr = app2(Sub, app2(Sub, "a", "b"), "c"); 384 | assert_eq!(format!("{}", expr), "a - b - c"); 385 | let expr = app2(Add, app2(Sub, "a", "b"), "c"); 386 | assert_eq!(format!("{}", expr), "a - b + c"); 387 | let expr = app2(Sub, app2(Add, "a", "b"), "c"); 388 | assert_eq!(format!("{}", expr), "a + b - c"); 389 | let expr = app2(Mul, app2(Sub, "a", "b"), "c"); 390 | assert_eq!(format!("{}", expr), "(a - b) * c"); 391 | let expr = app2(Sub, app2(Mul, "a", "b"), "c"); 392 | assert_eq!(format!("{}", expr), "a * b - c"); 393 | let expr = app2(Sub, "a", app2(Mul, "b", "c")); 394 | assert_eq!(format!("{}", expr), "a - b * c"); 395 | let expr = app2(Div, app2(Sub, "a", "b"), "c"); 396 | assert_eq!(format!("{}", expr), "(a - b) / c"); 397 | let expr = app2(Sub, app2(Div, "a", "b"), "c"); 398 | assert_eq!(format!("{}", expr), "a / b - c"); 399 | let expr = app2(Sub, "a", app2(Div, "b", "c")); 400 | assert_eq!(format!("{}", expr), "a - b / c"); 401 | let expr = app2(Div, "a", "b"); 402 | assert_eq!(format!("{}", expr), "a / b"); 403 | let expr = app2(Div, app2(Div, "a", "b"), "c"); 404 | assert_eq!(format!("{}", expr), "a / b / c"); 405 | let expr = app2(Eq, app2(Add, "a", "b"), "c"); 406 | assert_eq!(format!("{}", expr), "a + b = c"); 407 | let expr = app2(Or, "a", "b"); 408 | assert_eq!(format!("{}", expr), "a | b"); 409 | let expr = app2(And, "a", "b"); 410 | assert_eq!(format!("{}", expr), "a & b"); 411 | let expr = app2(Or, app2(And, "a", "b"), "c"); 412 | assert_eq!(format!("{}", expr), "a & b | c"); 413 | let expr = app2(And, app2(Or, "a", "b"), "c"); 414 | assert_eq!(format!("{}", expr), "(a | b) & c"); 415 | let expr = comp("f", "g"); 416 | assert_eq!(format!("{}", expr), "f · g"); 417 | let expr = constr("f", "x"); 418 | assert_eq!(format!("{}", expr), "f{x}"); 419 | let expr = constr(comp("f", "g"), "x"); 420 | assert_eq!(format!("{}", expr), "(f · g){x}"); 421 | let expr = comp("f", comp("g", "h")); 422 | assert_eq!(format!("{}", expr), "f · (g · h)"); 423 | let expr = comp(comp("f", "g"), "h"); 424 | assert_eq!(format!("{}", expr), "(f · g) · h"); 425 | let expr = typ("a", "b"); 426 | assert_eq!(format!("{}", expr), "a : b"); 427 | let expr = typ(typ("a", "b"), "c"); 428 | assert_eq!(format!("{}", expr), "(a : b) : c"); 429 | let expr = typ("a", typ("b", "c")); 430 | assert_eq!(format!("{}", expr), "a : (b : c)"); 431 | let expr = app(Neg, app(Neg, "a")); 432 | assert_eq!(format!("{}", expr), "-(-a)"); 433 | let expr = app(Not, app2(Or, "a", "b")); 434 | assert_eq!(format!("{}", expr), "!(a | b)"); 435 | let expr = app2(Or, app(Not, "a"), "b"); 436 | assert_eq!(format!("{}", expr), "!a | b"); 437 | let expr = app2(Or, "a", app(Not, "b")); 438 | assert_eq!(format!("{}", expr), "a | !b"); 439 | } 440 | 441 | struct Rule(Expr); 442 | 443 | impl fmt::Display for Rule { 444 | fn fmt(&self, w: &mut fmt::Formatter) -> Result<(), fmt::Error> { 445 | let parens = false; 446 | let rule = true; 447 | self.0.display(w, parens, rule) 448 | } 449 | } 450 | 451 | #[test] 452 | fn constraints() { 453 | let rule = Rule(arity_var("f", 1)); 454 | assert_eq!(format!("{}", rule), "f:[arity]1"); 455 | let rule = Rule(comp("f", arity_var("g", 1))); 456 | assert_eq!(format!("{}", rule), "f · g:[arity]1"); 457 | let rule = Rule(constr(comp("f", arity_var("g", 1)), "x")); 458 | assert_eq!(format!("{}", rule), "(f · g:[arity]1){x}"); 459 | let rule = Rule(app(comp("f", arity_var("g", 1)), "a")); 460 | assert_eq!(format!("{}", rule), "(f · g:[arity]1)(a)"); 461 | let rule = Rule(path("f", arity_var("g", 1))); 462 | assert_eq!(format!("{}", rule), "f[g:[arity]1]"); 463 | let rule = Rule(app(Neg, arity_var("f", 1))); 464 | assert_eq!(format!("{}", rule), "-f:[arity]1"); 465 | let rule = Rule(app(Not, arity_var("f", 1))); 466 | assert_eq!(format!("{}", rule), "!f:[arity]1"); 467 | let rule = Rule(app(Rty, arity_var("f", 1))); 468 | assert_eq!(format!("{}", rule), "(: f:[arity]1)"); 469 | let rule = Rule(app(Rlt, arity_var("f", 1))); 470 | assert_eq!(format!("{}", rule), "(< f:[arity]1)"); 471 | let rule = Rule(app(Triv, constr(arity_var("f", 1), "g"))); 472 | assert_eq!(format!("{}", rule), "∀(f:[arity]1{g})"); 473 | } 474 | } 475 | -------------------------------------------------------------------------------- /src/sym.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | use std::fmt; 3 | use super::Expr; 4 | 5 | /// Contains symbols and operators on symbols. 6 | #[derive(Clone, PartialEq, PartialOrd, Debug)] 7 | pub enum Symbol { 8 | /// A custom symbol. 9 | Custom(Arc), 10 | /// The wildcard symbol `_`. 11 | Any, 12 | /// A variable bound from context. 13 | /// 14 | /// This can be anything. 15 | Var(Arc), 16 | /// A function variable with specified arity (number of arguments). 17 | ArityVar(Arc, usize), 18 | /// A list variable. 19 | ListVar(Arc), 20 | /// A list variable of length 1. 21 | /// 22 | /// Lifts the value out of the list at binding. 23 | Singleton(Arc), 24 | /// A head-tail pattern match on a tuple. 25 | /// 26 | /// This requires the tuple to have at least length 2. 27 | /// It is to avoid cycles between reductions. 28 | HeadTailTup(Box, Box), 29 | /// A head-tail pattern match on a list. 30 | /// 31 | /// This requires the list to have at least length 2. 32 | /// It is to avoid cycles between reductions. 33 | HeadTailList(Box, Box), 34 | /// A value variable. 35 | /// 36 | /// This requires the expression to be `Ret` variant. 37 | /// It is used in special rules such as `(\k)(x) => \k`. 38 | RetVar(Arc), 39 | /// A value variable that is an integer. 40 | RetIntVar(Arc), 41 | /// A value variable that is positive or zero. 42 | RetPosVar(Arc), 43 | /// A value that is strictly positive (non-zero). 44 | RetStrictPosVar(Arc), 45 | /// A value variable that is negative and non-zero. 46 | /// 47 | /// Binds to its positive value. 48 | RetNegVar(Arc), 49 | /// A variable that is not a value variable. 50 | NotRetVar(Arc), 51 | /// Binds to left variable name that does not occur in expression 52 | /// bound to the right variable. 53 | NotInVarName(Arc, Arc), 54 | /// Compute a binary function. 55 | /// 56 | /// This is used when the right side of the rule computes something from two left side expressions. 57 | BinopRetVar(Arc, Arc, Box), 58 | /// Compute a ternary function. 59 | /// 60 | /// This is used when the right side of the rule computes something from three left side expressions. 61 | TernopRetVar(Arc, Arc, Arc, Box), 62 | /// Compute a unary function. 63 | /// 64 | /// This is used when the right side of the rule computes something from a left side expression. 65 | UnopRetVar(Arc, Box), 66 | /// A function without domain constraints. 67 | NoConstrVar(Arc), 68 | /// A variable that is not substitution. 69 | NoSubstVar(Arc), 70 | /// `dup` duplicate argument `\(x) = (x, x)`. 71 | Dup, 72 | /// `\false` for one argument. 73 | False1, 74 | /// `not`. 75 | Not, 76 | /// `id` for booleans. 77 | Idb, 78 | /// `\true` for one argument. 79 | True1, 80 | /// `\false` for two arguments. 81 | False2, 82 | /// `\true` for two arguments. 83 | True2, 84 | /// `and`. 85 | And, 86 | /// `or`. 87 | Or, 88 | /// `eq` for booleans. 89 | Eqb, 90 | /// `xor`. 91 | Xor, 92 | /// `nand`. 93 | Nand, 94 | /// `nor`. 95 | Nor, 96 | /// `exc`. 97 | Exc, 98 | /// `imply`. 99 | Imply, 100 | /// `fst` for booleans. 101 | Fstb, 102 | /// `snd` for booleans. 103 | Sndb, 104 | /// `even`. 105 | Even, 106 | /// `odd`. 107 | Odd, 108 | /// `abs`. 109 | Abs, 110 | /// `lt`. 111 | Lt, 112 | /// `(< _)(_)`. 113 | Rlt, 114 | /// `le`. 115 | Le, 116 | /// `(<= _)(_)`. 117 | Rle, 118 | /// `gt`. 119 | Gt, 120 | /// `(> _)(_)`. 121 | Rgt, 122 | /// `ge`. 123 | Ge, 124 | /// `(>= _)(_)`. 125 | Rge, 126 | /// `neg`. 127 | Neg, 128 | /// `inc`. 129 | Inc, 130 | /// `reci` (reciprocal). 131 | Reci, 132 | /// `conj` (complex conjugate). 133 | Conj, 134 | /// `norm` (vector norm). 135 | Norm, 136 | /// `sqnorm` (vector square norm). 137 | Sqnorm, 138 | /// `add`. 139 | Add, 140 | /// `sub`. 141 | Sub, 142 | /// `(- _)(_)` 143 | Rsub, 144 | /// `mul`. 145 | Mul, 146 | /// `mulc` (complex multiplication). 147 | Mulc, 148 | /// `div`. 149 | Div, 150 | /// `(/ _)(_)` 151 | Rdiv, 152 | /// `rem`. 153 | Rem, 154 | /// `pow`. 155 | Pow, 156 | /// `rpow`. 157 | Rpow, 158 | /// `sqrt`. 159 | Sqrt, 160 | /// `ln`. 161 | Ln, 162 | /// `log2`. 163 | Log2, 164 | /// `log10`. 165 | Log10, 166 | /// `exp`. 167 | Exp, 168 | /// `len`. 169 | Len, 170 | /// `concat`. 171 | Concat, 172 | /// `sum`. 173 | Sum, 174 | /// `min2`. 175 | Min2, 176 | /// `max2`. 177 | Max2, 178 | /// `min`. 179 | Min, 180 | /// `max`. 181 | Max, 182 | /// `arg_max`. 183 | ArgMax, 184 | /// `arg_min`. 185 | ArgMin, 186 | /// `soft_max`. 187 | SoftMax, 188 | /// `soft_min`. 189 | SoftMin, 190 | /// `soft_arg_max`. 191 | SoftArgMax, 192 | /// `soft_arg_min`. 193 | SoftArgMin, 194 | /// `range` [a, b]. 195 | Range, 196 | /// `rangel` [a, b). 197 | Rangel, 198 | /// `ranger` (a, b]. 199 | Ranger, 200 | /// `rangem` (a, b). 201 | Rangem, 202 | /// `prob` [0, 1]. 203 | Prob, 204 | /// `probl` [0, 1). 205 | Probl, 206 | /// `probr` (0, 1]. 207 | Probr, 208 | /// `probm` (0, 1). 209 | Probm, 210 | /// `mul_mat`. 211 | MulMat, 212 | /// `col` (index, matrix). 213 | /// 214 | /// Looks up column vector by index in a matrix. 215 | Col, 216 | /// `det`. 217 | Det, 218 | /// `dim`. 219 | Dim, 220 | /// `transpose`. 221 | Transpose, 222 | /// `is_square_mat`, 223 | IsSquareMat, 224 | /// `base` (len, index). 225 | /// 226 | /// Constructs a list with zeroes of specified length, 227 | /// but with `1` at the index. 228 | Base, 229 | /// `fst`. 230 | Fst, 231 | /// `snd`. 232 | Snd, 233 | /// `sin`. 234 | Sin, 235 | /// `asin`. 236 | Asin, 237 | /// `cos`. 238 | Cos, 239 | /// `acos`. 240 | Acos, 241 | /// `tan`. 242 | Tan, 243 | /// `atan`. 244 | Atan, 245 | /// `atan2`. 246 | Atan2, 247 | /// `dot`. 248 | Dot, 249 | /// `push`. 250 | Push, 251 | /// `push_front`. 252 | PushFront, 253 | /// `item` (index, list). 254 | Item, 255 | /// `el`. 256 | El, 257 | /// `re`. 258 | Re, 259 | /// `im`. 260 | Im, 261 | /// Generic `id`. 262 | Id, 263 | /// Generic `eq`. 264 | Eq, 265 | /// Generic `neq`. 266 | Neq, 267 | /// Function inverse `inv`. 268 | Inv, 269 | /// Derivative. 270 | Deriv, 271 | /// Integral. 272 | Integ, 273 | /// Partial derivative. 274 | Pariv, 275 | /// `if`. 276 | /// 277 | /// This is used in Boolean functions. 278 | If, 279 | /// Existential path `∃`. 280 | Ex, 281 | /// Trivial path `∀`. 282 | Triv, 283 | /// `any`, any type. 284 | AnyType, 285 | /// `\`, the type of `\x`. 286 | RetType, 287 | /// The type of lists. 288 | VecType, 289 | /// The judgement `(: a)(b)`. 290 | Rty, 291 | /// `vec_op`. 292 | /// 293 | /// Applies a binary function component-wise to lists. 294 | VecOp, 295 | /// `vec_uop`. 296 | /// 297 | /// Applies a unary function component-wise to lists. 298 | VecUop, 299 | /// `arity`. 300 | Arity, 301 | /// `pi` or `π`. 302 | Pi, 303 | /// `tau` or `τ`. 304 | Tau, 305 | /// `eps` or `ε`. 306 | Eps, 307 | /// `imag` (complex imaginary base). 308 | Imag, 309 | /// `imag2` (second complex imaginary base for quaternions). 310 | Imag2, 311 | /// `imag3` (third complex imaginary base for quaternions). 312 | Imag3, 313 | /// `type_of`. 314 | TypeOf, 315 | /// `bool`. 316 | BoolType, 317 | /// `f64`. 318 | F64Type, 319 | /// `quat` (type of quaternions). 320 | QuatType, 321 | /// `inf` (infinity). 322 | Inf, 323 | /// `both` (indeterminate). 324 | Both, 325 | /// `neither` (uninhabited). 326 | Neither, 327 | /// `subst` (substitution). 328 | Subst, 329 | } 330 | 331 | impl Symbol { 332 | /// Returns the operator presedence of symbol. 333 | pub fn precedence(&self) -> Option { 334 | use Symbol::*; 335 | 336 | Some(match self { 337 | Pow | Not => 3, 338 | Mul | Div | Rem | And => 4, 339 | Add | Sub | Or => 5, 340 | Eq => 6, 341 | _ => return None, 342 | }) 343 | } 344 | } 345 | 346 | impl From> for Symbol { 347 | fn from(val: Arc) -> Symbol { 348 | use Symbol::*; 349 | 350 | match &**val { 351 | "_" => Any, 352 | "dup" => Dup, 353 | "triv" | "∀" | "dom" => Triv, 354 | "ex" | "∃" | "codom" => Ex, 355 | "false1" => False1, 356 | "idb" => Idb, 357 | "not" => Not, 358 | "true1" => True1, 359 | "false2" => False2, 360 | "true2" => True2, 361 | "and" => And, 362 | "or" => Or, 363 | "eqb" => Eqb, 364 | "xor" => Xor, 365 | "nand" => Nand, 366 | "nor" => Nor, 367 | "exc" => Exc, 368 | "imply" => Imply, 369 | "fstb" => Fstb, 370 | "sndb" => Sndb, 371 | "neqb" => Xor, 372 | "id" => Id, 373 | "inv" => Inv, 374 | "lt" => Lt, 375 | "rlt" => Rlt, 376 | "le" => Le, 377 | "rle" => Rle, 378 | "gt" => Gt, 379 | "rgt" => Rgt, 380 | "ge" => Ge, 381 | "rge" => Rge, 382 | "mul" => Mul, 383 | "mulc" => Mulc, 384 | "div" => Div, 385 | "rem" => Rem, 386 | "pow" => Pow, 387 | "rpow" => Rpow, 388 | "sqrt" => Sqrt, 389 | "even" => Even, 390 | "odd" => Odd, 391 | "abs" => Abs, 392 | "neg" => Neg, 393 | "inc" => Inc, 394 | "reci" => Reci, 395 | "conj" => Conj, 396 | "norm" => Norm, 397 | "sqnorm" => Sqnorm, 398 | "add" => Add, 399 | "sub" => Sub, 400 | "len" => Len, 401 | "concat" => Concat, 402 | "sum" => Sum, 403 | "mul_mat" => MulMat, 404 | "col" => Col, 405 | "det" => Det, 406 | "dim" => Dim, 407 | "transpose" => Transpose, 408 | "is_square_mat" => IsSquareMat, 409 | "base" => Base, 410 | "fst" => Fst, 411 | "snd" => Snd, 412 | "ln" => Ln, 413 | "log2" => Log2, 414 | "log10" => Log10, 415 | "exp" => Exp, 416 | "min2" => Min2, 417 | "max2" => Max2, 418 | "min" => Min, 419 | "max" => Max, 420 | "arg_max" => ArgMax, 421 | "arg_min" => ArgMin, 422 | "soft_max" => SoftMax, 423 | "soft_min" => SoftMin, 424 | "soft_arg_max" => SoftArgMax, 425 | "soft_arg_min" => SoftArgMin, 426 | "range" => Range, 427 | "rangel" => Rangel, 428 | "ranger" => Ranger, 429 | "rangem" => Rangem, 430 | "prob" => Prob, 431 | "probl" => Probl, 432 | "probr" => Probr, 433 | "probm" => Probm, 434 | "eq" => Eq, 435 | "neq" => Neq, 436 | "if" => If, 437 | "sin" => Sin, 438 | "asin" => Asin, 439 | "cos" => Cos, 440 | "acos" => Acos, 441 | "tan" => Tan, 442 | "atan" => Atan, 443 | "atan2" => Atan2, 444 | "dot" => Dot, 445 | "item" => Item, 446 | "el" => El, 447 | "re" => Re, 448 | "im" => Im, 449 | "push" => Push, 450 | "push_front" => PushFront, 451 | "any" => AnyType, 452 | "\\" => RetType, 453 | "vec" => VecType, 454 | "rty" => Rty, 455 | "vec_op" => VecOp, 456 | "vec_uop" => VecUop, 457 | "arity" => Arity, 458 | "deriv" | "𝐝" => Deriv, 459 | "integ" | "∫" => Integ, 460 | "pariv" | "∂" => Pariv, 461 | "pi" | "π" => Pi, 462 | "tau" | "τ" => Tau, 463 | "eps" | "ε" => Eps, 464 | "imag" | "𝐢" => Imag, 465 | "imag2" | "𝐢₂" => Imag2, 466 | "imag3" | "𝐢₃" => Imag3, 467 | "type_of" => TypeOf, 468 | "bool" => BoolType, 469 | "f64" => F64Type, 470 | "quat" => QuatType, 471 | "inf" | "∞" => Inf, 472 | "both" => Both, 473 | "neither" => Neither, 474 | "subst" => Subst, 475 | _ => { 476 | // Custom symbols start with two lowercase alphabetic letters. 477 | let custom_symbol = { 478 | let mut chars = val.chars(); 479 | if let Some(ch) = chars.next() { 480 | ch.is_lowercase() && ch.is_alphabetic() && 481 | if let Some(ch) = chars.next() { 482 | ch.is_lowercase() && ch.is_alphabetic() 483 | } else {false} 484 | } else {false} 485 | }; 486 | if custom_symbol { 487 | Custom(val) 488 | } else { 489 | Var(val) 490 | } 491 | } 492 | } 493 | } 494 | } 495 | 496 | impl Symbol { 497 | /// Used to display format with additional options. 498 | pub fn display( 499 | &self, 500 | w: &mut fmt::Formatter<'_>, 501 | rule: bool, 502 | ) -> std::result::Result<(), fmt::Error> { 503 | use Symbol::*; 504 | 505 | match self { 506 | Custom(sym) => write!(w, "{}", sym)?, 507 | Dup => write!(w, "dup")?, 508 | False1 => write!(w, "false1")?, 509 | Not => write!(w, "not")?, 510 | Idb => write!(w, "idb")?, 511 | True1 => write!(w, "true1")?, 512 | False2 => write!(w, "false2")?, 513 | True2 => write!(w, "true2")?, 514 | And => write!(w, "and")?, 515 | Or => write!(w, "or")?, 516 | Eqb => write!(w, "eqb")?, 517 | Xor => write!(w, "xor")?, 518 | Nand => write!(w, "nand")?, 519 | Nor => write!(w, "nor")?, 520 | Exc => write!(w, "exc")?, 521 | Imply => write!(w, "imply")?, 522 | Fstb => write!(w, "fstb")?, 523 | Sndb => write!(w, "sndb")?, 524 | Even => write!(w, "even")?, 525 | Odd => write!(w, "odd")?, 526 | Abs => write!(w, "abs")?, 527 | Lt => write!(w, "lt")?, 528 | Rlt => write!(w, "rlt")?, 529 | Le => write!(w, "le")?, 530 | Rle => write!(w, "rle")?, 531 | Gt => write!(w, "gt")?, 532 | Rgt => write!(w, "rgt")?, 533 | Ge => write!(w, "ge")?, 534 | Rge => write!(w, "rge")?, 535 | Neg => write!(w, "neg")?, 536 | Inc => write!(w, "inc")?, 537 | Reci => write!(w, "reci")?, 538 | Conj => write!(w, "conj")?, 539 | Norm => write!(w, "norm")?, 540 | Sqnorm => write!(w, "sqnorm")?, 541 | Add => write!(w, "add")?, 542 | Sub => write!(w, "sub")?, 543 | Rsub => write!(w, "rsub")?, 544 | Mul => write!(w, "mul")?, 545 | Mulc => write!(w, "mulc")?, 546 | Div => write!(w, "div")?, 547 | Rdiv => write!(w, "rdiv")?, 548 | Rem => write!(w, "rem")?, 549 | Pow => write!(w, "pow")?, 550 | Rpow => write!(w, "rpow")?, 551 | Sqrt => write!(w, "sqrt")?, 552 | Ln => write!(w, "ln")?, 553 | Log2 => write!(w, "log2")?, 554 | Log10 => write!(w, "log10")?, 555 | Exp => write!(w, "exp")?, 556 | Len => write!(w, "len")?, 557 | Concat => write!(w, "concat")?, 558 | Sum => write!(w, "sum")?, 559 | Min2 => write!(w, "min2")?, 560 | Max2 => write!(w, "max2")?, 561 | Min => write!(w, "min")?, 562 | Max => write!(w, "max")?, 563 | ArgMax => write!(w, "arg_max")?, 564 | ArgMin => write!(w, "arg_min")?, 565 | SoftMax => write!(w, "soft_max")?, 566 | SoftMin => write!(w, "soft_min")?, 567 | SoftArgMax => write!(w, "soft_arg_max")?, 568 | SoftArgMin => write!(w, "soft_arg_min")?, 569 | Range => write!(w, "range")?, 570 | Rangel => write!(w, "rangel")?, 571 | Ranger => write!(w, "ranger")?, 572 | Rangem => write!(w, "rangem")?, 573 | Prob => write!(w, "prob")?, 574 | Probl => write!(w, "probl")?, 575 | Probr => write!(w, "probr")?, 576 | Probm => write!(w, "probm")?, 577 | MulMat => write!(w, "mul_mat")?, 578 | Col => write!(w, "col")?, 579 | Det => write!(w, "det")?, 580 | Dim => write!(w, "dim")?, 581 | Transpose => write!(w, "transpose")?, 582 | IsSquareMat => write!(w, "is_square_mat")?, 583 | Base => write!(w, "base")?, 584 | Fst => write!(w, "fst")?, 585 | Snd => write!(w, "snd")?, 586 | Sin => write!(w, "sin")?, 587 | Asin => write!(w, "asin")?, 588 | Cos => write!(w, "cos")?, 589 | Acos => write!(w, "acos")?, 590 | Tan => write!(w, "tan")?, 591 | Atan => write!(w, "atan")?, 592 | Atan2 => write!(w, "atan2")?, 593 | Dot => write!(w, "dot")?, 594 | Push => write!(w, "push")?, 595 | PushFront => write!(w, "push_front")?, 596 | Item => write!(w, "item")?, 597 | El => write!(w, "el")?, 598 | Re => write!(w, "re")?, 599 | Im => write!(w, "im")?, 600 | Id => write!(w, "id")?, 601 | Eq => write!(w, "eq")?, 602 | Neq => write!(w, "neq")?, 603 | Inv => write!(w, "inv")?, 604 | If => write!(w, "if")?, 605 | Any => write!(w, "_")?, 606 | Ex => write!(w, "∃")?, 607 | Triv => write!(w, "∀")?, 608 | AnyType => write!(w, "any")?, 609 | RetType => write!(w, "\\")?, 610 | VecType => write!(w, "vec")?, 611 | Rty => write!(w, "rty")?, 612 | VecOp => write!(w, "vec_op")?, 613 | VecUop => write!(w, "vec_uop")?, 614 | Arity => write!(w, "arity")?, 615 | Deriv => write!(w, "𝐝")?, 616 | Integ => write!(w, "∫")?, 617 | Pariv => write!(w, "∂")?, 618 | Pi => write!(w, "π")?, 619 | Tau => write!(w, "τ")?, 620 | Eps => write!(w, "ε")?, 621 | Imag => write!(w, "𝐢")?, 622 | Imag2 => write!(w, "𝐢₂")?, 623 | Imag3 => write!(w, "𝐢₃")?, 624 | TypeOf => write!(w, "type_of")?, 625 | BoolType => write!(w, "bool")?, 626 | F64Type => write!(w, "f64")?, 627 | QuatType => write!(w, "quat")?, 628 | Inf => write!(w, "∞")?, 629 | Both => write!(w, "both")?, 630 | Neither => write!(w, "neither")?, 631 | Subst => write!(w, "subst")?, 632 | Var(x) => write!(w, "{}", x)?, 633 | NoConstrVar(x) => if rule { 634 | write!(w, "{}!{{}}", x)? 635 | } else { 636 | write!(w, "{}", x)? 637 | }, 638 | NoSubstVar(x) => if rule { 639 | write!(w, "{}:!subst", x)? 640 | } else { 641 | write!(w, "{}", x)? 642 | }, 643 | ArityVar(x, arg) => if rule { 644 | write!(w, "{}:[arity]{}", x, arg)? 645 | } else { 646 | write!(w, "{}", x)? 647 | }, 648 | RetVar(x) => write!(w, "\\{}", x)?, 649 | RetIntVar(x) => write!(w, "\\{}:int", x)?, 650 | RetPosVar(x) => write!(w, "\\{}:(>= 0)", x)?, 651 | RetStrictPosVar(x) => write!(w, "\\{}:(> 0)", x)?, 652 | RetNegVar(x) => write!(w, "\\{}:(< 0)", x)?, 653 | NotRetVar(x) => write!(w, "!\\{}", x)?, 654 | NotInVarName(x, y) => write!(w, "{}!>{}", x, y)?, 655 | ListVar(x) => write!(w, "[{}..]", x)?, 656 | Singleton(x) => write!(w, "\\[{}]", x)?, 657 | HeadTailTup(x, y) => write!(w, "({}, {}..)", x, y)?, 658 | HeadTailList(x, y) => write!(w, "[{}, {}..]", x, y)?, 659 | BinopRetVar(x, y, f) => { 660 | match **f { 661 | Lt => write!(w, "compute::lt({}, {})", x, y)?, 662 | Le => write!(w, "compute::le({}, {})", x, y)?, 663 | Gt => write!(w, "compute::gt({}, {})", x, y)?, 664 | Ge => write!(w, "compute::ge({}, {})", x, y)?, 665 | Add => write!(w, "compute::add({}, {})", x, y)?, 666 | Sub => write!(w, "compute::sub({}, {})", x, y)?, 667 | Mul => write!(w, "compute::mul({}, {})", x, y)?, 668 | Div => write!(w, "compute::div({}, {})", x, y)?, 669 | Pow => write!(w, "compute::pow({}, {})", x, y)?, 670 | Rem => write!(w, "compute::rem({}, {})", x, y)?, 671 | Eq => write!(w, "compute::eq({}, {})", x, y)?, 672 | Concat => write!(w, "compute::concat({}, {})", x, y)?, 673 | MulMat => write!(w, "compute::mul_mat({}, {})", x, y)?, 674 | Push => write!(w, "compute::push({}, {})", x, y)?, 675 | PushFront => write!(w, "compute::push_front({}, {})", x, y)?, 676 | Max2 => write!(w, "compute::max2({}, {})", x, y)?, 677 | Min2 => write!(w, "compute::min2({}, {})", x, y)?, 678 | Item => write!(w, "compute::item({}, {})", x, y)?, 679 | Col => write!(w, "compute::col({}, {})", x, y)?, 680 | Base => write!(w, "compute::base({}, {})", x, y)?, 681 | Atan2 => write!(w, "compute::atan2({}, {})", x, y)?, 682 | _ => write!(w, "{:?}", self)?, 683 | } 684 | } 685 | TernopRetVar(x, y, z, f) => { 686 | match **f { 687 | Range => write!(w, "compute::range({}, {}, {})", x, y, z)?, 688 | Rangel => write!(w, "compute::rangel({}, {}, {})", x, y, z)?, 689 | Ranger => write!(w, "compute::ranger({}, {}, {})", x, y, z)?, 690 | Rangem => write!(w, "compute::rangem({}, {}, {})", x, y, z)?, 691 | _ => write!(w, "{:?}", self)?, 692 | } 693 | } 694 | UnopRetVar(x, f) => { 695 | match **f { 696 | Even => write!(w, "compute::even({})", x)?, 697 | Odd => write!(w, "compute::odd({})", x)?, 698 | Neg => write!(w, "compute::neg({})", x)?, 699 | Inc => write!(w, "compute::inc({})", x)?, 700 | Reci => write!(w, "compute::reci({})", x)?, 701 | Abs => write!(w, "compute::abs({})", x)?, 702 | Len => write!(w, "compute::len({})", x)?, 703 | Prob => write!(w, "compute::prob({})", x)?, 704 | Probl => write!(w, "compute::probl({})", x)?, 705 | Probr => write!(w, "compute::probr({})", x)?, 706 | Probm => write!(w, "compute::probm({})", x)?, 707 | Sqrt => write!(w, "compute::sqrt({})", x)?, 708 | Ln => write!(w, "compute::ln({})", x)?, 709 | Log2 => write!(w, "compute::log2({})", x)?, 710 | Log10 => write!(w, "compute::log10({})", x)?, 711 | Exp => write!(w, "compute::exp({})", x)?, 712 | Sin => write!(w, "compute::sin({})", x)?, 713 | Asin => write!(w, "compute::asin({})", x)?, 714 | Cos => write!(w, "compute::cos({})", x)?, 715 | Acos => write!(w, "compute::acos({})", x)?, 716 | Tan => write!(w, "compute::tan({})", x)?, 717 | Atan => write!(w, "compute::atan({})", x)?, 718 | Dim => write!(w, "compute::dim({})", x)?, 719 | IsSquareMat => write!(w, "compute::is_square_mat({})", x)?, 720 | Transpose => write!(w, "compute::transpose({})", x)?, 721 | Arity => write!(w, "compute::arity({})", x)?, 722 | TypeOf => write!(w, "compute::type_of({})", x)?, 723 | _ => write!(w, "{:?}", self)?, 724 | } 725 | } 726 | // _ => write!(w, "{:?}", self)?, 727 | } 728 | Ok(()) 729 | } 730 | } 731 | 732 | impl fmt::Display for Symbol { 733 | fn fmt(&self, w: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> { 734 | let rule = false; 735 | self.display(w, rule) 736 | } 737 | } 738 | -------------------------------------------------------------------------------- /examples/poi.rs: -------------------------------------------------------------------------------- 1 | use poi::*; 2 | use levenshtein::levenshtein; 3 | use std::collections::{BinaryHeap, HashSet}; 4 | 5 | const DEFAULT_GOAL_DEPTH: u64 = 2; 6 | 7 | fn main() { 8 | println!("=== Poi 0.24 ==="); 9 | println!("Type `help` for more information."); 10 | let ref mut std = std(); 11 | 12 | let mut prev_expr: Option = None; 13 | let mut goal: Option = None; 14 | let mut real_goal: Option = None; 15 | let mut goal_depth: u64 = DEFAULT_GOAL_DEPTH; 16 | let mut use_min_depth = true; 17 | let mut dirs: Vec = vec![]; 18 | 19 | // Levenshtein data. 20 | // 21 | // Detects whether an equivalence has already been tried. 22 | let mut levs: HashSet = HashSet::new(); 23 | // Picks minimum Levenshtein distance. 24 | let mut min_lev: BinaryHeap = BinaryHeap::new(); 25 | let mut auto_lev = false; 26 | let mut last_input_empty = false; 27 | 28 | loop { 29 | use std::io::{self, Write}; 30 | 31 | let mut input = String::new(); 32 | if auto_lev { 33 | input = "lev".into(); 34 | } else { 35 | print!("> "); 36 | io::stdout().flush().unwrap(); 37 | match io::stdin().read_line(&mut input) { 38 | Ok(_) => {} 39 | Err(_) => { 40 | println!("ERROR: Could not read input"); 41 | continue; 42 | } 43 | }; 44 | } 45 | 46 | // Ignore comments. 47 | if input.starts_with("// ") {continue}; 48 | 49 | let mut repeat = input.starts_with(" ") && input.trim() == "" || 50 | input.trim() == "repeat"; 51 | if !repeat {match input.trim() { 52 | "" => { 53 | // Exit program when empty strings are read. 54 | if last_input_empty {break}; 55 | last_input_empty = true; 56 | 57 | // Print separator for readability. 58 | print!("\n------------------------------------------------------------------------\n"); 60 | continue; 61 | } 62 | "help" => {print_help(); continue} 63 | "help goal" => {print_help_goal(); continue} 64 | "help norm" => {print_help_norm(); continue} 65 | "help eqv" => {print_help_eqv(); continue} 66 | "help sym" => {print_help_sym(); continue} 67 | "help asym" => {print_help_asym(); continue} 68 | "help dom" => {print_help_dom(); continue} 69 | "help triv" => {print_help_triv(); continue} 70 | "help ex" => {print_help_ex(); continue} 71 | "help rad" => {print_help_rad(); continue} 72 | "help imag" => {print_help_imag(); continue} 73 | "help eps" => {print_help_eps(); continue} 74 | "help deriv" => {print_help_deriv(); continue} 75 | "help integ" => {print_help_integ(); continue} 76 | "help list" => {print_help_list(); continue} 77 | "help symbol" => {print_help_symbol(); continue} 78 | "help script" => {print_help_script(); continue} 79 | "help catus" => {print_help_catus(); continue} 80 | "std" => {for k in &*std {println!("{}", k)}; continue} 81 | "inline all" => { 82 | if let Some(expr) = &prev_expr { 83 | prev_expr = Some(match expr.inline_all(&std) { 84 | Ok(x) => { 85 | repeat = true; 86 | x 87 | } 88 | Err(err) => { 89 | println!("ERROR: {:?}", err); 90 | continue; 91 | } 92 | }); 93 | } else { 94 | println!("ERROR: No previous expression"); 95 | continue; 96 | } 97 | } 98 | "bye" => break, 99 | "reset goal" => { 100 | goal = None; 101 | real_goal = None; 102 | println!("Poi: Goal is reset."); 103 | continue 104 | } 105 | "reset depth" => { 106 | goal_depth = DEFAULT_GOAL_DEPTH; 107 | println!("Poi: Goal depth is reset to {}.", goal_depth); 108 | continue; 109 | } 110 | "inc depth" => { 111 | goal_depth += 1; 112 | println!("Poi: Goal depth is set to {}.", goal_depth); 113 | continue; 114 | } 115 | "min depth" => { 116 | use_min_depth = true; 117 | println!("Poi: Automated minimum depth toward goal."); 118 | continue; 119 | } 120 | "pick depth" => { 121 | use_min_depth = false; 122 | println!("Poi: User chooses equivalence toward goal."); 123 | continue; 124 | } 125 | "clear dir" => { 126 | dirs.clear(); 127 | println!("Poi: Directories cleared."); 128 | continue; 129 | } 130 | "lev" => { 131 | if let Some(MinLev {lev, expr: e, ..}) = min_lev.pop() { 132 | println!("Poi: Found minimum {} lev.", lev); 133 | input = format!("{}", e); 134 | } else { 135 | auto_lev = false; 136 | print!("Poi: No minimum lev found."); 137 | if goal.is_none() {print!(" Goal is not set (use `goal `).")}; 138 | println!(""); 139 | continue; 140 | } 141 | } 142 | "auto lev" => { 143 | auto_lev = true; 144 | continue; 145 | } 146 | x => { 147 | // Print definitions of symbol. 148 | if x.starts_with("def ") { 149 | match parse_str(x[4..].trim(), &dirs) { 150 | Ok(Expr::Sym(s)) => { 151 | let mut found = false; 152 | for k in &*std { 153 | if let Knowledge::Def(a, b) = k { 154 | if a == &s { 155 | found = true; 156 | println!("{}", b); 157 | }; 158 | } 159 | } 160 | if !found {println!("(no definition found)")}; 161 | continue; 162 | } 163 | Err(err) => { 164 | println!("ERROR:\n{}", err); 165 | continue; 166 | } 167 | _ => { 168 | println!("ERROR:\nExpected symbol"); 169 | continue; 170 | } 171 | } 172 | } else if x.starts_with("inline ") { 173 | match parse_str(x[7..].trim(), &dirs) { 174 | Ok(Expr::Sym(s)) => { 175 | if let Some(expr) = &prev_expr { 176 | prev_expr = Some(match expr.inline(&s, &std) { 177 | Ok(x) => { 178 | repeat = true; 179 | x 180 | } 181 | Err(err) => { 182 | println!("ERROR: {:?}", err); 183 | continue; 184 | } 185 | }); 186 | } else { 187 | println!("ERROR: No previous expression"); 188 | continue; 189 | } 190 | } 191 | Err(err) => { 192 | println!("ERROR:\n{}", err); 193 | continue; 194 | } 195 | _ => { 196 | println!("ERROR:\nExpected symbol"); 197 | continue; 198 | } 199 | } 200 | } else if x.starts_with("echo ") { 201 | match parse_data_str(x[5..].trim(), &dirs) { 202 | Ok(x) => { 203 | match x { 204 | ParseData::Expr(x) => { 205 | println!("{}", x); 206 | println!("{:?}", x); 207 | } 208 | ParseData::Knowledge(xs) => { 209 | for x in xs { 210 | println!("{}", x); 211 | println!("{:?}", x); 212 | println!(""); 213 | } 214 | } 215 | } 216 | continue; 217 | } 218 | Err(err) => { 219 | println!("ERROR:\n{}", err); 220 | continue; 221 | } 222 | } 223 | } else if x.starts_with("eval ") { 224 | match parse_str(x[5..].trim(), &dirs) { 225 | Ok(x) => { 226 | match x.eval(&std) { 227 | Ok(x) => { 228 | println!("{}", x); 229 | continue; 230 | } 231 | Err(err) => { 232 | println!("ERROR:\n{:?}", err); 233 | continue; 234 | } 235 | } 236 | } 237 | Err(err) => { 238 | println!("ERROR:\n{}", err); 239 | continue; 240 | } 241 | } 242 | } else if x.starts_with("goal ") { 243 | match parse_str(x[5..].trim(), &dirs) { 244 | Ok(mut expr) => { 245 | // Reduce expression first. 246 | loop { 247 | if let Ok((nexpr, i)) = expr.reduce(std) { 248 | if nexpr == expr {break}; 249 | expr = nexpr; 250 | println!("{}\n∵ {}", expr, std[i]); 251 | } else { 252 | break; 253 | } 254 | } 255 | println!("new goal: {}", expr); 256 | goal = Some(expr); 257 | levs.clear(); 258 | min_lev.clear(); 259 | continue; 260 | } 261 | Err(err) => { 262 | println!("ERROR:\n{}", err); 263 | continue; 264 | } 265 | } 266 | } else if x.starts_with("subgoal ") { 267 | match parse_str(x[8..].trim(), &dirs) { 268 | Ok(mut expr) => { 269 | // Reduce expression first. 270 | loop { 271 | if let Ok((nexpr, i)) = expr.reduce(std) { 272 | if nexpr == expr {break}; 273 | expr = nexpr; 274 | println!("{}\n∵ {}", expr, std[i]); 275 | } else { 276 | break; 277 | } 278 | } 279 | println!("new subgoal: {}", expr); 280 | real_goal = goal.clone(); 281 | goal = Some(expr); 282 | repeat = true; 283 | } 284 | Err(err) => { 285 | println!("ERROR:\n{}", err); 286 | continue; 287 | } 288 | } 289 | } else if x.starts_with("auto ") { 290 | input = x[5..].trim().into(); 291 | auto_lev = true; 292 | } else if x.starts_with("open ") { 293 | if let Some(s) = json_str(&x[5..]) { 294 | println!("Poi: Added directory `{}`.", s); 295 | dirs.push(s); 296 | } 297 | continue; 298 | } else { 299 | // Reset Levenshtein data. 300 | levs.clear(); 301 | min_lev.clear(); 302 | } 303 | } 304 | }} 305 | 306 | last_input_empty = false; 307 | 308 | let mut expr = if repeat { 309 | if let Some(expr) = prev_expr {expr} else {continue} 310 | } else { 311 | match parse_data_str(&input, &dirs) { 312 | Ok(ParseData::Expr(expr)) => expr, 313 | Ok(ParseData::Knowledge(knowledge)) => { 314 | for k in &knowledge { 315 | println!("{};", k); 316 | } 317 | std.extend(knowledge.into_iter()); 318 | println!("Poi: Rules added."); 319 | continue; 320 | } 321 | Err(err) => { 322 | println!("ERROR:\n{}", err); 323 | continue; 324 | } 325 | } 326 | }; 327 | 328 | // Keeps track of goal depth to reduce search space 329 | // when getting closer to the goal using minimum depth selection. 330 | let mut min: Option = None; 331 | 'process_expr: loop { 332 | println!("{}", expr); 333 | loop { 334 | if let Ok((nexpr, i)) = expr.reduce(std) { 335 | if nexpr == expr {break}; 336 | expr = nexpr; 337 | println!("{}\n∵ {}", expr, std[i]); 338 | } else { 339 | break; 340 | } 341 | } 342 | 343 | let mut goal_txt = String::new(); 344 | let goal_reached = if let Some(g) = &goal { 345 | goal_txt = format!("{}", g); 346 | if &expr == g { 347 | goal = None; 348 | if real_goal.is_some() { 349 | goal = real_goal.clone(); 350 | real_goal = None; 351 | } 352 | true 353 | } else {false} 354 | } else {false}; 355 | 356 | if !goal_reached { 357 | let mut found_count = 0; 358 | let mut first_found: Option = None; 359 | let mut min_found: Option<(usize, u64)> = None; 360 | let equivalences = expr.equivalences(std); 361 | let mut equiv_levs: Vec> = vec![None; equivalences.len()]; 362 | loop { 363 | let mut line = false; 364 | let default_depth = min.unwrap_or(goal_depth); 365 | let depths = if use_min_depth && default_depth != 0 { 366 | // Perform a quick depth 0 scan before diving into proof depths. 367 | vec![0, default_depth] 368 | } else { 369 | vec![min.unwrap_or(goal_depth)] 370 | }; 371 | 'depths: for &depth in &depths { 372 | for i in 0..equivalences.len() { 373 | let mut cont = false; 374 | let mut br = false; 375 | let j = equivalences[i].1; 376 | let display = if let Some(g) = &goal { 377 | let eqv_expr = equivalences[i].0.reduce_all(std); 378 | let mut history = vec![expr.clone(), eqv_expr.clone()]; 379 | match find_goal( 380 | g, 381 | &goal_txt, 382 | &eqv_expr, 383 | std, 384 | depth, 385 | &mut history, 386 | &levs 387 | ) { 388 | Ok(d) => { 389 | found_count += 1; 390 | if first_found.is_none() { 391 | first_found = Some(i); 392 | } 393 | if min_found.is_none() || min_found.unwrap().1 > d { 394 | min_found = Some((i, d)); 395 | } 396 | if line {line = false; println!("")}; 397 | print!("depth: {} ", d); 398 | if d == 0 { 399 | expr = equivalences[i].0.clone(); 400 | cont = true; 401 | } else if min == Some(d) { 402 | br = true; 403 | } 404 | true 405 | } 406 | Err(x) => { 407 | equiv_levs[i] = x; 408 | false 409 | } 410 | } 411 | } else {true}; 412 | if display { 413 | println!("<=> {}\n ∵ {}", equivalences[i].0, std[j]); 414 | } else { 415 | if depth != 0 { 416 | line = true; 417 | print!("."); 418 | io::stdout().flush().unwrap(); 419 | } 420 | } 421 | if cont {continue 'process_expr}; 422 | if br {break 'depths}; 423 | } 424 | 425 | if goal.is_none() {break 'depths}; 426 | } 427 | 428 | if line {println!("")}; 429 | 430 | if use_min_depth && min_found.is_some() { 431 | expr = equivalences[min_found.unwrap().0].0.clone(); 432 | min = Some(min_found.unwrap().1 - 1); 433 | continue 'process_expr; 434 | } else if found_count == 1 && goal.is_some() { 435 | expr = equivalences[first_found.unwrap()].0.clone(); 436 | continue 'process_expr; 437 | } else if found_count > 0 || goal.is_none() { 438 | break; 439 | } else { 440 | if !auto_lev { 441 | println!("Poi: Could not find goal in {} steps (Tip: Use `inc depth`).", goal_depth); 442 | } 443 | break; 444 | } 445 | } 446 | 447 | if found_count == 0 && goal.is_some() { 448 | for i in 0..equivalences.len() { 449 | let txt = format!("{}", equivalences[i].0); 450 | let edit_dist = if let Some(x) = &equiv_levs[i] { 451 | x.0 452 | } else { 453 | // Use Levenshtein distance of unreduced equivalence. 454 | levenshtein(&txt, &goal_txt) 455 | }; 456 | let j = equivalences[i].1; 457 | if !levs.contains(&txt) { 458 | levs.insert(txt.clone()); 459 | let x = MinLev { 460 | lev: edit_dist, 461 | expr: equivalences[i].0.clone(), 462 | std_ind: j 463 | }; 464 | if !auto_lev { 465 | x.print(std); 466 | if let Some(expr) = equiv_levs[i].as_ref().map(|n| &n.1) { 467 | print!(" `{}`", expr); 468 | } 469 | println!(""); 470 | } 471 | min_lev.push(x); 472 | } 473 | } 474 | 475 | if auto_lev { 476 | if let Some(min_lev) = min_lev.peek() { 477 | min_lev.print(std); 478 | println!(""); 479 | } 480 | } 481 | } 482 | } 483 | 484 | println!("∴ {}", expr); 485 | if goal_reached { 486 | auto_lev = false; 487 | if goal.is_some() { 488 | println!("Poi: Subgoal achieved."); 489 | } else { 490 | println!("Q.E.D."); 491 | } 492 | } else { 493 | if goal.is_some() { 494 | let txt = format!("{}", expr); 495 | let edit_dist = levenshtein(&txt, &goal_txt); 496 | println!("{} lev", edit_dist); 497 | } 498 | } 499 | break; 500 | } 501 | 502 | prev_expr = Some(expr); 503 | } 504 | } 505 | 506 | // Parses a JSON string. 507 | fn json_str(txt: &str) -> Option { 508 | use read_token::ReadToken; 509 | let r = ReadToken::new(txt, 0); 510 | if let Some(range) = r.string() { 511 | if let Ok(txt) = r.parse_string(range.length) { 512 | Some(txt) 513 | } else { 514 | println!("ERROR:\nCould not parse string"); 515 | None 516 | } 517 | } else { 518 | println!("ERROR:\nExpected string"); 519 | None 520 | } 521 | } 522 | 523 | // Returns `Err(Some(lev))` for minimum Levenhstein distance found. 524 | fn find_goal( 525 | goal: &Expr, 526 | goal_txt: &str, 527 | expr: &Expr, 528 | std: &[Knowledge], 529 | depth: u64, 530 | history: &mut Vec, 531 | levs: &HashSet, 532 | ) -> Result> { 533 | if goal == expr {return Ok(0)}; 534 | 535 | let mut min_lev: Option<(usize, Expr)> = None; 536 | if depth > 0 { 537 | let equivalences = expr.equivalences(std); 538 | let n = history.len(); 539 | // Use breath-first search. 540 | for i in 0..equivalences.len() { 541 | let expr = equivalences[i].0.reduce_all(std); 542 | 543 | for i in 0..history.len() { 544 | if &history[i] == &expr {continue}; 545 | } 546 | 547 | if goal == &expr {return Ok(1)}; 548 | 549 | let txt = format!("{}", expr); 550 | 551 | if levs.contains(&txt) {continue}; 552 | 553 | let edit_dist = levenshtein(&txt, goal_txt); 554 | if min_lev.as_ref().map(|n| n.0 > edit_dist).unwrap_or(true) { 555 | min_lev = Some((edit_dist, expr.clone())); 556 | } 557 | history.push(expr); 558 | } 559 | for i in n..history.len() { 560 | // Add depth to the output. 561 | let expr = history[i].clone(); 562 | match find_goal(goal, goal_txt, &expr, std, depth - 1, history, levs) { 563 | Ok(d) => { 564 | return Ok(d+1); 565 | } 566 | Err(None) => {} 567 | Err(Some((lev, expr))) => { 568 | if min_lev.as_ref().map(|n| n.0 > lev).unwrap_or(true) { 569 | min_lev = Some((lev, expr)); 570 | } 571 | } 572 | } 573 | } 574 | } 575 | 576 | Err(min_lev) 577 | } 578 | 579 | #[derive(PartialEq)] 580 | struct MinLev { 581 | /// Levenhstein distance. 582 | pub lev: usize, 583 | /// Expression. 584 | pub expr: Expr, 585 | /// Axiom index. 586 | pub std_ind: usize, 587 | } 588 | 589 | impl MinLev { 590 | pub fn print(&self, std: &[Knowledge]) { 591 | print!("<=> {}\n ∵ {}\n {} min lev", 592 | self.expr, std[self.std_ind], self.lev); 593 | } 594 | } 595 | 596 | impl Eq for MinLev {} 597 | 598 | impl PartialOrd for MinLev { 599 | fn partial_cmp(&self, b: &Self) -> Option { 600 | Some(self.lev.cmp(&b.lev).reverse()) 601 | } 602 | } 603 | 604 | impl Ord for MinLev { 605 | fn cmp(&self, b: &Self) -> std::cmp::Ordering { 606 | self.lev.cmp(&b.lev).reverse() 607 | } 608 | } 609 | 610 | fn print_help() {print!("{}", include_str!("../assets/help/help.txt"))} 611 | fn print_help_goal() {print!("{}", include_str!("../assets/help/goal.txt"))} 612 | fn print_help_norm() {print!("{}", include_str!("../assets/help/norm.txt"))} 613 | fn print_help_eqv() {print!("{}", include_str!("../assets/help/eqv.txt"))} 614 | fn print_help_sym() {print!("{}", include_str!("../assets/help/sym.txt"))} 615 | fn print_help_asym() {print!("{}", include_str!("../assets/help/asym.txt"))} 616 | fn print_help_dom() {print!("{}", include_str!("../assets/help/dom.txt"))} 617 | fn print_help_triv() {print!("{}", include_str!("../assets/help/triv.txt"))} 618 | fn print_help_ex() {print!("{}", include_str!("../assets/help/ex.txt"))} 619 | fn print_help_rad() {print!("{}", include_str!("../assets/help/rad.txt"))} 620 | fn print_help_imag() {print!("{}", include_str!("../assets/help/imag.txt"))} 621 | fn print_help_eps() {print!("{}", include_str!("../assets/help/eps.txt"))} 622 | fn print_help_deriv() {print!("{}", include_str!("../assets/help/deriv.txt"))} 623 | fn print_help_integ() {print!("{}", include_str!("../assets/help/integ.txt"))} 624 | fn print_help_list() {print!("{}", include_str!("../assets/help/list.txt"))} 625 | fn print_help_symbol() {println!("{}", include_str!("../assets/help/symbol.txt"))} 626 | fn print_help_script() {println!("{}", include_str!("../assets/help/script.txt"))} 627 | fn print_help_catus() {println!("{}", include_str!("../assets/help/catus.txt"))} 628 | -------------------------------------------------------------------------------- /src/parsing.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use piston_meta::{Convert, Range}; 4 | 5 | fn parse_expr(node: &str, dirs: &[String], mut convert: Convert, ignored: &mut Vec) -> Result<(Range, Expr), ()> { 6 | let start = convert; 7 | let start_range = convert.start_node(node)?; 8 | convert.update(start_range); 9 | 10 | let mut expr: Option = None; 11 | loop { 12 | if let Ok(range) = convert.end_node(node) { 13 | convert.update(range); 14 | break; 15 | } else if let Ok((range, val)) = parse_alg(dirs, convert, ignored) { 16 | convert.update(range); 17 | expr = Some(val); 18 | } else if let Ok((range, val)) = parse_seq(dirs, convert, ignored) { 19 | convert.update(range); 20 | expr = Some(val); 21 | } else if let Ok((range, val)) = parse_tup(dirs, convert, ignored) { 22 | convert.update(range); 23 | if let Tup(val) = &val { 24 | // Reduce tuple singleton. 25 | if val.len() == 1 { 26 | expr = Some(val[0].clone()); 27 | continue; 28 | } 29 | } 30 | expr = Some(val); 31 | } else if let Ok((range, val)) = parse_list(dirs, convert, ignored) { 32 | convert.update(range); 33 | expr = Some(val); 34 | } else if let Ok((range, val)) = parse_rapp(dirs, convert, ignored) { 35 | convert.update(range); 36 | expr = Some(val); 37 | } else if let Ok((range, val)) = convert.meta_string("var") { 38 | convert.update(range); 39 | expr = Some(Sym(val.into())); 40 | } else if let Ok((range, _)) = convert.meta_bool("ret_ty") { 41 | convert.update(range); 42 | if let Some(Sym(x)) = expr { 43 | expr = Some(typ(Var(Arc::new(format!("{}", x))), RetType)); 44 | } else { 45 | expr = Some(Sym(RetType)); 46 | } 47 | } else if let Ok((range, val)) = convert.meta_bool("bool") { 48 | convert.update(range); 49 | expr = Some(val.into()); 50 | } else if let Ok((range, val)) = convert.meta_f64("num") { 51 | convert.update(range); 52 | expr = Some(val.into()); 53 | } else if let Ok((range, val)) = convert.meta_f64("num_pi") { 54 | convert.update(range); 55 | expr = Some(app2(Mul, val, Pi)); 56 | } else if let Ok((range, val)) = convert.meta_f64("num_tau") { 57 | convert.update(range); 58 | expr = Some(app2(Mul, val, Tau)); 59 | } else if let Ok((range, val)) = convert.meta_f64("num_eps") { 60 | convert.update(range); 61 | expr = Some(app2(Mul, val, Eps)); 62 | } else if let Ok((range, val)) = convert.meta_f64("num_imag") { 63 | convert.update(range); 64 | expr = Some(app2(Mul, val, Imag)); 65 | } else if let Ok((range, val)) = convert.meta_f64("num_imag2") { 66 | convert.update(range); 67 | expr = Some(app2(Mul, val, Imag2)); 68 | } else if let Ok((range, val)) = convert.meta_f64("num_imag3") { 69 | convert.update(range); 70 | expr = Some(app2(Mul, val, Imag3)); 71 | } else if let Ok((range, val)) = convert.meta_string("singleton") { 72 | convert.update(range); 73 | expr = Some(Sym(Symbol::Singleton(val))); 74 | } else if let Ok((range, val)) = convert.meta_string("list_var") { 75 | convert.update(range); 76 | expr = Some(Sym(Symbol::ListVar(val))); 77 | } else if let Ok((range, val)) = convert.meta_string("ret_int_var") { 78 | convert.update(range); 79 | expr = Some(Sym(Symbol::RetIntVar(val))); 80 | } else if let Ok((range, val)) = convert.meta_string("ret_pos_var") { 81 | convert.update(range); 82 | expr = Some(Sym(Symbol::RetPosVar(val))); 83 | } else if let Ok((range, val)) = convert.meta_string("ret_neg_var") { 84 | convert.update(range); 85 | expr = Some(Sym(Symbol::RetNegVar(val))); 86 | } else if let Ok((range, val)) = convert.meta_string("ret_var") { 87 | convert.update(range); 88 | expr = Some(Sym(Symbol::RetVar(val))); 89 | } else if let Ok((range, val)) = convert.meta_string("not_ret_var") { 90 | convert.update(range); 91 | expr = Some(Sym(Symbol::NotRetVar(val))); 92 | } else if let Ok((range, val)) = parse_compute(convert, ignored) { 93 | convert.update(range); 94 | expr = Some(val); 95 | } else if let Ok((range, val)) = parse_no_constr(convert, ignored) { 96 | convert.update(range); 97 | expr = Some(val); 98 | } else if let Ok((range, val)) = parse_no_subst(convert, ignored) { 99 | convert.update(range); 100 | expr = Some(val); 101 | } else if let Ok((range, val)) = parse_arity(convert, ignored) { 102 | convert.update(range); 103 | expr = Some(val); 104 | } else if let Ok((range, val)) = parse_not_in_var_name(convert, ignored) { 105 | convert.update(range); 106 | expr = Some(val); 107 | } else if let Ok((range, val)) = parse_head_tail_tup(convert, ignored) { 108 | convert.update(range); 109 | expr = Some(val); 110 | } else if let Ok((range, val)) = parse_head_tail_list(convert, ignored) { 111 | convert.update(range); 112 | expr = Some(val); 113 | } else if let Ok((range, val)) = convert.meta_string("poi") { 114 | convert.update(range); 115 | let mut found = false; 116 | for dir in dirs.iter().rev() { 117 | use std::fs::File; 118 | use std::io::Read; 119 | use std::path::PathBuf; 120 | 121 | let path = PathBuf::from(dir).join(val.as_ref().to_owned() + ".poi"); 122 | 123 | let mut data_file = match File::open(path) { 124 | Ok(f) => f, 125 | Err(_) => continue, 126 | }; 127 | let mut data = String::new(); 128 | data_file.read_to_string(&mut data).unwrap(); 129 | 130 | expr = Some(parse_str(&data, dirs).map_err(|err| { 131 | eprintln!("ERROR:\n{}", err); 132 | () 133 | })?); 134 | found = true; 135 | break; 136 | } 137 | if !found { 138 | eprintln!("ERROR:\nPoi file `{}` not found", val); 139 | return Err(()) 140 | }; 141 | } else { 142 | let range = convert.ignore(); 143 | convert.update(range); 144 | ignored.push(range); 145 | } 146 | } 147 | 148 | let expr = expr.ok_or(())?; 149 | Ok((convert.subtract(start), expr)) 150 | } 151 | 152 | fn parse_head_tail_list( 153 | mut convert: Convert, 154 | ignored: &mut Vec, 155 | ) -> Result<(Range, Expr), ()> { 156 | let start = convert; 157 | let node = "head_tail_list"; 158 | let start_range = convert.start_node(node)?; 159 | convert.update(start_range); 160 | 161 | let mut head: Option> = None; 162 | let mut tail: Option> = None; 163 | loop { 164 | if let Ok(range) = convert.end_node(node) { 165 | convert.update(range); 166 | break; 167 | } else if let Ok((range, val)) = convert.meta_string("head") { 168 | convert.update(range); 169 | head = Some(val); 170 | } else if let Ok((range, val)) = convert.meta_string("tail") { 171 | convert.update(range); 172 | tail = Some(val); 173 | } else { 174 | let range = convert.ignore(); 175 | convert.update(range); 176 | ignored.push(range); 177 | } 178 | } 179 | 180 | let head = head.ok_or(())?; 181 | let tail = tail.ok_or(())?; 182 | Ok((convert.subtract(start), 183 | Sym(Symbol::HeadTailList(Box::new(Sym(Var(head))), Box::new(Sym(Var(tail))))))) 184 | } 185 | 186 | fn parse_head_tail_tup( 187 | mut convert: Convert, 188 | ignored: &mut Vec, 189 | ) -> Result<(Range, Expr), ()> { 190 | let start = convert; 191 | let node = "head_tail_tup"; 192 | let start_range = convert.start_node(node)?; 193 | convert.update(start_range); 194 | 195 | let mut head: Option> = None; 196 | let mut tail: Option> = None; 197 | loop { 198 | if let Ok(range) = convert.end_node(node) { 199 | convert.update(range); 200 | break; 201 | } else if let Ok((range, val)) = convert.meta_string("head") { 202 | convert.update(range); 203 | head = Some(val); 204 | } else if let Ok((range, val)) = convert.meta_string("tail") { 205 | convert.update(range); 206 | tail = Some(val); 207 | } else { 208 | let range = convert.ignore(); 209 | convert.update(range); 210 | ignored.push(range); 211 | } 212 | } 213 | 214 | let head = head.ok_or(())?; 215 | let tail = tail.ok_or(())?; 216 | Ok((convert.subtract(start), 217 | Sym(Symbol::HeadTailTup(Box::new(Sym(Var(head))), Box::new(Sym(Var(tail))))))) 218 | } 219 | 220 | fn parse_arity( 221 | mut convert: Convert, 222 | ignored: &mut Vec, 223 | ) -> Result<(Range, Expr), ()> { 224 | let start = convert; 225 | let node = "arity"; 226 | let start_range = convert.start_node(node)?; 227 | convert.update(start_range); 228 | 229 | let mut fun: Option> = None; 230 | let mut arg: Option = None; 231 | loop { 232 | if let Ok(range) = convert.end_node(node) { 233 | convert.update(range); 234 | break; 235 | } else if let Ok((range, val)) = convert.meta_string("fun") { 236 | convert.update(range); 237 | fun = Some(val); 238 | } else if let Ok((range, val)) = convert.meta_f64("arg") { 239 | convert.update(range); 240 | arg = Some(val as usize); 241 | } else { 242 | let range = convert.ignore(); 243 | convert.update(range); 244 | ignored.push(range); 245 | } 246 | } 247 | 248 | let fun = fun.ok_or(())?; 249 | let arg = arg.ok_or(())?; 250 | Ok((convert.subtract(start), Sym(Symbol::ArityVar(fun, arg)))) 251 | } 252 | 253 | fn parse_not_in_var_name( 254 | mut convert: Convert, 255 | ignored: &mut Vec, 256 | ) -> Result<(Range, Expr), ()> { 257 | let start = convert; 258 | let node = "not_in_var_name"; 259 | let start_range = convert.start_node(node)?; 260 | convert.update(start_range); 261 | 262 | let mut x: Option> = None; 263 | let mut y: Option> = None; 264 | loop { 265 | if let Ok(range) = convert.end_node(node) { 266 | convert.update(range); 267 | break; 268 | } else if let Ok((range, val)) = convert.meta_string("x") { 269 | convert.update(range); 270 | x = Some(val); 271 | } else if let Ok((range, val)) = convert.meta_string("y") { 272 | convert.update(range); 273 | y = Some(val); 274 | } else { 275 | let range = convert.ignore(); 276 | convert.update(range); 277 | ignored.push(range); 278 | } 279 | } 280 | 281 | let x = x.ok_or(())?; 282 | let y = y.ok_or(())?; 283 | Ok((convert.subtract(start), Sym(Symbol::NotInVarName(x, y)))) 284 | } 285 | 286 | fn parse_no_constr( 287 | mut convert: Convert, 288 | ignored: &mut Vec, 289 | ) -> Result<(Range, Expr), ()> { 290 | let start = convert; 291 | let node = "no_constr"; 292 | let start_range = convert.start_node(node)?; 293 | convert.update(start_range); 294 | 295 | let mut fun: Option> = None; 296 | loop { 297 | if let Ok(range) = convert.end_node(node) { 298 | convert.update(range); 299 | break; 300 | } else if let Ok((range, val)) = convert.meta_string("fun") { 301 | convert.update(range); 302 | fun = Some(val); 303 | } else { 304 | let range = convert.ignore(); 305 | convert.update(range); 306 | ignored.push(range); 307 | } 308 | } 309 | 310 | let fun = fun.ok_or(())?; 311 | Ok((convert.subtract(start), Sym(Symbol::NoConstrVar(fun)))) 312 | } 313 | 314 | fn parse_no_subst( 315 | mut convert: Convert, 316 | ignored: &mut Vec, 317 | ) -> Result<(Range, Expr), ()> { 318 | let start = convert; 319 | let node = "no_subst"; 320 | let start_range = convert.start_node(node)?; 321 | convert.update(start_range); 322 | 323 | let mut fun: Option> = None; 324 | loop { 325 | if let Ok(range) = convert.end_node(node) { 326 | convert.update(range); 327 | break; 328 | } else if let Ok((range, val)) = convert.meta_string("fun") { 329 | convert.update(range); 330 | fun = Some(val); 331 | } else { 332 | let range = convert.ignore(); 333 | convert.update(range); 334 | ignored.push(range); 335 | } 336 | } 337 | 338 | let fun = fun.ok_or(())?; 339 | Ok((convert.subtract(start), Sym(Symbol::NoSubstVar(fun)))) 340 | } 341 | 342 | fn parse_compute( 343 | mut convert: Convert, 344 | ignored: &mut Vec, 345 | ) -> Result<(Range, Expr), ()> { 346 | let start = convert; 347 | let node = "compute"; 348 | let start_range = convert.start_node(node)?; 349 | convert.update(start_range); 350 | 351 | let mut fun: Option = None; 352 | let mut arg: Vec> = vec![]; 353 | loop { 354 | if let Ok(range) = convert.end_node(node) { 355 | convert.update(range); 356 | break; 357 | } else if let Ok((range, val)) = convert.meta_string("fun") { 358 | convert.update(range); 359 | fun = Some(val.into()); 360 | } else if let Ok((range, val)) = convert.meta_string("arg") { 361 | convert.update(range); 362 | arg.push(val); 363 | } else { 364 | let range = convert.ignore(); 365 | convert.update(range); 366 | ignored.push(range); 367 | } 368 | } 369 | 370 | let fun = fun.ok_or(())?; 371 | Ok((convert.subtract(start), match arg.len() { 372 | 1 => Sym(UnopRetVar(arg[0].clone(), Box::new(fun))), 373 | 2 => Sym(BinopRetVar(arg[0].clone(), arg[1].clone(), Box::new(fun))), 374 | 3 => Sym(TernopRetVar(arg[0].clone(), arg[1].clone(), arg[2].clone(), Box::new(fun))), 375 | _ => return Err(()) 376 | })) 377 | } 378 | 379 | fn parse_alg( 380 | dirs: &[String], 381 | mut convert: Convert, 382 | ignored: &mut Vec 383 | ) -> Result<(Range, Expr), ()> { 384 | let start = convert; 385 | let node = "alg"; 386 | let start_range = convert.start_node(node)?; 387 | convert.update(start_range); 388 | 389 | let mut op: Option = None; 390 | let mut left: Option = None; 391 | let mut unop: Option = None; 392 | let un = |expr: Expr, unop: &mut Option| { 393 | if let Some(Pariv) = unop { 394 | *unop = None; 395 | app2(Mul, Pariv, expr) 396 | } else if let Some(f) = unop { 397 | let f = f.clone(); 398 | *unop = None; 399 | app(f, expr) 400 | } else { 401 | expr 402 | } 403 | }; 404 | loop { 405 | if let Ok(range) = convert.end_node(node) { 406 | convert.update(range); 407 | break; 408 | } else if let Ok((range, val)) = parse_expr("alg_item", dirs, convert, ignored) { 409 | convert.update(range); 410 | if let (Some(expr), Some(op)) = (left, op.clone()) { 411 | left = Some(un(app2(op, expr, val), &mut unop)); 412 | } else { 413 | left = Some(val); 414 | } 415 | } else if let Ok((range, val)) = parse_alg(dirs, convert, ignored) { 416 | convert.update(range); 417 | if let (Some(expr), Some(op)) = (left, op.clone()) { 418 | left = Some(un(app2(op, expr, val), &mut unop)); 419 | } else { 420 | left = Some(un(val, &mut unop)); 421 | } 422 | } else if let Ok((range, _)) = convert.meta_bool("+") { 423 | convert.update(range); 424 | op = Some(Add); 425 | } else if let Ok((range, _)) = convert.meta_bool("-") { 426 | convert.update(range); 427 | op = Some(Sub); 428 | } else if let Ok((range, _)) = convert.meta_bool("*") { 429 | convert.update(range); 430 | op = Some(Mul); 431 | } else if let Ok((range, _)) = convert.meta_bool("/") { 432 | convert.update(range); 433 | op = Some(Div); 434 | } else if let Ok((range, _)) = convert.meta_bool("%") { 435 | convert.update(range); 436 | op = Some(Rem); 437 | } else if let Ok((range, _)) = convert.meta_bool("^") { 438 | convert.update(range); 439 | op = Some(Pow); 440 | } else if let Ok((range, _)) = convert.meta_bool("&") { 441 | convert.update(range); 442 | op = Some(And); 443 | } else if let Ok((range, _)) = convert.meta_bool("|") { 444 | convert.update(range); 445 | op = Some(Or); 446 | } else if let Ok((range, _)) = convert.meta_bool("++") { 447 | convert.update(range); 448 | op = Some(Concat); 449 | } else if let Ok((range, _)) = convert.meta_bool("<") { 450 | convert.update(range); 451 | op = Some(Lt); 452 | } else if let Ok((range, _)) = convert.meta_bool("<=") { 453 | convert.update(range); 454 | op = Some(Le); 455 | } else if let Ok((range, _)) = convert.meta_bool("=") { 456 | convert.update(range); 457 | op = Some(Eq); 458 | } else if let Ok((range, _)) = convert.meta_bool(">=") { 459 | convert.update(range); 460 | op = Some(Ge); 461 | } else if let Ok((range, _)) = convert.meta_bool(">") { 462 | convert.update(range); 463 | op = Some(Gt); 464 | } else if let Ok((range, _)) = convert.meta_bool("neg") { 465 | convert.update(range); 466 | unop = Some(Neg); 467 | } else if let Ok((range, _)) = convert.meta_bool("not") { 468 | convert.update(range); 469 | unop = Some(Not); 470 | } else if let Ok((range, _)) = convert.meta_bool("pariv") { 471 | convert.update(range); 472 | unop = Some(Pariv); 473 | } else { 474 | let range = convert.ignore(); 475 | convert.update(range); 476 | ignored.push(range); 477 | } 478 | } 479 | 480 | if unop.is_some() { 481 | left = if let Some(left) = left { 482 | Some(un(left, &mut unop)) 483 | } else {None}; 484 | } 485 | 486 | let left = left.ok_or(())?; 487 | Ok((convert.subtract(start), left)) 488 | } 489 | 490 | fn parse_tup(dirs: &[String], mut convert: Convert, ignored: &mut Vec) -> Result<(Range, Expr), ()> { 491 | let start = convert; 492 | let node = "tup"; 493 | let start_range = convert.start_node(node)?; 494 | convert.update(start_range); 495 | 496 | let mut items: Vec = vec![]; 497 | loop { 498 | if let Ok(range) = convert.end_node(node) { 499 | convert.update(range); 500 | break; 501 | } else if let Ok((range, val)) = parse_expr("item", dirs, convert, ignored) { 502 | convert.update(range); 503 | items.push(val); 504 | } else { 505 | let range = convert.ignore(); 506 | convert.update(range); 507 | ignored.push(range); 508 | } 509 | } 510 | 511 | Ok((convert.subtract(start), Tup(items))) 512 | } 513 | 514 | fn parse_list(dirs: &[String], mut convert: Convert, ignored: &mut Vec) -> Result<(Range, Expr), ()> { 515 | let start = convert; 516 | let node = "list"; 517 | let start_range = convert.start_node(node)?; 518 | convert.update(start_range); 519 | 520 | let mut items: Vec = vec![]; 521 | loop { 522 | if let Ok(range) = convert.end_node(node) { 523 | convert.update(range); 524 | break; 525 | } else if let Ok((range, val)) = parse_expr("item", dirs, convert, ignored) { 526 | convert.update(range); 527 | items.push(val); 528 | } else { 529 | let range = convert.ignore(); 530 | convert.update(range); 531 | ignored.push(range); 532 | } 533 | } 534 | 535 | Ok((convert.subtract(start), List(items))) 536 | } 537 | 538 | fn parse_rapp(dirs: &[String], mut convert: Convert, ignored: &mut Vec) -> Result<(Range, Expr), ()> { 539 | let start = convert; 540 | let node = "rapp"; 541 | let start_range = convert.start_node(node)?; 542 | convert.update(start_range); 543 | 544 | let mut sym: Option = None; 545 | let mut arg: Option = None; 546 | loop { 547 | if let Ok(range) = convert.end_node(node) { 548 | convert.update(range); 549 | break; 550 | } else if let Ok((range, _)) = convert.meta_bool("rty") { 551 | convert.update(range); 552 | sym = Some(Rty); 553 | } else if let Ok((range, _)) = convert.meta_bool("rlt") { 554 | convert.update(range); 555 | sym = Some(Rlt); 556 | } else if let Ok((range, _)) = convert.meta_bool("rle") { 557 | convert.update(range); 558 | sym = Some(Rle); 559 | } else if let Ok((range, _)) = convert.meta_bool("eq") { 560 | convert.update(range); 561 | sym = Some(Eq); 562 | } else if let Ok((range, _)) = convert.meta_bool("rgt") { 563 | convert.update(range); 564 | sym = Some(Rgt); 565 | } else if let Ok((range, _)) = convert.meta_bool("rge") { 566 | convert.update(range); 567 | sym = Some(Rge); 568 | } else if let Ok((range, _)) = convert.meta_bool("rsub") { 569 | convert.update(range); 570 | sym = Some(Rsub); 571 | } else if let Ok((range, _)) = convert.meta_bool("rdiv") { 572 | convert.update(range); 573 | sym = Some(Rdiv); 574 | } else if let Ok((range, _)) = convert.meta_bool("mul") { 575 | convert.update(range); 576 | sym = Some(Mul); 577 | } else if let Ok((range, _)) = convert.meta_bool("add") { 578 | convert.update(range); 579 | sym = Some(Add); 580 | } else if let Ok((range, _)) = convert.meta_bool("rpow") { 581 | convert.update(range); 582 | sym = Some(Rpow); 583 | } else if let Ok((range, val)) = parse_expr("arg", dirs, convert, ignored) { 584 | convert.update(range); 585 | arg = Some(val); 586 | } else { 587 | let range = convert.ignore(); 588 | convert.update(range); 589 | ignored.push(range); 590 | } 591 | } 592 | 593 | let sym = sym.ok_or(())?; 594 | let arg = arg.ok_or(())?; 595 | Ok((convert.subtract(start), app(sym, arg))) 596 | } 597 | 598 | fn parse_seq( 599 | dirs: &[String], 600 | mut convert: Convert, 601 | ignored: &mut Vec 602 | ) -> Result<(Range, Expr), ()> { 603 | let start = convert; 604 | let node = "seq"; 605 | let start_range = convert.start_node(node)?; 606 | convert.update(start_range); 607 | 608 | let mut op: Option = None; 609 | let mut left: Option = None; 610 | let mut right: Option = None; 611 | loop { 612 | if let Ok(range) = convert.end_node(node) { 613 | convert.update(range); 614 | break; 615 | } else if let Ok((range, val)) = parse_expr("left", dirs, convert, ignored) { 616 | convert.update(range); 617 | left = Some(val); 618 | } else if let Ok((range, val)) = parse_rapp(dirs, convert, ignored) { 619 | convert.update(range); 620 | left = Some(val); 621 | } else if let Ok((range, val)) = parse_alg(dirs, convert, ignored) { 622 | convert.update(range); 623 | left = Some(val); 624 | } else if let Ok((range, val)) = parse_list(dirs, convert, ignored) { 625 | convert.update(range); 626 | left = Some(val); 627 | } else if let Ok((range, val)) = parse_expr("path", dirs, convert, ignored) { 628 | convert.update(range); 629 | if let (Some(nleft), Some(nright), Some(nop)) = (&left, &right, op) { 630 | left = Some(EOp(nop, Box::new(nleft.clone()), Box::new(nright.clone()))); 631 | } 632 | right = Some(val); 633 | op = Some(Path); 634 | } else if let Ok((range, val)) = parse_expr("app", dirs, convert, ignored) { 635 | convert.update(range); 636 | if let (Some(nleft), Some(nright), Some(nop)) = (&left, &right, op) { 637 | left = Some(EOp(nop, Box::new(nleft.clone()), Box::new(nright.clone()))); 638 | } 639 | right = Some(val); 640 | op = Some(Apply); 641 | } else if let Ok((range, val)) = parse_expr("constr", dirs, convert, ignored) { 642 | convert.update(range); 643 | if let (Some(nleft), Some(nright), Some(nop)) = (&left, &right, op) { 644 | left = Some(EOp(nop, Box::new(nleft.clone()), Box::new(nright.clone()))); 645 | } 646 | right = Some(val); 647 | op = Some(Constrain); 648 | } else if let Ok((range, val)) = parse_expr("comp", dirs, convert, ignored) { 649 | convert.update(range); 650 | if let (Some(nleft), Some(nright), Some(nop)) = (&left, &right, op) { 651 | left = Some(EOp(nop, Box::new(nleft.clone()), Box::new(nright.clone()))); 652 | } 653 | right = Some(val); 654 | op = Some(Compose); 655 | } else if let Ok((range, val)) = parse_expr("typ", dirs, convert, ignored) { 656 | convert.update(range); 657 | if let (Some(nleft), Some(nright), Some(nop)) = (&left, &right, op) { 658 | left = Some(EOp(nop, Box::new(nleft.clone()), Box::new(nright.clone()))); 659 | } 660 | right = Some(val); 661 | op = Some(Type); 662 | } else { 663 | let range = convert.ignore(); 664 | convert.update(range); 665 | ignored.push(range); 666 | } 667 | } 668 | 669 | let op = op.ok_or(())?; 670 | let left = left.ok_or(())?; 671 | let right = right.ok_or(())?; 672 | Ok((convert.subtract(start), EOp(op, Box::new(left), Box::new(right)))) 673 | } 674 | 675 | fn parse_knowledge( 676 | dirs: &[String], 677 | mut convert: Convert, 678 | ignored: &mut Vec 679 | ) -> Result<(Range, Knowledge), ()> { 680 | enum KnowledgeOp { 681 | Eqv, 682 | EqvEval, 683 | Red, 684 | Def, 685 | } 686 | 687 | let start = convert; 688 | let node = "knowledge"; 689 | let start_range = convert.start_node(node)?; 690 | convert.update(start_range); 691 | 692 | let mut op: Option = None; 693 | let mut left: Option = None; 694 | let mut right: Option = None; 695 | loop { 696 | if let Ok(range) = convert.end_node(node) { 697 | convert.update(range); 698 | break; 699 | } else if let Ok((range, val)) = parse_expr("left", dirs, convert, ignored) { 700 | convert.update(range); 701 | left = Some(val); 702 | } else if let Ok((range, val)) = parse_expr("right", dirs, convert, ignored) { 703 | convert.update(range); 704 | right = Some(val); 705 | } else if let Ok((range, _)) = convert.meta_bool("eqveval") { 706 | convert.update(range); 707 | op = Some(KnowledgeOp::EqvEval); 708 | } else if let Ok((range, _)) = convert.meta_bool("eqv") { 709 | convert.update(range); 710 | op = Some(KnowledgeOp::Eqv); 711 | } else if let Ok((range, _)) = convert.meta_bool("red") { 712 | convert.update(range); 713 | op = Some(KnowledgeOp::Red); 714 | } else if let Ok((range, _)) = convert.meta_bool("def") { 715 | convert.update(range); 716 | op = Some(KnowledgeOp::Def); 717 | } else { 718 | let range = convert.ignore(); 719 | convert.update(range); 720 | ignored.push(range); 721 | } 722 | } 723 | 724 | let op = op.ok_or(())?; 725 | let left = left.ok_or(())?; 726 | let right = right.ok_or(())?; 727 | let knowledge = match op { 728 | KnowledgeOp::EqvEval => Knowledge::EqvEval(left, right), 729 | KnowledgeOp::Eqv => Knowledge::Eqv(left, right), 730 | KnowledgeOp::Red => Knowledge::Red(left, right), 731 | KnowledgeOp::Def => if let Sym(x) = left {Knowledge::Def(x, right)} else {return Err(())}, 732 | }; 733 | Ok((convert.subtract(start), knowledge)) 734 | } 735 | 736 | /// Stores result of parsing. 737 | pub enum ParseData { 738 | /// Parsed an expression. 739 | Expr(Expr), 740 | /// Parsed some knowledge. 741 | Knowledge(Vec), 742 | } 743 | 744 | impl ParseData { 745 | fn parse( 746 | dirs: &[String], 747 | mut convert: Convert, 748 | ignored: &mut Vec 749 | ) -> Result<(Range, ParseData), ()> { 750 | let start = convert; 751 | 752 | let data = if let Ok((range, val)) = parse_expr("expr", dirs, convert, ignored) { 753 | convert.update(range); 754 | ParseData::Expr(val) 755 | } else { 756 | let mut res = vec![]; 757 | while let Ok((range, val)) = parse_knowledge(dirs, convert, ignored) { 758 | convert.update(range); 759 | res.push(val); 760 | } 761 | ParseData::Knowledge(res) 762 | }; 763 | 764 | Ok((convert.subtract(start), data)) 765 | } 766 | } 767 | 768 | /// Parses an expression string. 769 | pub fn parse_str(data: &str, dirs: &[String]) -> Result { 770 | use piston_meta::{parse_errstr, syntax_errstr}; 771 | 772 | let syntax_src = include_str!("../assets/syntax.txt"); 773 | let syntax = syntax_errstr(syntax_src)?; 774 | 775 | let mut meta_data = vec![]; 776 | parse_errstr(&syntax, &data, &mut meta_data)?; 777 | 778 | // piston_meta::json::print(&meta_data); 779 | 780 | let convert = Convert::new(&meta_data); 781 | let mut ignored = vec![]; 782 | match parse_expr("expr", dirs, convert, &mut ignored) { 783 | Err(()) => Err("Could not convert meta data".into()), 784 | Ok((_, expr)) => Ok(expr), 785 | } 786 | } 787 | 788 | /// Parses an expression source file. 789 | pub fn parse(source: &str, dirs: &[String]) -> Result { 790 | use std::fs::File; 791 | use std::io::Read; 792 | 793 | let mut data_file = File::open(source).map_err(|err| 794 | format!("Could not open `{}`, {}", source, err))?; 795 | let mut data = String::new(); 796 | data_file.read_to_string(&mut data).unwrap(); 797 | 798 | parse_str(&data, dirs) 799 | } 800 | 801 | 802 | /// Parses a data string (expression or knowledge). 803 | pub fn parse_data_str(data: &str, dirs: &[String]) -> Result { 804 | use piston_meta::{parse_errstr, syntax_errstr}; 805 | 806 | let syntax_src = include_str!("../assets/syntax.txt"); 807 | let syntax = syntax_errstr(syntax_src)?; 808 | 809 | let mut meta_data = vec![]; 810 | parse_errstr(&syntax, &data, &mut meta_data)?; 811 | 812 | // piston_meta::json::print(&meta_data); 813 | 814 | let convert = Convert::new(&meta_data); 815 | let mut ignored = vec![]; 816 | match ParseData::parse(dirs, convert, &mut ignored) { 817 | Err(()) => Err("Could not convert meta data".into()), 818 | Ok((_, expr)) => Ok(expr), 819 | } 820 | } 821 | 822 | /// Parses a data source file (expression or knowledge). 823 | pub fn parse_data(source: &str, dirs: &[String]) -> Result { 824 | use std::fs::File; 825 | use std::io::Read; 826 | 827 | let mut data_file = File::open(source).map_err(|err| 828 | format!("Could not open `{}`, {}", source, err))?; 829 | let mut data = String::new(); 830 | data_file.read_to_string(&mut data).unwrap(); 831 | 832 | parse_data_str(&data, dirs) 833 | } 834 | 835 | #[cfg(test)] 836 | mod tests { 837 | use crate::*; 838 | 839 | #[test] 840 | fn test_not_ret_var() { 841 | let a: Expr = parse_str(r#"!\a"#, &[]).unwrap(); 842 | assert_eq!(a, not_ret_var("a")); 843 | } 844 | 845 | #[test] 846 | fn test_ret_bool() { 847 | let a: Expr = parse_str(r#"!\true"#, &[]).unwrap(); 848 | let b: Expr = app(Not, true); 849 | assert_eq!(a, b); 850 | 851 | let a: Expr = parse_str(r#"!\false"#, &[]).unwrap(); 852 | let b: Expr = app(Not, false); 853 | assert_eq!(a, b); 854 | 855 | let a: Expr = parse_str(r#"¬\true"#, &[]).unwrap(); 856 | let b: Expr = app(Not, true); 857 | assert_eq!(a, b); 858 | 859 | let a: Expr = parse_str(r#"¬\false"#, &[]).unwrap(); 860 | let b: Expr = app(Not, false); 861 | assert_eq!(a, b); 862 | } 863 | 864 | #[test] 865 | fn test_pow() { 866 | let a: Expr = parse_str(r#"a^b"#, &[]).unwrap(); 867 | let b: Expr = app2(Pow, "a", "b"); 868 | assert_eq!(a, b); 869 | 870 | let a: Expr = parse_str(r#"-a^b"#, &[]).unwrap(); 871 | let b: Expr = app(Neg, app2(Pow, "a", "b")); 872 | assert_eq!(a, b); 873 | } 874 | } 875 | -------------------------------------------------------------------------------- /assets/std.md: -------------------------------------------------------------------------------- 1 | # Poi Standard Library 2 | 3 | The Poi knowledge format is compatible with markdown text files: 4 | 5 | - Must start with `#` (markdown title) 6 | - A code block must use 3 backticks and `poi` 7 | - Rules are seperated by `;`. 8 | 9 | ### Definitions 10 | 11 | A definition uses the syntax ` := `. 12 | 13 | Definitions can be inlined under theorem proving 14 | in Poi Reduce with `inline all`. 15 | Under evaluation, definitions are always inlined. 16 | See `help` for more information. 17 | 18 | ```poi 19 | false1 := false; 20 | not := if(false)(true); 21 | idb := if(true)(false); 22 | true1 := true; 23 | and := if(if(true)(false))(false); 24 | or := if(true)(if(true)(false)); 25 | eqb := if(if(true)(false))(if(false)(true)); 26 | xor := if(if(false)(true))(if(true)(false)); 27 | nand := if(if(false)(true))(true); 28 | not := if(false)(if(false)(true)); 29 | exc := if(if(false)(true))(false); 30 | imply := if(if(true)(false))(true); 31 | fstb := if(true)(false); 32 | sndb := if(if(true)(false))(if(true)(false)); 33 | ``` 34 | 35 | ### Reductions 36 | 37 | A reduction uses the syntax ` => `. 38 | 39 | The pattern binds variables on the left side and synthesizes the expression on the right side. 40 | 41 | There is a trade-off between reductions and equivalences. 42 | Reductions are used to simplify automated theorem proving. 43 | 44 | #### Generic normalization 45 | 46 | Poi uses maximum function currying as standard form, 47 | because there are many ways of calling functions. 48 | This makes it easier to design rules that work in general. 49 | 50 | Algebraic expressions, e.g. `2 + 3`, uses standard form behind the scenes. 51 | 52 | Normalization of tuple arguments and domain constraints: 53 | 54 | ```poi 55 | x((y, z..)) => x(y)(z); 56 | x{(y, z..)} => x{y}{z}; 57 | ``` 58 | 59 | Redistribution: 60 | 61 | ```poi 62 | x{y}{z}(a)(b) => x{y}(a){z}(b); 63 | (g, f)((y, z..)) => (g(y)(z), f(y)(z)); 64 | ``` 65 | 66 | Concrete application and composition: 67 | 68 | ```poi 69 | \x(_) => x; 70 | \x · _ => x; 71 | ``` 72 | 73 | Duplication: 74 | ```poi 75 | dup(x) => (x, x); 76 | ``` 77 | 78 | #### If 79 | 80 | The `if` in Poi takes two arguments `if(a, b)`. 81 | It returns `a` when condition is `true`, 82 | and returns `b` when condition is `false`. 83 | 84 | ```poi 85 | if(x)(_)(true) => x; 86 | if(_)(x)(false) => x; 87 | if(x)(_){_}(true) => x; 88 | if(_)(x){_}(false) => x; 89 | ``` 90 | 91 | #### Quaterions 92 | 93 | Quaternions are lifted to a typed vector, 94 | in order to avoid combinatorial explosion in rules. 95 | 96 | Conversion/lifting rules: 97 | 98 | ```poi 99 | 𝐢₂ => [0, 0, 1, 0] : quat; 100 | 𝐢₃ => [0, 0, 0, 1] : quat; 101 | (𝐢 * (x : quat)) => ([0, 1, 0, 0] * x) : quat; 102 | ((x : quat) * 𝐢) => (x * [0, 1, 0, 0]) : quat; 103 | ((x * 𝐢) * (y : quat)) => (x * (𝐢 * (y : quat))); 104 | 105 | ((x : quat) * 𝐢) => ((x : quat) * (𝐢 : quat)); 106 | (𝐢 + (x : quat)) => ([0, 1, 0, 0] + x) : quat; 107 | ((x : quat) + 𝐢) => (x + [0, 1, 0, 0]) : quat; 108 | ((x : quat) * 𝐢₂) => (x * [0, 0, 1, 0]) : quat; 109 | ((x : quat) * 𝐢₃) => (x * [0, 0, 0, 1]) : quat; 110 | ((x : quat) + (y * 𝐢)) => (x + [0, y, 0, 0]) : quat; 111 | (x * 𝐢 + (y : quat)) => ([0, x, 0, 0] + y) : quat; 112 | ((x : quat) * (y : quat)) => (x * y) : quat; 113 | ((x : quat) + (y : quat)) => (x + y) : quat; 114 | x + (y : quat) => (x + y) : quat; 115 | [x, y, 𝐢, z] : quat => 𝐢 * 𝐢₂ + ([x, y, 0, z] : quat); 116 | [x, y, z, 𝐢] : quat => 𝐢 * 𝐢₃ + ([x, y, z, 0] : quat); 117 | ``` 118 | 119 | Quaternion algebra: 120 | 121 | ```poi 122 | -([x, y, z, w] : quat) => [-x, -y, -z, -w] : quat; 123 | s + ([x, y, z, w] : quat) => [s + x, s + y, s + z, s + w] : quat; 124 | s * ([x, y, z, w] : quat) => [s * x, s * y, s * z, s * w] : quat; 125 | ([x, y, z, w] : quat) * s => [x * s, y * s, z * s, w * s] : quat; 126 | 127 | ([x0, y0, z0, w0] + [x1, y1, z1, w1]) : quat => 128 | [x0+x1,y0+y1,z0+z1,w0+w1] : quat; 129 | 130 | ([x0, y0, z0, w0] * [x1, y1, z1, w1]) : quat => [ 131 | x0*x1-y0*y1-z0*z1-w0*w1, 132 | x0*y1+y0*x1+z0*w1-w0*z1, 133 | x0*z1+z0*x1-y0*w1+w0*y1, 134 | x0*w1+w0*x1+y0*z1-z0*y1, 135 | ] : quat; 136 | ``` 137 | 138 | #### Types 139 | 140 | Constants in Boolean algebra: 141 | 142 | ```poi 143 | type_of(false) => bool; 144 | type_of(true) => bool; 145 | ``` 146 | 147 | Unary operators in Boolean algebra (`bool -> bool`): 148 | 149 | ```poi 150 | false1[type_of](bool) => bool; 151 | not[type_of](bool) => bool; 152 | idb[type_of](bool) => bool; 153 | true1[type_of](bool) => bool; 154 | ``` 155 | 156 | Binary operators in Boolean algebra (`bool x bool -> bool`): 157 | 158 | ```poi 159 | and[type_of](bool)(bool) => bool; 160 | eqb[type_of](bool)(bool) => bool; 161 | exc[type_of](bool)(bool) => bool; 162 | false2[type_of](bool)(bool) => bool; 163 | fstb[type_of](bool)(bool) => bool; 164 | imply[type_of](bool)(bool) => bool; 165 | or[type_of](bool)(bool) => bool; 166 | nand[type_of](bool)(bool) => bool; 167 | nor[type_of](bool)(bool) => bool; 168 | sndb[type_of](bool)(bool) => bool; 169 | xor[type_of](bool)(bool) => bool; 170 | true2[type_of](bool)(bool) => bool; 171 | ``` 172 | 173 | Trigonometric operators: 174 | 175 | ```poi 176 | acos[type_of](f64) => f64; 177 | asin[type_of](f64) => f64; 178 | atan[type_of](f64) => f64; 179 | atan2[type_of](f64)(f64) => f64; 180 | cos[type_of](f64) => f64; 181 | exp[type_of](f64) => f64; 182 | ln[type_of](f64) => f64; 183 | log2[type_of](f64) => f64; 184 | log10[type_of](f64) => f64; 185 | sin[type_of](f64) => f64; 186 | sqrt[type_of](f64) => f64; 187 | tan[type_of](f64) => f64; 188 | ``` 189 | 190 | Arithmetic operators: 191 | 192 | ```poi 193 | eq[type_of](bool)(bool) => bool; 194 | add[type_of](f64)(f64) => f64; 195 | sub[type_of](f64)(f64) => f64; 196 | mul[type_of](f64)(f64) => f64; 197 | div[type_of](f64)(f64) => f64; 198 | rem[type_of](f64)(f64) => f64; 199 | pow[type_of](f64)(f64) => f64; 200 | rpow[type_of](f64)(f64) => f64; 201 | ``` 202 | 203 | List operators: 204 | 205 | ```poi 206 | base[type_of](f64)(f64) => vec; 207 | item[type_of](f64)(vec) => any; 208 | len[type_of](vec) => f64; 209 | concat[type_of](vec)(vec) => vec; 210 | push[type_of](vec)(f64) => vec; 211 | push_front[type_of](vec)(f64) => vec; 212 | ``` 213 | 214 | Vector operators: 215 | ```poi 216 | dot[type_of](vec)(vec) => f64; 217 | cross[type_of](vec)(vec) => vec; 218 | ``` 219 | 220 | Type utilities: 221 | 222 | ```poi 223 | type_of(\x) => compute::type_of(x); 224 | type_of([x..]) => vec; 225 | ``` 226 | 227 | #### Symmetric normal paths 228 | 229 | ```poi 230 | add[even] => eqb; 231 | add[exp] => mul; 232 | add[neg] => add; 233 | add[odd] => xor; 234 | and[not] => or; 235 | add[sqrt] => sqrt · (add · ((^ 2) · fst, (^ 2) · snd)); 236 | concat[len] => add; 237 | concat[max] => max2; 238 | concat[min] => min2; 239 | concat[sqnorm] => add; 240 | concat[sum] => add; 241 | eqb[not] => xor; 242 | inc[even] => not; 243 | max[neg] => min; 244 | max2[neg] => min2; 245 | min[neg] => max; 246 | min2[neg] => max2; 247 | mul[even] => or; 248 | mul[ln] => add; 249 | mul[log10] => add; 250 | mul[log2] => add; 251 | mul[neg] => neg · mul; 252 | mul[odd] => and; 253 | mul_mat[det] => mul; 254 | nand[not] => nor; 255 | nor[not] => nand; 256 | not[not] => not; 257 | or[not] => and; 258 | soft_max[neg] => soft_min; 259 | soft_min[neg] => soft_max; 260 | xor[not] => eqb; 261 | ``` 262 | 263 | Symmetric path utilities: 264 | 265 | ```poi 266 | f:[arity]2(x){g}[g] . g => f[g](true)(g(x)); 267 | ((if(f:[arity]1)(_) · g) · dup)[g]{id} => f[g]{id}; 268 | ((if(_)(f:[arity]1) · g) · dup)[g]{not} => f[g]{not}; 269 | ``` 270 | 271 | #### Asymmetric normal paths 272 | 273 | ```poi 274 | if(a)(b)[not → id] => if(b)(a); 275 | nand[not x not -> id] => and[not]; 276 | mul_mat[len ⨯ (item(1) · dim) → dim] => id; 277 | ``` 278 | 279 | #### Identity normal paths 280 | 281 | ```poi 282 | x[id] => x; 283 | id[x] => id; 284 | ``` 285 | 286 | #### Inverse 287 | 288 | ```poi 289 | inv(f) · f => id; 290 | f · inv(f) => id; 291 | inv(inv(f)) => f; 292 | 293 | inv((+ a)) => (- a); 294 | inv((- a)) => (+ a); 295 | ``` 296 | 297 | #### Identity 298 | 299 | ```poi 300 | x · id => x; 301 | id · x => x; 302 | (fst, snd) => id; 303 | 304 | not · not => idb; 305 | exp · ln => id; 306 | ln · exp => id; 307 | neg · neg => id; 308 | conj · conj => id; 309 | transpose · transpose => id; 310 | ``` 311 | 312 | #### Misc 313 | 314 | ```poi 315 | not · even => odd; 316 | not · odd => even; 317 | mul{(>= 0)}{(>= 0)}[rpow{(>= 0)}(_)] => mul; 318 | 319 | sqrt · sqnorm => norm; 320 | (^ 2) · norm => sqnorm; 321 | sqrt · (^ 2) => abs; 322 | 323 | false1(_) => false; 324 | true1(_) => true; 325 | not(\false) => true; 326 | not(\true) => false; 327 | id(x) => x; 328 | and(true) => idb; 329 | and(false) => false1; 330 | or(true) => true1; 331 | or(false) => idb; 332 | fstb(x)(y) => x; 333 | fst(x)(y) => x; 334 | sndb(x)(y) => y; 335 | snd(x)(y) => y; 336 | eqb(false) => not; 337 | eqb(true) => idb; 338 | ``` 339 | 340 | #### Complex numbers 341 | 342 | ```poi 343 | ε ^ 2 => 0; 344 | 𝐢 ^ 2 => -1; 345 | ``` 346 | 347 | Complex number utilities: 348 | ```poi 349 | ε * ε => ε ^ 2; 350 | 351 | 𝐢 * 𝐢 => 𝐢 ^ 2; 352 | 𝐢 + 𝐢 => 2𝐢; 353 | x * 𝐢 + 𝐢 => (x + 1) * 𝐢; 354 | 𝐢 + x * 𝐢 => (1 + x) * 𝐢; 355 | sqrt(-1) => 𝐢; 356 | ``` 357 | 358 | #### Misc 359 | 360 | ```poi 361 | sin(\x:int * τ) => sin(τ); 362 | cos(\x:int * τ) => cos(τ); 363 | tan(\x:int * τ) => tan(τ); 364 | -(\a + \b * x) => (-a) + (-b) * x; 365 | reci((\x + \y * 𝐢)) => x / (x^2 + y^2) + (neg(y) / (x^2 + y^2)) * 𝐢; 366 | \a - \b * x => a + (-b) * x; 367 | (\a + \b * x) - (\c + \d * x) => (a - c) + (b - d) * x; 368 | (\a + \b * x) + (\c * x) => a + (b + c) * x; 369 | (\a * x) + (\b * x) => (a + b) * x; 370 | x - x => 0; 371 | \a * x + x => (a + 1) * x; 372 | \a * x - x => (a - 1) * x; 373 | x + \a * x => (a + 1) * x; 374 | x - \a * x => (1 - a) * x; 375 | \a * (\b + \c * x) => (a * b) + (a * c) * x; 376 | ((\a + \b * x) * (\c * x)) => ((c * x) * (a + b * x)); 377 | (\a * x) * (\b + \c * x) => ((a * c) * x^2 + (a * b) * x); 378 | (\a + \b * x) * (\c + \d * x) => a * c + b * d * x^2 + (a * d + b * c) * x; 379 | (\a * x) * (\b * x) => (a * b) * x^2; 380 | \x / \y => compute::div(x, y); 381 | (\a + \b * ε) / (\c + \d * ε) => a / c + (b * c - a * d) / c ^ 2 * ε; 382 | x / (\a + \b * 𝐢) => x * reci(a + b * 𝐢); 383 | el(x)(y)(z) => item(y)(item(x)(z)); 384 | re{(: vec)}(x) => item(0)(x); 385 | re(a + _ * 𝐢) => a; 386 | im{(: vec)}(x) => item(1)(x); 387 | im(_ + a * 𝐢) => a; 388 | a * 𝐢 * 𝐢 => a * (𝐢 * 𝐢); 389 | mulc([x0, y0])([x1, y1]) => [x0 * x1 - y0 * y1, x0 * y1 + x1 * y0]; 390 | conj([x, y]) => [x, -y]; 391 | conj(a + b * 𝐢) => a + (-b) * 𝐢; 392 | ``` 393 | 394 | #### Computations 395 | 396 | ```poi 397 | -\x => compute::neg(x); 398 | \x < \y => compute::lt(x, y); 399 | \x <= \y => compute::le(x, y); 400 | \x = \y => compute::eq(x, y); 401 | \x >= \y => compute::ge(x, y); 402 | \x > \y => compute::gt(x, y); 403 | \x + \y => compute::add(x, y); 404 | \x - \y => compute::sub(x, y); 405 | \x * \y => compute::mul(x, y); 406 | \x % \y => compute::rem(x, y); 407 | \x ^ \y => compute::pow(x, y); 408 | (^ \x)(\y) => compute::pow(y, x); 409 | abs(\x) => compute::abs(x); 410 | arity(x) => compute::arity(x); 411 | base(\x)(\y) => compute::base(x, y); 412 | col(\k){(: vec)}(x) => compute::col(k, x); 413 | concat{(: vec)}(x){(: vec)}(y) => compute::concat(x, y); 414 | dim{(: vec)}(x) => compute::dim(x); 415 | even(\x) => compute::even(x); 416 | inc(\x) => compute::inc(x); 417 | item(\x)([y..]) => compute::item(x, y); 418 | is_square_mat{(: vec)}(x) => compute::is_square_mat(x); 419 | len{(: vec)}(x) => compute::len(x); 420 | max2(\x)(\y) => compute::max2(x, y); 421 | min2(\x)(\y) => compute::min2(x, y); 422 | mul_mat{(: vec)}(x){(: vec)}(y) => compute::mul_mat(x, y); 423 | norm(x) => sqrt(sqnorm(x)); 424 | odd(\x) => compute::odd(x); 425 | prob(\x) => compute::prob(x); 426 | probl(\x) => compute::probl(x); 427 | probm(\x) => compute::probm(x); 428 | probr(\x) => compute::probr(x); 429 | push([x..])(y) => compute::push(x, y); 430 | push_front([x..])(y) => compute::push_front(x, y); 431 | range(\x)(\y)(\z) => compute::range(x, y, z); 432 | rangel(\x)(\y)(\z) => compute::rangel(x, y, z); 433 | rangem(\x)(\y)(\z) => compute::rangem(x, y, z); 434 | ranger(\x)(\y)(\z) => compute::ranger(x, y, z); 435 | reci(\x) => compute::reci(x); 436 | sqnorm{(: vec)}(x) => sum(vec_op(mul)(x)(x)); 437 | transpose{(: vec)}(x) => compute::transpose(x); 438 | ``` 439 | 440 | Computation utilities: 441 | 442 | ```poi 443 | \x + (\y + z) => compute::add(x, y) + z; 444 | \x * (\y * z) => compute::mul(x, y) * z; 445 | ``` 446 | 447 | #### Misc 448 | 449 | ```poi 450 | (* x) · (mul · (g, (* y) · snd)) => (* x) · ((* y) · (mul · (g, snd))); 451 | (* x) · (mul · ((* y) · fst, g)) => (* x) · ((* y) · (mul · (fst, g))); 452 | (* x) · (* y) => (* x * y); 453 | 454 | -(-x) => x; 455 | 0 + x => x; 456 | x + 0 => x; 457 | 0 - x => -x; 458 | x - 0 => x; 459 | 1 * x => x; 460 | x * 1 => x; 461 | (* 0) => 0; 462 | _ * 0 => 0; 463 | \x / ∞ => 0; 464 | x ^ 1 => x; 465 | x ^ 0 => 1; 466 | 467 | x * (-y) => (-x) * y; 468 | ``` 469 | 470 | #### Concreteness 471 | 472 | Concreteness is used to in meta-reasoning about whether an expression 473 | will return a concrete value. 474 | 475 | ```poi 476 | (x : \) + (y : \) => (x + y) : \; 477 | (x : \) - (y : \) => (x - y) : \; 478 | (x : \) * (y : \) => (x * y) : \; 479 | (x : \) / (y : \) => (x / y) : \; 480 | (x : \) % (y : \) => (x % y) : \; 481 | (x : \) ^ (y : \) => (x ^ y) : \; 482 | (^ x : \)(y : \) => (^ x)(y) : \; 483 | ``` 484 | 485 | #### Vector operations 486 | 487 | Unary operators: 488 | 489 | ```poi 490 | not{(: vec)}([x]) => [!x]; 491 | not{(: vec)}([x, y..]) => [not(x)] ++ not{(: vec)}(y); 492 | neg{(: vec)}(\[x]) => [-x]; 493 | neg{(: vec)}([x, y..]) => [neg(x)] ++ neg{(: vec)}(y); 494 | sum{(: vec)}([x, y..]) => add(x)(sum{(: vec)}(y)); 495 | sum{(: vec)}(\[x]) => x; 496 | max{(: vec)}([x, y..]) => max2(x)(max{(: vec)}(y)); 497 | max{(: vec)}(\[x]) => x; 498 | min{(: vec)}([x, y..]) => min2(x)(min{(: vec)}(y)); 499 | min{(: vec)}(\[x]) => x; 500 | arg_max{(: vec)}([x, y..]) => 501 | if(0)(1 + arg_max{(: vec)}(y))(max2(x, max{(: vec)}(y)) = x); 502 | arg_max{(: vec)}([x]) => 0; 503 | arg_min{(: vec)}([x, y..]) => 504 | if(0)(1 + arg_min{(: vec)}(y))(min2(x, min{(: vec)}(y)) = x); 505 | arg_min{(: vec)}([x]) => 0; 506 | soft_max{(: vec)}(x) => ln(sum{(: vec)}(exp{(: vec)}(x))); 507 | soft_min{(: vec)}(x) => -ln(sum{(: vec)}(exp{(: vec)}(-x))); 508 | soft_arg_max(x){(: vec)}(y) => exp(x) / sum(exp{(: vec)}(y)); 509 | soft_arg_min(x){(: vec)}(y) => exp(-x) / sum(exp{(: vec)}(-y)); 510 | ln{(: vec)}([x]) => [ln(x)]; 511 | ln{(: vec)}([x, y..]) => [ln(x)] ++ ln{(: vec)}(y); 512 | exp{(: vec)}([x]) => [exp(x)]; 513 | exp{(: vec)}([x, y..]) => [exp(x)] ++ exp{(: vec)}(y); 514 | ``` 515 | 516 | Binary operators: 517 | 518 | ```poi 519 | and{(: vec)}(x){(: vec)}(y) => vec_op(and)(x)(y); 520 | or{(: vec)}(x){(: vec)}(y) => vec_op(or)(x)(y); 521 | add{(: vec)}(x){(: vec)}(y) => vec_op(add)(x)(y); 522 | sub{(: vec)}(x){(: vec)}(y) => vec_op(sub)(x)(y); 523 | mul{(: vec)}(x){(: vec)}(y) => vec_op(mul)(x)(y); 524 | div{(: vec)}(x){(: vec)}(y) => vec_op(div)(x)(y); 525 | rem{(: vec)}(x){(: vec)}(y) => vec_op(rem)(x)(y); 526 | pow{(: vec)}(x){(: vec)}(y) => vec_op(pow)(x)(y); 527 | rpow{(: vec)}(x){(: vec)}(y) => vec_op(rpow)(x)(y); 528 | ``` 529 | 530 | Vector operation reductions: 531 | 532 | ```poi 533 | vec_op(f)([x0, y0..])([x1, y1..]) => concat([f(x0)(x1)])(vec_op(f)(y0)(y1)); 534 | vec_op(f)(\[x])(\[y]) => [f(x)(y)]; 535 | vec_uop(f)([x, y..]) => concat([f(x)])(vec_uop(f)(y)); 536 | vec_uop(f)(\[x]) => [f(x)]; 537 | ``` 538 | 539 | #### Boolean algebra 540 | 541 | - Decidable requires Law of Excluded Middle 542 | - Constructive is the same as undecidable 543 | 544 | Modus ponens (constructive): 545 | 546 | ```poi 547 | a & imply(a)(b) => b; 548 | ``` 549 | 550 | And fst (constructive): 551 | 552 | ```poi 553 | imply(a & b)(a) => true; 554 | ``` 555 | 556 | Bound negative and positive (constructive): 557 | 558 | ```poi 559 | !a & b | a => b | a; 560 | a & b | !a => b | !a; 561 | ``` 562 | 563 | Double negation utilities (decidable): 564 | 565 | ```poi 566 | !(!x) => x; 567 | not · (not · x) => x; 568 | ``` 569 | 570 | Reverse modus tollens utility (decidable): 571 | 572 | ```poi 573 | !(!b) | !a => b | !a; 574 | ``` 575 | 576 | Tautology: 577 | 578 | ```poi 579 | a | !a => true; 580 | ``` 581 | 582 | Implication transitivity (constructive): 583 | 584 | ```poi 585 | imply(imply(a)(b) & imply(b)(c))(imply(a)(c)) => true; 586 | ``` 587 | 588 | #### Misc 589 | 590 | ```poi 591 | dot{(: vec)}([x0, y0]){(: vec)}([x1, y1]) => x0 * x1 + y0 * y1; 592 | cross{(: vec)}([a1, a2, a3]){(: vec)}([b1, b2, b3]) => [ 593 | a2 * b3 - a3 * b2, 594 | a3 * b1 - a1 * b3, 595 | a1 * b2 - a2 * b1, 596 | ]; 597 | ``` 598 | 599 | #### Ranges 600 | 601 | ```poi 602 | and · (le, ge) => eq; 603 | and · (f, f) => f; 604 | and · ((>= \x), (>= \y)) => (>= max2(x)(y)); 605 | and · ((> \x), (> \y)) => (> max2(x)(y)); 606 | and · ((<= \x), (<= \y)) => (<= min2(x)(y)); 607 | and · ((< \x), (< \y)) => (< min2(x)(y)); 608 | and · ((< x), (<= x)) => (< x); 609 | and · ((<= x), (< y)) => and · ((< y), (<= x)); 610 | and · ((< x), (> x)) => false; 611 | and · ((> x), (< y)) => and · ((< y), (> x)); 612 | and · ((> x), (<= y)) => and · ((<= y), (> x)); 613 | and · ((<= x), (>= x)) => (= x); 614 | and · ((> x), (>= x)) => (> x); 615 | and · ((>= x), (> y)) => and · ((> y), (>= x)); 616 | and · ((>= x), (< y)) => and · ((< y), (>= x)); 617 | and · ((>= x), (<= y)) => and · ((<= y), (>= x)); 618 | and · ((< \x), (> \y)) => if(false)(rangem(min2(x)(y))(max2(x)(y)))(x <= y); 619 | and · ((< \x), (>= \y)) => if(false)(rangel(min2(x)(y))(max2(x)(y)))(x <= y); 620 | and · ((<= \x), (> \y)) => if(false)(ranger(min2(x)(y))(max2(x)(y)))(x <= y); 621 | and · ((<= \x), (>= \y)) => if(false)(range(min2(x)(y))(max2(x)(y)))(x < y); 622 | and · ((< \x), (<= \y)) => if((< x))((<= y))(x <= y); 623 | and · ((> \x), (>= \y)) => if((> x))((>= y))(x >= y); 624 | ``` 625 | 626 | ```poi 627 | or · ((< x), (<= x)) => (<= x); 628 | or · ((<= x), (< y)) => or · ((< y), (<= x)); 629 | or · ((< x), (= x)) => (<= x); 630 | or · ((= x), (> x)) => (>= x); 631 | or · ((< x), (>= x)) => true; 632 | or · ((<= x), (> x)) => true; 633 | or · ((<= x), (>= x)) => true; 634 | or · ((>= x), (< y)) => or · ((< y), (>= x)); 635 | or · ((> x), (<= y)) => or · ((<= y), (> x)); 636 | or · ((>= x), (<= y)) => or · ((<= y), (>= x)); 637 | or · ((= y), (< x)) => or · ((< x), (= y)); 638 | or · ((> x), (= y)) => or · ((= y), (> x)); 639 | or · ((= x), (>= x)) => (>= x); 640 | or · ((<= x), (= x)) => (<= x); 641 | or · ((>= x), (= y)) => or · ((= y), (>= x)); 642 | or · ((= x), (<= y)) => or · ((<= y), (= x)); 643 | or · ((> x), (>= x)) => (>= x); 644 | or · ((>= x), (> y)) => or · ((> y), (>= x)); 645 | or · ((< \x), (<= \y)) => if((<= y))((< x))(x <= y); 646 | or · ((> \x), (>= \y)) => if((>= y))(gt(x))(x >= y); 647 | or · (f, f) => f; 648 | ``` 649 | 650 | ```poi 651 | not · (< x) => (>= x); 652 | not · (<= x) => (> x); 653 | not · (>= x) => (< x); 654 | not · (> x) => (<= x); 655 | ``` 656 | 657 | #### Derivatives 658 | 659 | You can use `deriv` or `𝐝` (unicode). 660 | 661 | ```poi 662 | 𝐝(!\x)(x) => 1; 663 | 𝐝(!\x)(\y) => 0; 664 | 𝐝(!\x)(y:\) => 0; 665 | 𝐝(!\x)(\k * y) => k * 𝐝(x)(y); 666 | 𝐝(!\x)(k:\ * y) => k:\ * 𝐝(x)(y); 667 | 𝐝(!\x)(π * y) => π * 𝐝(x)(y); 668 | 𝐝(!\x)(τ * y) => τ * 𝐝(x)(y); 669 | 𝐝(!\x)(x ^ \k) => k * x ^ (k - 1); 670 | 𝐝(!\x)(sin(x)) => cos(x); 671 | 𝐝(!\x)(cos(x)) => -sin(x); 672 | 𝐝(!\x)(sin(\k * x)) => k * cos(k * x); 673 | 𝐝(!\x)(cos(\k * x)) => -k * sin(k * x); 674 | 𝐝(!\x)(exp(x)) => exp(x); 675 | 𝐝(!\x)(exp(\k * x)) => k * exp(k * x); 676 | 𝐝(!\x)(y + z) => 𝐝(x)(y) + 𝐝(x)(z); 677 | 𝐝(!\x)(y - z) => 𝐝(x)(y) - 𝐝(x)(z); 678 | ``` 679 | 680 | Constant derivative simplification: 681 | 682 | ```poi 683 | 𝐝(x)((a : \) ^ \b) => 0; 684 | ``` 685 | 686 | Derivatives utilities: 687 | 688 | ```poi 689 | 𝐝(!\x)(\k * m:\ * y) => k * m:\ * 𝐝(x)(y); 690 | 𝐝(!\x)((x - s:\)^2) => 2 * (x - s:\); 691 | ``` 692 | 693 | #### Indefinite integrals 694 | 695 | You can use `integ` or `∫` (unicode). 696 | 697 | ```poi 698 | ∫(!\x)(c)(x) => c + 0.5 * x ^ 2; 699 | ∫(!\x)(c)(\k) => c + k * x; 700 | ∫(!\x)(c)(\k * y) => k * ∫(x)(c / k)(y); 701 | ∫(!\x)(c)(x ^ \k) => c + 1 / (k + 1) * x ^ (k + 1); 702 | ∫(!\x)(c)(cos(x)) => c + sin(x); 703 | ∫(!\x)(c)(sin(x)) => c + -cos(x); 704 | ∫(!\x)(c)(exp(x)) => c + exp(x); 705 | ∫(!\x)(c)(exp(\k * x)) => c + 1 / k * exp(k * x); 706 | ∫(!\x)(c)(d(x)(y) * exp(y)) => c + exp(y); 707 | ∫(!\x)(c)(\k ^ x) => c + k ^ x / ln(k); 708 | ∫(!\x)(c)(ln(x)) => c + (-x + x * ln(x)); 709 | ``` 710 | 711 | Indefinite integral utilities: 712 | ```poi 713 | (\k * ((c / \k) + y)) => c + k * y; 714 | ``` 715 | 716 | ### Partial derivatives 717 | 718 | You can use `pariv * x` or `∂x` (unicode). 719 | 720 | Notice that there is an ambiguity in the standard notation, 721 | which is fixed by using the following notation: 722 | 723 | - `∂(y) / ∂x` means taking the partial derivative of `y` with respect to `x` 724 | - `∂y / ∂x` means the change of `y` with respect to `x` 725 | 726 | ```poi 727 | ∂(a + b) / ∂c => ∂(a) / ∂c + ∂(b) / ∂c; 728 | ∂(a - b) / ∂c => ∂(a) / ∂c - ∂(b) / ∂c; 729 | ∂(x) / ∂x => 𝐝(x)(x); 730 | ∂(x^\k) / ∂x => 𝐝(x)(x^k); 731 | ∂((x - s)^\k) / (∂ * x!>s) => k * (x - s) ^ (k - 1); 732 | ∂(a * b) / (∂ * x!>a) => a * (∂(b) / ∂x); 733 | ∂(x) / (∂ * y!>x) => 𝐝(y)(x:\); 734 | ∂((∂x / ∂t) ^ 2) / (∂ * x!>t) => 0; 735 | ``` 736 | 737 | Partial derivatives utilities: 738 | ```poi 739 | ∂^2 * x / ∂t^2 <=> ∂ / ∂t * (∂x / ∂t); 740 | ``` 741 | 742 | #### Equality domain constraints 743 | 744 | ```poi 745 | and{eq} => fstb; 746 | or{eq} => fstb; 747 | eq{eq} => true; 748 | add{eq}(x)(_) => 2 * x; 749 | mul{eq}(x)(_) => x ^ 2; 750 | sub{eq} => 0; 751 | max2{eq} => fst; 752 | min2{eq} => fst; 753 | ``` 754 | 755 | Equality constraint utilities: 756 | ```poi 757 | \x{eq}(_) => x; 758 | eq{(: vec)}(x){(: vec)}(x) => eq{eq}(x)(x); 759 | ``` 760 | 761 | #### Misc 762 | 763 | ```poi 764 | div{and · (eq, (> 0) · fst)} => 1; 765 | f{true2} => f; 766 | f{true1} => f; 767 | (x^\k * x) => x^(k + 1); 768 | (x * x^\k) => x^(k + 1); 769 | (x^\a * x^\b) => x^(a + b); 770 | (x * x) => x^2; 771 | (g . (* x))(y) => g(x * y); 772 | ``` 773 | 774 | #### Trivial paths (domains) 775 | 776 | You can type `triv` instead of `∀`. 777 | For more information, see `help triv`. 778 | 779 | ```poi 780 | ∀(f:[arity]2{g0}{g1}) => (g0, g1); 781 | ∀(f:[arity]1{g}) => g; 782 | ∀(f:!{}) => true; 783 | ``` 784 | 785 | #### Existential paths (codomains) 786 | 787 | You can type `ex` instead of `∃`. 788 | For more information, see `help ex`. 789 | 790 | Generic laws: 791 | 792 | ```poi 793 | ∃(\x) => eq(x); 794 | ∃(f{f}) => idb; 795 | ``` 796 | 797 | ```poi 798 | ∃(false1) => not; 799 | ∃(not) => true1; 800 | ∃(idb) => true1; 801 | ∃(true1) => idb; 802 | ∃(and) => true1; 803 | ∃(or) => true1; 804 | ∃(nand) => true1; 805 | ∃(nor) => true1; 806 | ∃(xor) => true1; 807 | ∃(eqb) => true1; 808 | ∃(exc) => true1; 809 | ∃(imply) => true1; 810 | ∃(fstb) => true1; 811 | ∃(sndb) => true1; 812 | ∃(id) => true; 813 | 814 | ∃(add{(>= x)}{(>= y)}) => (>= x + y); 815 | ∃(add{(> x)}{(> y)}) => (> x + y); 816 | ∃(add{(<= x)}{(<= y)}) => (<= x + y); 817 | ∃(add{(< x)}{(< y)}) => (< x + y); 818 | 819 | ∃(even · (/ \k){even}) => true; 820 | ``` 821 | 822 | #### Function inequalities 823 | 824 | Unary boolean function inequalities: 825 | 826 | ```poi 827 | id = false => false; 828 | id = true => false; 829 | not = false => false; 830 | not = true => false; 831 | id = not => false; 832 | ``` 833 | 834 | Function inequality utilities: 835 | 836 | ```poi 837 | false = x => x = false; 838 | true = x => x = true; 839 | not = id => false; 840 | ``` 841 | 842 | #### Constants 843 | 844 | ```poi 845 | !\a + \b => b + a; 846 | !\a + b:\ => b:\ + a; 847 | !\a * \b => b * a; 848 | !\a * b:\ => b:\ * a; 849 | !\a + (\b + c) => b + (a + c); 850 | !\a + (b:\ + c) => b:\ + (a + c); 851 | \a + !\b - !\c => a + (b - c); 852 | a:\ + !\b - !\c => a:\ + (b - c); 853 | ``` 854 | 855 | Constants utilities: 856 | 857 | ```poi 858 | \a * b * (\c * d) => (a * c) * b * d; 859 | ``` 860 | 861 | #### Substitution 862 | 863 | ```poi 864 | subst(a)(a)(b) => b; 865 | ``` 866 | 867 | #### Misc 868 | 869 | ```poi 870 | idb => id; 871 | fstb => fst; 872 | sndb => snd; 873 | 874 | len · concat => concat[len] · (len · fst, len · snd); 875 | sum · concat => concat[sum] · (sum · fst, sum · snd); 876 | max · concat => concat[max] · (max · fst, max · snd); 877 | min · concat => concat[min] · (min · fst, min · snd); 878 | sqnorm · concat => concat[sqnorm] · (sqnorm · fst, sqnorm · snd); 879 | norm · concat => sqrt · (sqnorm · concat); 880 | len · base(x) => x; 881 | item(0) · dim => len; 882 | (f · fst){x}(a){_}(_) => f{x}(a); 883 | (f · fst)(a)(_) => f(a); 884 | (f · snd){_}(_){x}(a) => f{x}(a); 885 | (f · snd)(_)(a) => f(a); 886 | (f · fst){_}(a)(_) => f(a); 887 | (f · snd){_}(_)(a) => f(a); 888 | (f · fst) · (x, _) => f · x; 889 | (f · snd) · (_, x) => f · x; 890 | 891 | (x, y) · (a, b) => (x · (a, b), y · (a, b)); 892 | (x, y, z) · (a, b, c) => (x · (a, b, c), y · (a, b, c), z · (a, b, c)); 893 | 894 | cos(x) ^ 2 + sin(x) ^ 2 => 1; 895 | 896 | f:!{}([x..]) => f{(: vec)}(x); 897 | 898 | (-(x + y)) => (-x + -y); 899 | (-(x * y)) => (-x * y); 900 | (x + x) => (2 * x); 901 | ``` 902 | 903 | ### Equivalences 904 | 905 | Equivalences are used to present available choices after reduction. 906 | 907 | An equivalence uses the syntax ` <=> `. 908 | When `<=>>` is used, it means reduction from left to right under evaluation. 909 | 910 | Equivalences are similar to reductions, but with the difference 911 | that they might work both ways. 912 | Some equivalences have fewer variables or uses `compute::` on one side, 913 | which means they can only be used from left to right. 914 | 915 | #### Substitution 916 | 917 | ```poi 918 | imply(a = b)(c:!subst) <=> imply(a = b)(subst(c)(a)(b)); 919 | subst(f:!subst(a))(b)(c) <=> subst(f)(b)(c)(a); 920 | subst(f(a:!subst))(b)(c) <=> f(subst(a)(b)(c)); 921 | ``` 922 | 923 | #### Catuṣkoṭi existential lift 924 | 925 | Catuṣkoṭi existential lift is used to reason about indeterminacy. 926 | The equation of the form `f(x) = y` where `y` is 927 | `true/false/both/neither` (a 4-value logic) is translated into an equation using the existential path `∃(f{(= x)})`. 928 | For more information, see `help catus`. 929 | 930 | ```poi 931 | f(x) = both <=> ∃(f{(= x)}) = true; 932 | f(x) = neither <=> ∃(f{(= x)}) = false; 933 | f(x) = true <=> ∃(f{(= x)}) = idb; 934 | f(x) = false <=> ∃(f{(= x)}) = not; 935 | ∃(f:[arity]1[g]{h}) <=> ∃(g · f{h · g}); 936 | ``` 937 | 938 | #### Trigonometry 939 | 940 | ```poi 941 | acos(\x) <=>> compute::acos(x); 942 | asin(\x) <=>> compute::asin(x); 943 | atan(\x) <=>> compute::atan(x); 944 | atan2(\x)(\y) <=>> compute::atan2(x, y); 945 | cos(\x) <=>> compute::cos(x); 946 | exp(\x) <=>> compute::exp(x); 947 | ln(\x) <=>> compute::ln(x); 948 | log2(\x) <=>> compute::log2(x); 949 | log10(\x) <=>> compute::log10(x); 950 | sin(\x) <=>> compute::sin(x); 951 | sqrt(\x:(< 0)) <=>> mul(sqrt(x))(𝐢); 952 | sqrt(\x:(>= 0)) <=> compute::sqrt(x); 953 | tan(\x) <=>> compute::tan(x); 954 | ``` 955 | 956 | Constants: 957 | ```poi 958 | pi <=>> 3.141592653589793; 959 | tau <=>> 6.283185307179586; 960 | ``` 961 | 962 | Trigonometry utilities: 963 | ```poi 964 | 2π <=> τ; 965 | cos(τ) => 1; 966 | sin(τ) => 0; 967 | sqrt(1) => 1; 968 | sqrt(x) ^ 2 => x; 969 | tan(τ) => 0; 970 | ``` 971 | 972 | #### Ranges to probabilities 973 | 974 | ```poi 975 | range(0)(1) <=> prob; 976 | rangel(0)(1) <=> probl; 977 | ranger(0)(1) <=> probr; 978 | rangem(0)(1) <=> probm; 979 | ``` 980 | 981 | #### Misc 982 | 983 | ```poi 984 | (= true) <=> idb; 985 | (= false) <=> not; 986 | 987 | x ^ 2 <=> x * x; 988 | 989 | (>= x) <=> le(x); 990 | (> x) <=> lt(x); 991 | (<= x) <=> ge(x); 992 | (< x) <=> gt(x); 993 | and · (f, g) <=> and · (g, f); 994 | or · (f, g) <=> or · (g, f); 995 | nand · (f, g) <=> nand · (g, f); 996 | nor · (f, g) <=> nor · (g, f); 997 | (not · and) · (not · fst, not · snd) <=> or; 998 | 999 | el(x)(y) <=> item(x) · item(y); 1000 | 1001 | a - (b - c) <=> a - b + c; 1002 | a - b - c <=> a - (b + c); 1003 | ((a - b) * c) <=> (a * c - b * c); 1004 | ((a + b) * c) <=> (a * c + b * c); 1005 | ((a + b) - c) <=> (a + (b - c)); 1006 | 1007 | ((a + b)^2) <=> (a^2 + 2 * a * b + b^2); 1008 | ((a + b)^3) <=> (a^3 + 3 * a^2 * b + 3 * a * b^2 + b^3); 1009 | ((a - b)^2) <=> (a^2 - 2 * a * b + b^2); 1010 | ((a + b) * (a - b)) <=> (a^2 - b^2); 1011 | ((a * b)^2) <=> (a^2 * b^2); 1012 | ((a / b)^2) <=> (a^2 / b^2); 1013 | (a / b / b) <=> (a / b^2); 1014 | (^ a)(b) <=> (b ^ a); 1015 | 1016 | (a * b + a * c) / d <=> (a / d) * (b + c); 1017 | (a * b - a * c) / d <=> (a / d) * (b - c); 1018 | 0.5 * a <=> a / 2; 1019 | a^2 + a <=> a * (a + 1); 1020 | a^2 - a <=> a * (a - 1); 1021 | x * (x - 1) / 2 <=> ((2 * x - 1)^2 - 1) / 8; 1022 | ``` 1023 | 1024 | #### Distributivity 1025 | 1026 | ```poi 1027 | a * (b + c) <=> a * b + a * c; 1028 | a * (b - c) <=> a * b - a * c; 1029 | a & (b | c) <=> a & b | a & c; 1030 | ``` 1031 | 1032 | #### Associativity 1033 | 1034 | ```poi 1035 | a + (b + c) <=> a + b + c; 1036 | a * (b * c) <=> a * b * c; 1037 | a | (b | c) <=> a | b | c; 1038 | a & (b & c) <=> a & b & c; 1039 | ``` 1040 | 1041 | #### Commutativity 1042 | 1043 | ```poi 1044 | a & b <=> b & a; 1045 | a | b <=> b | a; 1046 | (a = b) <=> (b = a); 1047 | a + b <=> b + a; 1048 | a * b <=> b * a; 1049 | nand(a)(b) <=> nand(b)(a); 1050 | nor(a)(b) <=> nor(b)(a); 1051 | xor(a)(b) <=> xor(b)(a); 1052 | ``` 1053 | 1054 | Commutativity utilities: 1055 | 1056 | ```poi 1057 | x0 + x1 + x2 + x3 <=> x3 + x0 + x1 + x2; 1058 | x0 + x1 + x2 + x3 <=> x0 + x3 + x2 + x1; 1059 | x0 + x1 + x2 + x3 + x4 + x5 + x6 + x7 <=> 1060 | x7 + x0 + x1 + x2 + x3 + x4 + x5 + x6; 1061 | x0 + x1 + x2 + x3 + x4 + x5 + x6 + x7 <=> 1062 | x0 + x7 + x6 + x5 + x4 + x3 + x2 + x1; 1063 | x0 + x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 <=> 1064 | x11 + x0 + x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10; 1065 | x0 + x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 <=> 1066 | x0 + x11 + x10 + x9 + x8 + x7 + x6 + x5 + x4 + x3 + x2 + x1; 1067 | 1068 | x0 * x1 * x2 <=> x0 * x2 * x1; 1069 | x0 * x1 * x2 * x3 <=> x0 * x2 * x1 * x3; 1070 | 1071 | x0 | x1 | x2 | x3 <=> x3 | x0 | x1 | x2; 1072 | x0 | x1 | x2 | x3 <=> x0 | x3 | x2 | x1; 1073 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 <=> 1074 | x7 | x0 | x1 | x2 | x3 | x4 | x5 | x6; 1075 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 <=> 1076 | x0 | x7 | x6 | x5 | x4 | x3 | x2 | x1; 1077 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | x10 | x11 <=> 1078 | x11 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | x10; 1079 | x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | x10 | x11 <=> 1080 | x0 | x11 | x10 | x9 | x8 | x7 | x6 | x5 | x4 | x3 | x2 | x1; 1081 | 1082 | x0 & x1 & x2 <=> x0 & x2 & x1; 1083 | x0 & x1 & x2 & x3 <=> x0 & x2 & x1 & x3; 1084 | ``` 1085 | 1086 | #### Algebra utilities 1087 | 1088 | ```poi 1089 | a + -1 * b <=> a - b; 1090 | (a + b * ε) * (c + d * ε) <=> a * c + (a * d + c * b) * ε; 1091 | (a + b * 𝐢) * (c + d * 𝐢) <=> a * c - b * d + (a * d + c * b) * 𝐢; 1092 | ``` 1093 | 1094 | #### Function inverses 1095 | 1096 | ```poi 1097 | inv(id) <=> id; 1098 | inv(not) <=> not; 1099 | ``` 1100 | 1101 | #### Algebra misc 1102 | 1103 | ```poi 1104 | inc(x) <=> 1 + x; 1105 | (x + -y) <=> (x - y); 1106 | neg(x + y) <=> neg(x) - y; 1107 | neg(x) <=> -1 * x; 1108 | (a + b = c) <=> ((-a + a) + b = -a + c); 1109 | (a - b = c) <=> (a - (b - b) = c + b); 1110 | (a + b = c) <=> (b = c - a); 1111 | (a + b = c) <=> (a = c - b); 1112 | (a + b = c + b) <=> (a = c); 1113 | mul(-1) <=> neg; 1114 | (a - b = c - b) <=> (a = c); 1115 | (a / b = c / b) <=> (a * (b / b) = c * (b / b)); 1116 | (a / c + b / c) <=> ((a + b) / c); 1117 | ((a * b) / c) <=> (a * (b / c)); 1118 | (a * b - a) <=> (a * (b - 1)); 1119 | (- y)(x) <=> (x - y); 1120 | (x / y) <=> x * reci(y); 1121 | (/ y)(x) <=>> (x / y); 1122 | x^(a/b) <=> (x^a)^(1/b); 1123 | ``` 1124 | 1125 | #### Logarithms and exponentials 1126 | 1127 | ```poi 1128 | log10(x) <=> ln(x) / ln(10); 1129 | log2(x) <=> ln(x) / ln(2); 1130 | ln(y ^ x) <=> x * ln(y); 1131 | log2(y ^ x) <=> x * log2(y); 1132 | log10(y ^ x) <=> x * log10(y); 1133 | ln(x * y) <=> ln(x) + ln(y); 1134 | log10(x * y) <=> log10(x) + log10(y); 1135 | log2(x * y) <=> log2(x) + log2(y); 1136 | exp(x + y) <=> exp(x) * exp(y); 1137 | exp(x * ln(y)) <=> y^x; 1138 | ``` 1139 | 1140 | #### Boolean algebra 1141 | 1142 | - Decidable requires Law of Excluded Middle 1143 | - Constructive is the same as undecidable 1144 | 1145 | ```poi 1146 | imply <=> or · not; 1147 | imply · and <=> imply · (id, imply · snd); 1148 | not · nand <=> and; 1149 | not · nor <=> or; 1150 | not · and <=> nand; 1151 | not · or <=> nor; 1152 | not · eqb <=> xor; 1153 | not · xor <=> eqb; 1154 | ``` 1155 | 1156 | De Morgan's laws utility (decidable): 1157 | 1158 | ```poi 1159 | !(a & b) <=> !a | !b; 1160 | !(a | b) <=> !a & !b; 1161 | ``` 1162 | 1163 | Imply: 1164 | 1165 | ```poi 1166 | imply(a)(c) & imply(b)(c) <=> imply(a | b)(c); 1167 | imply(a)(b) <=> imply(!b)(!a); 1168 | imply(a)(b) & imply(b)(a) <=> eqb(a)(b); 1169 | ``` 1170 | 1171 | Xor: 1172 | 1173 | ```poi 1174 | xor(a)(b) <=> a & !b | !a & b; 1175 | ``` 1176 | 1177 | #### Generic transformations 1178 | 1179 | ```poi 1180 | (f:[arity]1 · g) <=> f:[arity]1[inv(g) -> id]; 1181 | (f · (g0 · fst, g1 · snd)) <=> f[inv(g0) x inv(g1) -> id]; 1182 | (f · inv(g)) <=> f[g -> id]; 1183 | (f · (inv(g0) · fst, inv(g1) · snd)) <=> f[g0 x g1 -> id]; 1184 | f[id -> g2] · inv(g1) <=> f[g1 -> g2]; 1185 | f[id -> g2] · g1 <=> f[inv(g1) -> g2]; 1186 | inv(f) <=> id[f -> id]; 1187 | inv(f · g) <=> inv(g) · inv(f); 1188 | id[(f · g) -> id] <=> id[f -> id[g -> id]]; 1189 | ((g2 · f · g1) = h) <=> (f = (inv(g2) · h · inv(g1))); 1190 | ((f · g) = h) <=> (((inv(f) · f) · g) = (inv(f) · h)); 1191 | ((f · g) = h) <=> ((f · (g · inv(g))) = (h · inv(g))); 1192 | (f[g] = h) <=> (f = h[inv(g)]); 1193 | (f[g1 -> g2] = h) <=> (f = h[inv(g1) -> inv(g2)]); 1194 | (f[g0 x g1 -> g2] = h) <=> (f = h[inv(g0) x inv(g1) -> inv(g2)]); 1195 | (f[g1 -> id] = h[id -> inv(g2)]) <=> (f[g1 -> g2] = h); 1196 | h · f[g -> id] <=> f[g -> h]; 1197 | f[id -> g] <=> g · f; 1198 | f[id x id -> g] <=> g · f; 1199 | f:!{}(a)(a) <=> f{eq}(a)(a); 1200 | f:!{} · (g, g) <=> f{eq} · (g, g); 1201 | f[g][h] <=> f[h · g]; 1202 | f[g0 -> g1][g2 -> g3] <=> f[(g2 · g0) -> (g3 · g1)]; 1203 | f[g0 x g1 -> g2][h0 x h1 -> h2] <=> f[(h0 · g0) x (h1 · g1) -> (h2 · g2)]; 1204 | f · (g · (h0, h1)) <=> (f · g) · (h0, h1); 1205 | (f · (g0, g1)) · (h0, h1) <=> f · ((g0, g1) · (h0, h1)); 1206 | f · (g · h) <=> (f · g) · h; 1207 | g · f:[arity]1 <=> f:[arity]1[g] · g; 1208 | g · f:[arity]1{g} <=> f:[arity]1{g}[g] · g; 1209 | f:[arity]1[g] <=> f:[arity]1[g -> id][id -> g]; 1210 | (f · (g0, g1)){x}(a){y}(b) <=> f(g0{x}(a){y}(b))(g1{x}(a){y}(b)); 1211 | (f · (g0, g1))(a)(b) <=> f(g0(a)(b))(g1(a)(b)); 1212 | (f · (g0, g1))(a) <=> f(g0(a))(g1(a)); 1213 | (f · (g0(a), g1(b)))(c) <=> (f · (g0 · fst, g1 · snd))(a)(b)(c); 1214 | (f · g:[arity]2)(a)(b) <=> f(g:[arity]2(a)(b)); 1215 | (f · g:[arity]2){x}(a){y}(b) <=> f(g:[arity]2{x}(a){y}(b)); 1216 | (f · g:[arity]1)(a) <=>> f(g:[arity]1(a)); 1217 | (f · g:[arity]2){x}(a){y}(b) <=> (f · g:[arity]2{x}{y})(a)(b); 1218 | (f · g:[arity]1){x}(a) <=>> f(g:[arity]1{x}(a)); 1219 | (g · f:[arity]2){_}(a){_}(b) <=>> f:[arity]2[g](g(a))(g(b)); 1220 | (g · f:[arity]1){_}(a) <=>> f:[arity]1[g](g(a)); 1221 | (g · f:[arity]2)(a)(b) <=>> f:[arity]2[g](g(a))(g(b)); 1222 | g · f:[arity]2 <=> f:[arity]2[g] · (g · fst, g · snd); 1223 | (g · f:[arity]1)(a) <=> f:[arity]1[g](g(a)); 1224 | (g, f)(a) <=> (g(a), f(a)); 1225 | f · (g0, g1)(a) <=> (f · (g0, g1))(a); 1226 | (g · f){h} <=> g · f{h}; 1227 | f[g0 x g1 -> g2] · (g0 · fst, g1 · snd) <=> g2 · f; 1228 | f[g0 -> g1] · g0 <=> g1 · f; 1229 | f[id x g0 -> g1] · (fst, g0 · snd) <=> g1 · f; 1230 | f[g0 x id -> g1] · (g0 · fst, snd) <=> g1 · f; 1231 | f[g -> g] <=> f[g]; 1232 | f[g x g -> g] <=> f[g]; 1233 | (f(x) = g(x)) <=> (f = g); 1234 | g2 · f[g0 x g1 -> id] <=> (g2 · f)[g0 x g1 -> id]; 1235 | h · f[g0 x g1 -> id] <=> f[g0 x g1 -> h]; 1236 | (f:[arity]2 · (fst, g:[arity]2 · snd))(a)(b)(c) <=> 1237 | f:[arity]2(a)(g:[arity]2(b)(c)); 1238 | f:[arity]2(a)(g:[arity]1(b)) <=> (f:[arity]2 · (fst, g:[arity]1 · snd))(a)(b); 1239 | f:[arity]2(g:[arity]1(a))(b) <=> (f:[arity]2 · (g:[arity]1 · fst, snd))(a)(b); 1240 | ``` 1241 | --------------------------------------------------------------------------------