tnt 1.0.2

Simple runtime validated proofs in number theory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
use indexmap::IndexSet;
use num::BigUint;
use std::{
    convert::TryFrom,
    fs::File,
    io::{Error, Write},
    slice::Iter,
};

use crate::{production::*, Formula, LogicError, Term};

/// All the rules of production.
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Rule {
    Axiom,
    Specification,
    Generalization,
    Existence,
    Successor,
    Predecessor,
    InterchangeAE,
    InterchangeEA,
    Symmetry,
    Transitivity,
    Supposition,
    Implication,
    Induction,
}

/// Information tracked about each Formula
#[derive(Clone)]
pub struct TheoremFrame {
    pub formula: Formula,
    pub depth: usize,
    pub position: usize,
    pub annotation: String,
    pub rule: Rule,
    pub scope: usize,
}

impl TheoremFrame {
    pub fn new(
        formula: Formula,
        depth: usize,
        position: usize,
        annotation: String,
        rule: Rule,
        scope: usize,
    ) -> TheoremFrame {
        TheoremFrame {
            formula,
            depth,
            position,
            annotation,
            rule,
            scope,
        }
    }
}

/// Enforces valid use of deductive logic to produce proofs in Typographical Number Theory and outputs formatted results.
#[derive(Clone)]
pub struct Deduction {
    index: usize,
    scope_stack: Vec<usize>,
    scope_cur: usize,
    title: String,
    axioms: Vec<Formula>,
    theorems: Vec<TheoremFrame>,
}

// When 'true' forces the theorems to be printed every time they are added, helps with debugging
const NOISY: bool = false;

impl Deduction {
    pub fn custom(title: &str, axioms: Vec<Formula>) -> Deduction {
        Deduction {
            index: 0,
            scope_stack: vec![0],
            scope_cur: 0,
            title: title.to_string(),
            axioms,
            theorems: Vec::<TheoremFrame>::new(),
        }
    }

    /**

     * These are the axiomatic statements of the TNT formal system, they don't align strictly with the Peano Axioms but they define the same arithmetic properties for addition and multiplication. The axioms are as follows:
     *
     * Aa:~Sa=0                  for all a, it is false that (a + 1) is 0
     *
     * Aa:(a+0)=a                for all a, (a + 0) = a
     *
     * Aa:Ab:(a+Sb)=S(a+b)       for all a and b, (a + (b + 1)) = ((a + b) + 1)
     *
     * Aa:(a\*0)=0               for all a, (a × 0) = 0
     *
     * Aa:Ab:(a\*Sb)=((a\*b)+a)  for all a and b, (a × (b + 1)) = ((a × b) + a)
     */

    pub fn new(title: &str) -> Deduction {
        let peano_axioms = vec![
            Formula::try_from("Aa:~Sa=0").unwrap(),
            Formula::try_from("Aa:(a+0)=a").unwrap(),
            Formula::try_from("Aa:Ab:(a+Sb)=S(a+b)").unwrap(),
            Formula::try_from("Aa:(a*0)=0").unwrap(),
            Formula::try_from("Aa:Ab:(a*Sb)=((a*b)+a)").unwrap(),
        ];

        Deduction {
            index: 0,
            scope_stack: vec![0],
            scope_cur: 0,
            title: title.to_string(),
            axioms: peano_axioms,
            theorems: Vec::<TheoremFrame>::new(),
        }
    }

    // Internal methods
    // Get a theorem if it is in an accessible scope
    fn get_theorem(&self, n: usize) -> Result<&Formula, LogicError> {
        // Check the scope
        let tscope = self.theorems[n].scope;
        if tscope == self.scope_cur || self.scope_stack.contains(&tscope) {
            return Ok(&self.theorems[n].formula);
        }
        let msg = format!("Scope Error: position {n} is not in an accessible scope");
        Err(LogicError::new(msg))
    }

