CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

| Download

Try doing some basic maths questions in the Lean Theorem Prover. Functions, real numbers, equivalence relations and groups. Click on README.md and then on "Open in CoCalc with one click".

Project: Xena
Views: 18536
License: APACHE
/-
Copyright (c) 2019 Robert Y. Lewis . All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Robert Y. Lewis
-/

import data.rat.basic

/-!
# Meta operations on ℚ

This file defines functions for dealing with rational numbers as expressions.

They are not defined earlier in the hierarchy, in the `tactic` or `meta` folders, since we do not
want to import `data.rat.basic` there.

## Main definitions

* `rat.mk_numeral` embeds a rational `q` as a numeral expression into a type supporting the needed
  operations. It does not need a tactic state.
* `rat.reflect` specializes `rat.mk_numeral` to `ℚ`.
* `expr.of_rat` behaves like `rat.mk_numeral`, but uses the tactic state to infer the needed
  structure on the target type.

* `expr.to_rat` evaluates a normal numeral expression as a rat.
* `expr.eval_rat` evaluates a numeral expression with arithmetic operations as a rat.

-/

/--
`rat.mk_numeral q` embeds `q` as a numeral expression inside a type with 0, 1, +, -, and /

`type`: an expression representing the target type. This must live in Type 0.
`has_zero`, `has_one`, `has_add`: expressions of the type `has_zero %%type`, etc.

This function is similar to `expr.of_rat` but takes more hypotheses and is not tactic valued.
 -/
meta def rat.mk_numeral (type has_zero has_one has_add has_neg has_div : expr) : ℚ → expr
| ⟨num, denom, _, _⟩ :=
  let nume := num.mk_numeral type has_zero has_one has_add has_neg in
  if denom = 1 then nume else
    let dene := denom.mk_numeral type has_zero has_one has_add in
    `(@has_div.div.{0} %%type %%has_div %%nume %%dene)

/-- `rat.reflect q` represents the rational number `q` as a numeral expression of type `ℚ`. -/
protected meta def rat.reflect : ℚ → expr :=
rat.mk_numeral `(ℚ) `((by apply_instance : has_zero ℚ))
         `((by apply_instance : has_one ℚ))`((by apply_instance : has_add ℚ))
         `((by apply_instance : has_neg ℚ)) `(by apply_instance : has_div ℚ)

section
local attribute [semireducible] reflected
meta instance : has_reflect ℚ := rat.reflect
end

/-- Evaluates an expression as a rational number,
if that expression represents a numeral or the quotient of two numerals. -/
protected meta def expr.to_nonneg_rat : expr → option ℚ
| `(%%e₁ / %%e₂) := do m ← e₁.to_nat, n ← e₂.to_nat, some (rat.mk m n)
| e              := do n ← e.to_nat, return (rat.of_int n)

/-- Evaluates an expression as a rational number,
if that expression represents a numeral, the quotient of two numerals,
the negation of a numeral, or the negation of the quotient of two numerals. -/
protected meta def expr.to_rat : expr → option ℚ
| `(has_neg.neg %%e) := do q ← e.to_nonneg_rat, some (-q)
| e                  := e.to_nonneg_rat

/-- Evaluates an expression into a rational number, if that expression is built up from
  numerals, +, -, *, /, ⁻¹  -/
protected meta def expr.eval_rat : expr → option ℚ
| `(has_zero.zero _) := some 0
| `(has_one.one _) := some 1
| `(bit0 %%q) := (*) 2 <$> q.eval_rat
| `(bit1 %%q) := (+) 1 <$> (*) 2 <$> q.eval_rat
| `(%%a + %%b) := (+) <$> a.eval_rat <*> b.eval_rat
| `(%%a - %%b) := has_sub.sub <$> a.eval_rat <*> b.eval_rat
| `(%%a * %%b) := (*) <$> a.eval_rat <*> b.eval_rat
| `(%%a / %%b) := (/) <$> a.eval_rat <*> b.eval_rat
| `(-(%%a)) := has_neg.neg <$> a.eval_rat
| `((%%a)⁻¹) := has_inv.inv <$> a.eval_rat
| _ := none

/-- `expr.of_rat α q` embeds `q` as a numeral expression inside the type `α`.
Lean will try to infer the correct type classes on `α`, and the tactic will fail if it cannot.
This function is similar to `rat.mk_numeral` but it takes fewer hypotheses and is tactic valued.
-/
protected meta def expr.of_rat (α : expr) : ℚ → tactic expr
| ⟨(n:ℕ), d, h, c⟩   := do
  e₁ ← expr.of_nat α n,
  if d = 1 then return e₁ else
  do e₂ ← expr.of_nat α d,
  tactic.mk_app ``has_div.div [e₁, e₂]
| ⟨-[1+n], d, h, c⟩ := do
  e₁ ← expr.of_nat α (n+1),
  e ← (if d = 1 then return e₁ else do
    e₂ ← expr.of_nat α d,
    tactic.mk_app ``has_div.div [e₁, e₂]),
  tactic.mk_app ``has_neg.neg [e]