    // The last theorem on the list is always in an accessible scope.
    fn get_last_theorem(&self) -> &Formula {
        &self.theorems.last().unwrap().formula
    }

    // Pushes a new TheoremFrame and updates the index
    fn push_new(&mut self, theorem: Formula, annotation: String, rule: Rule) {
        if NOISY {
            if rule == Rule::Supposition {
                println!("{}begin supposition", "   ".repeat(self.depth() - 1))
            } else if rule == Rule::Implication {
                println!("{}end supposition", "   ".repeat(self.depth()))
            }
            println!(
                "{}{}) {} [{}]",
                "   ".repeat(self.depth()),
                self.index,
                theorem,
                annotation
            )
        }
        let depth = match rule {
            Rule::Supposition => self.depth() + 1,
            Rule::Implication => self.depth() - 1,
            _ => self.depth(),
        };
        let t = TheoremFrame {
            formula: theorem,
            depth: depth,
            position: self.index,
            annotation,
            rule,
            scope: self.scope_cur,
        };

        self.theorems.push(t);
        self.index += 1;
    }

    /// Current depth of the deduction.
    pub fn depth(&self) -> usize {
        if self.theorems.is_empty() {
            0
        } else {
            self.theorems.last().unwrap().depth
        }
    }

    /// Reference a TheoremFrame.
    pub fn theorem(&self, n: usize) -> &TheoremFrame {
        &self.theorems[n]
    }

    /// Return the last TheoremFrame.
    pub fn last_theorem(&self) -> &TheoremFrame {
        &self.theorems.last().unwrap()
    }

    /// Iterate over TheoremFrames of the Deduction.
    pub fn theorems(&self) -> Iter<TheoremFrame> {
        self.theorems.iter()
    }

    /// Write the Deduction.
    pub fn pretty_string(&self) -> String {
        let mut out = String::new();
        let mut prev_depth = 0;
        for (pos, t) in self.theorems.iter().enumerate() {
            if t.depth > prev_depth {
                let begin = format!("\n{}begin supposition", "   ".repeat(prev_depth));
                out.push_str(&begin);
            } else if t.depth < prev_depth {
                let end = format!("\n{}end supposition", "   ".repeat(t.depth));
                out.push_str(&end);
            } else {
            }
            let line = format!(
                "\n{}{}) {}",
                "   ".repeat(t.depth),
                pos,
                t.formula.to_string()
            );
            out.push_str(&line);
            prev_depth = t.depth;
        }
        out
    }

    /// Create an annotated LaTeX file the given file name that displays the Deduction.
    pub fn latex_file(&self, filename: &str) -> Result<(), Error> {
        let filename = format!("{}.tex", filename);
        let mut file = File::create(filename)?;

        let section_title = format!("\\section*{{{}}}\n", self.title);

        file.write(b"\\documentclass[fleqn,11pt]{article}\n")?;
        file.write(b"\\usepackage{amsmath}\n")?;
        file.write(b"\\allowdisplaybreaks\n")?;
        file.write(b"\\begin{document}\n")?;
        file.write(&section_title.into_bytes())?;
        file.write(b"\\begin{flalign*}\n")?;

        let mut prev_depth = 0;
        for (pos, t) in self.theorems.iter().enumerate() {
            if t.depth > prev_depth {
                let line = format!(
                    "&{}\\text{{begin supposition}}&\\\\\n",
                    "   ".repeat(prev_depth)
                )
                .into_bytes();
                file.write(&line)?;
            } else if t.depth < prev_depth {
                let line = format!("&{}\\text{{end supposition}}&\\\\\n", "   ".repeat(t.depth))
                    .into_bytes();
                file.write(&line)?;
            }

            let line = format!(
                "&\\hspace{{{}em}}{})\\hspace{{1em}}{}\\hspace{{2em}}\\textbf{{[{}]}}\\\\\n",
                t.depth * 2,
                pos,
                t.formula.to_latex(),
                t.annotation
            )
            .into_bytes();
            file.write(&line)?;

            prev_depth = t.depth;
        }

        file.write(b"\\end{flalign*}\n")?;
        file.write(b"\\end{document}")?;
        Ok(())
    }

    pub fn english(&self) -> String {
        let mut out = String::new();
        for t in self.theorems.iter() {
            out.push_str(&format!(
                "\n{}) {} [{}]",
                t.position,
                t.formula.to_english(),
                t.annotation
            ));
        }
        out
    }

    // Logical methods
    /// Push any axiom of the Deduction system into the theorems.
    pub fn add_axiom(&mut self, premise: usize) -> Result<(), LogicError> {
        if let Some(axiom) = self.axioms.get(premise) {
            self.push_new(axiom.clone(), "axiom".to_string(), Rule::Axiom);
            Ok(())
        } else {
            Err(LogicError(format!(
                "Axiom Error: there is no axiom #{premise}"
            )))
        }
    }

    /// Push a new theorem which replaces var with the provided Term in theorem n.
    pub fn specification(
        &mut self,
        n: usize,
        var_name: &'static str,
        term: &Term,
    ) -> Result<(), LogicError> {
        let t = specification(self.get_theorem(n)?, &var_name, term)?;
        let r = format!("specification of {var_name} to {term} in theorem {n}");
        self.push_new(t, r, Rule::Specification);
        Ok(())
    }

    /// Push a new theorem that adds universal quantification of var in theorem n.
    pub fn generalization(&mut self, n: usize, var_name: &'static str) -> Result<(), LogicError> {
        if self.depth() != 0 {
            let mut free_vars = IndexSet::<String>::new();
            self.get_theorem(self.scope_cur)?
                .get_vars_free(&mut free_vars);
            if free_vars.contains(&var_name.to_string()) {
                let msg = format!(
                    "Generalization Error: the variable {var_name} is free in the supposition {}",
                    self.get_theorem(self.scope_cur)?
                );
                return Err(LogicError::new(msg));
            }
        }
        let t = generalization(self.get_theorem(n)?, var_name);
        let r = format!("generalization of {var_name} in theorem {n}");
        self.push_new(t?, r, Rule::Generalization);
        Ok(())
    }

    /// Push a new theorem that adds existence quantification of var in theorem n.
    pub fn existence(&mut self, n: usize, var_name: &str) -> Result<(), LogicError> {
        let t = Formula::exists(var_name, &self.theorems[n].formula.clone());
        let r = format!("existence of {var_name} in theorem {n}");
        self.push_new(t, r, Rule::Existence);
        Ok(())
    }

    /// Push a new theorem that applies the successor to each side of a theorem n.
    pub fn successor(&mut self, n: usize) -> Result<(), LogicError> {
        let t = successor(self.get_theorem(n)?);
        let r = format!("successor of theorem {n}");
        self.push_new(t?, r, Rule::Successor);
        Ok(())
    }

    /// Push a new theorem that strips the successor to each side of a theorem n.
    pub fn predecessor(&mut self, n: usize) -> Result<(), LogicError> {
        let t = predecessor(self.get_theorem(n)?);
        let r = format!("predecessor of theorem {n}");
        self.push_new(t?, r, Rule::Predecessor);
        Ok(())
    }

    /// Push a new theorem that takes theorem n and changes the negated existential quantifier at the given position to a universal quantifer followed by a negation.
    pub fn interchange_ea(
        &mut self,
        n: usize,
        var_name: &str,
        pos: usize,
    ) -> Result<(), LogicError> {
        let t = interchange_ea(self.get_theorem(n)?, var_name, pos);
        let r = format!("interchange ~E{var_name}: for A{var_name}:~ in theorem {n}");
        self.push_new(t?, r, Rule::InterchangeEA);
        Ok(())
    }

    /// Push a new theorem that takes theorem n and changes the universal quantifer followed by a negation at the given position with a negated existential quantifier.
    pub fn interchange_ae(
        &mut self,
        n: usize,
        var_name: &str,
        pos: usize,
    ) -> Result<(), LogicError> {
        let t = interchange_ae(self.get_theorem(n)?, var_name, pos);
        let r = format!("interchange A{var_name}:~ for ~E{var_name}: in theorem {n}");
        self.push_new(t?, r, Rule::InterchangeAE);
        Ok(())
    }

    /// Push a new theorem that flips the left and right sides of theorem n.
    pub fn symmetry(&mut self, n: usize) -> Result<(), LogicError> {
        let t = symmetry(self.get_theorem(n)?);
        let r = format!("symmetry of theorem {n}");
        self.push_new(t?, r, Rule::Symmetry);
        Ok(())
    }

    /// Push a new theorem that is an equality of the left term and right term of formula n1 and n2.
    pub fn transitivity(&mut self, n1: usize, n2: usize) -> Result<(), LogicError> {
        let t = transitivity(self.get_theorem(n1)?, self.get_theorem(n2)?);
        let r = format!("transitivity of theorem {n1} and theorem {n2}");
        self.push_new(t?, r, Rule::Transitivity);
        Ok(())
    }

    /// Begin a supposition taking an arbitrary Formula as the premise.
    pub fn supposition(&mut self, premise: Formula) -> Result<(), LogicError> {
        // Push the current scope onto the stack and name the new scope after the index where it starts
        self.scope_stack.push(self.scope_cur);
        self.scope_cur = self.index;
        self.push_new(premise, "supposition".to_string(), Rule::Supposition);
        Ok(())
    }

    /// End a supposition and push a new theorem that the premise of the supposition implies the final theorem of the supposition.
    pub fn implication(&mut self) -> Result<(), LogicError> {
        // Create the formula and annotation
        let t = Formula::implies(self.get_theorem(self.scope_cur)?, self.get_last_theorem());
        let r = format!(
            "implication of theorem {} and theorem {}",
            self.scope_cur, self.index
        );

        // Pop the top of the stack and make it the new scope
        self.scope_cur = self.scope_stack.pop().unwrap();
        self.push_new(t, r, Rule::Implication);
        Ok(())
    }

    /// Push a new theorem that is induction on given variable with base and general being the position of theorems that state the base case and general case.
    pub fn induction(
        &mut self,
        var_name: &str,
        base: usize,
        general: usize,
    ) -> Result<(), LogicError> {
        let t = induction(
            var_name,
            self.get_theorem(base)?,
            self.get_theorem(general)?,
        );
        let r = format!("induction of {var_name} on theorems {base} and {general}");
        self.push_new(t?, r, Rule::Induction);
        Ok(())
    }

    fn vars_in_order(&self) -> IndexSet<String> {
        let mut vars = IndexSet::new();
        for theorem in self.theorems.iter() {
            theorem.formula.get_vars(&mut vars)
        }
        vars
    }

    // /// Produce a Deduction of identical form with all Formulas in austere form.
    pub fn austere(&self) -> Deduction {
        let vars = self.vars_in_order();
        let mut out = self.clone();
        for theorem in out.theorems.iter_mut() {
            theorem.formula.to_austere_with(&vars);
        }
        out
    }

    // /// Change all Formulas in the Deduction to their austere form.
    pub fn to_austere(&mut self) {
        let vars = self.vars_in_order();
        for theorem in self.theorems.iter_mut() {
            theorem.formula.to_austere_with(&vars);
        }
    }

    // /// Convert the Deduction to a (very large) integer. (No seriously its going to be a gigantic number.)
    pub fn arithmetize(&self) -> BigUint {
        let austere = self.austere();
        let mut n: Vec<u8> = Vec::new();
        for t in austere.theorems().rev() {
            n.extend(t.formula.to_string().into_bytes().iter());
            n.push(32);
        }
        BigUint::from_bytes_be(&n)
    }
}