PVS 6.0 Prelude

% The PVS prelude.

% The prelude consists of theories that are built in to the PVS system.
% It is typechecked the same as any other PVS theory, but there are hooks
% in the typechecker that require most of these theories to be available,
% hence the order of the theories is important.  For example, no formulas
% may be declared before the booleans are available, as the formula is
% expected to have type bool.  Since definitions implicitly involve both
% formulas and equality, the booleans theory may not include any
% definitions.  Formulas are given below as AXIOMs, POSTULATEs, and LEMMAs.
% POSTULATEs are formulas that can be proved using the decision procedures,
% but would have to be given as axioms in a pure development of the theory.
% AXIOMs are formulas that cannot be proved, and LEMMAS are formulas that
% have been proved.

% --------------------------------------------------------------------
% PVS
% Copyright (C) 2006, SRI International.  All Rights Reserved.

% This program is free software; you can redistribute it and/or
% modify it under the terms of the GNU General Public License
% as published by the Free Software Foundation; either version 2
% of the License, or (at your option) any later version.

% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.

% You should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
% --------------------------------------------------------------------

% booleans declares the type boolean and its abbreviation bool, along
% with the boolean constants true and false and the boolean connectives.
% The properties of the connectives are given later, but the connectives
% are built in to the typechecker so must be provided early on.
% Note: the boolean type could be defined as the enumeration type {false,
% true}, but booleans are primitive; the correct handling of enumeration
% types requires the boolean type.

booleans: THEORY
 BEGIN

  boolean: NONEMPTY_TYPE
  bool: NONEMPTY_TYPE = boolean
  FALSE, TRUE: bool
  NOT, ¬: [bool -> bool]
  AND, &, ∧, OR, ∨, IMPLIES, =>, ⇒, WHEN, IFF, <=>, ⇔: [bool, bool -> bool]

 END booleans


% equalities contains the declaration for =.  It has a single type
% parameter.  Properties of equality are given in equality_props.

equalities [T: TYPE]: THEORY
 BEGIN

  =: [T, T -> boolean]

 END equalities


notequal[T: TYPE]: THEORY
 BEGIN
  x, y: VAR T

  /=(x, y): boolean = NOT (x = y);
  ≠: [T, T -> bool] = /=
 END notequal


% if_def provides the polymorphic declaration of the IF-THEN-ELSE
% connective.  Note that the declaration for IF is for a 3-ary function,
% and that the IF-THEN-ELSE form is simply an alternative syntax.

if_def [T: TYPE]: THEORY
 BEGIN

  IF:[boolean, T, T -> T]

 END if_def


% boolean_props provides lemmas about the boolean constants and
% connectives.  The lemmas define them in terms of IF-THEN-ELSE, though
% these lemmas should never be needed since the prover "knows" the
% connectives as primitives.  WHEN is a special case - it is translated to
% IMPLIES with the arguments reversed by the typechecker.

boolean_props: THEORY
 BEGIN
  A, B: VAR bool

  bool_exclusive: POSTULATE not (false = true)
  bool_inclusive: POSTULATE A = false or A = true

  not_def:       POSTULATE (not A) = IF A THEN false ELSE true ENDIF
  and_def:       POSTULATE (A and B) = IF A THEN B ELSE false ENDIF
  syand_def:     POSTULATE & = and
  or_def:        POSTULATE (A or B) = IF A THEN true ELSE B ENDIF
  implies_def:   POSTULATE (A implies B) = IF A THEN B ELSE true ENDIF
  syimplies_def: POSTULATE => = implies
  when_def:      POSTULATE (A when B) = (B implies A)
  iff_def:       POSTULATE (A iff B) = ((A and B) or (not A and not B))
  syiff_def:     POSTULATE <=> = iff

  excluded_middle: LEMMA A OR NOT A  

 END boolean_props


% xor_def provides the definition for XOR.  Note that this is not built in
% to the prover, so this definition will need to be expanded in order to use
% it.

xor_def: THEORY
 BEGIN
  A, B: VAR bool
  XOR(A, B): bool = (A /= B)
  
  xor_def: LEMMA (A xor B) = IF A THEN NOT B ELSE B ENDIF
 END xor_def


% quantifier_props defines some useful properties of quantifiers.  Note
% that these work well with the higher-order matching facility of the prover.

quantifier_props [t: TYPE]: THEORY
 BEGIN
  x: VAR t
  p, q: VAR [t -> bool]

  not_exists: LEMMA (EXISTS x: p(x)) = NOT (FORALL x: NOT p(x))

  exists_not: LEMMA (EXISTS x: NOT p(x)) = NOT (FORALL x: p(x))

  exists_or: LEMMA
    (EXISTS x: p(x) OR q(x)) = ((EXISTS x: p(x)) OR (EXISTS x: q(x)))

  exists_implies: LEMMA
    (EXISTS x: p(x) IMPLIES q(x)) = ((EXISTS x: NOT p(x)) OR (EXISTS x: q(x)))

  exists_and: LEMMA
    (EXISTS x: p(x) AND q(x)) IMPLIES ((EXISTS x: p(x)) AND (EXISTS x: q(x)))

  not_forall: LEMMA (FORALL x: p(x)) = NOT (EXISTS x: NOT p(x))

  forall_not: LEMMA (FORALL x: NOT p(x)) = NOT (EXISTS x: p(x))

  forall_and: LEMMA
    (FORALL x: p(x) AND q(x)) = ((FORALL x: p(x)) AND (FORALL x: q(x)))

  forall_or: LEMMA
    ((FORALL x: p(x)) OR (FORALL x: q(x))) IMPLIES (FORALL x: p(x) OR q(x))

 END quantifier_props  


% defined_types provides the declarations for types pred and setof

defined_types [t: TYPE]: THEORY
 BEGIN
  pred: TYPE = [t -> bool]
  PRED: TYPE = [t -> bool]
  predicate: TYPE = [t -> bool]
  PREDICATE: TYPE = [t -> bool]
  setof: TYPE = [t -> bool]
  SETOF: TYPE = [t -> bool]
 END defined_types


% exists1 provides a unique existence function; it takes a predicate
% and asserts that there is one and only one element of the type that
% satisfies the predicate.  The expression "exists1! (x:t): p(x)" is
% translated to "exists1(LAMBDA (x:t): p(x))".

exists1 [T: TYPE]: THEORY
 BEGIN
  x, y: VAR T
  p, q: VAR pred[T]

  unique?(p): bool = FORALL x, y: p(x) AND p(y) IMPLIES x = y

  exists1(p): bool = (EXISTS x: p(x)) AND unique?(p)

  unique_lem: LEMMA
    (FORALL x: p(x) IMPLIES q(x)) IMPLIES (unique?(q) IMPLIES unique?(p))

  exists1_lem: LEMMA (exists1! x: p(x)) IMPLIES (EXISTS x: p(x))

 END exists1


% equality_props provides some properties of IF and =.

equality_props[T: TYPE]: THEORY
 BEGIN

  x, y, z: VAR T
  b: VAR bool
  
  IF_true: POSTULATE IF true THEN x ELSE y ENDIF = x

  IF_false: POSTULATE IF false THEN x ELSE y ENDIF = y

  IF_same: LEMMA IF b THEN x ELSE x ENDIF = x

  reflexivity_of_equals: POSTULATE x = x

  transitivity_of_equals: POSTULATE x = y AND y = z IMPLIES x = z

  symmetry_of_equals: POSTULATE x = y IMPLIES y = x

 END equality_props


% if_props

if_props [s, t: TYPE]: THEORY
 BEGIN
  a, b, c: VAR bool
  x, y: VAR s
  f: VAR [s -> t]
  
  lift_if1: LEMMA
    f(IF a THEN x ELSE y ENDIF) = IF a THEN f(x) ELSE f(y) ENDIF

  lift_if2: LEMMA
    IF (IF a THEN b ELSE c ENDIF) THEN x ELSE y ENDIF
     = IF a THEN (IF b THEN x ELSE y ENDIF)
            ELSE (IF c THEN x ELSE y ENDIF) ENDIF

 END if_props


% functions provides the basic properties of functions.  Because of the
% type equivalence of [[t1,...,tn] -> t] and [t1,...,tn -> t], this
% theory handles any function arity.  However, this doesn't handle
% dependent function types, since the domain and range can't be separated.

functions [D, R: TYPE]: THEORY
 BEGIN
  f, g: VAR [D -> R]
  x, x1, x2: VAR D
  y: VAR R
  Drel: VAR PRED[[D, D]]
  Rrel: VAR PRED[[R, R]]

  extensionality_postulate: POSTULATE
     (FORALL (x: D): f(x) = g(x)) IFF f = g

  % The extensionality lemma is provided as it is easier to use
  % as a rewrite than the above postulate.
  extensionality: LEMMA
     (FORALL (x: D): f(x) = g(x)) IMPLIES f = g

  congruence: POSTULATE f = g AND x1 = x2 IMPLIES f(x1) = g(x2)

  eta: LEMMA (LAMBDA (x: D): f(x)) = f

  injective?(f): bool = (FORALL x1, x2: (f(x1) = f(x2) => (x1 = x2)))

  surjective?(f): bool = (FORALL y: (EXISTS x: f(x) = y))

  bijective?(f): bool = injective?(f) & surjective?(f)

  bij_is_inj: JUDGEMENT (bijective?) SUBTYPE_OF (injective?)
  
  bij_is_surj: JUDGEMENT (bijective?) SUBTYPE_OF (surjective?)

  domain(f): TYPE = D

  range(f): TYPE = R

  graph(f)(x, y): bool = (f(x) = y)

  preserves(f, Drel, Rrel): bool =
      FORALL x1, x2: Drel(x1, x2) IMPLIES Rrel(f(x1), f(x2))

  % Curried form
  preserves(Drel, Rrel)(f): bool = preserves(f, Drel, Rrel)

  inverts(f, Drel, Rrel): bool =
      FORALL x1, x2: Drel(x1, x2) IMPLIES Rrel(f(x2), f(x1))

  % Curried form
  inverts(Drel, Rrel)(f): bool = inverts(f, Drel, Rrel)

 END functions


functions_alt [D, R: TYPE, Drel: PRED[[D, D]], Rrel: PRED[[R, R]]]: THEORY
 BEGIN
  f: VAR [D -> R]
  preserves: [[D -> R] -> bool] = preserves(Drel, Rrel)
  inverts: [[D -> R] -> bool] = inverts(Drel, Rrel)
 END functions_alt


transpose[T1, T2, T3: TYPE]: THEORY
 BEGIN
  f: VAR [T1 -> [T2 -> T3]]
  x: VAR T1
  y: VAR T2

  transpose(f)(y)(x): T3 = f(x)(y)
 END transpose


% restrict is the restriction operator on functions, allowing a
% function defined on a supertype to be applied to a subtype.  Note
% that it is a conversion, so will be inserted automatically when needed.
% Note also that the typechecker expands restrict automatically in some
% cases if it is fully applied, i.e., restrict(f)(x) is replaced with f(x).

restrict [T: TYPE, S: TYPE FROM T, R: TYPE]: THEORY
 BEGIN

  f: VAR [T -> R]
  s: VAR S

  restrict(f)(s): R = f(s)
  CONVERSION restrict

  injective_restrict: LEMMA
    injective?(f) IMPLIES injective?(restrict(f))

  restrict_of_inj_is_inj: JUDGEMENT
    restrict(f: (injective?[T,R])) HAS_TYPE (injective?[S,R])

 END restrict


% restrict_props provides the lemma that extending a function from a given
% domain type to the same type is the identity.  This usually comes about
% because of theory instantiation, and the typechecker has this rule built
% in, so it won't be needed in general.

restrict_props[T: TYPE, R: TYPE]: THEORY
 BEGIN
  f: VAR [T -> R]
  restrict_full: LEMMA restrict[T, T, R](f) = f
 END restrict_props


% extend is the inverse of restrict.  The difference is that a default
% value must be provided, which keeps it from being a conversion, in
% general (but see extend_bool).

extend [T: TYPE, S: TYPE FROM T, R: TYPE, d: R]: THEORY
 BEGIN

  f: VAR [S -> R]
  t: VAR T

  extend(f)(t): R = IF S_pred(t) THEN f(t) ELSE d ENDIF

  restrict_extend: LEMMA restrict[T,S,R](extend(f)) = f

 END extend


% extend_bool provides a conversion for boolean valued extensions,
% providing the default value of false.  Thus a set of nats "is" a set
% of ints.

extend_bool [T: TYPE, S: TYPE FROM T]: THEORY
 BEGIN

  CONVERSION extend[T, S, bool, false]

 END extend_bool


% extend_props provides the lemma that extending a function from a given
% domain type to the same type is the identity.  This usually comes about
% because of theory instantiation, and the typechecker has this rule built
% in, so it won't be needed in general.

extend_props [T: TYPE, R: TYPE, d: R]: THEORY
 BEGIN
  f: VAR [T -> R]
  extend_full: LEMMA extend[T, T, R, d](f) = f
 END extend_props

%-------------------------------------------------------------------------
%
%  Cases in which the introduction of extend preserves properties.
%
%  ---------------------------------------------------------------------
%      Author: Jerry James (jamesj@acm.org), University of Kansas
%
%  extend_func_props
%-------------------------------------------------------------------------
extend_func_props[T: TYPE, S: TYPE FROM T, R: TYPE, d: R]: THEORY
 BEGIN

  surjective_extend: JUDGEMENT
    extend[T, S, R, d](f: (surjective?[S, R])) HAS_TYPE (surjective?[T, R])

 END extend_func_props


% The K combinator is used to trigger lambda conversions.
% Due to user demand, it is turned off by default.

K_conversion [T1, T2: TYPE]: THEORY
 BEGIN
  K_conversion(x:T1)(y:T2): T1 = x
%  CONVERSION K_conversion
 END K_conversion

K_props [T1, T2: TYPE, S: TYPE FROM T1]: THEORY
 BEGIN
  K_preserves: JUDGEMENT K_conversion[T1, T2](x:S)(y:T2) HAS_TYPE S
  K_preserves1: JUDGEMENT K_conversion[T1, T2](x:S) HAS_TYPE [T2 -> S]
 END K_props

% identity simply defines the identity function.  The identity is used for
% conversion expressions.  For example, "foo: T" is translated to
% "(LAMBDA (x:T): x)(foo)"

identity [T: TYPE]: THEORY
 BEGIN
  x: VAR T
  I: (bijective?[T, T]) = (LAMBDA x: x)
  id: (bijective?[T, T]) = (LAMBDA x: x)
  identity: (bijective?[T, T]) = (LAMBDA x: x)
 END identity


identity_props [T: TYPE, S: TYPE FROM T]: THEORY
 BEGIN
  x: VAR S
  I_preserves: JUDGEMENT I[T](x) HAS_TYPE S
  id_preserves: JUDGEMENT id[T](x) HAS_TYPE S
  identity_preserves: JUDGEMENT identity[T](x) HAS_TYPE S
 END identity_props


% relations defines properties that are useful in specifying binary
% relations.

relations [T: TYPE]: THEORY
 BEGIN

  R: VAR PRED[[T, T]]
  x, y, z: VAR T

  eq: pred[[T, T]] = (LAMBDA x, y: x = y)

  reflexive?(R): bool = FORALL x: R(x, x)

  irreflexive?(R): bool = FORALL x: NOT R(x, x)

  symmetric?(R): bool = FORALL x, y: R(x, y) IMPLIES R(y, x)

  antisymmetric?(R): bool = FORALL x, y: R(x, y) & R(y, x) => x = y

  connected?(R): bool = FORALL x, y: x /= y IMPLIES R(x, y) OR R(y, x)

  transitive?(R): bool = FORALL x, y, z: R(x, y) & R(y, z) => R(x, z)

  equivalence?(R): bool = reflexive?(R) AND symmetric?(R) AND transitive?(R)

  equivalence: TYPE = (equivalence?)

  equiv_is_reflexive:  JUDGEMENT (equivalence?) SUBTYPE_OF (reflexive?)
  equiv_is_symmetric:  JUDGEMENT (equivalence?) SUBTYPE_OF (symmetric?)
  equiv_is_transitive: JUDGEMENT (equivalence?) SUBTYPE_OF (transitive?)

  % Reflexive closure
  RC(R)(x, y): bool = (x = y) OR R(x, y)

  % Transitive closure
  TC(R)(x, y): INDUCTIVE bool = R(x,y) OR (EXISTS z: TC(R)(x,z) AND TC(R)(z,y))

  % Reflexive-transitive closure
  RTC(R)(x, y): bool = (x = y) OR TC(R)(x, y)

 END relations


% orders defines the usual ordering relations.  Be careful not to read too
% much into these definitions; < and <= are variables ranging over binary
% relations, not actual orders.  Their use below is suggestive of the
% defining properties.

orders [T: TYPE]: THEORY
 BEGIN
  x, y: VAR T
  <=, < : VAR pred[[T, T]]
  p: VAR pred[T]

  preorder?(<=): bool = reflexive?(<=) & transitive?(<=)

  preorder_is_reflexive:  JUDGEMENT (preorder?) SUBTYPE_OF (reflexive?[T])
  preorder_is_transitive: JUDGEMENT (preorder?) SUBTYPE_OF (transitive?[T])
  equiv_is_preorder:      JUDGEMENT (equivalence?[T]) SUBTYPE_OF (preorder?)

  partial_order?(<=): bool = preorder?(<=) & antisymmetric?(<=)

  po_is_preorder:      JUDGEMENT (partial_order?) SUBTYPE_OF (preorder?)
  po_is_antisymmetric: JUDGEMENT
    (partial_order?) SUBTYPE_OF (antisymmetric?[T])

  strict_order?(<): bool = irreflexive?(<) & transitive?(<)

  strict_is_irreflexive: JUDGEMENT
    (strict_order?) SUBTYPE_OF (irreflexive?[T])
  strict_order_is_antisymmetric: JUDGEMENT
    (strict_order?) SUBTYPE_OF (antisymmetric?[T])
  strict_is_transitive: JUDGEMENT
    (strict_order?) SUBTYPE_OF (transitive?[T])

  dichotomous?(<=): bool = (FORALL x, y: (x <= y OR y <= x))

  total_order?(<=): bool = partial_order?(<=) & dichotomous?(<=)

  total_is_po:          JUDGEMENT (total_order?) SUBTYPE_OF (partial_order?)
  total_is_dichotomous: JUDGEMENT (total_order?) SUBTYPE_OF (dichotomous?)

  linear_order?(<=): bool = total_order?(<=)

  linear_is_total: JUDGEMENT (linear_order?) SUBTYPE_OF (total_order?)
  total_is_linear: JUDGEMENT (total_order?)  SUBTYPE_OF (linear_order?)

  trichotomous?(<): bool = (FORALL x, y: x < y OR y < x OR x = y)

  strict_total_order?(<): bool = strict_order?(<) & trichotomous?(<)

  strict_total_is_strict: JUDGEMENT
    (strict_total_order?) SUBTYPE_OF (strict_order?)
  strict_total_is_trichotomous: JUDGEMENT
    (strict_total_order?) SUBTYPE_OF (trichotomous?)

  well_founded?(<): bool =
     (FORALL p:
        (EXISTS y: p(y))
	   IMPLIES (EXISTS (y:(p)): (FORALL (x:(p)): (NOT x < y))))

  strict_well_founded?(<): bool = strict_order?(<) & well_founded?(<)

  strict_well_founded_is_strict: JUDGEMENT
    (strict_well_founded?) SUBTYPE_OF (strict_order?)
  strict_well_founded_is_well_founded: JUDGEMENT
    (strict_well_founded?) SUBTYPE_OF (well_founded?)

  well_ordered?(<): bool = strict_total_order?(<) & well_founded?(<)

  well_ordered_is_strict_total: JUDGEMENT
    (well_ordered?) SUBTYPE_OF (strict_total_order?)
  well_ordered_is_well_founded: JUDGEMENT
    (well_ordered?) SUBTYPE_OF (well_founded?)

  nonempty_pred: TYPE = {p: pred[T] | EXISTS (x: T): p(x)}

  pe: VAR pred[T]

  upper_bound?(<)(x, pe): bool = FORALL (y: (pe)): y < x

  upper_bound?(<)(pe)(x): bool = upper_bound?(<)(x, pe)

  lower_bound?(<)(x, pe): bool = FORALL (y: (pe)): x < y

  lower_bound?(<)(pe)(x): bool = lower_bound?(<)(x, pe)

  least_upper_bound?(<)(x, pe): bool =
    upper_bound?(<)(x, pe) AND
      FORALL y: upper_bound?(<)(y, pe) IMPLIES (x < y OR x = y)

  least_upper_bound?(<)(pe)(x): bool = least_upper_bound?(<)(x, pe)

  greatest_lower_bound?(<)(x, pe): bool =
    lower_bound?(<)(x, pe) AND
      FORALL y: lower_bound?(<)(y, pe) IMPLIES (y < x OR x = y)

  greatest_lower_bound?(<)(pe)(x): bool = greatest_lower_bound?(<)(x, pe)

 END orders


orders_alt [T: TYPE, < : pred[[T, T]], pe: nonempty_pred[T]]: THEORY
 BEGIN
  x: VAR T
  upper_bound?: [T -> bool] = upper_bound?(<)(pe)
  least_upper_bound?: [T -> bool] = least_upper_bound?(<)(pe)
  lower_bound?: [T -> bool] = lower_bound?(<)(pe)
  greatest_lower_bound?: [T -> bool] = greatest_lower_bound?(<)(pe)
  
  least_upper_bound_is_upper_bound: JUDGEMENT
    (least_upper_bound?) SUBTYPE_OF (upper_bound?)

  greatest_lower_bound_is_lower_bound: JUDGEMENT
    (greatest_lower_bound?) SUBTYPE_OF (lower_bound?)
 END orders_alt


%-------------------------------------------------------------------------
%
%  Cases in which the introduction of restrict preserves properties.
%
%  ---------------------------------------------------------------------
%      Author: Jerry James (jamesj@acm.org), University of Kansas
%
%  restrict_order_props
%-------------------------------------------------------------------------
restrict_order_props[T: TYPE, S: TYPE FROM T]: THEORY
 BEGIN

  reflexive_restrict: JUDGEMENT
    restrict(R: (reflexive?[T])) HAS_TYPE (reflexive?[S])

  irreflexive_restrict: JUDGEMENT
    restrict(R: (irreflexive?[T])) HAS_TYPE (irreflexive?[S])

  symmetric_restrict: JUDGEMENT
    restrict(R: (symmetric?[T])) HAS_TYPE (symmetric?[S])

  antisymmetric_restrict: JUDGEMENT
    restrict(R: (antisymmetric?[T])) HAS_TYPE (antisymmetric?[S])

  connected_restrict: JUDGEMENT
    restrict(R: (connected?[T])) HAS_TYPE (connected?[S])

  transitive_restrict: JUDGEMENT
    restrict(R: (transitive?[T])) HAS_TYPE (transitive?[S])

  equivalence_restrict: JUDGEMENT
    restrict(R: (equivalence?[T])) HAS_TYPE (equivalence?[S])

  preorder_restrict: JUDGEMENT
    restrict(R: (preorder?[T])) HAS_TYPE (preorder?[S])

  partial_order_restrict: JUDGEMENT
    restrict(R: (partial_order?[T])) HAS_TYPE (partial_order?[S])

  strict_order_restrict: JUDGEMENT
    restrict(R: (strict_order?[T])) HAS_TYPE (strict_order?[S])

  dichotomous_restrict: JUDGEMENT
    restrict(R: (dichotomous?[T])) HAS_TYPE (dichotomous?[S])

  total_order_restrict: JUDGEMENT
    restrict(R: (total_order?[T])) HAS_TYPE (total_order?[S])

  trichotomous_restrict: JUDGEMENT
    restrict(R: (trichotomous?[T])) HAS_TYPE (trichotomous?[S])

  strict_total_order_restrict: JUDGEMENT
    restrict(R: (strict_total_order?[T])) HAS_TYPE (strict_total_order?[S])

  well_founded_restrict: JUDGEMENT
    restrict(R: (well_founded?[T])) HAS_TYPE (well_founded?[S])

  well_ordered_restrict: JUDGEMENT
    restrict(R: (well_ordered?[T])) HAS_TYPE (well_ordered?[S])

 END restrict_order_props


% Courtesy of Jerry James
extend_order_props[T: TYPE, S: TYPE FROM T]: THEORY
 BEGIN

  irreflexive_extend: JUDGEMENT
    extend[[T, T], [S, S], bool, FALSE](R: (irreflexive?[S]))
        HAS_TYPE (irreflexive?[T])

  symmetric_extend: JUDGEMENT
    extend[[T, T], [S, S], bool, FALSE](R: (symmetric?[S]))
        HAS_TYPE (symmetric?[T])

  antisymmetric_extend: JUDGEMENT
    extend[[T, T], [S, S], bool, FALSE](R: (antisymmetric?[S]))
        HAS_TYPE (antisymmetric?[T])

  transitive_extend: JUDGEMENT
    extend[[T, T], [S, S], bool, FALSE](R: (transitive?[S]))
        HAS_TYPE (transitive?[T])

  strict_order_extend: JUDGEMENT
    extend[[T, T], [S, S], bool, FALSE](R: (strict_order?[S]))
        HAS_TYPE (strict_order?[T])

 END extend_order_props


% wf_induction defines induction for any type that has a well-founded
% relation.

wf_induction [T: TYPE, < : (well_founded?[T])]: THEORY
 BEGIN

  wf_induction: LEMMA
    (FORALL (p: pred[T]):
      (FORALL (x: T):
        (FORALL (y: T): y<x IMPLIES p(y))
           IMPLIES p(x))
    IMPLIES
      (FORALL (x:T): p(x)))

 END wf_induction


% measure_induction builds on well-founded induction.  It allows
% induction over a type T for which a measure function m is defined.

measure_induction [T: TYPE, M: TYPE, m: [T -> M], < : (well_founded?[M])]: THEORY
 BEGIN

 measure_induction: LEMMA
  (FORALL (p: pred[T]):
     (FORALL (x: T): 
         (FORALL (y: T): m(y) < m(x) IMPLIES  p(y))
             IMPLIES p(x))
    IMPLIES (FORALL (x: T): p(x)))

 END measure_induction


% epsilons provides a "choice" function that does not have a nonemptiness
% requirement on the predicate.  Given a predicate over the type t,
% epsilon produces an element of satisfying that predicate if one exists,
% and otherwise produces an arbitrary element of that type.
% "epsilon! (x:t): p(x)" is translated to "epsilon(LAMBDA (x:t): p(x))".
% Note that the type parameter is given as nonempty, which means that
% there is an nonempty ASSUMPTION automatically generated for this theory.

epsilons [T: NONEMPTY_TYPE]: THEORY
 BEGIN
  p: VAR pred[T]
  x: VAR T

  epsilon(p): T

  epsilon_ax: AXIOM (EXISTS x: p(x)) => p(epsilon(p))

 END epsilons


% sets provides the polymorphic set type, along with the usual set
% operations.  It is important to bear in mind that a set is just
% a predicate.

sets [T: TYPE]: THEORY
 BEGIN

  set: TYPE = setof[T]

  x, y: VAR T

  a, b, c: VAR set

  p: VAR PRED[T]

  member(x, a): bool = a(x)

  empty?(a): bool = (FORALL x: NOT member(x, a))

  emptyset: set = {x | false}

  nonempty?(a): bool = NOT empty?(a)

  nonempty_set: TYPE = (nonempty?)

  full?(a): bool = (FORALL x: member(x, a))

  fullset: set = {x | true}

  nontrivial?(a): bool = a /= emptyset & a /= fullset

  subset?(a, b): bool = (FORALL x: member(x, a) => member(x, b))

  strict_subset?(a, b): bool = subset?(a, b) & a /= b

  union(a, b): set = {x | member(x, a) OR member(x, b)}

  intersection(a, b): set = {x | member(x, a) AND member(x, b)}

  disjoint?(a, b): bool = empty?(intersection(a, b))

  complement(a): set = {x | NOT member(x, a)} 

  difference(a, b): set = {x | member(x, a) AND NOT member(x, b)}

  symmetric_difference(a, b): set =
    union(difference(a, b), difference(b, a))

  every(p)(a): bool = FORALL (x:(a)): p(x)

  every(p, a): bool = FORALL (x:(a)): p(x)

  some(p)(a): bool = EXISTS (x:(a)): p(x)

  some(p, a): bool = EXISTS (x:(a)): p(x)

  singleton?(a): bool = (EXISTS (x:(a)): (FORALL (y:(a)): x = y))

  singleton(x): (singleton?) = {y | y = x}

  % CONVERSION+ singleton

  add(x, a): (nonempty?) = {y | x = y OR member(y, a)}

  remove(x, a): set = {y | x /= y AND member(y, a)}

  % A choice function for nonempty sets
  % No longer defined, so that grind will not expand epsilon.
  % It's generally easier to use typepred on choose than to bring in epsilon_ax
  % Can always bring in choose_is_epsilon axiom if the expansion is desired.
  choose(p: (nonempty?)): (p) % = epsilon(p)

  choose_is_epsilon: AXIOM FORALL (p: (nonempty?)): choose(p) = epsilon(p)

  the(p: (singleton?)): (p)

  the_lem: LEMMA FORALL (p: (singleton?)): the(p) = epsilon(p)

  the_prop: LEMMA FORALL (p: (singleton?)): p(the(p))

  singleton_elt(a: (singleton?)): T = the! x: member(x, a)

  CONVERSION+ singleton_elt

  is_singleton: LEMMA
    FORALL a: (nonempty?(a) AND FORALL x, y: a(x) AND a(y) IMPLIES (x=y))
      IMPLIES singleton?(a)

  singleton_elt_lem: LEMMA singleton?(a) and a(x) IMPLIES singleton_elt(a) = x

  singleton_elt_def: LEMMA singleton?(a) IMPLIES singleton_elt(a) = choose(a)

  singleton_singleton: LEMMA singleton?(a) IMPLIES (EXISTS x: a = singleton(x))

  singleton_rew: LEMMA singleton_elt(singleton(x)) = x

  AUTO_REWRITE+ singleton_rew

  rest(a): set = IF empty?(a) THEN a ELSE remove(choose(a), a) ENDIF

  setofsets: TYPE = setof[setof[T]]
  
  A, B : Var setofsets

  powerset(a): setofsets = {b | subset?(b, a)}

  Union(A): set = {x | EXISTS (a: (A)): a(x)}

  Intersection(A): set = {x | FORALL (a: (A)): a(x)}

  nonempty_singleton: JUDGEMENT (singleton?) SUBTYPE_OF (nonempty?)
  nonempty_union1: JUDGEMENT union(a: (nonempty?), b: set) HAS_TYPE (nonempty?)
  nonempty_union2: JUDGEMENT union(a: set, b: (nonempty?)) HAS_TYPE (nonempty?)

 END sets


% Lemmas about sets - many of these came from Bruno Dutertre in the
% more_set_lemmas theory of the finite_sets library

sets_lemmas [T: TYPE]: THEORY
 BEGIN

  x, y: VAR T
  a, b, c: VAR set[T]
  A, B : Var setofsets[T]

  extensionality: LEMMA
      (FORALL x: member(x, a) IFF member(x, b)) IMPLIES (a = b)

  emptyset_is_empty?: LEMMA  empty?(a) IFF a = emptyset

  empty_no_members: LEMMA NOT member(x, emptyset)

  emptyset_min: LEMMA subset?(a, emptyset) IMPLIES a = emptyset

  nonempty_member: LEMMA nonempty?(a) IFF EXISTS x: member(x, a)

  fullset_member: LEMMA member(x, fullset)

  fullset_max: LEMMA subset?(fullset, a)  IMPLIES a = fullset

  fullset_is_full?: LEMMA full?(a) IFF a = fullset
  
  nonempty_exists: LEMMA nonempty?(a) IFF EXISTS (x: (a)): TRUE

  subset_emptyset: LEMMA subset?(emptyset, a)

  subset_fullset: LEMMA subset?(a, fullset)

  subset_reflexive: LEMMA subset?(a, a)

  subset_antisymmetric: LEMMA subset?(a, b) AND subset?(b, a) IMPLIES a = b

  subset_transitive: LEMMA
    subset?(a, b) AND subset?(b, c) IMPLIES subset?(a, c)

  subset_partial_order: LEMMA partial_order?(subset?[T])

  subset_is_partial_order: JUDGEMENT
    subset?[T] HAS_TYPE (partial_order?[set[T]])

  strict_subset_irreflexive: LEMMA NOT strict_subset?(a, a)

  strict_subset_transitive: LEMMA
    strict_subset?(a, b) AND strict_subset?(b, c) IMPLIES
     strict_subset?(a, c)

  strict_subset_strict_order: LEMMA strict_order?(strict_subset?[T])

  strict_subset_is_strict_order: JUDGEMENT
    strict_subset?[T] HAS_TYPE (strict_order?[set[T]])

  union_idempotent: LEMMA union(a, a) = a

  union_commutative: LEMMA union(a, b) = union(b, a)

  union_associative: LEMMA union(union(a, b), c) = union(a, union(b, c))

  union_empty: LEMMA union(a, emptyset) = a

  union_full: LEMMA union(a, fullset) = fullset

  union_subset1: LEMMA subset?(a, union(a, b))

  union_subset2: LEMMA subset?(a, b) IMPLIES union(a, b) = b

  subset_union: LEMMA subset?(union(a, b), c) = (subset?(a, c) AND subset?(b, c)) 

  union_upper_bound : LEMMA
    subset?(a, c) and subset?(b, c) IMPLIES subset?(union(a, b), c)
  
  union_difference: LEMMA union(a, b) = union(a, difference(b, a))

  union_diff_subset: LEMMA subset?(a, b) IMPLIES union(a, difference(b, a)) = b

  intersection_idempotent: LEMMA intersection(a, a) = a

  intersection_commutative: LEMMA intersection(a, b) = intersection(b, a)

  intersection_associative: LEMMA
    intersection(intersection(a, b), c) = intersection(a, intersection(b, c))

  intersection_empty: LEMMA intersection(a, emptyset) = emptyset

  intersection_full: LEMMA intersection(a, fullset) = a

  intersection_subset1: LEMMA subset?(intersection(a, b), a)

  intersection_subset2: LEMMA subset?(a, b) IMPLIES intersection(a, b) = a

  intersection_lower_bound: LEMMA
    subset?(c, a) and subset?(c, b) IMPLIES subset?(c, intersection(a, b))

  distribute_intersection_union: LEMMA
    intersection(a, union(b, c))
        = union(intersection(a, b), intersection(a, c))

  distribute_union_intersection: LEMMA
    union(a, intersection(b, c)) = intersection(union(a, b), union(a, c))

  complement_emptyset: LEMMA complement(emptyset[T]) = fullset

  complement_fullset: LEMMA complement(fullset[T]) = emptyset

  complement_complement: LEMMA complement(complement(a)) = a

  complement_equal: LEMMA complement(a) = complement(b) IFF a = b

  subset_complement: LEMMA
    subset?(complement(a), complement(b)) IFF subset?(b, a)

  demorgan1: LEMMA
    complement(union(a, b)) = intersection(complement(a), complement(b))

  demorgan2: LEMMA
    complement(intersection(a, b)) = union(complement(a), complement(b))

  difference_emptyset1: LEMMA difference(a, emptyset) = a

  difference_emptyset2: LEMMA difference(emptyset, a) = emptyset

  difference_fullset1: LEMMA difference(a, fullset) = emptyset

  difference_fullset2: LEMMA difference(fullset, a) = complement(a)

  difference_intersection: LEMMA
    difference(a, b) = intersection(a, complement(b))

  difference_difference1: LEMMA
    difference(difference(a, b), c) = difference(a, union(b, c))

  difference_difference2: LEMMA
    difference(a, difference(b, c))
      = union(difference(a, b), intersection(a, c))

  difference_subset : LEMMA  subset?(difference(a, b), a)

  difference_subset2: LEMMA
    subset?(a, b) IMPLIES difference(a, b) = emptyset

  difference_disjoint: LEMMA disjoint?(a, difference(b, a))

  difference_disjoint2: LEMMA disjoint?(a, b) IMPLIES difference(a, b) = a

  diff_union_inter: LEMMA
    difference(union(a, b), a) = difference(b, intersection(a, b))

  nonempty_add: LEMMA NOT empty?(add(x, a))

  member_add: LEMMA member(x, a) IMPLIES add(x, a) = a

  member_add_reduce: LEMMA member(x, add(y, a)) = (x = y or member(x, a))

  member_remove: LEMMA NOT member(x, a) IMPLIES remove(x, a) = a

  add_remove_member: LEMMA member(x, a) IMPLIES add(x, remove(x, a)) = a

  remove_add_member: LEMMA NOT member(x, a) IMPLIES remove(x, add(x, a)) = a

  subset_add: LEMMA subset?(a, add(x, a))

  add_as_union: LEMMA add(x, a) = union(a, singleton(x))

  singleton_as_add: LEMMA singleton(x) = add(x, emptyset)

  subset_remove: LEMMA subset?(remove(x, a), a)

  remove_as_difference: LEMMA remove(x, a) = difference(a, singleton(x))

  remove_member_singleton: LEMMA remove(x, singleton(x)) = emptyset

  choose_rest: LEMMA NOT empty?(a) IMPLIES add(choose(a), rest(a)) = a

  choose_member: LEMMA NOT empty?(a) IMPLIES member(choose(a), a)
  
  choose_not_member: LEMMA
      NOT empty?(a) IMPLIES NOT member(choose(a), rest(a))
  
  rest_not_equal: LEMMA NOT empty?(a) IMPLIES rest(a) /= a

  rest_member: LEMMA member(x,rest(a)) IMPLIES member(x,a)

  rest_subset : LEMMA subset?(rest(a), a)

  choose_add: LEMMA choose(add(x, a)) = x OR member(choose(add(x, a)), a)

  choose_rest_or: LEMMA
    member(x,a) IMPLIES member(x,rest(a)) OR x = choose(a)

  choose_singleton: LEMMA choose(singleton(x)) = x

  rest_singleton: LEMMA rest(singleton(x)) = emptyset[T]
  
  singleton_subset: LEMMA member(x, a) IFF subset?(singleton(x), a)

  rest_empty_lem: LEMMA NOT empty?(a) AND empty?(rest(a))
                          IMPLIES a = singleton(choose(a))

  singleton_disjoint: LEMMA NOT member(x, a) IMPLIES disjoint?(singleton(x), a)

  disjoint_remove_left: LEMMA
    disjoint?(a, b) IMPLIES disjoint?(remove(x, a), b)

  disjoint_remove_right: LEMMA
    disjoint?(a, b) IMPLIES disjoint?(a, remove(x, b))

  union_disj_remove_left: LEMMA
    disjoint?(a, b) AND a(x) IMPLIES
      union(remove(x, a), b) = remove(x, union(a, b))

  union_disj_remove_right: LEMMA
    disjoint?(a, b) AND b(x) IMPLIES
      union(a, remove(x, b)) = remove(x, union(a, b))

  subset_powerset: LEMMA subset?(a, b) IFF member(a, powerset(b))

  empty_powerset: LEMMA empty?(a) IFF singleton?(powerset(a))

  powerset_emptyset: LEMMA member(emptyset, powerset(a))

  nonempty_powerset: JUDGEMENT powerset(a) HAS_TYPE (nonempty?[set[T]])

  powerset_union: LEMMA Union(powerset(a)) = a

  powerset_intersection: LEMMA empty?(Intersection(powerset(a)))

  powerset_subset: LEMMA subset?(a, b) IFF subset?(powerset(a), powerset(b))

  Union_empty: LEMMA empty?(Union(A)) IFF empty?(A) OR every(empty?)(A)

  Union_full: LEMMA
    full?(Union(A)) IFF (FORALL x: EXISTS (a: (A)): member(x, a))

  Union_member: LEMMA member(x,Union(A)) IFF (EXISTS (a:(A)): member(x,a))

  Union_subset: LEMMA FORALL (a: (A)): subset?(a, Union(A))

  Union_surjective: JUDGEMENT
    Union HAS_TYPE (surjective?[setofsets[T], set[T]])

  Union_emptyset_rew: LEMMA Union(emptyset[set[T]]) = emptyset

  Union_union_rew: LEMMA nonempty?(A) IMPLIES
                            Union(A) = union(choose(A), Union(rest(A)))

  Intersection_empty: LEMMA
    empty?(Intersection(A)) IFF
     (FORALL x: EXISTS (a: (A)): NOT member(x, a))

  Intersection_full: LEMMA full?(Intersection(A)) IFF every(full?)(A)

  Intersection_member: LEMMA
    member(x,Intersection(A)) IFF (FORALL (a:(A)): member(x,a))

  %% The Intersection function should only be applied to nonempty sets of
  %% sets.  This is why.
  Intersection_empty_full: COROLLARY full?(Intersection(emptyset[set[T]]))

  Intersection_surjective: JUDGEMENT
    Intersection HAS_TYPE (surjective?[setofsets[T], set[T]])

  Intersection_intersection_rew: LEMMA
    nonempty?(A) IMPLIES
      Intersection(A) = intersection(choose(A), Intersection(rest(A)))

  Complement(A): setofsets[T] = {a | EXISTS (b: (A)): a = complement(b)}

  Complement_empty: LEMMA empty?(Complement(A)) IFF empty?(A)

  Complement_full: LEMMA full?(Complement(A)) IFF full?(A)

  Complement_Complement: LEMMA Complement(Complement(A)) = A

  subset_Complement: LEMMA
    subset?(Complement(A), Complement(B)) IFF subset?(A, B)

  Complement_bijective: JUDGEMENT
    Complement HAS_TYPE (bijective?[setofsets[T], setofsets[T]])

  Demorgan1: LEMMA complement(Union(A)) = Intersection(Complement(A))
  Demorgan2: LEMMA complement(Intersection(A)) = Union(Complement(A))

END sets_lemmas


%-------------------------------------------------------------------------
%
%  An alternate formulation of function inverses.  The version above
%  requires that the domain type be nonempty.  This causes two
%  difficulties:
%
%  (1) Functions from empty types to empty types have inverses, but the
%      function_inverse theory cannot describe them.
%
%  (2) Bijections always have inverses, but function_inverse (uselessly)
%      generates TCCs requiring that the domain type be nonempty.
%
%  Furthermore, for non-injective functions, the function_inverse theory
%  only gives a way of generating *some* inverse, using the Axiom of Choice.
%  There is no way to ask whether some function *is* an inverse of the given
%  function.
%
%  This theory is presented in two parts.  The first part makes no
%  assumptions about the domain and range types at all.  It presents
%  basic definitions, and some results that can be proven with no
%  information on the types.  The second part uses an assumption on the
%  input types, which is constructed to be as easily satisfiable as
%  possible.  However, this is still not as convenient as the
%  function_inverse version when the domain type is known to be nonempty.
%
%  ---------------------------------------------------------------------
%      Author: Jerry James (jamesj@acm.org), University of Kansas
%-------------------------------------------------------------------------
function_inverse_def[D: TYPE, R: TYPE]: THEORY
 BEGIN

  d: VAR D
  r: VAR R
  f: VAR [D -> R]
  g: VAR [R -> D]

  left_inverse?(g, f): bool = FORALL d: g(f(d)) = d
  right_inverse?(g, f): bool = FORALL r: f(g(r)) = r
  inverse?(g, f): bool = FORALL r: (EXISTS d: f(d) = r) => f(g(r)) = r

  left_inverse?(f)(g): MACRO bool = left_inverse?(g, f)
  right_inverse?(f)(g): MACRO bool = right_inverse?(g, f)
  inverse?(f)(g): MACRO bool = inverse?(g, f)

  %%% Left inverses / injective functions

  left_inverse_is_inverse: LEMMA
    FORALL f, (g: (left_inverse?(f))): inverse?(g, f)

  left_inj_surj: LEMMA
    FORALL f, (g: (left_inverse?(f))): injective?(f) AND surjective?(g)

  inj_left_alt: LEMMA
    FORALL (f: (injective?[D, R])), (g: (inverse?(f))): left_inverse?(g, f)

  surj_inv_alt: COROLLARY
    FORALL (f: (injective?[D, R])), (g: (inverse?(f))): surjective?(g)

  injective_inverse_alt: LEMMA
    FORALL (f: (injective?[D, R])), (g: (inverse?(f))): r = f(d) => g(r) = d

  comp_inverse_left_inj_alt: LEMMA
    FORALL (f: (injective?[D, R])), (g: (inverse?(f))): g(f(d)) = d

  noninjective_inverse_exists: LEMMA
    FORALL f: injective?(f) OR (EXISTS g: inverse?(g, f))

  %%% Right inverses / surjective functions

  right_inverse_is_inverse: LEMMA
    FORALL f, (g: (right_inverse?(f))): inverse?(g, f)

  right_surj_inj: LEMMA
    FORALL f, (g: (right_inverse?(f))): surjective?(f) AND injective?(g)

  surj_right_alt: LEMMA
    FORALL (f: (surjective?[D, R])), (g: (inverse?(f))): right_inverse?(g, f)

  inj_inv_alt: COROLLARY
    FORALL (f: (surjective?[D, R])), (g: (inverse?(f))): injective?(g)

  surjective_inverse_alt: LEMMA
    FORALL (f: (surjective?[D, R])), (g: (inverse?(f))): g(r) = d => r = f(d)

  comp_inverse_right_surj_alt: LEMMA
    FORALL (f: (surjective?[D, R])), (g: (inverse?(f))): f(g(r)) = r

  surjective_inverse_exists: LEMMA
    FORALL (f: (surjective?[D, R])): EXISTS g: inverse?(g, f)

  %%% Left-right inverses / bijective functions

  left_right_bij: COROLLARY
    FORALL f, g:
      right_inverse?(g, f) AND left_inverse?(g, f) =>
       bijective?(f) AND bijective?(g)

  bij_left_right: COROLLARY
    FORALL (f: (bijective?[D, R])), (g: (inverse?(f))):
      right_inverse?(g, f) AND left_inverse?(g, f)

  bij_inv_is_bij_alt: COROLLARY
    FORALL (f: (bijective?[D, R])), (g: (inverse?(f))): bijective?(g)

  bijective_inverse_alt: COROLLARY
    FORALL (f: (bijective?[D, R])), (g: (inverse?(f))): g(r) = d IFF r = f(d)

  comp_inverse_right_alt: COROLLARY
    FORALL (f: (bijective?[D, R])), (g: (inverse?(f))): f(g(r)) = r

  comp_inverse_left_alt: COROLLARY
    FORALL (f: (bijective?[D, R])), (g: (inverse?(f))): g(f(d)) = d

  bijective_inverse_exists: LEMMA
    FORALL (f: (bijective?[D, R])): exists1(inverse?(f))

  % The following are provided as support for discharging the
  % assumption TCCs arising from the use of function_inverse_alt

  exists_inv1: LEMMA
    (EXISTS g: TRUE) IFF ((EXISTS (d: D): TRUE) OR (FORALL (r: R): FALSE))
  exists_inv2: LEMMA
    (EXISTS (f: (surjective?[D, R])): TRUE) =>
       ((EXISTS (d: D): TRUE) OR (FORALL (r: R): FALSE))
  exists_inv3: LEMMA
    (EXISTS f: NOT injective?(f)) =>
       ((EXISTS (d: D): TRUE) OR (FORALL (r: R): FALSE))

 END function_inverse_def


% function_inverse.  In practice this definition will only be useful
% for injective functions.  This is not defined in functions because the
% epsilon! operator forces the domain type to be nonempty.  Note
% that this theory may not be instantiated through dependent function
% types.

function_inverse[D: NONEMPTY_TYPE, R: TYPE]: THEORY
 BEGIN
  x: VAR D
  y: VAR R
  f: VAR [D -> R]
  g: VAR [R -> D]

  inverse(f)(y): D = (epsilon! x: f(x) = y)

  unique_bijective_inverse: JUDGEMENT
    inverse(f:(bijective?[D,R]))(y) HAS_TYPE {x:D | f(x) = y}

  bijective_inverse_is_bijective: JUDGEMENT
    inverse(f:(bijective?[D,R])) HAS_TYPE (bijective?[R,D])

  surjective_inverse: LEMMA
    FORALL (f:(surjective?[D, R])):
       inverse(f)(y) = x IMPLIES y = f(x)

  inverse_surjective: LEMMA
    FORALL (f:(surjective?[D, R])):
       f(inverse(f)(y)) = y

  injective_inverse: LEMMA
    FORALL (f:(injective?[D, R])):
       y = f(x) IMPLIES inverse(f)(y) = x

  inverse_injective: LEMMA
    FORALL (f:(injective?[D, R])):
       inverse(f)(f(x)) = x

  bijective_inverse: LEMMA
    FORALL (f:(bijective?[D, R])):
       inverse(f)(y) = x IFF y = f(x)

  bij_inv_is_bij: LEMMA
    bijective?(f) IMPLIES bijective?(inverse(f))

%   left_inverse?(g, f): bool = (FORALL x: g(f(x)) = x)

%   right_inverse?(g, f): bool = (FORALL y: f(g(y)) = y)

  surj_right: LEMMA surjective?(f) IFF right_inverse?(inverse(f), f)

  inj_left: LEMMA injective?(f) IFF left_inverse?(inverse(f), f)

  inj_inv: LEMMA surjective?(f) IMPLIES injective?(inverse(f))

  surj_inv: LEMMA injective?(f) IMPLIES surjective?(inverse(f))

  inv_inj_is_surj: JUDGEMENT
    inverse(f: (injective?[D, R])) HAS_TYPE (surjective?[R, D])
  inv_surj_is_inj: JUDGEMENT
    inverse(f: (surjective?[D, R])) HAS_TYPE (injective?[R, D])

  comp_inverse_right_surj: LEMMA
	FORALL (f:(surjective?[D,R])): f(inverse(f)(y)) = y

  comp_inverse_left_inj: LEMMA
	FORALL (f:(injective?[D,R])): inverse(f)(f(x)) = x

  comp_inverse_right: LEMMA
	FORALL (f:(bijective?[D,R])): f(inverse(f)(y)) = y

  comp_inverse_left: LEMMA
	FORALL (f:(bijective?[D,R])): inverse(f)(f(x)) = x

 END function_inverse


% This theory was provided by Jerry James
function_inverse_alt[D: TYPE, R: TYPE]: THEORY
 BEGIN

  ASSUMING

   % The function_inverse theory, in effect, forces its users to prove the
   % first disjunction; however, any of them give us the ability to define
   % inverses.  Note that the first two disjunctions are implied by each of
   % the others.  The axiom is expressed this way for the convenience of its
   % users, who may have any one of these pieces of evidence of invertibility
   % at hand.
   inverse_types: ASSUMPTION
     (EXISTS (d: D): TRUE) OR
     (FORALL (r: R): FALSE)

  ENDASSUMING

  d: VAR D
  r: VAR R
  f: VAR [D -> R]
  g: VAR [R -> D]

  inverses(f): TYPE+ = (inverse?(f))

  inverse_alt(f): inverses(f) = choose({g: inverses(f) | TRUE})


  bijective_inverse_is_inverse_alt: COROLLARY
    FORALL (f: (bijective?[D, R])), (g: inverses(f)): g = inverse_alt(f)

  unique_bijective_inverse_alt: JUDGEMENT
    inverse_alt(f: (bijective?[D,R]))(r) HAS_TYPE {d | f(d) = r}

  bijective_inverse_alt_is_bijective: JUDGEMENT
    inverse_alt(f: (bijective?[D,R])) HAS_TYPE (bijective?[R,D])

  inv_inj_is_surj_alt: JUDGEMENT
    inverse_alt(f: (injective?[D, R])) HAS_TYPE (surjective?[R, D])

  inv_surj_is_inj_alt: JUDGEMENT
    inverse_alt(f: (surjective?[D, R])) HAS_TYPE (injective?[R, D])

 END function_inverse_alt


% function_image provides the image and inverse_image functions.
% inverse_image is not the same as inverse; it is defined for all
% functions, not just injections, and returns a set.

function_image [D, R: TYPE]: THEORY
 BEGIN
  f: VAR [D -> R]
  x: VAR D
  y: VAR R
  X, X1, X2: VAR set[D]
  Y, Y1, Y2: VAR set[R]

  fun_exists: LEMMA (EXISTS y: TRUE) OR (NOT EXISTS x: TRUE)
                          IMPLIES (EXISTS f: TRUE)

  image(f, X): set[R] = {y: R | (EXISTS (x:(X)): y = f(x))}

  image(f)(X): set[R] = image(f, X)

  inverse_image(f, Y): set[D] = {x: D | member(f(x), Y)}

  inverse_image(f)(Y): set[D] = inverse_image(f, Y)

  image_inverse_image: LEMMA
    subset?(image(f, inverse_image(f, Y)), Y)

  inverse_image_image: LEMMA
    subset?(X, inverse_image(f, image(f, X)))

  image_subset: LEMMA
    subset?(X1, X2) IMPLIES subset?(image(f, X1), image(f, X2))

  inverse_image_subset: LEMMA
    subset?(Y1, Y2) IMPLIES subset?(inverse_image(f, Y1), inverse_image(f, Y2))

  image_union: LEMMA
    image(f, union(X1, X2)) = union(image(f, X1), image(f, X2))

  image_intersection: LEMMA
    subset?(image(f, intersection(X1, X2)),
            intersection(image(f, X1), image(f, X2)))

  inverse_image_union: LEMMA
    inverse_image(f, union(Y1, Y2))
      = union(inverse_image(f, Y1), inverse_image(f, Y2))

  inverse_image_intersection: LEMMA
    inverse_image(f, intersection(Y1, Y2))
      = intersection(inverse_image(f, Y1), inverse_image(f, Y2))

  inverse_image_complement: LEMMA
    inverse_image(f, complement(Y)) = complement(inverse_image(f, Y))

 END function_image


% functions_props defines function composition.  It can't be defined in
% functions because it involves three type parameters.

function_props [T1, T2, T3: TYPE]: THEORY
 BEGIN
  x: VAR T1
  f1: VAR [T1 -> T2]
  f2: VAR [T2 -> T3]
  X: VAR set[T1]
  R1: VAR PRED[[T1, T1]]
  R2: VAR PRED[[T2, T2]]
  R3: VAR PRED[[T3, T3]]

  o(f2, f1)(x): T3 = f2(f1(x))

  composition_injective: JUDGEMENT
    o(f2: (injective?[T2, T3]), f1: (injective?[T1, T2]))
      HAS_TYPE (injective?[T1, T3])

  composition_surjective: JUDGEMENT
    o(f2: (surjective?[T2, T3]), f1: (surjective?[T1, T2]))
      HAS_TYPE (surjective?[T1, T3])

  composition_bijective: JUDGEMENT
    o(f2: (bijective?[T2, T3]), f1: (bijective?[T1, T2]))
      HAS_TYPE (bijective?[T1, T3])

  image_composition: LEMMA
    image(f2, image(f1, X)) = image(f2 o f1, X)

  preserves_composition: LEMMA
    preserves(f1, R1, R2) AND preserves(f2, R2, R3)
      IMPLIES preserves(f2 o f1, R1, R3)

  inverts_composition1: LEMMA
    preserves(f1, R1, R2) AND inverts(f2, R2, R3)
      IMPLIES inverts(f2 o f1, R1, R3)

  inverts_composition2: LEMMA
    inverts(f1, R1, R2) AND preserves(f2, R2, R3)
      IMPLIES inverts(f2 o f1, R1, R3)
      
 END function_props


function_props_alt [T1, T2, T3: TYPE, R1: PRED[[T1, T1]],
                    R2: PRED[[T2, T2]], R3: PRED[[T3, T3]]]: THEORY
 BEGIN 
   composition_preserves: JUDGEMENT
     o(f2: (preserves[T2, T3, R2, R3]), f1: (preserves[T1, T2, R1, R2]))
       HAS_TYPE (preserves[T1, T3, R1, R3])

   composition_inverts1: JUDGEMENT
     o(f2: (preserves[T2, T3, R2, R3]), f1: (inverts[T1, T2, R1, R2]))
       HAS_TYPE (inverts[T1, T3, R1, R3])

   composition_inverts2: JUDGEMENT
     o(f2: (inverts[T2, T3, R2, R3]), f1: (preserves[T1, T2, R1, R2]))
       HAS_TYPE (inverts[T1, T3, R1, R3])
 END function_props_alt  


% function_props2 defines the associativity of function composition.  It
% can't be defined in function_props because it involves four type
% parameters.

function_props2 [T1, T2, T3, T4: TYPE]: THEORY
 BEGIN
  f1: VAR [T1 -> T2]
  f2: VAR [T2 -> T3]
  f3: VAR [T3 -> T4]

  assoc: LEMMA (f3 o f2) o f1 = f3 o (f2 o f1)
 END function_props2


% relation_defs defines operators on relations between possibly different
% types.  Note that some of the names are overloaded with those given in
% functions - in practice this should not cause any problems.

relation_defs [T1, T2: TYPE]: THEORY
 BEGIN
  R, S: VAR pred[[T1, T2]]
  x: VAR T1
  y: VAR T2
  X: VAR set[T1]
  Y: VAR set[T2]

  domain?(R)(x: T1): bool = EXISTS (y: T2): R(x, y)

  range?(R)(y: T2): bool = EXISTS (x: T1): R(x, y)

  domain(R): TYPE = {x: T1 | EXISTS (y: T2): R(x, y)}

  range(R): TYPE = {y: T2 | EXISTS (x: T1): R(x, y)}

  rinverse(R)(y, x): bool = R(x, y)

  rcomplement(R)(x, y): bool = NOT R(x, y)

  runion(R, S)(x, y): bool = R(x, y) OR S(x, y)

  rintersection(R, S)(x, y): bool = R(x, y) AND S(x, y)

  image(R, X): set[T2] = {y: T2 | EXISTS (x: (X)): R(x, y)}

  image(R)(X): set[T2] = image(R, X)

  preimage(R, Y): set[T1] = {x: T1 | EXISTS (y: (Y)): R(x, y)}

  preimage(R)(Y): set[T1] = preimage(R, Y)

  postcondition(R, X): set[T2] = {y: T2 | EXISTS (x: (X)): R(x, y)}

  postcondition(R)(X): set[T2] = postcondition(R, X)

  precondition(R, Y): set[T1] = {x: T1 | FORALL (y: T2 | R(x, y)): Y(y)}

  precondition(R)(Y): set[T1] = precondition(R, Y)

  converse(R): pred[[T2, T1]] = (LAMBDA (y: T2), (x: T1): R(x, y))

  isomorphism?(R): bool =
    (EXISTS (f: (bijective?[T1, T2])): R = graph(f))

  total?(R): bool = FORALL (x: T1): EXISTS (y: T2): R(x, y)

  onto?(R): bool = FORALL (y: T2): EXISTS (x: T1): R(x, y)

 END relation_defs


% relation_props

relation_props [T1, T2, T3: TYPE]: THEORY
 BEGIN
  R1: VAR pred[[T1, T2]]
  R2: VAR pred[[T2, T3]]
  x: VAR T1
  y: VAR T2
  z: VAR T3

  o(R1, R2)(x, z): bool = EXISTS y: R1(x, y) AND R2(y, z)

  total_composition: LEMMA total?(R1) & total?(R2) => total?(R1 o R2)

  onto_composition: LEMMA onto?(R1) & onto?(R2) => onto?(R1 o R2)

  composition_total: JUDGEMENT
    o(R1: (total?[T1, T2]), R2: (total?[T2, T3])) HAS_TYPE (total?[T1, T3])

  composition_onto: JUDGEMENT
    o(R1: (onto?[T1, T2]), R2: (onto?[T2, T3])) HAS_TYPE (onto?[T1, T3])

 END relation_props


relation_props2 [T1, T2, T3, T4: TYPE]: THEORY
 BEGIN
  R1: VAR pred[[T1, T2]]
  R2: VAR pred[[T2, T3]]
  R3: VAR pred[[T3, T4]]

  assoc: LEMMA (R1 o R2) o R3 = R1 o (R2 o R3)
 END relation_props2


%-------------------------------------------------------------------------
%
%  Properties of converses of relations on a single type.
%
%  ---------------------------------------------------------------------
%      Author: Jerry James (jamesj@acm.org), University of Kansas
%
%-------------------------------------------------------------------------
relation_converse_props[T: TYPE]: THEORY
 BEGIN

  reflexive_converse: JUDGEMENT
    converse(R: (reflexive?[T])) HAS_TYPE (reflexive?[T])

  irreflexive_converse: JUDGEMENT
    converse(R: (irreflexive?[T])) HAS_TYPE (irreflexive?[T])

  symmetric_converse: JUDGEMENT
    converse(R: (symmetric?[T])) HAS_TYPE (symmetric?[T])

  antisymmetric_converse: JUDGEMENT
    converse(R: (antisymmetric?[T])) HAS_TYPE (antisymmetric?[T])

  connected_converse: JUDGEMENT
    converse(R: (connected?[T])) HAS_TYPE (connected?[T])

  transitive_converse: JUDGEMENT
    converse(R: (transitive?[T])) HAS_TYPE (transitive?[T])

  equivalence_converse: JUDGEMENT
    converse(R: (equivalence?[T])) HAS_TYPE (equivalence?[T])

  preorder_converse: JUDGEMENT
    converse(R: (preorder?[T])) HAS_TYPE (preorder?[T])

  partial_order_converse: JUDGEMENT
    converse(R: (partial_order?[T])) HAS_TYPE (partial_order?[T])

  strict_order_converse: JUDGEMENT
    converse(R: (strict_order?[T])) HAS_TYPE (strict_order?[T])

  dichotomous_converse: JUDGEMENT
    converse(R: (dichotomous?[T])) HAS_TYPE (dichotomous?[T])

  total_order_converse: JUDGEMENT
    converse(R: (total_order?[T])) HAS_TYPE (total_order?[T])

  trichotomous_converse: JUDGEMENT
    converse(R: (trichotomous?[T])) HAS_TYPE (trichotomous?[T])

  strict_total_order_converse: JUDGEMENT
    converse(R: (strict_total_order?[T])) HAS_TYPE (strict_total_order?[T])

 END relation_converse_props


indexed_sets[index, T: TYPE]: THEORY
 BEGIN
  i: VAR index
  x: VAR T
  A, B: VAR [index -> set[T]]
  S: VAR set[T]

  IUnion(A): set[T] = {x | EXISTS i: A(i)(x)}

  IIntersection(A): set[T] = {x | FORALL i: A(i)(x)}

  IUnion_Union: LEMMA
    IUnion(A) = Union(image(A)(fullset[index]))

  IIntersection_Intersection: LEMMA
    IIntersection(A) = Intersection(image(A)(fullset[index]))

  IUnion_union: LEMMA
    IUnion(LAMBDA i: union(A(i), B(i))) = union(IUnion(A), IUnion(B))

  IIntersection_intersection: LEMMA
    IIntersection(LAMBDA i: intersection(A(i), B(i)))
        = intersection(IIntersection(A), IIntersection(B))

  IUnion_intersection: LEMMA
    IUnion(LAMBDA i: intersection(A(i), S))
        = intersection(IUnion(A), S)

  IIntersection_union: LEMMA
    IIntersection(LAMBDA i: union(A(i), S))
        = union(IIntersection(A), S)
 END indexed_sets


% operator_defs

operator_defs[T: TYPE]: THEORY
 BEGIN
  o, *: VAR [T, T -> T]
  -: VAR [T -> T]
  x, y, z: VAR T

  commutative?(o):     bool = (FORALL x, y: x o y = y o x)

  associative?(o):     bool = (FORALL x, y, z: (x o y) o z = x o (y o z))

  left_identity?(o)(y): bool = (FORALL x: y o x = x)

  right_identity?(o)(y): bool = (FORALL x: x o y = x)

  identity?(o)(y):     bool = (FORALL x: x o y = x AND y o x = x)

  has_identity?(o):    bool = (EXISTS y: identity?(o)(y))

  zero?(o)(y):         bool = (FORALL x: x o y = y AND y o x = y)

  has_zero?(o):        bool = (EXISTS y: zero?(o)(y))

  inverses?(o)(-)(y):  bool = (FORALL x: x o -x = y AND (-x) o x = y)

  has_inverses?(o):    bool = (EXISTS -, y: inverses?(o)(-)(y))

  distributive?(*, o): bool = (FORALL x, y, z: x * (y o z) = (x * y) o (x * z))

 END operator_defs


% numbers provides the number type, which is the highest numeric type.
% Its primary purpose is to provide a way to extend the reals, for
% example, the extended reals can be declared as a subtype of number that
% contains all the reals and the points at infinity.

numbers: THEORY
 BEGIN

  number: NONEMPTY_TYPE

 END numbers


% number-fields provides the field axioms.  This allows development of,
% for example, complex numbers or nonstandard reals as subtypes, without
% having to extend the field operators.  Extended reals are not a subtype
% of number_field, as division by zero is allowed in the extended reals.
% Note that order relations are not defined here.

number_fields: THEORY
 BEGIN
  number_field: NONEMPTY_TYPE FROM number
  numfield: NONEMPTY_TYPE = number_field
  number_field?(n: number): bool = number_field_pred(n)

  % The following declarations, if they could be expanded, are built in to
  %  the typechecker and prover:
  % 0, 1, 2, ... : number_field
  % AXIOM 0 /= 1 AND 0 /= 2 AND 1 /= 2 AND ...

  nonzero_number: NONEMPTY_TYPE = {r: number_field | r /= 0} CONTAINING 1
  nznum: NONEMPTY_TYPE = nonzero_number

  +, -, *: [numfield, numfield -> numfield]
  /: [numfield, nznum -> numfield]
  -: [numfield -> numfield]

  % Field Axioms - these are not provable using the decision
  % procedures, as the operators are translated to uninterpreted
  % functions, so that x * x = -1 is consistent.  When the
  % arguments are (a subtype of) real, then the translation is to
  % interpreted symbols.

  x, y, z: VAR numfield
  n0x: VAR nznum

  commutative_add : POSTULATE x + y = y + x

  associative_add : POSTULATE x + (y + z) = (x + y) + z

  identity_add    : POSTULATE x + 0 = x

  inverse_add     : AXIOM x + -x = 0

  minus_add       : AXIOM x - y = x + -y

  commutative_mult: AXIOM x * y = y * x

  associative_mult: AXIOM x * (y * z) =  (x * y) * z

  identity_mult   : AXIOM 1 * x = x

  inverse_mult    : AXIOM n0x * (1/n0x) = 1

  div_def         : AXIOM y/n0x = y * (1/n0x)

  distributive    : POSTULATE x * (y + z) = (x * y) + (x * z)

 END number_fields
  

% reals defines the real numbers as an uninterpreted subtype of number.
% Though not explicitly specified, all numeric constants are known to be
% of type real (hence number). Note that / is defined only when the second
% argument is nonzero.  This theory should not generally be used in
% auto-rewrite, since the inequalities are already known to the decision
% procedures, and tend to get rewritten to disjunctions, and leads to
% unnecessary case splits.

reals: THEORY
 BEGIN

  real: NONEMPTY_TYPE FROM number_field

  real?(n:number): bool = number_field_pred(n) AND real_pred(n)

  % The following declarations, if they could be expanded, are built in to
  %  the typechecker:
  % AXIOM real_pred(0) AND real_pred(1) AND real_pred(2) AND ...
  % JUDGEMENT 0, 1, 2, ... HAS_TYPE real

  nonzero_real: NONEMPTY_TYPE = {r: real | r /= 0} CONTAINING 1
  nzreal: NONEMPTY_TYPE = nonzero_real
  % nzreal_is_nznum: JUDGEMENT nonzero_real SUBTYPE_OF nonzero_number

  x, y: VAR real
  n0z: VAR nzreal

  closed_plus:    AXIOM real_pred(x + y)
  closed_minus:   AXIOM real_pred(x - y)
  closed_times:   AXIOM real_pred(x * y)
  closed_divides: AXIOM real_pred(x / n0z)
  closed_neg:     AXIOM real_pred(-x)

  real_plus_real_is_real:  JUDGEMENT +(x,y) HAS_TYPE real
  real_minus_real_is_real: JUDGEMENT -(x,y) HAS_TYPE real
  real_times_real_is_real: JUDGEMENT *(x,y) HAS_TYPE real
  real_div_nzreal_is_real: JUDGEMENT /(x,n0z) HAS_TYPE real

  minus_real_is_real: JUDGEMENT -(x) HAS_TYPE real

  <(x, y):  bool
  <=(x, y): bool = x < y OR x = y;
  >(x, y):  bool = y < x;
  >=(x, y): bool = y <= x

  reals_totally_ordered: POSTULATE strict_total_order?(<)

  % Built in:
  % AXIOM 0 < 1 AND 1 < 2 AND ...

 END reals


% real_axioms provides the usual commutativity, associativity, etc.
% axioms about the real numbers.  Note that many of these properties
% also hold for the rationals, integers, and natural numbers.  These
% properties are all known to the decision procedures of PVS, so should
% rarely need to be cited.  These axioms were taken from
% Royden's "Real Analysis" pp.29-31

real_axioms: THEORY
 BEGIN
  x, y, z: VAR real
  n0x: VAR nzreal

  % Field Axioms are now in number_fields

  % Order Axioms

  posreal_add_closed : POSTULATE x > 0 AND y > 0 IMPLIES x + y > 0

  posreal_mult_closed: AXIOM x > 0 AND y > 0 IMPLIES x * y > 0

  posreal_neg        : POSTULATE x > 0 IMPLIES NOT -x > 0

  trichotomy         : POSTULATE x > 0 OR x = 0 OR 0 > x


  % Completeness Axiom defined in bounded_reals

 END real_axioms


bounded_real_defs: THEORY
BEGIN

  x, y: VAR real

  % Completeness Axiom

  S: VAR (nonempty?[real])

  upper_bound?(x, S): bool = FORALL (s: (S)): s <= x

  upper_bound?(S)(x): bool = upper_bound?(x, S)

  lower_bound?(x, S): bool = FORALL (s: (S)): x <= s

  lower_bound?(S)(x): bool = lower_bound?(x, S)

  least_upper_bound?(x, S): bool =
    upper_bound?(x, S) AND
      FORALL y: upper_bound?(y, S) IMPLIES (x <= y)

  least_upper_bound?(S)(x): bool = least_upper_bound?(x, S)

  greatest_lower_bound?(x, S): bool =
    lower_bound?(x, S) AND
      FORALL y: lower_bound?(y, S) IMPLIES (y <= x)

  greatest_lower_bound?(S)(x): bool = greatest_lower_bound?(x, S)

  real_complete: AXIOM
    FORALL S:
      (EXISTS y: upper_bound?(y, S)) IMPLIES
        (EXISTS y: least_upper_bound?(y, S))

  real_lower_complete: LEMMA
    FORALL S:
      (EXISTS y: lower_bound?(y, S)) IMPLIES 
        (EXISTS x: greatest_lower_bound?(x, S))


  % lub and glb

  bounded_above?(S): bool = (EXISTS x: upper_bound?(x, S))

  bounded_below?(S): bool = (EXISTS x: lower_bound?(x, S))

  bounded?(S): bool = bounded_above?(S) AND bounded_below?(S)

  bounded_set: TYPE = (bounded?)

  SA: VAR (bounded_above?)

  lub_exists: LEMMA (EXISTS x: least_upper_bound?(x, SA))

  lub(SA): {x | least_upper_bound?(x, SA)}

  lub_lem: LEMMA lub(SA) = x IFF least_upper_bound?(x, SA)

  SB: VAR (bounded_below?)

  glb_exists: LEMMA (EXISTS x: greatest_lower_bound?(x, SB))

  glb(SB): {x | greatest_lower_bound?(x, SB)}

  glb_lem: LEMMA glb(SB) = x IFF greatest_lower_bound?(x, SB)

END bounded_real_defs


bounded_real_defs_alt [S: (nonempty?[real])]: THEORY
 BEGIN
  x: VAR real
  
  upper_bound?: [real -> bool] = upper_bound?(S)
  lower_bound?: [real -> bool] = lower_bound?(S)
  least_upper_bound?: [real -> bool] = least_upper_bound?(S)
  greatest_lower_bound?: [real -> bool] = greatest_lower_bound?(S)

  lub_is_upper_bound: JUDGEMENT
    (least_upper_bound?) SUBTYPE_OF (upper_bound?)

  glb_is_lower_bound: JUDGEMENT
    (greatest_lower_bound?) SUBTYPE_OF (lower_bound?)
    
 END bounded_real_defs_alt


% reals_types declares some useful subtypes of the reals and some
% associated judgements.

real_types: THEORY
 BEGIN
  x: VAR real

  nonneg_real: NONEMPTY_TYPE = {x: real        | x >= 0} CONTAINING 0
  nonpos_real: NONEMPTY_TYPE = {x: real        | x <= 0} CONTAINING 0
  posreal:     NONEMPTY_TYPE = {x: nonneg_real | x > 0}  CONTAINING 1
  negreal:     NONEMPTY_TYPE = {x: nonpos_real | x < 0}  CONTAINING -1
  nnreal: TYPE = nonneg_real
  npreal: TYPE = nonpos_real

  posreal_is_nzreal: JUDGEMENT posreal SUBTYPE_OF nzreal
  negreal_is_nzreal: JUDGEMENT negreal SUBTYPE_OF nzreal

  nzx, nzy: VAR nzreal
  px, py:   VAR posreal
  nx, ny:   VAR negreal
  nnx, nny: VAR nonneg_real
  npx, npy: VAR nonpos_real

  nonneg_real_add_closed: LEMMA nnx + nny >= 0
  nonpos_real_add_closed: LEMMA npx + npy <= 0
  negreal_add_closed    : LEMMA nx + ny < 0

  nonneg_real_mult_closed: LEMMA nnx * nny >= 0

  nzreal_times_nzreal_is_nzreal: JUDGEMENT *(nzx, nzy) HAS_TYPE nzreal
  nzreal_div_nzreal_is_nzreal:   JUDGEMENT /(nzx, nzy) HAS_TYPE nzreal
  minus_nzreal_is_nzreal:        JUDGEMENT -(nzx)      HAS_TYPE nzreal

  nnreal_plus_nnreal_is_nnreal:  JUDGEMENT +(nnx, nny) HAS_TYPE nnreal
  nnreal_times_nnreal_is_nnreal: JUDGEMENT *(nnx, nny) HAS_TYPE nnreal
  nnreal_div_posreal_is_nnreal:  JUDGEMENT /(nnx, py)  HAS_TYPE nnreal
  nnreal_div_negreal_is_npreal:  JUDGEMENT /(nnx, ny)  HAS_TYPE npreal

  npreal_plus_npreal_is_npreal:  JUDGEMENT +(npx, npy) HAS_TYPE npreal
  npreal_times_npreal_is_nnreal: JUDGEMENT *(npx, npy) HAS_TYPE nnreal
  npreal_div_posreal_is_npreal:  JUDGEMENT /(npx, py)  HAS_TYPE npreal
  npreal_div_negreal_is_nnreal:  JUDGEMENT /(npx, ny)  HAS_TYPE nnreal

  posreal_plus_nnreal_is_posreal:   JUDGEMENT +(px, nny) HAS_TYPE posreal
  nnreal_plus_posreal_is_posreal:   JUDGEMENT +(nnx, py) HAS_TYPE posreal
  posreal_times_posreal_is_posreal: JUDGEMENT *(px, py) HAS_TYPE posreal
  posreal_div_posreal_is_posreal:   JUDGEMENT /(px, py) HAS_TYPE posreal

  negreal_plus_negreal_is_negreal:  JUDGEMENT +(nx, ny) HAS_TYPE negreal
  negreal_times_negreal_is_posreal: JUDGEMENT *(nx, ny) HAS_TYPE posreal
  negreal_div_negreal_is_posreal:   JUDGEMENT /(nx, ny) HAS_TYPE posreal

 END real_types


% rationals defines the rational numbers as an uninterpreted subtype of real.
% The basic number operations are redeclared in order to specify that
% they are closed, e.g. the sum of two rationals is a rational.

rationals: THEORY
 BEGIN

  rational: NONEMPTY_TYPE FROM real
  rat: NONEMPTY_TYPE = rational

  rational?(n: number): bool =
    number_field_pred(n) AND real_pred(n) AND rational_pred(n)

  % The following declarations, if they could be expanded, are built in to
  %  the typechecker:
  % AXIOM rational_pred(0) AND rational_pred(1) AND rational_pred(2) AND ...
  % JUDGEMENT 0, 1, 2, ... HAS_TYPE rational

  nonzero_rational: NONEMPTY_TYPE = {r: rational | r /= 0} CONTAINING 1
  nzrat: NONEMPTY_TYPE = nonzero_rational
  % nzrat_is_nzreal: JUDGEMENT nonzero_rational SUBTYPE_OF nonzero_real

  x, y: VAR rat
  n0z: VAR nzrat

  closed_plus:    AXIOM rational_pred(x + y)
  closed_minus:   AXIOM rational_pred(x - y)
  closed_times:   AXIOM rational_pred(x * y)
  closed_divides: AXIOM rational_pred(x / n0z)
  closed_neg:     AXIOM rational_pred(-x)

  rat_plus_rat_is_rat:  JUDGEMENT +(x,y) HAS_TYPE rat
  rat_minus_rat_is_rat: JUDGEMENT -(x,y) HAS_TYPE rat
  rat_times_rat_is_rat: JUDGEMENT *(x,y) HAS_TYPE rat
  rat_div_nzrat_is_rat: JUDGEMENT /(x,n0z) HAS_TYPE rat

  minus_rat_is_rat: JUDGEMENT -(x) HAS_TYPE rat

  nonneg_rat: NONEMPTY_TYPE = {r: rational   | r >= 0} CONTAINING 0
  nonpos_rat: NONEMPTY_TYPE = {r: rational   | r <= 0} CONTAINING 0
  posrat:     NONEMPTY_TYPE = {r: nonneg_rat | r > 0}  CONTAINING 1
  negrat:     NONEMPTY_TYPE = {r: nonpos_rat | r < 0}  CONTAINING -1
  nnrat: NONEMPTY_TYPE = nonneg_rat
  nprat: NONEMPTY_TYPE = nonpos_rat

  nnx, nny: VAR nonneg_rat
  npx, npy: VAR nonpos_rat
  px, py: VAR posrat
  nx, ny: VAR negrat
  n0x, n0y: VAR nzrat

  % nnrat_is_nnreal:   JUDGEMENT nonneg_rat SUBTYPE_OF nonneg_real
  % nprat_is_npreal:   JUDGEMENT nonpos_rat SUBTYPE_OF nonpos_real
  % posrat_is_posreal: JUDGEMENT posrat     SUBTYPE_OF posreal
  % negrat_is_negreal: JUDGEMENT negrat     SUBTYPE_OF negreal
  posrat_is_nzrat:   JUDGEMENT posrat     SUBTYPE_OF nzrat
  negrat_is_nzrat:   JUDGEMENT negrat     SUBTYPE_OF nzrat

  nzrat_times_nzrat_is_nzrat: JUDGEMENT *(n0x, n0y) HAS_TYPE nzrat
  nzrat_div_nzrat_is_nzrat:   JUDGEMENT /(n0x, n0y) HAS_TYPE nzrat
  minus_nzrat_is_nzrat:       JUDGEMENT -(n0x)      HAS_TYPE nzrat

  nnrat_plus_nnrat_is_nnrat:  JUDGEMENT +(nnx, nny) HAS_TYPE nonneg_rat
  nnrat_times_nnrat_is_nnrat: JUDGEMENT *(nnx, nny) HAS_TYPE nonneg_rat
  nnrat_div_posrat_is_nnrat:  JUDGEMENT /(nnx, py)  HAS_TYPE nonneg_rat
  nnrrat_div_negrat_is_nprat: JUDGEMENT /(nnx, ny)  HAS_TYPE nprat

  nprat_plus_nprat_is_nprat:  JUDGEMENT +(npx, npy) HAS_TYPE nprat
  nprat_times_nprat_is_nnrat: JUDGEMENT *(npx, npy) HAS_TYPE nnrat
  nprat_div_posrat_is_nprat:  JUDGEMENT /(npx, py)  HAS_TYPE nprat
  nprat_div_negrat_is_nnrat:  JUDGEMENT /(npx, ny)  HAS_TYPE nnrat

  posrat_plus_nnrat_is_posrat:   JUDGEMENT +(px, nny) HAS_TYPE posrat
  nnrat_plus_posrat_is_posrat:   JUDGEMENT +(nnx, py) HAS_TYPE posrat
  posrat_times_posrat_is_posrat: JUDGEMENT *(px, py)  HAS_TYPE posrat
  posrat_div_posrat_is_posrat:   JUDGEMENT /(px, py)  HAS_TYPE posrat

  negrat_plus_negrat_is_negrat:  JUDGEMENT +(nx, ny) HAS_TYPE negrat
  negrat_times_negrat_is_posrat: JUDGEMENT *(nx, ny) HAS_TYPE posrat
  negrat_div_negrat_is_posrat:   JUDGEMENT /(nx, ny) HAS_TYPE posrat

 END rationals


% integers defines the integers as an uninterpreted subtype of rational.
% The basic number operations are redeclared in order to specify that
% they are closed, e.g. the sum of two integers is an integer.

integers: THEORY
 BEGIN

  integer: NONEMPTY_TYPE FROM rational
  int: NONEMPTY_TYPE = integer

  integer?(n:number): bool =
    number_field_pred(n) AND real_pred(n)
      AND rational_pred(n) AND integer_pred(n)

  % The following declarations, if they could be expanded, are built in to
  %  the typechecker:
  % AXIOM integer_pred(0) AND integer_pred(1) AND integer_pred(2) AND ...
  % JUDGEMENT 0, 1, 2, ... HAS_TYPE integer

  nonzero_integer: NONEMPTY_TYPE = {i: int | i /= 0} CONTAINING 1
  nzint: NONEMPTY_TYPE = nonzero_integer
  % nzint_is_nzrat: JUDGEMENT nonzero_integer SUBTYPE_OF nonzero_rational

  i, j: VAR int
  n0i, n0j: VAR nzint

  closed_plus:  AXIOM integer_pred(i + j)
  closed_minus: AXIOM integer_pred(i - j)
  closed_times: AXIOM integer_pred(i * j)
  closed_neg:   AXIOM integer_pred(-i)

  upfrom(i): NONEMPTY_TYPE = {s: int | s >= i} CONTAINING i
  above(i):  NONEMPTY_TYPE = {s: int | s > i} CONTAINING i + 1

  int_plus_int_is_int:  JUDGEMENT +(i,j) HAS_TYPE int
  int_minus_int_is_int: JUDGEMENT -(i,j) HAS_TYPE int
  int_times_int_is_int: JUDGEMENT *(i,j) HAS_TYPE int

  minus_int_is_int: JUDGEMENT -(i) HAS_TYPE int
  minus_nzint_is_nzint: JUDGEMENT -(n0i) HAS_TYPE nzint
 
  nonneg_int: NONEMPTY_TYPE = {i: int        | i >= 0} CONTAINING 0
  nonpos_int: NONEMPTY_TYPE = {i: int        | i <= 0} CONTAINING 0
  posint:     NONEMPTY_TYPE = {i: nonneg_int | i > 0}  CONTAINING 1
  negint:     NONEMPTY_TYPE = {i: nonpos_int | i < 0}  CONTAINING -1

  posnat: NONEMPTY_TYPE = posint

  nni, nnj: VAR nonneg_int
  npi, npj: VAR nonpos_int
  pi, pj: VAR posint
  ni, nj: VAR negint

  % Built in:
  % JUDGEMENT 1, 2, 3, ... HAS_TYPE posint

  % nnint_is_nnrat:   JUDGEMENT nonneg_int SUBTYPE_OF nonneg_rat
  % npint_is_nprat:   JUDGEMENT nonpos_int SUBTYPE_OF nonpos_rat
  % posint_is_posrat: JUDGEMENT posint     SUBTYPE_OF posrat
  % negint_is_negrat: JUDGEMENT negint     SUBTYPE_OF negrat
  posint_is_nzint:  JUDGEMENT posint     SUBTYPE_OF nzint
  negint_is_nzint:  JUDGEMENT negint     SUBTYPE_OF nzint

  nzint_times_nzint_is_nzint:    JUDGEMENT *(n0i, n0j) HAS_TYPE nzint
  
  nnint_plus_nnint_is_nnint:	 JUDGEMENT +(nni, nnj) HAS_TYPE nonneg_int
  nnint_times_nnint_is_nnint:    JUDGEMENT *(nni, nnj) HAS_TYPE nonneg_int
  
  npint_plus_npint_is_npint:	 JUDGEMENT +(npi, npj) HAS_TYPE nonpos_int
  npint_times_npint_is_nnint:    JUDGEMENT *(npi, npj) HAS_TYPE nonneg_int
  
  posint_plus_nnint_is_posint:   JUDGEMENT +(pi, nnj)  HAS_TYPE posint
  nnint_plus_posint_is_posint:   JUDGEMENT +(nni, pj)  HAS_TYPE posint
  posint_times_posint_is_posint: JUDGEMENT *(pi, pj)   HAS_TYPE posint
  
  negint_plus_negint_is_negint:  JUDGEMENT +(ni, nj)   HAS_TYPE negint
  negint_times_negint_is_posint: JUDGEMENT *(ni, nj)   HAS_TYPE posint

  % Note: subrange may be an empty type
  subrange(i, j): TYPE = {k: int | i <= k AND k <= j}

  even?(i): bool = EXISTS j: i = j * 2
  odd?(i): bool  = EXISTS j: i = j * 2 + 1

  even_int: NONEMPTY_TYPE = (even?) CONTAINING 0
  odd_int:  NONEMPTY_TYPE = (odd?)  CONTAINING 1

  % The following declarations, if they could be expanded, are built in to
  %  the typechecker:
  % JUDGEMENT 0, 2, 4, ... HAS_TYPE even_int
  % JUDGEMENT 1, 3, 5, ... HAS_TYPE odd_int
  
  e1, e2: VAR even_int
  o1, o2: VAR odd_int

  odd_is_nzint: JUDGEMENT odd_int SUBTYPE_OF nzint

  even_plus_even_is_even:  JUDGEMENT +(e1,e2) HAS_TYPE even_int
  even_minus_even_is_even: JUDGEMENT -(e1,e2) HAS_TYPE even_int
  odd_plus_odd_is_even:    JUDGEMENT +(o1,o2) HAS_TYPE even_int
  odd_minus_odd_is_even:   JUDGEMENT -(o1,o2) HAS_TYPE even_int
  odd_plus_even_is_odd:	   JUDGEMENT +(o1,e2) HAS_TYPE odd_int
  odd_minus_even_is_odd:   JUDGEMENT -(o1,e2) HAS_TYPE odd_int
  even_plus_odd_is_odd:	   JUDGEMENT +(e1,o2) HAS_TYPE odd_int
  even_minus_odd_is_odd:   JUDGEMENT -(e1,o2) HAS_TYPE odd_int
  even_times_int_is_even:  JUDGEMENT *(e1,i)  HAS_TYPE even_int
  int_times_even_is_even:  JUDGEMENT *(i,e2)  HAS_TYPE even_int
  odd_times_odd_is_odd:	   JUDGEMENT *(o1,o2) HAS_TYPE odd_int
  minus_even_is_even:	   JUDGEMENT -(e1)    HAS_TYPE even_int
  minus_odd_is_odd:	   JUDGEMENT -(o1)    HAS_TYPE odd_int

 END integers


% naturalnumbers defines the natural numbers as a subtype of integer.
% The sum and product operations are redeclared in order to specify that
% they are closed, e.g. the sum of two natural numbers is a natural
% number.  The successor and predecessor functions are defined here.

naturalnumbers: THEORY
 BEGIN

  naturalnumber: TYPE = nonneg_int
  nat: NONEMPTY_TYPE = naturalnumber

  % The following declaration, if it could be expanded, is built in to
  %  the typechecker:
  % JUDGEMENT 0, 1, 2, ... HAS_TYPE naturalnumber

  i, j, k: VAR nat
  n: VAR posnat

  upfrom_nat_is_nat:   JUDGEMENT upfrom(i) SUBTYPE_OF nat
  upfrom_posnat_is_posnat: JUDGEMENT upfrom(n) SUBTYPE_OF posnat
  above_nat_is_posnat: JUDGEMENT above(i) SUBTYPE_OF posnat
  subrange_nat_is_nat: JUDGEMENT subrange(i,j) SUBTYPE_OF nat
  subrange_posnat_is_posnat: JUDGEMENT subrange(n,j) SUBTYPE_OF posnat

  upto(i):   NONEMPTY_TYPE = {s: nat | s <= i} CONTAINING i
  below(i):  TYPE = {s: nat | s < i}  % may be empty

%  nat_plus_nat_is_nat: JUDGEMENT +(i,j) HAS_TYPE nat
%  nat_times_nat_is_nat: JUDGEMENT *(i,j) HAS_TYPE nat
  
  succ(i): nat = i + 1

  pred(i): nat = IF i > 0 THEN i - 1 ELSE 0 ENDIF;

  ~(i, j): nat = IF i > j THEN i - j ELSE 0 ENDIF

  wf_nat: AXIOM well_founded?(LAMBDA i, j: i < j)

  p: VAR pred[nat]

  nat_induction: LEMMA
    (p(0) AND (FORALL j: p(j) IMPLIES p(j+1)))
        IMPLIES (FORALL i: p(i))

  % Strong induction on naturals.

  NAT_induction: LEMMA 
    (FORALL j: (FORALL k: k < j IMPLIES p(k)) IMPLIES p(j))
       IMPLIES (FORALL i: p(i))

  % This seems out of place - it is here so we can prove it by induction
  x: VAR int
  even_or_odd: THEOREM even?(x) IFF NOT odd?(x)

  odd_iff_not_even:  LEMMA odd?(x)  IFF NOT even?(x)
  even_iff_not_odd:  LEMMA even?(x) IFF NOT odd?(x)
  odd_or_even_int:   LEMMA odd?(x)  OR even?(x)
  odd_iff_even_succ: LEMMA odd?(x)  IFF even?(x+1)
  even_iff_odd_succ: LEMMA even?(x) IFF odd?(x+1)

  even_plus1:        LEMMA even?(x) IFF NOT even?(x+1)
  odd_plus1:         LEMMA odd?(x) IFF NOT odd?(x+1)

  even_div2:         LEMMA even?(x) IMPLIES integer_pred(x/2)
  odd_div2:          LEMMA odd?(x)  IMPLIES integer_pred((x-1)/2)

 END naturalnumbers


min_nat[T: TYPE FROM nat]: THEORY
 BEGIN
  S: VAR (nonempty?[T])
  a, x: VAR T

  min(S): {a | S(a) AND (FORALL x: S(x) IMPLIES a <= x)}

  minimum?(a, S) : bool = S(a) AND (FORALL x: S(x) IMPLIES a <= x)

  min_def : LEMMA 
     FORALL (S: (nonempty?[T])) : min(S) = a IFF minimum?(a, S)

 END min_nat


% real_defs defines some useful real functions and
% provides all the judgements for the subtypes nzreal, nonneg_real,
% posreal, rat, nzrat, nonneg_rat, posrat, int, nzint, nat, and posint.
% For abs, we don't declare all these possibilities, since abs is the
% identity on the nonneg and pos number types.  Note that for abs, max,
% and min the range type is dependent on the domain, providing information
% in the type that is usually provided through separate lemmas.

real_defs: THEORY
 BEGIN
  m, n: VAR real

  sgn(m): int = IF m >= 0 THEN 1 ELSE -1 ENDIF

  abs(m): {n: nonneg_real | n >= m AND n >= -m}
    = IF m < 0 THEN -m ELSE m ENDIF

  nonzero_abs_is_pos: JUDGEMENT abs(x:nzreal) HAS_TYPE {y: posreal | y >= x}
  rat_abs_is_nonneg: JUDGEMENT abs(q:rat) HAS_TYPE {r: nonneg_rat | r >= q}
  nzrat_abs_is_pos: JUDGEMENT abs(q:nzrat) HAS_TYPE {r: posrat | r >= q}
  int_abs_is_nonneg: JUDGEMENT abs(i:int) HAS_TYPE {j: nonneg_int | j >= i}
  nzint_abs_is_pos: JUDGEMENT abs(i:nzint) HAS_TYPE {j: posint | j >= i}

  max(m, n): {p: real | p >= m AND p >= n}
    = IF m < n THEN n ELSE m ENDIF

  min(m, n): {p: real | p <= m AND p <= n}
    = IF m > n THEN n ELSE m ENDIF

  % real subtype judgements

  nzreal_max: JUDGEMENT
    max(x,y:nzreal) HAS_TYPE {z: nzreal | z >= x AND z >= y}
  nzreal_min: JUDGEMENT
    min(x,y:nzreal) HAS_TYPE {z: nzreal | z <= x AND z <= y}

  nonneg_real_max: JUDGEMENT
    max(x,y:nonneg_real) HAS_TYPE {z: nonneg_real | z >= x AND z >= y}
  nonneg_real_min: JUDGEMENT
    min(x,y:nonneg_real) HAS_TYPE {z: nonneg_real | z <= x AND z <= y}

  posreal_max: JUDGEMENT
    max(x,y:posreal) HAS_TYPE {z: posreal | z >= x AND z >= y}
  posreal_min: JUDGEMENT
    min(x,y:posreal) HAS_TYPE {z: posreal | z <= x AND z <= y}

  % rat subtype judgements

  rat_max: JUDGEMENT
    max(q,r:rat) HAS_TYPE {s: rat | s >= q AND s >= r}
  rat_min: JUDGEMENT
    min(q,r:rat) HAS_TYPE {s: rat | s <= q AND s <= r}

  nzrat_max: JUDGEMENT
    max(q,r:nzrat) HAS_TYPE {s: nzrat | s >= q AND s >= r}
  nzrat_min: JUDGEMENT
    min(q,r:nzrat) HAS_TYPE {s: nzrat | s <= q AND s <= r}

  nonneg_rat_max: JUDGEMENT
    max(q,r:nonneg_rat) HAS_TYPE {s: nonneg_rat | s >= q AND s >= r}
  nonneg_rat_min: JUDGEMENT
    min(q,r:nonneg_rat) HAS_TYPE {s: nonneg_rat | s <= q AND s <= r}

  posrat_max: JUDGEMENT
    max(q,r:posrat) HAS_TYPE {s: posrat | s >= q AND s >= r}
  posrat_min: JUDGEMENT
    min(q,r:posrat) HAS_TYPE {s: posrat | s <= q AND s <= r}

  % int subtype judgements

  int_max: JUDGEMENT
    max(i,j:int) HAS_TYPE {k: int | i <= k AND j <= k}
  int_min: JUDGEMENT
    min(i,j:int) HAS_TYPE {k: int | k <= i AND k <= j}

  nzint_max: JUDGEMENT
    max(i,j:nzint) HAS_TYPE {k: nzint | i <= k AND j <= k}
  nzint_min: JUDGEMENT
    min(i,j:nzint) HAS_TYPE {k: nzint | k <= i AND k <= j}

  nat_max: JUDGEMENT
    max(i,j:nat) HAS_TYPE {k: nat | i <= k AND j <= k}
  nat_min: JUDGEMENT
    min(i,j:nat) HAS_TYPE {k: nat | k <= i AND k <= j}

  posint_max: JUDGEMENT
    max(i,j:posint) HAS_TYPE {k: posint | i <= k AND j <= k}
  posint_min: JUDGEMENT
    min(i,j:posint) HAS_TYPE {k: posint | k <= i AND k <= j}

  % Useful facts about maximum and minimum of two reals
  %
  % Author : Alfons Geser, National Institute of Aerospace
  % Date   : Aug 26, 2003
  a,b,c : VAR real

  min_le : LEMMA min(a,b) <= c IFF (a <= c OR b <= c)
  min_lt : LEMMA min(a,b) < c IFF (a < c OR b < c)
  min_ge : LEMMA min(a,b) >= c IFF (a >= c AND b >= c)
  min_gt : LEMMA min(a,b) > c IFF (a > c AND b > c)
  le_min : LEMMA a <= min(b,c) IFF (a <= b AND a <= c)
  lt_min : LEMMA a < min(b,c) IFF (a < b AND a < c)
  ge_min : LEMMA a >= min(b,c) IFF (a >= b OR a >= c)
  gt_min : LEMMA a > min(b,c) IFF (a > b OR a > c)

  max_le : LEMMA max(a,b) <= c IFF (a <= c AND b <= c)
  max_lt : LEMMA max(a,b) < c IFF (a < c AND b < c)
  max_ge : LEMMA max(a,b) >= c IFF (a >= c OR b >= c)
  max_gt : LEMMA max(a,b) > c IFF (a > c OR b > c)
  le_max : LEMMA a <= max(b,c) IFF (a <= b OR a <= c)
  lt_max : LEMMA a < max(b,c) IFF (a < b OR a < c)
  ge_max : LEMMA a >= max(b,c) IFF (a >= b AND a >= c)
  gt_max : LEMMA a > max(b,c) IFF (a > b AND a > c)

 END real_defs


% real_props contains useful properties about real numbers.  Most of
% these are known to the decision procedures of PVS, but there are some
% nonlinear ones that cannot be gotten automatically.  Nonlinear
% expressions are those that involve multiplication or division by two
% non-numeric terms.  Thus x*y and x/c are nonlinear, while 2001*x + 39*z is
% linear.  Note that it doesn't matter whether the terms are constants
% or variables, only whether they are actual numbers.

real_props: THEORY
 BEGIN
  w, x, y, z: VAR real
  n0w, n0x, n0y, n0z: VAR nonzero_real
  nnw, nnx, nny, nnz: VAR nonneg_real
  pw, px, py, pz: VAR posreal
  npw, npx, npy, npz: VAR nonpos_real
  nw, nx, ny, nz: VAR negreal

  inv_ne_0: LEMMA 1/n0x /= 0

  % Cancellation Laws for equality

  both_sides_plus1: LEMMA (x + z = y + z) IFF x = y

  both_sides_plus2: LEMMA (z + x = z + y) IFF x = y

  both_sides_minus1: LEMMA (x - z = y - z) IFF x = y

  both_sides_minus2: LEMMA (z - x = z - y) IFF x = y

  both_sides_times1: LEMMA (x * n0z = y * n0z) IFF x = y

  both_sides_times2: LEMMA (n0z * x = n0z * y) IFF x = y

  both_sides_div1: LEMMA (x/n0z = y/n0z) IFF x = y

  both_sides_div2: LEMMA (n0z/n0x = n0z/n0y) IFF n0x = n0y

  times_plus: LEMMA (x + y) * (z + w) = x*z + x*w + y*z + y*w

  times_div1: LEMMA x * (y/n0z) = (x * y)/n0z

  times_div2: LEMMA (x/n0z) * y = (x * y)/n0z

  div_times: LEMMA (x/n0x) * (y/n0y) = (x*y)/(n0x*n0y)

  div_eq_zero: LEMMA x/n0z = 0 IFF x = 0

  div_simp: LEMMA n0x/n0x = 1

  div_cancel1: LEMMA n0z * (x/n0z) = x

  div_cancel2: LEMMA (x/n0z) * n0z = x

  div_cancel3: LEMMA x/n0z = y IFF x = y * n0z

  div_cancel4: LEMMA x = y/n0z IFF x * n0z = y

  cross_mult: LEMMA (x/n0x = y/n0y) IFF (x * n0y = y * n0x)

  add_div: LEMMA (x/n0x) + (y/n0y) = (x * n0y + y * n0x)/(n0x * n0y)

  minus_div1: LEMMA (x/n0x) - (y/n0y) = (x * n0y - y * n0x)/(n0x * n0y)

  minus_div2: LEMMA (x/n0x - y/n0x) = (x - y)/n0x

  div_distributes: LEMMA (x/n0z) + (y/n0z) =  (x + y)/n0z

  div_distributes_minus: LEMMA (x/n0z) - (y/n0z) =  (x - y)/n0z

  div_div1: LEMMA (x / (n0y / n0z)) = ((x * n0z) / n0y)

  div_div2: LEMMA ((x / n0y) / n0z) = (x / (n0y * n0z))

  idem_add_is_zero: LEMMA x + x = x IMPLIES x = 0

  zero_times1: LEMMA 0 * x = 0

  zero_times2: LEMMA x * 0 = 0

  zero_times3: LEMMA x * y = 0 IFF x = 0 OR y = 0

  neg_times_neg: LEMMA (-x) * (-y) = x * y

  zero_is_neg_zero: LEMMA -0 = 0


  % Order lemmas for <

  strict_lt : LEMMA strict_total_order?(<)

  trich_lt: LEMMA x < y OR x = y OR y < x

  tri_unique_lt1: LEMMA x < y IMPLIES (x /= y AND NOT(y < x))

  tri_unique_lt2: LEMMA x = y IMPLIES (NOT(x < y) AND NOT(y < x))

  zero_not_lt_zero: LEMMA NOT 0 < 0

  neg_lt: LEMMA 0 < -x IFF x < 0

  pos_times_lt: LEMMA 0 < x * y IFF (0 < x AND 0 < y) OR (x < 0 AND y < 0)

  neg_times_lt: LEMMA x * y < 0 IFF (0 < x AND y < 0) OR (x < 0 AND 0 < y)

  quotient_pos_lt: FORMULA 0 < 1/n0x IFF 0 < n0x
  
  quotient_neg_lt: FORMULA 1/n0x < 0 IFF n0x < 0

  pos_div_lt: LEMMA 0 < x/n0y IFF (0 < x AND 0 < n0y) OR (x < 0 AND n0y < 0)

  neg_div_lt: LEMMA x/n0y < 0 IFF (0 < x AND n0y < 0) OR (x < 0 AND 0 < n0y)

  div_mult_pos_lt1: LEMMA z/py < x IFF z < x * py

  div_mult_pos_lt2: LEMMA x < z/py IFF x * py < z

  div_mult_neg_lt1: LEMMA z/ny < x IFF x * ny < z

  div_mult_neg_lt2: LEMMA x < z/ny IFF z < x * ny
  

  % Cancellation laws for <

  both_sides_plus_lt1: LEMMA x + z < y + z IFF x < y

  both_sides_plus_lt2: LEMMA z + x < z + y IFF x < y

  both_sides_minus_lt1: LEMMA x - z < y - z IFF x < y

  both_sides_minus_lt2: LEMMA z - x < z - y IFF y < x

  both_sides_times_pos_lt1: LEMMA x * pz < y * pz IFF x < y

  both_sides_times_pos_lt2: LEMMA pz * x < pz * y IFF x < y

  both_sides_times_neg_lt1: LEMMA x * nz < y * nz IFF y < x

  both_sides_times_neg_lt2: LEMMA nz * x < nz * y IFF y < x

  both_sides_div_pos_lt1: LEMMA x/pz < y/pz IFF x < y

  both_sides_div_pos_lt2: LEMMA pz/px < pz/py IFF py < px

  both_sides_div_pos_lt3: LEMMA nz/px < nz/py IFF px < py

  both_sides_div_neg_lt1: LEMMA x/nz < y/nz IFF y < x

  both_sides_div_neg_lt2: LEMMA pz/nx < pz/ny IFF ny < nx

  both_sides_div_neg_lt3: LEMMA nz/nx < nz/ny IFF nx < ny

  lt_plus_lt1: LEMMA x <= y AND z < w IMPLIES x + z < y + w

  lt_plus_lt2: LEMMA x < y AND z <= w IMPLIES x + z < y + w

  lt_minus_lt1: LEMMA x <= y AND w < z IMPLIES x - z < y - w

  lt_minus_lt2: LEMMA x < y AND w <= z IMPLIES x - z < y - w

  lt_times_lt_pos1: LEMMA px <= y AND nnz < w IMPLIES px * nnz < y * w

  lt_times_lt_pos2: LEMMA nnx < y AND pz <= w IMPLIES nnx * pz < y * w

  lt_div_lt_pos1: LEMMA px <= y AND pz < w IMPLIES px/w < y/pz

  lt_div_lt_pos2: LEMMA nnx < y AND pz <= w IMPLIES nnx/w < y/pz

  lt_times_lt_neg1: LEMMA x <= ny AND z < npw IMPLIES ny * npw < x * z

  lt_times_lt_neg2: LEMMA x < npy AND z <= nw IMPLIES npy * nw < x * z

  lt_div_lt_neg1: LEMMA x <= ny AND z < nw IMPLIES ny/z < x/nw

  lt_div_lt_neg2: LEMMA x < npy AND z <= nw IMPLIES npy/z < x/nw


  % Order lemmas for <=

  total_le: LEMMA total_order?(<=)

  dich_le: LEMMA x <= y OR y <= x

  zero_le_zero: LEMMA 0 <= 0

  neg_le: LEMMA 0 <= -x IFF x <= 0

  pos_times_le: LEMMA 0 <= x * y IFF (0 <= x AND 0 <= y) OR (x <= 0 AND y <= 0)

  neg_times_le: LEMMA x * y <= 0 IFF (0 <= x AND y <= 0) OR (x <= 0 AND 0 <= y)

  quotient_pos_le: FORMULA 0 <= 1/n0x IFF 0 <= n0x
  
  quotient_neg_le: FORMULA 1/n0x <= 0 IFF n0x <= 0

  pos_div_le: LEMMA
    0 <= x/n0y IFF (0 <= x AND 0 <= n0y) OR (x <= 0 AND n0y <= 0)

  neg_div_le: LEMMA
    x/n0y <= 0 IFF (0 <= x AND n0y <= 0) OR (x <= 0 AND 0 <= n0y)

  div_mult_pos_le1: LEMMA z/py <= x IFF z <= x * py

  div_mult_pos_le2: LEMMA x <= z/py IFF x * py <= z

  div_mult_neg_le1: LEMMA z/ny <= x IFF x * ny <= z

  div_mult_neg_le2: LEMMA x <= z/ny IFF z <= x * ny


  % Cancellation laws for <=

  both_sides_plus_le1: LEMMA x + z <= y + z IFF x <= y

  both_sides_plus_le2: LEMMA z + x <= z + y IFF x <= y

  both_sides_minus_le1: LEMMA x - z <= y - z IFF x <= y

  both_sides_minus_le2: LEMMA z - x <= z - y IFF y <= x

  both_sides_times_pos_le1: LEMMA x * pz <= y * pz IFF x <= y

  both_sides_times_pos_le2: LEMMA pz * x <= pz * y IFF x <= y

  both_sides_times_neg_le1: LEMMA x * nz <= y * nz IFF y <= x

  both_sides_times_neg_le2: LEMMA nz * x <= nz * y IFF y <= x

  both_sides_div_pos_le1: LEMMA x/pz <= y/pz IFF x <= y

  both_sides_div_pos_le2: LEMMA pz/px <= pz/py IFF py <= px

  both_sides_div_pos_le3: LEMMA nz/px <= nz/py IFF px <= py

  both_sides_div_neg_le1: LEMMA x/nz <= y/nz IFF y <= x

  both_sides_div_neg_le2: LEMMA pz/nx <= pz/ny IFF ny <= nx

  both_sides_div_neg_le3: LEMMA nz/nx <= nz/ny IFF nx <= ny

  le_plus_le: LEMMA x <= y AND z <= w IMPLIES x + z <= y + w

  le_minus_le: LEMMA x <= y AND w <= z IMPLIES x - z <= y - w

  le_times_le_pos: LEMMA nnx <= y AND nnz <= w IMPLIES nnx * nnz <= y * w

  le_div_le_pos: LEMMA nnx <= y AND pz <= w IMPLIES nnx/w <= y/pz

  le_times_le_neg: LEMMA x <= npy AND z <= npw IMPLIES npy * npw <= x * z

  le_div_le_neg: LEMMA x <= npy AND z <= nw IMPLIES npy/z <= x/nw


  % Order lemmas for >

  strict_gt : LEMMA strict_total_order?(>)

  trich_gt: LEMMA x > y OR x = y OR y > x

  tri_unique_gt1: LEMMA x > y IMPLIES (x /= y AND NOT(y > x))

  tri_unique_gt2: LEMMA x = y IMPLIES (NOT(x > y) AND NOT(y > x))

  zero_not_gt_zero: LEMMA NOT 0 > 0

  neg_gt: LEMMA 0 > -x IFF x > 0

  pos_times_gt: LEMMA x * y > 0 IFF (0 > x AND 0 > y) OR (x > 0 AND y > 0)

  neg_times_gt: LEMMA 0 > x * y IFF (0 > x AND y > 0) OR (x > 0 AND 0 > y)

  quotient_pos_gt: FORMULA 1/n0x > 0 IFF n0x > 0
  
  quotient_neg_gt: FORMULA 0 > 1/n0x IFF 0 > n0x

  pos_div_gt: LEMMA x/n0y > 0 IFF (0 > x AND 0 > n0y) OR (x > 0 AND n0y > 0)

  neg_div_gt: LEMMA 0 > x/n0y IFF (0 > x AND n0y > 0) OR (x > 0 AND 0 > n0y)

  % Cancellation laws for >

  both_sides_plus_gt1: LEMMA x + z > y + z IFF x > y

  both_sides_plus_gt2: LEMMA z + x > z + y IFF x > y

  both_sides_minus_gt1: LEMMA x - z > y - z IFF x > y

  both_sides_minus_gt2: LEMMA z - x > z - y IFF y > x

  both_sides_times_pos_gt1: LEMMA x * pz > y * pz IFF x > y

  both_sides_times_pos_gt2: LEMMA pz * x > pz * y IFF x > y

  both_sides_times_neg_gt1: LEMMA x * nz > y * nz IFF y > x

  both_sides_times_neg_gt2: LEMMA nz * x > nz * y IFF y > x

  both_sides_div_pos_gt1: LEMMA x/pz > y/pz IFF x > y

  both_sides_div_pos_gt2: LEMMA pz/px > pz/py IFF py > px

  both_sides_div_pos_gt3: LEMMA nz/px > nz/py IFF px > py

  both_sides_div_neg_gt1: LEMMA x/nz > y/nz IFF y > x

  both_sides_div_neg_gt2: LEMMA pz/nx > pz/ny IFF ny > nx

  both_sides_div_neg_gt3: LEMMA nz/nx > nz/ny IFF nx > ny

  gt_plus_gt1: LEMMA x >= y AND z > w IMPLIES x + z > y + w

  gt_plus_gt2: LEMMA x > y AND z >= w IMPLIES x + z > y + w

  gt_minus_gt1: LEMMA x >= y AND w > z IMPLIES x - z > y - w

  gt_minus_gt2: LEMMA x > y AND w >= z IMPLIES x - z > y - w

  gt_times_gt_pos1: LEMMA x >= py AND z > nnw IMPLIES x * z > py * nnw

  gt_times_gt_pos2: LEMMA x > nny AND z >= pw IMPLIES x * z > nny * pw

  gt_div_gt_pos1: LEMMA x >= py AND z > pw IMPLIES x/pw > py/z

  gt_div_gt_pos2: LEMMA x > nny AND z >= pw IMPLIES x/pw > nny/z

  gt_times_gt_neg1: LEMMA nx >= y AND npz > w IMPLIES y * w > nx * npz

  gt_times_gt_neg2: LEMMA npx > y AND nz >= w IMPLIES y * w > npx * nz

  gt_div_gt_neg1: LEMMA nx >= y AND nz > w IMPLIES y/nz > nx/w

  gt_div_gt_neg2: LEMMA npx > y AND nz >= w IMPLIES y/nz > npx/w


  % Order lemmas for >=

  total_ge : LEMMA total_order?(>=)

  dich_ge: LEMMA x >= y OR y >= x

  zero_ge_zero: LEMMA 0 >= 0

  neg_ge: LEMMA 0 >= -x IFF x >= 0

  pos_times_ge: LEMMA x * y >= 0 IFF (0 >= x AND 0 >= y) OR (x >= 0 AND y >= 0)

  neg_times_ge: LEMMA 0 >= x * y IFF (0 >= x AND y >= 0) OR (x >= 0 AND 0 >= y)

  quotient_pos_ge: FORMULA 1/n0x >= 0 IFF n0x >= 0
  
  quotient_neg_ge: FORMULA 0 >= 1/n0x IFF 0 >= n0x

  pos_div_ge: LEMMA
    x/n0y >= 0 IFF (0 >= x AND 0 >= n0y) OR (x >= 0 AND n0y >= 0)

  neg_div_ge: LEMMA
    0 >= x/n0y IFF (0 >= x AND n0y >= 0) OR (x >= 0 AND 0 >= n0y)

  div_mult_pos_ge1: LEMMA z/py >= x IFF z >= x * py

  div_mult_pos_ge2: LEMMA x >= z/py IFF x * py >= z

  div_mult_neg_ge1: LEMMA z/ny >= x IFF x * ny >= z

  div_mult_neg_ge2: LEMMA x >= z/ny IFF z >= x * ny


  % Cancellation laws for >=

  both_sides_plus_ge1: LEMMA x + z >= y + z IFF x >= y

  both_sides_plus_ge2: LEMMA z + x >= z + y IFF x >= y

  both_sides_minus_ge1: LEMMA x - z >= y - z IFF x >= y

  both_sides_minus_ge2: LEMMA z - x >= z - y IFF y >= x

  both_sides_times_pos_ge1: LEMMA x * pz >= y * pz IFF x >= y

  both_sides_times_pos_ge2: LEMMA pz * x >= pz * y IFF x >= y

  both_sides_times_neg_ge1: LEMMA x * nz >= y * nz IFF y >= x

  both_sides_times_neg_ge2: LEMMA nz * x >= nz * y IFF y >= x

  both_sides_div_pos_ge1: LEMMA x/pz >= y/pz IFF x >= y

  both_sides_div_pos_ge2: LEMMA pz/px >= pz/py IFF py >= px

  both_sides_div_pos_ge3: LEMMA nz/px >= nz/py IFF px >= py

  both_sides_div_neg_ge1: LEMMA x/nz >= y/nz IFF y >= x

  both_sides_div_neg_ge2: LEMMA pz/nx >= pz/ny IFF ny >= nx

  both_sides_div_neg_ge3: LEMMA nz/nx >= nz/ny IFF nx >= ny

  ge_plus_ge: LEMMA x >= y AND z >= w IMPLIES x + z >= y + w

  ge_minus_ge: LEMMA x >= y AND w >= z IMPLIES x - z >= y - w

  ge_times_ge_pos: LEMMA x >= nny AND z >= nnw IMPLIES x * z >= nny * nnw

  ge_div_ge_pos: LEMMA x >= nny AND z >= pw IMPLIES x/pw >= nny/z

  ge_times_ge_neg: LEMMA npx >= y AND npz >= w IMPLIES y * w >= npx * npz

  ge_div_ge_neg: LEMMA npx >= y AND nz >= w IMPLIES y/nz >= npx/w

  nonzero_times1: LEMMA n0x * y = 0 IFF y = 0

  nonzero_times2: LEMMA x * n0y = 0 IFF x = 0

  nonzero_times3: LEMMA n0x * n0y = 0 IFF FALSE

  % Useful lemmas about the multiplicative identity

  eq1_gt: FORMULA x > 1 AND x * y = 1 IMPLIES y < 1

  eq1_ge: FORMULA x >= 1 AND x * y = 1 IMPLIES y <= 1
  
  eqm1_gt: FORMULA x > 1 AND x * y = -1 IMPLIES y > -1

  eqm1_ge: FORMULA x >= 1 AND x * y = -1 IMPLIES y >= -1

  eqm1_lt: FORMULA x < -1 AND x * y = -1 IMPLIES y < 1

  eqm1_le: FORMULA x <= -1 AND x * y = -1 IMPLIES y <= 1

  sqrt_1: LEMMA x * x = 1 IFF x = 1 OR x = -1

  sqrt_1_lt: LEMMA x * x < 1 IMPLIES abs(x) < 1

  sqrt_1_le: LEMMA x * x <= 1 IMPLIES abs(x) <= 1

  idem_mult: LEMMA x * x = x IFF x = 0 OR x = 1


  i, j: VAR int

  product_1: LEMMA i >= 0 AND j >= 0 AND i*j = 1 IMPLIES i = 1 AND j = 1

  product_m1: LEMMA i >= 0 AND j <= 0 AND i*j = -1 IMPLIES i = 1 AND j = -1


  % Properties of absolute value

  triangle: LEMMA abs(x+y) <= abs(x) + abs(y)  

  abs_mult: LEMMA abs(x * y) = abs(x) * abs(y)

  abs_div: LEMMA abs(x / n0y) = abs(x) / abs(n0y)

  abs_abs: LEMMA abs(abs(x)) = abs(x)

  abs_square: LEMMA abs(x * x) = x * x

  abs_limits: LEMMA -(abs(x) + abs(y)) <= x + y AND x + y <= abs(x) + abs(y)


  % The axiom of Archimedes 

  axiom_of_archimedes: LEMMA
    FORALL (x: real): EXISTS (i: int): x < i

  archimedean: LEMMA
    FORALL (px: posreal): EXISTS (n: posnat): 1/n < px

  real_lt_is_strict_total_order: JUDGEMENT
    < HAS_TYPE (strict_total_order?[real])

  real_le_is_total_order: JUDGEMENT <= HAS_TYPE (total_order?[real])

  real_gt_is_strict_total_order: JUDGEMENT
    > HAS_TYPE (strict_total_order?[real])

  real_ge_is_total_order: JUDGEMENT >= HAS_TYPE (total_order?[real])

 END real_props

%% Contains extra properties about reals needed by the formula
%% manipulation strategies in package Manip.  Can be seen as an
%% extension of the real_props theory in the prelude.

extra_real_props: THEORY
BEGIN

%% Same variable declarations as real_props in the prelude:

  w, x, y, z: VAR real
  n0w, n0x, n0y, n0z: VAR nonzero_real
  nnw, nnx, nny, nnz: VAR nonneg_real
  pw, px, py, pz: VAR posreal
  npw, npx, npy, npz: VAR nonpos_real
  nw, nx, ny, nz: VAR negreal


%% Lemmas used to handle cases where a multiplier or divisor can be
%% either positive or negative.

pos_neg_split: LEMMA  EXISTS px, nx: n0x = px or n0x = nx


div_mult_pos_neg_lt1: LEMMA
    z/n0y < x IFF IF n0y > 0 THEN z < x * n0y ELSE x * n0y < z ENDIF

div_mult_pos_neg_lt2: LEMMA
    x < z/n0y IFF IF n0y > 0 THEN x * n0y < z ELSE z < x * n0y ENDIF

div_mult_pos_neg_le1: LEMMA
    z/n0y <= x IFF IF n0y > 0 THEN z <= x * n0y ELSE x * n0y <= z ENDIF

div_mult_pos_neg_le2: LEMMA
    x <= z/n0y IFF IF n0y > 0 THEN x * n0y <= z ELSE z <= x * n0y ENDIF

div_mult_pos_neg_gt1: LEMMA
    z/n0y > x IFF IF n0y > 0 THEN z > x * n0y ELSE x * n0y > z ENDIF

div_mult_pos_neg_gt2: LEMMA
    x > z/n0y IFF IF n0y > 0 THEN x * n0y > z ELSE z > x * n0y ENDIF

div_mult_pos_neg_ge1: LEMMA
    z/n0y >= x IFF IF n0y > 0 THEN z >= x * n0y ELSE x * n0y >= z ENDIF

div_mult_pos_neg_ge2: LEMMA
    x >= z/n0y IFF IF n0y > 0 THEN x * n0y >= z ELSE z >= x * n0y ENDIF


both_sides_times_pos_neg_lt1: LEMMA
  IF n0z > 0 THEN x * n0z < y * n0z ELSE y * n0z < x * n0z ENDIF IFF x < y

both_sides_times_pos_neg_lt2: LEMMA
  IF n0z > 0 THEN n0z * x < n0z * y ELSE n0z * y < n0z * x ENDIF IFF x < y

both_sides_times_pos_neg_le1: LEMMA
  IF n0z > 0 THEN x * n0z <= y * n0z ELSE y * n0z <= x * n0z ENDIF IFF x <= y

both_sides_times_pos_neg_le2: LEMMA
  IF n0z > 0 THEN n0z * x <= n0z * y ELSE n0z * y <= n0z * x ENDIF IFF x <= y

both_sides_times_pos_neg_gt1: LEMMA
  IF n0z > 0 THEN x * n0z > y * n0z ELSE y * n0z > x * n0z ENDIF IFF x > y

both_sides_times_pos_neg_gt2: LEMMA
  IF n0z > 0 THEN n0z * x > n0z * y ELSE n0z * y > n0z * x ENDIF IFF x > y

both_sides_times_pos_neg_ge1: LEMMA
  IF n0z > 0 THEN x * n0z >= y * n0z ELSE y * n0z >= x * n0z ENDIF IFF x >= y

both_sides_times_pos_neg_ge2: LEMMA
  IF n0z > 0 THEN n0z * x >= n0z * y ELSE n0z * y >= n0z * x ENDIF IFF x >= y


%% The naming scheme below follows that of the both_sides_div_+/-_R1/2/3
%% series in real_props as consistently as possible.

both_sides_div_pos_neg_lt1: LEMMA
  IF n0z > 0 THEN x/n0z < y/n0z ELSE y/n0z < x/n0z ENDIF IFF x < y

both_sides_div_pos_neg_lt2: LEMMA
  IF n0z > 0 THEN n0z/px < n0z/py ELSE n0z/py < n0z/px ENDIF IFF py < px

both_sides_div_pos_neg_lt3: LEMMA
  IF n0z > 0 THEN n0z/nx < n0z/ny ELSE n0z/ny < n0z/nx ENDIF IFF ny < nx

both_sides_div_pos_neg_le1: LEMMA
  IF n0z > 0 THEN x/n0z <= y/n0z ELSE y/n0z <= x/n0z ENDIF IFF x <= y

both_sides_div_pos_neg_le2: LEMMA
  IF n0z > 0 THEN n0z/px <= n0z/py ELSE n0z/py <= n0z/px ENDIF IFF py <= px

both_sides_div_pos_neg_le3: LEMMA
  IF n0z > 0 THEN n0z/nx <= n0z/ny ELSE n0z/ny <= n0z/nx ENDIF IFF ny <= nx

both_sides_div_pos_neg_gt1: LEMMA
  IF n0z > 0 THEN x/n0z > y/n0z ELSE y/n0z > x/n0z ENDIF IFF x > y

both_sides_div_pos_neg_gt2: LEMMA
  IF n0z > 0 THEN n0z/px > n0z/py ELSE n0z/py > n0z/px ENDIF IFF py > px

both_sides_div_pos_neg_gt3: LEMMA
  IF n0z > 0 THEN n0z/nx > n0z/ny ELSE n0z/ny > n0z/nx ENDIF IFF ny > nx

both_sides_div_pos_neg_ge1: LEMMA
  IF n0z > 0 THEN x/n0z >= y/n0z ELSE y/n0z >= x/n0z ENDIF IFF x >= y

both_sides_div_pos_neg_ge2: LEMMA
  IF n0z > 0 THEN n0z/px >= n0z/py ELSE n0z/py >= n0z/px ENDIF IFF py >= px

both_sides_div_pos_neg_ge3: LEMMA
  IF n0z > 0 THEN n0z/nx >= n0z/ny ELSE n0z/ny >= n0z/nx ENDIF IFF ny >= nx


%% Less restrictive lemmas used by strategy MULT-BY than the 
%% both_sides_times series in real_props.  Allows multiplier to be
%% zero in some cases.  Variable z changed to w so it would
%% precede x,y during INST and make all lemmas consistent.

  both_sides_times1_imp: LEMMA x = y IMPLIES x * w = y * w

  both_sides_times2_imp: LEMMA x = y IMPLIES w * x = w * y

  both_sides_times_pos_le1_imp: LEMMA x <= y IMPLIES x * nnw <= y * nnw

  both_sides_times_pos_le2_imp: LEMMA x <= y IMPLIES nnw * x <= nnw * y

  both_sides_times_neg_le1_imp: LEMMA y <= x IMPLIES x * npw <= y * npw

  both_sides_times_neg_le2_imp: LEMMA y <= x IMPLIES npw * x <= npw * y

  both_sides_times_pos_ge1_imp: LEMMA x >= y IMPLIES x * nnw >= y * nnw

  both_sides_times_pos_ge2_imp: LEMMA x >= y IMPLIES nnw * x >= nnw * y

  both_sides_times_neg_ge1_imp: LEMMA y >= x IMPLIES x * npw >= y * npw

  both_sides_times_neg_ge2_imp: LEMMA y >= x IMPLIES npw * x >= npw * y


  both_sides_times_pos_neg_le1_imp: LEMMA
    x <= y IMPLIES IF w >= 0 THEN x * w <= y * w ELSE y * w <= x * w ENDIF

  both_sides_times_pos_neg_le2_imp: LEMMA
    x <= y IMPLIES IF w >= 0 THEN w * x <= w * y ELSE w * y <= w * x ENDIF

  both_sides_times_pos_neg_ge1_imp: LEMMA
    x >= y IMPLIES IF w >= 0 THEN x * w >= y * w ELSE y * w >= x * w ENDIF

  both_sides_times_pos_neg_ge2_imp: LEMMA
    x >= y IMPLIES IF w >= 0 THEN w * x >= w * y ELSE w * y >= w * x ENDIF


%% Additional lemmas needed to fill in gaps left by real_props.

  zero_times4: LEMMA 0 = x * y IFF x = 0 OR y = 0

  times_div_cancel1: LEMMA (n0z * x) / n0z = x

  times_div_cancel2: LEMMA (x * n0z) / n0z = x

%% Replacements for real_props lemmas to correct inconsistent naming.

  div_mult_pos_gt1: LEMMA z/py > x IFF z > x * py

  div_mult_pos_gt2: LEMMA x > z/py IFF x * py > z

  div_mult_neg_gt1: LEMMA z/ny > x IFF x * ny > z

  div_mult_neg_gt2: LEMMA x > z/ny IFF z > x * ny


%% Transitivity lemmas for inequality reasoning.  Normally unnecessary
%% but sometimes helpful for forcing case splits.

lt_cut: LEMMA x < y AND y < z IMPLIES x < z
le_cut: LEMMA x <= y AND y <= z IMPLIES x <= z
gt_cut: LEMMA x > y AND y > z IMPLIES x > z
ge_cut: LEMMA x >= y AND y >= z IMPLIES x >= z


%% Rules used to deduce inequalities on products of reals.  These rules
%% stipulate only sufficient conditions for the inequalities to hold.
%% The most common and useful cases are included, although additional
%% cases might be desirable later.

le_times_le_any1: LEMMA
	IF w >= 0 IFF x >= 0
	   THEN IF y >= 0 IFF z >= 0
		   THEN abs(w) <= abs(y) AND abs(x) <= abs(z)
		   ELSE (w = 0 OR x = 0) AND (y = 0 OR z = 0)
	        ENDIF
	   ELSE IF y >= 0 IFF z >= 0
		   THEN true
		   ELSE abs(w) >= abs(y) AND abs(x) >= abs(z)
	        ENDIF
        ENDIF
	  IMPLIES w * x <= y * z

ge_times_ge_any1: LEMMA
	IF y >= 0 IFF z >= 0
	   THEN IF w >= 0 IFF x >= 0
		   THEN abs(w) >= abs(y) AND abs(x) >= abs(z)
		   ELSE (w = 0 OR x = 0) AND (y = 0 OR z = 0)
	        ENDIF
	   ELSE IF w >= 0 IFF x >= 0
		   THEN true
		   ELSE abs(w) <= abs(y) AND abs(x) <= abs(z)
	        ENDIF
        ENDIF
	  IMPLIES w * x >= y * z

lt_times_lt_any1: LEMMA
	IF w = 0 OR x = 0
	   THEN 0 < y AND 0 < z OR y < 0 AND z < 0
	ELSIF w > 0 IFF x > 0
	   THEN (y > 0 IFF z > 0) AND
                (abs(w) <= abs(y) AND abs(x) <  abs(z) OR
                 abs(w) <  abs(y) AND abs(x) <= abs(z))
	ELSIF y > 0 IFF z > 0
           THEN true
	   ELSE abs(w) >= abs(y) AND abs(x) >  abs(z) OR
                abs(w) >  abs(y) AND abs(x) >= abs(z)
        ENDIF
	  IMPLIES w * x < y * z

gt_times_gt_any1: LEMMA
	IF y = 0 OR z = 0
	   THEN 0 < w AND 0 < x OR w < 0 AND x < 0
	ELSIF y > 0 IFF z > 0
	   THEN (w > 0 IFF x > 0) AND
                (abs(w) >= abs(y) AND abs(x) >  abs(z) OR
                 abs(w) >  abs(y) AND abs(x) >= abs(z))
	ELSIF w > 0 IFF x > 0
           THEN true
	   ELSE abs(w) <= abs(y) AND abs(x) <  abs(z) OR
                abs(w) <  abs(y) AND abs(x) <= abs(z)
        ENDIF
	  IMPLIES w * x > y * z


%% Contrapositive forms of series R_times_R_any1.  They stipulate
%% necessary conditions for the inequalities to hold.

le_times_le_any2: LEMMA
	w * x <= y * z IMPLIES
	IF y = 0 OR z = 0
	   THEN (0 >= w OR 0 >= x) AND (w >= 0 OR x >= 0)
	ELSIF y > 0 IFF z > 0
	   THEN (w > 0 IFF x > 0) IMPLIES
                (abs(w) <  abs(y) OR abs(x) <= abs(z)) AND
                (abs(w) <= abs(y) OR abs(x) <  abs(z))
	ELSE NOT (w > 0 IFF x > 0) AND
	     (abs(w) >  abs(y) OR abs(x) >= abs(z)) AND
             (abs(w) >= abs(y) OR abs(x) >  abs(z))
        ENDIF

ge_times_ge_any2: LEMMA
	w * x >= y * z IMPLIES
	IF w = 0 OR x = 0
	   THEN (0 >= y OR 0 >= z) AND (y >= 0 OR z >= 0)
	ELSIF w > 0 IFF x > 0
	   THEN (y > 0 IFF z > 0) IMPLIES
                (abs(w) >  abs(y) OR abs(x) >= abs(z)) AND
                (abs(w) >= abs(y) OR abs(x) > abs(z))
	ELSE NOT (y > 0 IFF z > 0) AND
             (abs(w) <  abs(y) OR abs(x) <= abs(z)) AND
             (abs(w) <= abs(y) OR abs(x) <  abs(z))
        ENDIF

lt_times_lt_any2: LEMMA
	w * x < y * z IMPLIES
	IF y >= 0 IFF z >= 0
	   THEN IF w >= 0 IFF x >= 0
		   THEN abs(w) < abs(y) OR abs(x) < abs(z)
		   ELSE (w /= 0 AND x /= 0) OR (y /= 0 AND z /= 0)
	        ENDIF
	   ELSE NOT (w >= 0 IFF x >= 0) AND
		(abs(w) > abs(y) OR abs(x) > abs(z))
        ENDIF

gt_times_gt_any2: LEMMA
	w * x > y * z IMPLIES
	IF w >= 0 IFF x >= 0
	   THEN IF y >= 0 IFF z >= 0
		   THEN abs(w) > abs(y) OR abs(x) > abs(z)
		   ELSE (w /= 0 AND x /= 0) OR (y /= 0 AND z /= 0)
	        ENDIF
	   ELSE NOT (y >= 0 IFF z >= 0) AND
		(abs(w) < abs(y) OR abs(x) < abs(z))
        ENDIF


END extra_real_props

%%% This file contains some simplification rules missing in real_props.
%%% Part of the Field package of Cesar Munoz
extra_tegies : THEORY
BEGIN
  x,y : VAR real
  n0z : VAR nzreal

  neg_mult : LEMMA
    -x*y =  -(x*y)

  mult_neg : LEMMA
    x*-y =  -(x*y)

  neg_add : LEMMA
    -x + y =  y - x

  add_neg : LEMMA
    x + -y =  x - y

  neg_div : LEMMA
    -x/n0z = -(x/n0z)

  div_neg : LEMMA
    x/-n0z = -(x/n0z)

  one_times: LEMMA
    1*x = x

  neg_one_times: LEMMA
    -1*x = -x

  zero_div: LEMMA
    0/n0z = 0

  neg_neg: LEMMA
    --x = x

END extra_tegies



% rational_props contains the properties of rational numbers.  This mostly
% consists of subtypes and judgements, as most of the properties given in
% real_props are inherited.

rational_props: THEORY
 BEGIN
  x, y: VAR real
  i: VAR int
  n0j: VAR nzint
  p: VAR posnat
  r: VAR rat

  rational_pred_ax: AXIOM EXISTS i, n0j: r = i/n0j
  rational_pred_ax2: LEMMA EXISTS i, p: r = i/p

  density_positive : LEMMA
      0 <= x AND x < y IMPLIES (EXISTS r: x < r AND r < y)
  
  density: LEMMA x < y IMPLIES (EXISTS r: x < r AND r < y)

 END rational_props


% integer_props contains the properties of integers and naturalnumbers.

integer_props: THEORY
 BEGIN
  m, n: VAR nat
  i, j, k: VAR int
  n0j: VAR nzint

  N: VAR (nonempty?[nat])

  I: VAR (nonempty?[int])

  integer_pred_ax: LEMMA EXISTS n: i = n OR i = -n

  div_simple: LEMMA (EXISTS k: i = k*n0j) = integer_pred(i/n0j)

  lub_nat: LEMMA
    upper_bound?(m, N)
      => EXISTS (n:(N)): least_upper_bound?(n, N)

  lub_int: LEMMA
    upper_bound?(i, I)
      => EXISTS (j:(I)): least_upper_bound?(j, I)

  glb_nat: LEMMA
    EXISTS (n:(N)): greatest_lower_bound?(n, N)

  glb_int: LEMMA
    lower_bound?(i, I)
      => EXISTS (j:(I)): greatest_lower_bound?(j, I)

 END integer_props


% The definitions for floor and ceiling, courtesy of
%   Paul S. Miner   		           email: p.s.miner@larc.nasa.gov
%   Mail Stop 130			     fax: (804) 864-4234
%   NASA Langley Research Center	   phone: (804) 864-6201
%   Hampton, Virginia 23681-0001

floor_ceil: THEORY
 BEGIN
  
  x, y: VAR real
  py:  VAR posreal
  j: VAR nonzero_integer
  i, k: VAR integer
  
  floor_exists: LEMMA EXISTS i: i <= x & x < i + 1
  
  ceiling_exists: LEMMA EXISTS i: x <= i & i < x + 1
  
  floor(x): {i | i <= x & x < i + 1}
  
  fractional(x): {x | 0 <= x & x < 1} = x - floor(x)
  
  ceiling(x): {i | x <= i & i < x + 1}
  
  floor_def: LEMMA floor(x) <= x & x < floor(x) + 1
  
  ceiling_def: LEMMA x <= ceiling(x) & ceiling(x) < x + 1
  
  floor_ceiling_reflect1: LEMMA floor(-x) = -ceiling(x)
  
  floor_ceiling_reflect2: LEMMA ceiling(-x) = -floor(x)
  
  nonneg_floor_is_nat: JUDGEMENT floor(x:nonneg_real) HAS_TYPE nat
  nonneg_ceiling_is_nat: JUDGEMENT ceiling(x:nonneg_real) HAS_TYPE nat
  
  floor_int: LEMMA floor(i) = i
  
  ceiling_int: LEMMA ceiling(i) = i
  
  floor_plus_int: LEMMA floor(x + i) = floor(x) + i
  
  ceiling_plus_int: LEMMA ceiling(x + i) = ceiling(x) + i
  
  floor_ceiling_nonint: LEMMA NOT integer?(x) => ceiling(x) - floor(x) = 1

  floor_ceiling_int: lemma floor(i)=ceiling(i)
  
  floor_neg: LEMMA 
        floor(x) = IF integer?(x) THEN -floor(-x) ELSE -floor(-x) - 1 ENDIF
  
  real_parts: LEMMA x = floor(x) + fractional(x)
  
  floor_plus: LEMMA 
        floor(x + y) = floor(x) + floor(y) + floor(fractional(x) + fractional(y))
  
  ceiling_plus: LEMMA
        ceiling(x + y) = floor(x) + floor(y) + ceiling(fractional(x) + fractional(y))
  
  floor_split: lemma i = floor(i/2)+ceiling(i/2)

  floor_within_1: lemma x - floor(x) < 1

  ceiling_within_1: lemma ceiling(x) - x < 1

  floor_val:   LEMMA i >= k*j AND i < (k+1)*j IMPLIES floor(i/j) = k 

  floor_small: LEMMA  abs(i) < abs(j) IMPLIES
                         floor(i/j) = IF i/j >= 0 THEN 0 ELSE -1 ENDIF

  floor_eq_0:  LEMMA floor(x) = 0 IMPLIES x >= 0 AND x < 1

  fractional_plus: LEMMA
        fractional(x+y) = fractional(fractional(x) + fractional(y))

  floor_div: LEMMA floor(x/py) = i IFF py*i <= x AND x < py*(i+1)
  floor_0:   LEMMA floor(x)    = 0 IFF 0    <= x AND x < 1

 END floor_ceil


% exponentiation provides the definitions expt and ^.  expt multiplies a
% real by itself the number of times specified, where 0 times returns a 1
% (thus expt(0,0) = 1).  ^ is defined in terms of expt to work for
% integers, but in this case if the integer negative then the real
% argument must be nonzero; this leads to the dependent type given below.

exponentiation: THEORY
 BEGIN
  r: VAR real
  q: VAR rat
  nnq: VAR nonneg_rat
  m, n: VAR nat
  pm, pn: VAR posnat
  i, j: VAR int
  n0i, n0j: VAR nzint
  x, y: VAR real
  px, py: VAR posreal
  n0x, n0y: VAR nzreal
  gt1x, gt1y: VAR {r: posreal | r > 1}
  lt1x, lt1y: VAR {r: posreal | r < 1}
  ge1x, ge1y: VAR {r: posreal | r >= 1}
  le1x, le1y: VAR {r: posreal | r <= 1}

  expt(r, n): RECURSIVE real =
    IF n = 0 THEN 1
    ELSE r * expt(r, n-1)
    ENDIF
   MEASURE n;

  expt_pos_aux: LEMMA expt(px, n) > 0

  expt_nonzero_aux: LEMMA expt(n0x, n) /= 0;

  nnreal_expt:  JUDGEMENT expt(x:nnreal,  n:nat) HAS_TYPE nnreal
  posreal_expt: JUDGEMENT expt(x:posreal, n:nat) HAS_TYPE posreal
  nzreal_expt:  JUDGEMENT expt(x:nzreal,  n:nat) HAS_TYPE nzreal
  rat_expt:     JUDGEMENT expt(x:rat,     n:nat) HAS_TYPE rat
  nnrat_expt:   JUDGEMENT expt(x:nnrat,   n:nat) HAS_TYPE nnrat
  posrat_expt:  JUDGEMENT expt(x:posrat,  n:nat) HAS_TYPE posrat
  int_expt:     JUDGEMENT expt(x:int,     n:nat) HAS_TYPE int
  nat_expt:     JUDGEMENT expt(x:nat,     n:nat) HAS_TYPE nat
  posnat_expt:  JUDGEMENT expt(x:posnat,  n:nat) HAS_TYPE posnat

  ^(r: real, i:{i:int | r /= 0 OR i >= 0}): real
    = IF i >= 0 THEN expt(r, i) ELSE 1/expt(r, -i) ENDIF

  expt_pos: LEMMA px^i > 0

  expt_nonzero: LEMMA n0x^i /= 0

  nnreal_exp:  JUDGEMENT ^(x:nnreal,  i:int | x/=0 OR i>=0) HAS_TYPE nnreal
  posreal_exp: JUDGEMENT ^(x:posreal, i:int) HAS_TYPE posreal
  nzreal_exp:  JUDGEMENT ^(x:nzreal,  i:int) HAS_TYPE nzreal
  rat_exp:     JUDGEMENT ^(x:rat,     i:int | x/=0 OR i>=0) HAS_TYPE rat
  nnrat_exp:   JUDGEMENT ^(x:nnrat,   i:int | x/=0 OR i>=0) HAS_TYPE nnrat
  posrat_exp:  JUDGEMENT ^(x:posrat,  i:int) HAS_TYPE posrat
  int_exp:     JUDGEMENT ^(x:int, n:nat)    HAS_TYPE int
  nat_exp:     JUDGEMENT ^(x:nat, n:nat)    HAS_TYPE nat
  posint_exp:  JUDGEMENT ^(x:posint, n:nat) HAS_TYPE posint

  % Properties of expt

  expt_x0_aux: LEMMA expt(x, 0) = 1

  expt_x1_aux: LEMMA expt(x, 1) = x

  expt_1n_aux: LEMMA expt(1, n) = 1

  increasing_expt_aux: LEMMA expt(gt1x, m+2) > gt1x

  decreasing_expt_aux: LEMMA expt(lt1x, m+2) < lt1x

  expt_1_aux: LEMMA expt(px, n + 1) = 1 IFF px = 1

  expt_plus_aux: LEMMA expt(n0x, m + n) = expt(n0x, m) * expt(n0x, n)

  expt_minus_aux: LEMMA
    m >= n IMPLIES expt(n0x, m - n) = expt(n0x, m)/expt(n0x, n)

  expt_times_aux: LEMMA expt(n0x, m * n) = expt(expt(n0x, m), n)

  expt_divide_aux: LEMMA 1/expt(n0x, m * n) = expt(1/expt(n0x, m), n)

  both_sides_expt1_aux: LEMMA expt(px, m+1) = expt(px, n+1) IFF m = n OR px = 1

  both_sides_expt2_aux: LEMMA expt(px, pm) = expt(py, pm) IFF px = py

  both_sides_expt_pos_lt_aux: LEMMA
    expt(px, m+1) < expt(py, m+1) IFF px < py

  both_sides_expt_gt1_lt_aux: LEMMA
    expt(gt1x, m+1) < expt(gt1x, n+1) IFF m < n

  both_sides_expt_lt1_lt_aux: LEMMA
    expt(lt1x, m+1) < expt(lt1x, n+1) IFF n < m

  both_sides_expt_pos_le_aux: LEMMA
    expt(px, m+1) <= expt(py, m+1) IFF px <= py

  both_sides_expt_gt1_le_aux: LEMMA
    expt(gt1x, m+1) <= expt(gt1x, n+1) IFF m <= n

  both_sides_expt_lt1_le_aux: LEMMA
    expt(lt1x, m+1) <= expt(lt1x, n+1) IFF n <= m

  both_sides_expt_pos_gt_aux: LEMMA
    expt(px, m+1) > expt(py, m+1) IFF px > py

  both_sides_expt_gt1_gt_aux: LEMMA
    expt(gt1x, m+1) > expt(gt1x, n+1) IFF m > n

  both_sides_expt_lt1_gt_aux: LEMMA
    expt(lt1x, m+1) > expt(lt1x, n+1) IFF n > m

  both_sides_expt_pos_ge_aux: LEMMA
    expt(px, m+1) >= expt(py, m+1) IFF px >= py

  both_sides_expt_gt1_ge_aux: LEMMA
    expt(gt1x, m+1) >= expt(gt1x, n+1) IFF m >= n

  both_sides_expt_lt1_ge_aux: LEMMA
    expt(lt1x, m+1) >= expt(lt1x, n+1) IFF n >= m

  expt_of_mult: LEMMA expt(x * y, n) = expt(x, n) * expt(y, n)

  expt_of_div: LEMMA expt(x / n0y, n) = expt(x, n) / expt(n0y, n)

  expt_of_inv: LEMMA expt(1 / n0x, n) = 1 / expt(n0x, n)

  expt_of_abs: LEMMA expt(abs(x), n) = abs(expt(x, n))

  abs_of_expt_inv: LEMMA abs(1 / expt(n0x, n)) = 1 / expt(abs(n0x),n)

  % Properties of ^

  expt_x0: LEMMA x^0 = 1

  expt_x1: LEMMA x^1 = x
  expt_x2: LEMMA x^2 = x*x
  expt_x3: LEMMA x^3 = x*x*x
  expt_x4: LEMMA x^4 = x*x*x*x

  expt_1i: LEMMA 1^i = 1

  expt_eq_0: LEMMA x^pn = 0 IFF x = 0

  expt_plus: LEMMA n0x^(i + j) = n0x^i * n0x^j

  expt_times: LEMMA n0x^(i * j) = (n0x^i)^j

  expt_inverse: LEMMA n0x^(-i) = 1/(n0x^i)

  expt_div: LEMMA n0x^i/n0x^j = n0x^(i-j)

  both_sides_expt1: LEMMA px ^ n0i = px ^ n0j IFF n0i = n0j OR px = 1

  both_sides_expt2: LEMMA px ^ n0i = py ^ n0i IFF px = py

  b: VAR above(1)

  pos_expt_gt: LEMMA n < b^n

  expt_ge1: LEMMA b^n >= 1

  both_sides_expt_pos_lt: LEMMA px ^ pm < py ^ pm IFF px < py

  both_sides_expt_gt1_lt: LEMMA gt1x ^ i < gt1x ^ j IFF i < j

  both_sides_expt_lt1_lt: LEMMA lt1x ^ i < lt1x ^ j IFF j < i

  both_sides_expt_pos_le: LEMMA px ^ pm <= py ^ pm IFF px <= py

  both_sides_expt_gt1_le: LEMMA gt1x ^ i <= gt1x ^ j IFF i <= j

  both_sides_expt_lt1_le: LEMMA lt1x ^ i <= lt1x ^ j IFF j <= i

  both_sides_expt_pos_gt: LEMMA px ^ pm > py ^ pm IFF px > py

  both_sides_expt_gt1_gt: LEMMA gt1x ^ i > gt1x ^ j IFF i > j

  both_sides_expt_lt1_gt: LEMMA lt1x ^ i > lt1x ^ j IFF j > i

  both_sides_expt_pos_ge: LEMMA px ^ pm >= py ^ pm IFF px >= py

  both_sides_expt_gt1_ge: LEMMA gt1x ^ i >= gt1x ^ j IFF i >= j

  both_sides_expt_lt1_ge: LEMMA lt1x ^ i >= lt1x ^ j IFF j >= i

  expt_gt1_pos: LEMMA gt1x^pm >= gt1x

  expt_gt1_neg: LEMMA gt1x^(-pm) < 1

  expt_gt1_nonpos: LEMMA gt1x^(-m) <= 1

  mult_expt: LEMMA (n0x * n0y) ^i = n0x^i * n0y^i

  div_expt : LEMMA (n0x / n0y)^i = n0x^i / n0y^i

  inv_expt : LEMMA (1 / n0x)^i = 1 / n0x^i

  abs_expt: LEMMA abs(n0x)^i = abs(n0x^i)

  abs_hat_nat : LEMMA abs(x)^n = abs(x^n)

  expt_minus1_abs:   LEMMA abs((-1)^i) = 1
  even_m1_pow: LEMMA even?(i) IMPLIES (-1)^i = 1
  not_even_m1_pow: LEMMA NOT even?(i) IMPLIES (-1)^i = -1

  expt_lt1_bound1: LEMMA  expt(lt1x, n) <= 1

  expt_lt1_bound2: LEMMA  expt(lt1x, pn) < 1

  expt_gt1_bound1: LEMMA  1 <= expt(gt1x, n)

  expt_gt1_bound2: LEMMA  1 < expt(gt1x, pn)

  large_expt: LEMMA 1 < px  IMPLIES (FORALL py: EXISTS n: py < expt(px, n))

  small_expt: LEMMA px < 1  IMPLIES (FORALL py: EXISTS n: expt(px, n) < py)

  exponent_adjust: LEMMA b^i+b^(i-pm) < b^(i+1)

  exp_of_exists: lemma exists i: b^i <= py & py < b^(i+1)

 END exponentiation


% Euclidean division courtesy of Bruno Dutertre

euclidean_division : THEORY
 BEGIN
  a, i : VAR nat
  b : VAR posnat
  n : VAR int

  %-----------------------------------------------
  %  Type mod(b) = 0.. b-1
  %  (replace below[b] to reduce number of TCCs)
  %-----------------------------------------------

  mod(b) : NONEMPTY_TYPE = { i | i <  b}

  %-----------------------
  %  Euclidean division
  %-----------------------

  euclid_nat : LEMMA
        EXISTS (q : nat), (r : mod(b)) : a = b * q + r

  euclid_int : PROPOSITION
        EXISTS (q : int), (r : mod(b)) : n = b * q + r

  unique_quotient : PROPOSITION
        FORALL (q1, q2 : int), (r1, r2 : mod(b)) :
            b * q1 + r1 = b * q2 + r2 IMPLIES q1 = q2

  unique_remainder : COROLLARY
        FORALL (q1, q2 : int), (r1, r2 : mod(b)) :
            b * q1 + r1 = b * q2 + r2 IMPLIES r1 = r2

  unique_division : COROLLARY
        FORALL (q1, q2 : int), (r1, r2 : mod(b)) :
            b * q1 + r1 = b * q2 + r2 IMPLIES q1 = q2 AND r1 = r2

 END euclidean_division


%  Divisibility relation
%  For integers and natural numbers
%  Provided by Bruno Dutertre
divides : THEORY
 BEGIN
  n, m, l, x, y : VAR int
  p, q : VAR nat
  nz : VAR nzint

  %--------------------
  %  Divides relation
  %--------------------

  divides(n, m): bool = EXISTS x : m = n * x

  divides(n)(m): bool = divides(n, m)

  mult_divides1: JUDGEMENT *(n, m) HAS_TYPE (divides(n))
  mult_divides2: JUDGEMENT *(n, m) HAS_TYPE (divides(m))

  %----------------
  %  Easy lemmas
  %----------------

  divides_sum : LEMMA
     divides(x, n) AND divides(x, m) IMPLIES divides(x, n + m)

  divides_diff : LEMMA
     divides(x, n) AND divides(x, m) IMPLIES divides(x, n - m)

  divides_opposite : LEMMA
     divides(x, - n) IFF divides(x, n)

  opposite_divides : LEMMA
     divides(- x, n) IFF divides(x, n)

  divides_prod1 : LEMMA
     divides(x, n) IMPLIES divides(x, n * m)

  divides_prod2 : LEMMA
     divides(x, n) IMPLIES divides(x, m * n)

  %---------------
  %  Elimination
  %---------------

  divides_prod_elim1 : LEMMA
     divides(nz * n, nz * m) IFF divides(n, m)

  divides_prod_elim2 : LEMMA
     divides(n* nz, m * nz) IFF divides(n, m)

  %------------------------------------
  %  Reflexivity and transitivity
  %------------------------------------

  divides_reflexive: LEMMA divides(n, n)

  divides_transitive: LEMMA
     divides(n, m) AND divides(m, l) IMPLIES divides(n, l)

  %-----------------------------------------
  %  Mutual divisors are equal or opposite
  %-----------------------------------------

  product_one : LEMMA 
     x * y = 1 IFF (x = 1 AND y = 1) OR (x = -1 AND y = -1)

  mutual_divisors : LEMMA 
     divides(n, m) AND divides(m, n) IMPLIES n = m OR n = - m

  mutual_divisors_nat: LEMMA
     divides(p, q) AND divides(q, p) IMPLIES p = q
  
  %--------------------------------------
  %  special properties of zero and one
  %--------------------------------------

  one_divides : LEMMA divides(1, n)

  divides_zero : LEMMA divides(n, 0)

  zero_div_zero : LEMMA divides(0, n) IFF n = 0

  divisors_of_one : LEMMA divides(n, 1) IFF n = 1 OR n = -1

  one_div_one : LEMMA divides(p, 1) IFF p = 1

  %--------------------------------
  %  comparisons divisor/multiple
  %--------------------------------

  divisor_smaller: LEMMA divides(p, q) IMPLIES q=0 OR p <= q

  divides_next: LEMMA divides(n, n + 1) IFF n = 1 OR n = -1

  p1: VAR above(1)
  divides_plus_1: LEMMA
    divides(p1, nz) => NOT divides(p1, nz + 1)
  
 END divides


% Modulo arithmetic defines rem and associated properties
% Provided by Bruno Dutertre
modulo_arithmetic : THEORY
 BEGIN
  x, y, z, t, q, i : VAR int
  n0x : VAR nzint
  px, py, b: VAR posnat
  n: VAR nat

  %---------------------
  %  Remainder of x/b
  %---------------------

  rem(b)(x): {r: mod(b) | EXISTS q: x = b * q + r}

  rem_def: LEMMA
     FORALL (r : mod(b)): rem(b)(x) = r IFF EXISTS q: x = b * q + r

  rem_def2: LEMMA
     FORALL (r : mod(b)): rem(b)(x) = r IFF divides(b, x - r)

  rem_def3: LEMMA
     FORALL (r : mod(b)): rem(b)(x) = r IFF divides(b, r - x)


  %------------------------
  %  Remainder for easy x
  %------------------------

  rem_mod: LEMMA FORALL (r : mod(b)): rem(b)(r) = r

  rem_mod2: LEMMA 0 <= x AND x < b IMPLIES rem(b)(x) = x

  rem_zero: LEMMA rem(b)(0) = 0

  rem_self: LEMMA rem(b)(b) = 0

  rem_multiple1: LEMMA rem(b)(b * x) = 0

  rem_multiple2: LEMMA rem(b)(x * b) = 0

  rem_one: LEMMA b /= 1 IMPLIES rem(b)(1) = 1

  rem_minus_one: LEMMA rem(b)(-1) = b-1


  %-------------------------
  %  Congruence properties
  %-------------------------

  same_remainder: LEMMA
     rem(b)(x) = rem(b)(y) IFF divides(b, x - y)


  rem_rem: LEMMA rem(b)(rem(b)(x)) = rem(b)(x)


  rem_sum: LEMMA
     rem(b)(x) = rem(b)(y) AND rem(b)(z) = rem(b)(t) 
        IMPLIES rem(b)(x + z) = rem(b)(y + t)

  rem_sum1: LEMMA rem(b)(rem(b)(x) + y) = rem(b)(x + y)

  rem_sum2: LEMMA rem(b)(x + rem(b)(y)) = rem(b)(x + y)


  rem_diff: LEMMA
     rem(b)(x) = rem(b)(y) AND rem(b)(z) = rem(b)(t)
        IMPLIES rem(b)(x - z) = rem(b)(y - t)

  rem_diff1: LEMMA rem(b)(rem(b)(x) - y) = rem(b)(x - y)

  rem_diff2: LEMMA rem(b)(x - rem(b)(y)) = rem(b)(x - y)



  rem_prod1: LEMMA rem(b)(rem(b)(x) * y) = rem(b)(x * y)

  rem_prod2: LEMMA rem(b)(x * rem(b)(y)) = rem(b)(x * y)

  rem_prod: LEMMA
     rem(b)(x) = rem(b)(y) AND rem(b)(z) = rem(b)(t)
        IMPLIES rem(b)(x * z) = rem(b)(y * t)


  rem_expt: LEMMA
      rem(b)(x) = rem(b)(y) IMPLIES rem(b)(expt(x, n)) = rem(b)(expt(y, n))

  rem_expt1: LEMMA rem(b)(expt(rem(b)(x), n)) = rem(b)(expt(x, n))



  rem_sum_elim1: LEMMA
     rem(b)(x + y) = rem(b)(x + z) IFF rem(b)(y) = rem(b)(z)

  rem_sum_elim2: LEMMA
     rem(b)(y + x) = rem(b)(z + x) IFF rem(b)(y) = rem(b)(z)

  rem_diff_elim1: LEMMA
     rem(b)(x - y) = rem(b)(x - z) IFF rem(b)(y) = rem(b)(z)

  rem_diff_elim2: LEMMA
     rem(b)(y - x) = rem(b)(z - x) IFF rem(b)(y) = rem(b)(z)

  rem_opposite_elim: LEMMA
     rem(b)(- x) = rem(b)(- y) IFF rem(b)(x) = rem(b)(y)

  % Provide a name for the function asserted to exist by euclid_int
  ndiv(x,b) : {q: int | x = b * q + rem(b)(x)}

  ndiv_lt: LEMMA ndiv(x,b) <= x/b

  JUDGEMENT ndiv(n,b) HAS_TYPE upto(n)

  rem_floor: LEMMA FORALL b, x: x = rem(b)(x) + b * floor(x / b)

  rem_base: LEMMA
    FORALL b, x, i, n:
      rem(b)(x) = rem(b + n)(x + i) IFF divides(b + n, i - n * floor(x / b))

  rem_sum_floor: LEMMA
    FORALL b, x, i:
      rem(b)(x + i) = rem(b)(x) + i - b * floor((rem(b)(x) + i) / b)

  rem_sum_assoc: COROLLARY
    FORALL b, x, n: rem(b)(x + n) = rem(b)(x) + n IFF rem(b)(x) < b - n

  rem_add_one: LEMMA
    FORALL b, x:
      rem(b)(x + 1) = rem(b)(x) + 1 OR
       (rem(b)(x) = b - 1 AND rem(b)(x + 1) = 0)

  rem_wrap: LEMMA
    FORALL b, x, (n: below(b)):
      rem(b)(x) < rem(b)(x + n) IFF rem(b)(x) < b - n AND n > 0

  rem_wrap_eq: COROLLARY
    FORALL b, x, (n: below(b)):
      rem(b)(x) <= rem(b)(x + n) IFF rem(b)(x) < b - n OR divides(b, n)

 END modulo_arithmetic


% subrange_induction defines induction schemas for subranges.  This is a
% parameterized theory so that it may be used as a name to the prover
% induction commands.  subrange_induction is the weak form, and
% SUBRANGE_induction is the strong form of induction.

subrange_inductions[i: int, j: upfrom(i)]: THEORY
 BEGIN
  k, m: VAR subrange(i, j)
  p: VAR pred[subrange(i, j)]

  subrange_induction: LEMMA
    (p(i) AND (FORALL k: k < j AND p(k) IMPLIES p(k + 1)))
        IMPLIES (FORALL k: p(k))

  SUBRANGE_induction: LEMMA
    (FORALL k: (FORALL m: m < k IMPLIES p(m)) IMPLIES p(k))
      IMPLIES (FORALL k: p(k))

 END subrange_inductions


% bounded_int_inductions provides the weak and strong forms of induction for
% the various bounded subtypes of int.

bounded_int_inductions[m: int]: THEORY
 BEGIN
  pf: VAR pred[upfrom(m)]
  jf, kf: VAR upfrom(m)

  upfrom_induction: LEMMA
    (pf(m) AND (FORALL jf: pf(jf) IMPLIES pf(jf + 1)))
       IMPLIES (FORALL jf: pf(jf))

  UPFROM_induction: LEMMA
    (FORALL jf: (FORALL kf: kf < jf IMPLIES pf(kf)) IMPLIES pf(jf))
      IMPLIES (FORALL jf: pf(jf))

  pa: VAR pred[above(m)]
  ja, ka: VAR above(m)

  above_induction: LEMMA
    (pa(m+1) AND (FORALL ja: pa(ja) IMPLIES pa(ja + 1)))
      IMPLIES (FORALL ja: pa(ja))

  ABOVE_induction: LEMMA
    (FORALL ja: (FORALL ka: ka < ja IMPLIES pa(ka)) IMPLIES pa(ja))
      IMPLIES (FORALL ja: pa(ja))

 END bounded_int_inductions


% bounded_nat_inductions provides the weak and strong forms of induction for
% the various bounded subtypes of nat.

bounded_nat_inductions[m: nat]: THEORY
 BEGIN
  pt: VAR pred[upto(m)]
  jt, kt: VAR upto(m)

  upto_induction: LEMMA
    (pt(0) AND (FORALL jt: jt < m AND pt(jt) IMPLIES pt(jt + 1)))
      IMPLIES (FORALL jt: pt(jt))

  UPTO_induction: LEMMA
    (FORALL jt: (FORALL kt: kt < jt IMPLIES pt(kt)) IMPLIES pt(jt))
      IMPLIES (FORALL jt: pt(jt))

  pb: VAR pred[below(m)]
  jb, kb: VAR below(m)

  below_induction: LEMMA
    ((m > 0 IMPLIES pb(0))
        AND (FORALL jb: jb < m - 1 AND pb(jb) IMPLIES pb(jb + 1)))
      IMPLIES (FORALL jb: pb(jb))

  BELOW_induction: LEMMA
    (FORALL jb: (FORALL kb: kb < jb IMPLIES pb(kb)) IMPLIES pb(jb))
      IMPLIES (FORALL jb: pb(jb))

 END bounded_nat_inductions


% subrange_type

subrange_type[m, n: int] : THEORY
 BEGIN
  subrange: TYPE = subrange(m, n)
 END subrange_type


% int_types defines some useful subtypes of the integers for
% backward compatibility.

int_types[m: int] : THEORY
 BEGIN
  upfrom: NONEMPTY_TYPE = upfrom(m)
  above:  NONEMPTY_TYPE = above(m)
 END int_types


% nat_types defines some useful subtypes of the natural numbers for
% backward compatibility.

nat_types[m: nat] : THEORY
 BEGIN
  upto:   NONEMPTY_TYPE = upto(m)
  below:  TYPE = below(m)
 END nat_types


nat_fun_props : THEORY
%------------------------------------------------------------------------
%      Author:  Bruno Dutertre 
%
%      Special properties of injective/surgective functions over nats
%------------------------------------------------------------------------

BEGIN

  n, m : VAR nat

  injection_n_to_m: THEOREM
	(EXISTS (f: [below(n) -> below(m)]): injective?(f)) IMPLIES n <= m

  injection_n_to_m_var: THEOREM
	(EXISTS (f: [below(n) -> below(m)]): injective?(f)) IFF n <= m

  surjection_n_to_m: THEOREM
	(EXISTS (f: [below(n) -> below(m)]): surjective?(f)) IMPLIES m <= n

  surjection_n_to_m_var: THEOREM
	(EXISTS (f: [below(n) -> below(m)]): surjective?(f)) IFF 
	    (m > 0 AND m <= n) OR (m = 0 AND n = 0)

  bijection_n_to_m: THEOREM
	(EXISTS (f: [below(n) -> below(m)]): bijective?(f)) IFF n = m


  injection_n_to_m2: THEOREM
	(EXISTS (f: [upto(n) -> upto(m)]): injective?(f)) IFF n <= m

  surjection_n_to_m2: THEOREM
	(EXISTS (f: [upto(n) -> upto(m)]): surjective?(f)) IFF m <= n

  bijection_n_to_m2: THEOREM
	(EXISTS (f: [upto(n) -> upto(m)]): bijective?(f)) IFF n = m


  surj_equiv_inj: THEOREM
	FORALL (f: [below(n) -> below(n)]): surjective?(f) IFF injective?(f)
	
  inj_equiv_bij: THEOREM
	FORALL (f: [below(n) -> below(n)]): bijective?(f) IFF injective?(f)

  surj_equiv_bij: THEOREM
	FORALL (f: [below(n) -> below(n)]): bijective?(f) IFF surjective?(f)


  surj_equiv_inj2: THEOREM
	FORALL (f: [upto(n) -> upto(n)]): surjective?(f) IFF injective?(f)
	
  inj_equiv_bij2: THEOREM
	FORALL (f: [upto(n) -> upto(n)]): bijective?(f) IFF injective?(f)

  surj_equiv_bij2: THEOREM
	FORALL (f: [upto(n) -> upto(n)]): bijective?(f) IFF surjective?(f)

END nat_fun_props


%------------------------------------------------------------------------
%
%  finite sets (basic definitions and closure properties)
%  -----------------------------------------------------
%      Authors:  Damir Jamsek
%                Ricky W. Butler
%                Sam Owre
%                C. Michael Holloway
%
%  This theory defines finite sets as the subtype of sets[T] that
%  satisfy the predicate "is_finite".  This predicate states
%  that there is an injective function into below[N] for some N.
%
%------------------------------------------------------------------------
%
% finite_sets_def, card_def, and finite_sets are combined here to make
% things a bit more backward compatible.  These were all originally
% part of the finite_sets library.

finite_sets[T: TYPE]: THEORY
% beginning of original finite_sets_def theory
 BEGIN
  x, y, z: VAR T
  s: VAR set[T]
  N: VAR nat

  is_finite(s): bool = (EXISTS N, (f: [(s) -> below[N]]):
                          injective?(f)) 

  finite_set: TYPE = (is_finite) CONTAINING emptyset[T]

  non_empty_finite_set: TYPE = {s: finite_set | NOT empty?(s)}

  is_finite_surj: LEMMA (EXISTS (N: nat), (f: [below[N] -> (s)]):
                            surjective?(f)) IFF is_finite(s) 


  A, B: VAR finite_set
  NA, NB: VAR non_empty_finite_set

  finite_subset:       LEMMA subset?(s,A) IMPLIES is_finite(s)
  finite_intersection: LEMMA is_finite(intersection(A,B))
  finite_add:          LEMMA is_finite(add(x,A))

  % -------- Judgements --------------------------------------------------

  nonempty_finite_is_nonempty: JUDGEMENT
    non_empty_finite_set SUBTYPE_OF (nonempty?[T])

  finite_singleton: JUDGEMENT singleton(x) HAS_TYPE finite_set

  finite_union: JUDGEMENT union(A, B) HAS_TYPE finite_set
  finite_intersection1: JUDGEMENT intersection(s, A) HAS_TYPE finite_set
  finite_intersection2: JUDGEMENT intersection(A, s) HAS_TYPE finite_set
  finite_difference: JUDGEMENT difference(A, s) HAS_TYPE finite_set

  nonempty_finite_union1: JUDGEMENT union(NA, B) HAS_TYPE non_empty_finite_set

  nonempty_finite_union2: JUDGEMENT union(A, NB) HAS_TYPE non_empty_finite_set

  nonempty_add_finite: JUDGEMENT add(x, A) HAS_TYPE non_empty_finite_set

  finite_remove: JUDGEMENT remove(x, A) HAS_TYPE finite_set

  finite_rest: JUDGEMENT rest(A) HAS_TYPE finite_set

  finite_emptyset: JUDGEMENT emptyset HAS_TYPE finite_set

  nonempty_singleton_finite: JUDGEMENT
   singleton(x) HAS_TYPE non_empty_finite_set


  % -------- Base type is finite -----------------------------------------

  is_finite_type: bool = (EXISTS N, (g:[T -> below[N]]): injective?(g))

  finite_full:         LEMMA is_finite_type IFF is_finite(fullset[T])
  finite_type_set:     LEMMA is_finite_type IMPLIES is_finite(s)
  finite_complement:   LEMMA is_finite_type IMPLIES is_finite(complement(s))

% end of original finite_sets_def

% beginning of original card_def theory

%------------------------------------------------------------------------
%
%  Fundamental definition of card
%
%      Authors:  Bruno Dutertre 
%                Ricky W. Butler
%------------------------------------------------------------------------

  S, S2: VAR finite_set
  n, m: VAR nat
  p : VAR posnat

  inj_set(S): (nonempty?[nat]) = 
                 { n | EXISTS (f : [(S)->below[n]]) : injective?(f) }

  Card(S): nat = min(inj_set(S))

  inj_Card    : LEMMA Card(S) = n IMPLIES 
                          (EXISTS (f : [(S) -> below[n]]) : injective?(f))

  reduce_inj  : LEMMA (FORALL (f : [(S)->below[p]]) :
                          injective?(f) AND NOT surjective?(f) IMPLIES 
                              (EXISTS (g: [(S)->below[p-1]]): injective?(g)))

  Card_injection: LEMMA (EXISTS (f : [(S)->below[n]]) : injective?(f)) 
                            IMPLIES Card(S) <= n
  
  Card_surjection : LEMMA (EXISTS (f : [(S)->below[n]]) : surjective?(f)) 
                              IMPLIES n <= Card(S) 
        
  Card_bijection : THEOREM
        Card(S) = n IFF (EXISTS (f : [(S)->below[n]]) : bijective?(f))

  Card_disj_union: THEOREM disjoint?(S,S2) IMPLIES 
                              Card(union(S,S2)) = Card(S) + Card(S2)

% ------------------------------------------------------------------------
  card(S): {n: nat| n = Card(S)}               % inhibit expansion

  card_def        : THEOREM card(S) = Card(S)  % But if you really need to

% end of original card_def theory

% beginning of original finite_sets theory 

  card_emptyset   : THEOREM card(emptyset[T]) = 0

  empty_card      : THEOREM empty?(S) IFF card(S) = 0

  card_empty?     : THEOREM (card(S) = 0) = empty?(S)

  card_is_0       : THEOREM (card(S) = 0) = (S = emptyset)

  nonempty_card   : THEOREM nonempty?(S) IFF card(S) > 0

  card_singleton  : THEOREM card(singleton(x)) = 1

  card_one        : THEOREM card(S) = 1 IFF (EXISTS x : S = singleton(x))

  card_disj_union : THEOREM disjoint?(A,B) IMPLIES 
                              card(union(A,B)) = card(A) + card(B)

  card_diff_subset: THEOREM subset?(A, B) IMPLIES 
                                  card(difference(B, A)) = card(B) - card(A)

  card_subset     : THEOREM subset?(A,B) IMPLIES card(A) <= card(B)

  card_plus       : THEOREM card(A) + card(B) = 
                                card(union(A, B)) + card(intersection(A,B))  

  card_union      : THEOREM card(union(A,B)) = card(A) + card(B) -
                                               card(intersection(A,B))

  card_add        : THEOREM card(add(x, S)) = card(S) 
                                                   + IF S(x) THEN 0 ELSE 1 ENDIF
  card_add_gt0    : THEOREM card(add(x,S)) > 0

  card_remove     : THEOREM card(remove(x, S)) = card(S)
                                                   - IF S(x) THEN 1 ELSE 0 ENDIF

  card_rest       : THEOREM NOT empty?(S) IMPLIES card(rest(S)) = card(S) - 1

  same_card_subset: THEOREM subset?(A, B) AND card(A) = card(B) 
                               IMPLIES A = B

  smaller_card_subset : THEOREM subset?(A, B) AND card(A) < card(B) IMPLIES
                                 (EXISTS x : member(x, B) AND NOT member(x, A))

  card_strict_subset: THEOREM strict_subset?(A,B) IMPLIES card(A) < card(B)

  card_1_has_1    : THEOREM card(S) >= 1 IMPLIES (EXISTS (x: T): S(x))

  card_2_has_2    : THEOREM card(S) >= 2 IMPLIES
                              (EXISTS (x,y: T): x /= y AND S(x) AND S(y))

  card_intersection_le: THEOREM card(intersection(A,B)) <= card(A) AND
                              card(intersection(A,B)) <= card(B)

  card_bij        : THEOREM card(S) = N
                             IFF (EXISTS (f: [(S) -> below[N]]): bijective?(f))

  card_bij_inv    : THEOREM card(S) = N
                             IFF (EXISTS (f: [below[N] -> (S)]): bijective?(f))

  bij_exists      : COROLLARY (EXISTS (f: [(S) -> below(card(S))]):
                                       bijective?(f))

  bij(S: finite_set): {f:[(S) -> below(card(S))] | bijective?(f)}

  ibij(S: non_empty_finite_set): {f:[below(card(S)) -> (S)] | bijective?(f)} =
       inverse(bij(S))

  bij_ibij: LEMMA FORALL (S: non_empty_finite_set, ii: below(card(S))):
      bij(S)(ibij(S)(ii)) = ii

  ibij_bij: LEMMA FORALL (S: non_empty_finite_set, x:T):
      S(x) IMPLIES ibij(S)(bij(S)(x)) = x

  is_finite_exists_N: LEMMA FORALL (g: [below[N] -> T]):
            is_finite({r: T | EXISTS (n: below[N]): r = g(n)})

%  card_n_has_n    : THEOREM card(S) >= n IMPLIES
%                              (EXISTS (X: [below[N] -> T]):
%                                   (FORALL (i: below[N]): S(X(i))) AND
%                                   (FORALL (i,j: below[N]): X(i) /= X(j)))

  P, P1, P2: VAR pred[T]

  finite_pred: LEMMA is_finite(fullset[T]) IMPLIES
                        is_finite[T]({x: T | P(x)})

  finite_pred2:  LEMMA is_finite(P) IMPLIES
                         is_finite[T]({x: T | P(x)})

  card_implies: LEMMA is_finite(fullset[T]) AND
                      (FORALL (x: T): P1(x) IMPLIES P2(x)) 
             IMPLIES card({x: T | P1(x)}) <= card({x: T | P2(x)})

  finite_induction: THEOREM
    FORALL (p: pred[set[T]]):
      (FORALL (n: nat), (S: set[T]):
         (EXISTS (f: [(S) -> below[n]]): injective?(f)) => p(S))
       => (FORALL (FS: finite_set): p(FS))
               
END finite_sets


% Courtesy Jerry James
restrict_set_props[T: TYPE, S: TYPE FROM T]: THEORY
 BEGIN

  restrict_finite: LEMMA
    FORALL (a: set[T]): is_finite(a) => is_finite(restrict[T, S, bool](a))

  finite_restrict: JUDGEMENT
    restrict[T, S, bool](a: finite_set[T]) HAS_TYPE finite_set[S]

  empty_restrict: JUDGEMENT
    restrict[T, S, bool](a: (empty?[T])) HAS_TYPE (empty?[S])

  card_restrict: LEMMA
    FORALL (a: finite_set[T]): card(restrict[T, S, bool](a)) <= card(a)

 END restrict_set_props


% Courtesy Jerry James
extend_set_props[T: TYPE, S: TYPE FROM T]: THEORY
 BEGIN

  finite_extension: LEMMA
    FORALL (a: set[S]):
      is_finite(extend[T, S, bool, FALSE](a)) IFF is_finite(a)

  finite_extend: JUDGEMENT
    extend[T, S, bool, FALSE](a: finite_set[S]) HAS_TYPE finite_set[T]

  empty_extend: JUDGEMENT
    extend[T, S, bool, FALSE](a: (empty?[S])) HAS_TYPE (empty?[T])

  nonempty_extend: JUDGEMENT
    extend[T, S, bool, FALSE](a: (nonempty?[S])) HAS_TYPE (nonempty?[T])

  singleton_extend: JUDGEMENT
    extend[T, S, bool, FALSE](a: (singleton?[S])) HAS_TYPE (singleton?[T])

  card_extend: LEMMA
    FORALL (a: finite_set[S]): card(extend[T, S, bool, FALSE](a)) = card(a)

  empty?_extend: LEMMA
    FORALL (a: set[S]): 
      empty?(extend[T, S, bool, FALSE](a)) IFF empty?(a)

  nonempty?_extend: LEMMA
    FORALL (a: set[S]): 
      nonempty?(extend[T, S, bool, FALSE](a)) IFF nonempty?(a)

  singleton?_extend: LEMMA
    FORALL (a: set[S]): 
      singleton?(extend[T, S, bool, FALSE](a)) IFF singleton?(a)

  subset_extend: LEMMA
    FORALL (a, b: set[S]): 
      subset?(extend[T, S, bool, FALSE](a), extend[T, S, bool, FALSE](b)) IFF
         subset?(a, b)

  union_extend: LEMMA
    FORALL (a, b: set[S]): 
      union(extend[T, S, bool, FALSE](a), extend[T, S, bool, FALSE](b))
        = extend[T, S, bool, FALSE](union(a, b))

  intersection_extend: LEMMA
    FORALL (a, b: set[S]): 
      intersection(extend[T, S, bool, FALSE](a), extend[T, S, bool, FALSE](b))
        = extend[T, S, bool, FALSE](intersection(a, b))

  difference_extend: LEMMA
    FORALL (a, b: set[S]): 
      difference(extend[T, S, bool, FALSE](a), extend[T, S, bool, FALSE](b))
        = extend[T, S, bool, FALSE](difference(a, b))

  add_extend: LEMMA
    FORALL (x: S, a: set[S]): 
      add(x, extend[T, S, bool, FALSE](a))
        = extend[T, S, bool, FALSE](add(x, a))

  remove_extend: LEMMA
    FORALL (x: S, a: set[S]): 
      remove(x, extend[T, S, bool, FALSE](a))
        = extend[T, S, bool, FALSE](remove(x, a))

 END extend_set_props


%-------------------------------------------------------------------------
%
%  Additions to the function_image theory.  These additions
%  show that the image of a finite set is finite, and that any injective
%  function is bijective with its image.
%
%  ---------------------------------------------------------------------
%      Author: Jerry James (jamesj@acm.org), University of Kansas
%-------------------------------------------------------------------------
function_image_aux[D: TYPE, R: TYPE]: THEORY
 BEGIN

  S: VAR finite_set[D]
  f: VAR [D -> R]
  inj: VAR (injective?[D, R])

  finite_image: JUDGEMENT image(f, S) HAS_TYPE finite_set[R]

  card_image: LEMMA FORALL f, S: card(image(f, S)) <= card(S)

  card_injective_image: LEMMA FORALL inj, S: card(image(inj, S)) = card(S)

  bijective_image: LEMMA
    FORALL inj: bijective?[D, (image(inj, fullset[D]))](inj)

 END function_image_aux


% function_iterate provides the iterate function, which composes a
% function with itself a specified number of times.  The function must
% have the same domain and range.  Two useful properties of iterate are
% also provided.

function_iterate[T: TYPE]: THEORY
 BEGIN
  f: VAR [T -> T]
  m, n: VAR nat
  x: VAR T

  iterate(f, n)(x): RECURSIVE T =
    IF n = 0 THEN x ELSE f(iterate(f, n-1)(x)) ENDIF
    MEASURE n

  iterate_add: LEMMA
    iterate(f, m) o iterate(f, n) = iterate(f, m + n)

  iterate_add_applied: LEMMA 
    iterate(f,m)(iterate(f, n)(x)) = iterate(f, m + n)(x)

  iterate_add_one: LEMMA 
    iterate(f, n)(f(x)) = iterate(f, n+1)(x)

  iterate_mult: LEMMA
    iterate(iterate(f, m), n) = iterate(f, m * n)

  iterate_invariant: LEMMA
    f(iterate(f, n)(x)) = iterate(f, n)(f(x))
  
 END function_iterate


% sequences provides the polymorphic sequence type, as a function from
% nat to the base type.  The usual sequence functions are also provided.
% Note that these are infinite sequences, and do not contain finite
% sequences as a subtype.

sequences[T: TYPE]: THEORY
 BEGIN

  sequence: TYPE = [nat->T]
  i, n: VAR nat
  x: VAR T
  p: VAR pred[T]
  seq: VAR sequence
  Trel: VAR PRED[[T, T]]

  nth(seq, n): T = seq(n)

  suffix(seq, n): sequence =
    (LAMBDA i: seq(i+n))

  first(seq): T = nth(seq, 0)

  rest(seq): sequence = suffix(seq, 1)

  delete(n, seq): sequence =
    (LAMBDA i: (IF i < n THEN seq(i) ELSE seq(i + 1) ENDIF))

  insert(x, n, seq): sequence =
    (LAMBDA i: (IF i < n THEN seq(i)
                ELSIF i = n THEN x
                ELSE seq(i - 1) ENDIF))

  add(x, seq): sequence = insert(x, 0, seq)

  insert_delete: LEMMA insert(nth(seq, n), n, delete(n, seq)) = seq

  add_first_rest: LEMMA add(first(seq), rest(seq)) = seq

  every(p)(seq): bool = (FORALL n: p(nth(seq, n)))

  every(p, seq): bool = (FORALL n: p(nth(seq, n)))

  some(p)(seq): bool = (EXISTS n: p(nth(seq, n)))

  some(p, seq): bool = (EXISTS n: p(nth(seq, n)))

  sequence_induction: LEMMA
    p(nth(seq, 0)) AND (FORALL n: p(nth(seq, n)) IMPLIES p(nth(seq, n + 1)))
      IMPLIES every(p)(seq)

  ascends?(seq, Trel): bool = preserves(seq, (LAMBDA i, n: i <= n), Trel)

  descends?(seq, Trel): bool = inverts(seq, (LAMBDA i, n: i <= n), Trel)

 END sequences


% seq_functions defines the map function that generates the image of a
% sequence under a function.

seq_functions[D, R: TYPE]: THEORY
 BEGIN
  f: VAR [D -> R]
  s: VAR sequence[D]
  n: VAR nat

  map(f)(s): sequence[R] = (LAMBDA n: f(nth(s, n)))

  map(f, s): sequence[R] = (LAMBDA n: f(nth(s, n)))

 END seq_functions


% This theory defines finite sequences as a dependent type.  Two finite
% sequences are concatenated with the operator 'o', and a subsequence can be
% extracted with the operator '^'.  Note that ^ allows any natural numbers
% m and n to define the range; if m > n then the empty sequence is returned

finite_sequences [T: TYPE]: THEORY
 BEGIN
  finite_sequence: TYPE = [# length: nat, seq: [below[length] -> T] #]
  finseq: TYPE = finite_sequence

  fs, fs1, fs2, fs3: VAR finseq
  m, n: VAR nat

  empty_seq: finseq =
    (# length := 0,
       seq := (LAMBDA (x: below[0]): epsilon! (t:T): true) #)

  finseq_appl(fs): [below[length(fs)] -> T] = fs`seq;

  CONVERSION finseq_appl;

  o(fs1, fs2): finseq =
     LET l1 = fs1`length,
         lsum = l1 + fs2`length
      IN (# length := lsum,
            seq := (LAMBDA (n:below[lsum]):
                      IF n < l1
                         THEN fs1`seq(n)
                         ELSE fs2`seq(n-l1)
                      ENDIF) #);

  p: VAR [nat, nat]

  ^(fs, p): finseq =
    LET (m, n) = p
     IN IF m > n OR m >= fs`length
        THEN empty_seq
        ELSE LET len = min(n - m + 1, fs`length - m)
              IN (# length := len,
                    seq := (LAMBDA (x: below[len]): fs`seq(x + m)) #)
        ENDIF;

  % This is the same as above, but returns domain [m..n-1] rather than [m..n]
  % More generally useful, but the above is kept for backward compatibility
  ^^(fs, p): finseq =
    LET (m, n) = p
     IN IF m >= n OR m >= fs`length
        THEN empty_seq
        ELSE LET len = min(n - m, fs`length - m)
              IN (# length := len,
                    seq := (LAMBDA (x: below[len]): fs`seq(x + m)) #)
        ENDIF

  extract1(fs:{fs | fs`length = 1}): T = fs`seq(0)

  CONVERSION extract1

  o_assoc: LEMMA fs1 o (fs2 o fs3) = (fs1 o fs2) o fs3

 END finite_sequences


more_finseq [T: TYPE]: THEORY
  BEGIN
   seq: TYPE = finseq[T]

   rr, ss, tt: VAR seq

   x, y, z: VAR T

   prefix?(rr, ss): bool =
     rr`length <= ss`length AND
       (FORALL (i: below(rr`length)): rr`seq(i) = ss`seq(i))

   prefix_closed?(X : set[seq]): bool =
     FORALL ss: X(ss) IMPLIES (FORALL (rr | prefix?(rr, ss)): X(rr))

   add(x, rr): finseq[T] =
     rr WITH [`length := rr`length + 1,
              `seq(rr`length) := x]

  END more_finseq


% ordstruct provides the building blocks for defining the
% ordinals up to epsilon_0.

ordstruct: DATATYPE
 BEGIN
  zero: zero?
  add(coef: posnat, exp: ordstruct, rest: ordstruct): nonzero?
 END ordstruct


% ordinals uses the ordstruct datatype to define the ordinals up to
% epsilon_0, by providing an ordering on ordstruct and defining the
% ordinals to be those elements of type ordstruct that are in a
% Cantor normal form with respect to the ordering.  Thus
%    add(i, u, v) = (omega^u)*i + v
%    add(i, u, add(j, v, w)) = (omega^u)*i + (omega^v)*j + w, where u>v.  

ordinals: THEORY
 BEGIN
  i, j, k: VAR posnat
  m, n, o: VAR nat
  u, v, w, x, y, z: VAR ordstruct
  size: [ordstruct->nat] = reduce[nat](0, (LAMBDA i, m, n: 1 + m+n));

  <(x, y): RECURSIVE bool =
     CASES x OF
        zero: NOT zero?(y),
        add(i, u, v): CASES y OF
                        zero: FALSE,
                        add(j, z, w): (u<z) OR
                                      (u=z) AND (i<j) OR
                                      (u=z) AND (i=j) AND (v<w)
                      ENDCASES
     ENDCASES
   MEASURE size(x);

  >(x, y): bool = y < x;
  <=(x, y): bool = x < y OR x = y;
  >=(x, y): bool = y < x OR y = x

  ordinal?(x): RECURSIVE bool =
    CASES x OF
      zero: TRUE,
      add(i, u, v): (ordinal?(u) AND ordinal?(v) AND 
                      CASES v OF
                        zero: TRUE,
                        add(k, r, s): r < u
                      ENDCASES)
    ENDCASES
   MEASURE size

  ordinal: NONEMPTY_TYPE = (ordinal?)

  r, s, t: VAR ordinal
  
  ordinal_irreflexive: LEMMA NOT r < r

  ordinal_antisym: LEMMA r < s IMPLIES NOT s < r

  ordinal_antisymmetric: LEMMA r <= s AND s <= r IMPLIES r = s

  ordinal_transitive: LEMMA r < s AND s < t IMPLIES r < t

  ordinal_trichotomy: LEMMA r < s OR r = s OR s < r

  p: VAR pred[ordinal]

  ordinal_induction: AXIOM
    (FORALL r: (FORALL s: s < r IMPLIES p(s)) IMPLIES p(r))
      IMPLIES (FORALL r: p(r))

  well_founded_le: LEMMA
    well_founded?(LAMBDA (r, s: (ordinal?)): r < s)

 END ordinals


% lex2 provides a lexical ordering for pairs of natural numbers.
% This illustrates the use of ordinals.

lex2: THEORY
  BEGIN

  i, j, m, n: VAR nat

  lex2(m, n): ordinal = 
    (IF m=0
       THEN IF n = 0
               THEN zero
               ELSE add(n, zero, zero)
            ENDIF
       ELSIF n = 0 THEN add(m, add(1,zero,zero),zero)
       ELSE add(m, add(1,zero,zero), add(n,zero, zero))
     ENDIF)

  lex2_lt: LEMMA
    lex2(i, j) < lex2(m, n) =
     (i < m OR (i = m AND j < n))

 END lex2


% list provides the datatype definition for lists.

list [T: TYPE]: DATATYPE 
 BEGIN
  null: null?
  cons (car: T, cdr:list):cons?

 END list


% list_props provides the length, append, and reverse functions.
% These could have been defined using the reduce function generated
% for the list datatype, but this is harder to work with in an
% interactive proof.

list_props [T: TYPE]: THEORY
 BEGIN
  l, l1, l2, l3: VAR list[T]
  x: VAR T
  P, Q: VAR PRED[T]

  length(l): RECURSIVE nat =
    CASES l OF
      null: 0,
      cons(x, y): length(y) + 1
    ENDCASES
   MEASURE reduce_nat(0, (LAMBDA (x: T), (n: nat): n + 1))

  member(x, l): RECURSIVE bool =
    CASES l OF
      null: FALSE,
      cons(hd, tl): x = hd OR member(x, tl)
    ENDCASES
   MEASURE length(l)

  member_null: LEMMA member(x, l) IMPLIES NOT null?(l)

  nth(l, (n:below[length(l)])): RECURSIVE T =
    IF n = 0 THEN car(l) ELSE nth(cdr(l), n-1) ENDIF
   MEASURE length(l)

  append(l1, l2): RECURSIVE list[T] =
    CASES l1 OF
      null: l2,
      cons(x, y): cons(x, append(y, l2))
    ENDCASES
    MEASURE length(l1)

  reverse(l): RECURSIVE list[T] =
    CASES l OF
      null: l,
      cons(x, y): append(reverse(y), cons(x, null))
    ENDCASES
    MEASURE length

  append_null: LEMMA append(l, null) = l

  append_assoc: LEMMA
     append(append(l1, l2), l3) = append(l1, append(l2, l3))

  reverse_append: LEMMA
    reverse(append(l1, l2)) = append(reverse(l2), reverse(l1))

  reverse_reverse: LEMMA reverse(reverse(l)) = l

  length_append: LEMMA length(append(l1, l2)) = length(l1) + length(l2)

  length_reverse: LEMMA length(reverse(l)) = length(l)

  a, b, c: VAR T

  list_rep: LEMMA (: a, b, c :) = cons(a, cons(b, cons(c, null)))

  every_append: LEMMA
    every(P)(append(l1, l2)) IFF (every(P)(l1) AND every(P)(l2))

  every_disjunct1: LEMMA
    every(P)(l) IMPLIES every(LAMBDA (x: T): P(x) OR Q(x))(l)

  every_disjunct2: LEMMA
    every(Q)(l) IMPLIES every(LAMBDA (x: T): P(x) OR Q(x))(l)

  every_conjunct: LEMMA
    every(LAMBDA (x: T): P(x) AND Q(x))(l) => (every(P)(l) AND every(Q)(l))

  every_conjunct2: LEMMA
    (every(P)(l) AND every(Q)(l)) => every(LAMBDA (x: T): P(x) AND Q(x))(l)

  every_member: LEMMA every({c: T | member(c, l)})(l)

  every_nth: LEMMA
      every(P)(l) IFF FORALL (i:below(length(l))): P(nth(l,i))

 END list_props


% map_props gives the commutativity properties of composition and map,
% for both sequences and lists.

map_props [T1, T2, T3: TYPE]: THEORY
 BEGIN
  f1: VAR [T1 -> T2]
  f2: VAR [T2 -> T3]
  s: VAR sequence[T1]
  l: VAR list[T1]

  map_list_composition: LEMMA map(f2)(map(f1)(l)) = map(f2 o f1)(l)

  map_seq_composition: LEMMA map(f2)(map(f1)(s)) = map(f2 o f1)(s)

 END map_props

more_map_props [T1, T2: TYPE]: THEORY
 BEGIN
  f: VAR [T1 -> T2]
  l: VAR list[T1]

  map_length: LEMMA length(map(f)(l)) = length(l)

  map_nth_rw: LEMMA
    FORALL (i: below(length(l))):
      nth(map(f)(l), i) = f(nth(l, i))

END more_map_props


% filters defines filter functions for sets and lists, which take a set
% (list) and a predicate and return the set (list) of those elements
% that satisfy the predicate.  Both the curried and uncurried forms are
% given.  Note that filter on lists could be defined using reduce, but
% becomes harder to work with in an interactive proof.

filters[T: TYPE]: THEORY
 BEGIN
  s: VAR set[T]
  l: VAR list[T]
  p: VAR pred[T]
  
  filter(s, p): set[T] = {x: T | s(x) & p(x)}

  filter(p)(s): set[T] = {x: T | s(x) & p(x)}

  filter(l, p): RECURSIVE list[T] =
    CASES l OF
      null: null,
      cons(x, y): IF p(x) THEN cons(x, filter(y, p)) ELSE filter(y, p) ENDIF
    ENDCASES
    MEASURE length(l)

  filter(p)(l): RECURSIVE list[T] =
    CASES l OF
      null: null,
      cons(x, y): IF p(x) THEN cons(x, filter(p)(y)) ELSE filter(p)(y) ENDIF
    ENDCASES
    MEASURE length(l)
 END filters


% list2finseq

list2finseq[T: TYPE]: THEORY
 BEGIN
  l: VAR list[T]
  fs: VAR finseq[T]
  n: VAR nat

  list2finseq(l): finseq[T] =
    (# length := length(l),
       seq := (LAMBDA (x: below[length(l)]): nth(l, x)) #)

  finseq2list_rec(fs, (n: nat | n <= length(fs))): RECURSIVE list[T] =
    IF n = 0
       THEN null
       ELSE cons(fs`seq(length(fs) - n),
                 finseq2list_rec(fs, n-1))
    ENDIF
    MEASURE n

  finseq2list(fs): list[T] = finseq2list_rec(fs, length(fs))

  CONVERSION list2finseq, finseq2list

 END list2finseq


% list2set

list2set[T:TYPE]: THEORY
 BEGIN
  l: VAR list[T]
  x: VAR T

  list2set(l) : RECURSIVE set[T] =
    CASES l OF
      null: emptyset[T],
      cons(x, y): add(x, list2set(y))
    ENDCASES
   MEASURE length

  CONVERSION list2set

 END list2set


% The disjointness theory defines the pairwise_disjoint? function.
% This is used in generating the disjointness axiom for large datatypes
% or enumeration types.  This is because for N constructors, N(N-1)/2
% terms need to be constructed.  Thus care must be taken if the list
% is longer than a few hundred elements.

disjointness: THEORY
 BEGIN
  l: VAR list[bool]
  pairwise_disjoint?(l): RECURSIVE boolean =
    cases l of
      null: true,
      cons(x,y): every(LAMBDA (z:bool): NOT (x AND z))(y)
                  AND pairwise_disjoint?(y)
    endcases
   MEASURE length(l)
 END disjointness


% characters - this follows the ASCII control codes, of which only the
% first 128 are defined:
%
%     |  0 NUL|  1 SOH|  2 STX|  3 ETX|  4 EOT|  5 ENQ|  6 ACK|  7 BEL|
%     |  8 BS |  9 HT | 10 NL | 11 VT | 12 NP | 13 CR | 14 SO | 15 SI |
%     | 16 DLE| 17 DC1| 18 DC2| 19 DC3| 20 DC4| 21 NAK| 22 SYN| 23 ETB|
%     | 24 CAN| 25 EM | 26 SUB| 27 ESC| 28 FS | 29 GS | 30 RS | 31 US |
%     | 32 SP | 33  ! | 34  " | 35  # | 36  $ | 37  % | 38  & | 39  ' |
%     | 40  ( | 41  ) | 42  * | 43  + | 44  , | 45  - | 46  . | 47  / |
%     | 48  0 | 49  1 | 50  2 | 51  3 | 52  4 | 53  5 | 54  6 | 55  7 |
%     | 56  8 | 57  9 | 58  : | 59  ; | 60  < | 61  = | 62  > | 63  ? |
%     | 64  @ | 65  A | 66  B | 67  C | 68  D | 69  E | 70  F | 71  G |
%     | 72  H | 73  I | 74  J | 75  K | 76  L | 77  M | 78  N | 79  O |
%     | 80  P | 81  Q | 82  R | 83  S | 84  T | 85  U | 86  V | 87  W |
%     | 88  X | 89  Y | 90  Z | 91  [ | 92  \ | 93  ] | 94  ^ | 95  _ |
%     | 96  ` | 97  a | 98  b | 99  c |100  d |101  e |102  f |103  g |
%     |104  h |105  i |106  j |107  k |108  l |109  m |110  n |111  o |
%     |112  p |113  q |114  r |115  s |116  t |117  u |118  v |119  w |
%     |120  x |121  y |122  z |123  { |124  | |125  } |126  ~ |127 DEL|

character: DATATYPE
 BEGIN
  char(code:below[256]):char?
 END character


% strings - the strings theory introduces the char type and represents
% strings as a finite sequence of chars.  The lemmas are useful for
% rewriting.  This theory is useful to auto-rewrite with, but make sure
% that list2finseq is not also an auto-rewrite rule.
%
% strings are input as a sequence of characters between double
% quotes.  The \ is an escape character, whose value depends on the
% following character(s).  \" and \\ are used to embed a " and \ in a string.
% A \0 followed by an octal number (< 0400), a \ followed by 
% a decimal number (< 256) or a \x or \X followed by a
% hexidecimal number (< FF) represents that character number. 
% Decimal numbers must be written without leading zeros, as 
% otherwise they will get interpreted as octal (for instance 
% "\065" = "\53" = "\x35"). If the character following such an escape 
% is a digit one has to use the maximal number of digits in the escape 
% (for instance "\00530" = "50" while "\0530" is illegal). Therefore, 
% if one wants to put escape (\27) followed by a 0 into a string one 
% has to use two escapes ("\27\30") or an octal or hex escape 
% ("\00330" = "\x1b0") because \270 is illegal (270 > 255) and 
% \027 is interpreted as octal.
% The other possibilities are:
%  \a  - 007 (C-g, Bell)
%  \b  - 008 (C-h, Backspace)
%  \t  - 009 (C-i, Tab)
%  \n  - 010 (C-j, Newline)
%  \v  - 011 (C-k, VT)
%  \f  - 012 (C-l, Newpage)
%  \r  - 013 (C-m, Return)

strings: THEORY
 BEGIN
  char: TYPE = (char?)
  string: TYPE = finite_sequence[char]

  l1, l2: VAR list[char]

  c1, c2: VAR char

  fseq_lem: LEMMA
    (list2finseq(l1) = list2finseq(l2)) = (l1 = l2)

  cons_lem: LEMMA (cons(c1,l1) = cons(c2,l2)) = (c1 = c2 & l1 = l2)

  char_lem: LEMMA (c1 = c2) = (code(c1) = code(c2))
 END strings


lift[T: TYPE]: DATATYPE
 BEGIN
  bottom: bottom?
  up(down: T): up?
 END lift


union[T1, T2: TYPE]: DATATYPE
 BEGIN
  inl(left: T1): inl?
  inr(right: T2): inr?
 END union


% Defines fixpoints for sets, needed to support the mucalculus model checker.

mucalculus[T:TYPE]: THEORY
 BEGIN

  s: VAR T
  p, p1, p2: VAR pred[T]
  predicate_transformer: TYPE = [pred[T]->pred[T]]
  pt: VAR predicate_transformer
  setofpred: VAR pred[pred[T]]

  <=(p1,p2): bool = FORALL s: p1(s) IMPLIES p2(s)

  monotonic?(pt): bool =
      FORALL p1, p2: p1 <= p2 IMPLIES pt(p1) <= pt(p2)

  pp: VAR (monotonic?)

  fixpoint?(pp, p): bool = (pp(p) = p)

  fixpoint?(pp)(p): bool = fixpoint?(pp, p)

  glb(setofpred): pred[T] =
      LAMBDA s: (FORALL p: member(p,setofpred) IMPLIES p(s))

  % least fixpoint
  lfp(pp): pred[T] = glb({p | pp(p) <= p})

  lfp_induction: FORMULA pp(p) <= p IMPLIES lfp(pp) <= p

  mu(pp): pred[T] = lfp(pp)

  lfp?(pp, p1): bool =
    fixpoint?(pp, p1) AND FORALL p2: fixpoint?(pp,p2) IMPLIES p1 <= p2

  lfp?(pp)(p1): bool = lfp?(pp, p1)

  lub(setofpred): pred[T] = LAMBDA s: EXISTS p: member(p,setofpred) AND p(s)

  % greatest fixpoint
  gfp(pp): pred[T] = lub({p | p <= (pp(p))})

  gfp_induction: FORMULA p <= pp(p) IMPLIES p <= gfp(pp)

  nu(pp): pred[T] = gfp(pp)

  gfp?(pp, p1): bool =
    fixpoint?(pp,p1) AND FORALL p2: fixpoint?(pp,p2) IMPLIES p2 <= p1

  gfp?(pp)(p1): bool = gfp?(pp, p1)

 END mucalculus


% Basic CTL temporal operators (without fairness) defined in mu-calculus

ctlops[state : TYPE]: THEORY
 BEGIN
  u,v,w: VAR state
  f,g,Q,P,p1,p2: VAR pred[state]
  Z: VAR pred[[state, state]]

  N: VAR [state, state -> bool]

  CONVERSION+ K_conversion

  EX(N,f)(u):bool = (EXISTS v: (f(v) AND N(u, v)))

  EG(N,f):pred[state] = nu(LAMBDA Q: (f AND EX(N,Q)))

  EU(N,f,g):pred[state] = mu(LAMBDA Q: (g OR (f AND EX(N,Q))))

  % Other operator definitions
  EF(N,f):pred[state] = EU(N, TRUE, f)
  AX(N,f):pred[state] = NOT EX(N, NOT f)
  AF(N,f):pred[state] = NOT EG(N, NOT f)
  AG(N,f):pred[state] = NOT EF(N, NOT f)
  AU(N,f,g):pred[state] = NOT EU(N, NOT g, (NOT f AND NOT g)) AND AF(N, g)

  CONVERSION- K_conversion
 END ctlops


% Fair versions of CTL operators where
% fairAG(N, f)(Ff)(s) means f always holds along every N-path
% from s along which the fairness predicate Ff holds infinitely
% often.  This is different from the usual linear-time notion of 
% fairness where the strong form asserts that if an action A is 
% enabled infinitely often, it is taken infinitely often, and the 
% weak form asserts that if any action that is continuously enabled 
% is taken infinitely often.  

fairctlops  [ state : TYPE ]: THEORY
 BEGIN
  u,v,w: VAR state
  f,g,Q,P,p1,p2: VAR pred[state]
  N: VAR [state, state->bool]
  Ff: VAR pred[state] % Fair formula

  CONVERSION+ K_conversion

  fairEG(N, f)(Ff): pred[state] =
    nu(LAMBDA P: EU(N, f, f AND Ff AND EX(N, P)))

  fairAF(N,f)(Ff):pred[state]  = NOT fairEG(N, NOT f)(Ff)

  fair?(N,Ff):pred[state] = fairEG(N,LAMBDA u: TRUE)(Ff)

  fairEX(N,f)(Ff):pred[state] = EX(N,f AND fair?(N,Ff))

  fairEU(N,f,g)(Ff):pred[state] = EU(N,f,g AND fair?(N,Ff))

  fairEF(N,f)(Ff):pred[state] = EF(N,f AND fair?(N,Ff))

  fairAX(N,f)(Ff):pred[state] = NOT fairEX(N, NOT f)(Ff)

  fairAG(N,f)(Ff):pred[state] = NOT fairEF(N,NOT f)(Ff)

  fairAU(N,f,g)(Ff):pred[state] =
	NOT fairEU(N,NOT g,NOT f AND NOT g)(Ff)
	    AND fairAF(N,g)(Ff)

  CONVERSION- K_conversion

 END fairctlops


% Fair versions of CTL operators with lists of fairness conditions.
% FairAG(N,f)(Fflist)(s) means f always holds on every N-path from
% s along which each predicate in Fflist holds infinitely often.

Fairctlops[state : TYPE]: THEORY
 BEGIN
  u,v,w: VAR state
  f,g,Q,P,p1,p2: VAR pred[state]
  N: VAR [state, state->bool]
  Fflist, Gflist: VAR list[pred[state]] % Fair formula

  CONVERSION+ K_conversion

  CheckFair(Q, N, f, Fflist): RECURSIVE pred[state] =
    (CASES Fflist OF
     cons(Ff, Gflist):
	     EU(N, f, f AND Ff AND
		      EX(N, CheckFair(Q, N, f, Gflist))),
     null : Q
     ENDCASES)
    MEASURE length(Fflist)

  FairEG(N, f)(Fflist): pred[state] =
    nu(LAMBDA P: CheckFair(P, N, f, Fflist))

  FairAF(N,f)(Fflist):pred[state]  = NOT FairEG(N, NOT f)(Fflist)

  Fair?(N,Fflist):pred[state] = FairEG(N,LAMBDA u: TRUE)(Fflist)

  FairEX(N,f)(Fflist):pred[state] = EX(N,f AND Fair?(N,Fflist))

  FairEU(N,f,g)(Fflist):pred[state] = EU(N,f,g AND Fair?(N,Fflist))

  FairEF(N,f)(Fflist):pred[state] = EF(N,f AND Fair?(N,Fflist))

  FairAX(N,f)(Fflist):pred[state] = NOT FairEX(N, NOT f)(Fflist)

  FairAG(N,f)(Fflist):pred[state] = NOT FairEF(N,NOT f)(Fflist)

  FairAU(N,f,g)(Fflist):pred[state] =
	NOT FairEU(N,NOT g,NOT f AND NOT g)(Fflist)
	    AND FairAF(N,g)(Fflist)

  % Turn off K_conversion so that it is not a conversion by default.
  CONVERSION- K_conversion
 END Fairctlops


bit: THEORY
% -----------------------------------------------------------------------
% The bit theory defines bits and their properties. Bits are interpreted
% as the natural numbers 0 and 1.
% -----------------------------------------------------------------------
BEGIN

  bit  : TYPE = bool
  nbit : TYPE = below(2)  %  could be {n: nat | n = 0 OR n = 1}

  b: VAR bit

  bit_cases: LEMMA b = FALSE OR b = TRUE

  b0: [below(1) -> bit] = (LAMBDA (i: below(1)): FALSE)
  b1: [below(1) -> bit] = (LAMBDA (i: below(1)): TRUE)

  b2n(b:bool)     : nbit = IF b THEN 1 ELSE 0 ENDIF 
  n2b(nb: nbit)   : bool = (nb = 1)

  CONVERSION b2n

END bit


%------------------------------------------------------------------------
%
% Definition of a bitvector
% -----------------------------------------
%
%   Defines:
%  
%       bvec: TYPE = [below(N) -> bit]
%
%       ^(bv, (i: below(N))): bit
%
%   Establishes:
%  
%
%------------------------------------------------------------------------
bv[N: nat]: THEORY
BEGIN

  bvec : TYPE = [below(N) -> bit]

  b: VAR bit
  bv: VAR bvec
  i: VAR below[N]

  bvec0(i): bit = FALSE
  bvec1(i): bit = TRUE
  fill(b)(i): bit = b;

  %-----------------------------------------------------------------------    
  % The implementation of the bitvector can be hidden through use of
  % the ^ function below.
  %-----------------------------------------------------------------------  
    
  ^(bv, i): bit = bv(i)

END bv


exp2: THEORY
BEGIN

  n,m, x1, x2: VAR nat
  
  exp2(n: nat): RECURSIVE posnat = IF n = 0 THEN 1 ELSE 2 * exp2(n - 1) ENDIF
    MEASURE n

  JUDGEMENT exp2(n: nat) HAS_TYPE above(n)

% === Properties of exp2 ===================================================

  exp2_def	 : LEMMA exp2(n) = 2^n
 
  exp2_pos	 : LEMMA exp2(n) > 0
 
  exp2_n	 : LEMMA exp2(n+1) = 2*exp2(n)
 
  exp2_sum	 : LEMMA exp2(n + m) = exp2(n) * exp2(m)
 
  exp2_minus	 : LEMMA (FORALL n,(k:upto(n)): exp2(n-k)=exp2(n)/exp2(k))
 
  exp2_strictpos : LEMMA n > 0 IMPLIES exp2(n) > 1
 
  exp2_lt	 : LEMMA n < m IMPLIES exp2(n) < exp2(m)

  exp_prop       : LEMMA x1 < exp2(n) AND  x2 < exp2(m) 
                           IMPLIES x1 * exp2(m) + x2 < exp2(n + m)

END exp2


% bv_cnv: THEORY

% BEGIN

%   CONVERSION fill[1]

% END bv_cnv


bv_concat_def [n:nat, m:nat ]: THEORY
%------------------------------------------------------------------------
% This theory defines the concatenation operator o for bitvectors:
%
%   o: [bvec[n], bvec[m] -> bvec[n+m]] 
%
% The theory is instantiated with the sizes of the input bit vectors.
%------------------------------------------------------------------------

BEGIN

  o(bvn: bvec[n], bvm: bvec[m]): bvec[n+m] =
       (LAMBDA (nm: below(n+m)): IF nm < m THEN bvm(nm)
                                 ELSE bvn(nm - m)
                                 ENDIF)

END bv_concat_def


bv_bitwise [N: nat] : THEORY
% -----------------------------------------------------------------------
% Defines bit-wise logical operations on bit vectors.
%
%    Introduces:
%       OR : bv1 OR bv2
%       &  : bv1 & bv2
%       IFF: bv1 IFF bv2
%       NOT: NOT bv1
%       XOR: bv1 XOR bv2
% -----------------------------------------------------------------------  
BEGIN

  i: VAR below(N) 
  OR(bv1,bv2: bvec[N]) : bvec[N] = (LAMBDA i: bv1(i) OR bv2(i));
  
  AND(bv1,bv2: bvec[N]): bvec[N] = (LAMBDA i: bv1(i) AND bv2(i)) ;

  IFF(bv1,bv2: bvec[N]): bvec[N] = (LAMBDA i: bv1(i) IFF bv2(i)) ;

  NOT(bv: bvec[N])     : bvec[N] = (LAMBDA i: NOT bv(i)) ;

  XOR(bv1,bv2: bvec[N]): bvec[N] = (LAMBDA i: XOR(bv1(i),bv2(i))) ;

% === The following lemmas can be used to hide the implementation
%     details of the bitvector

  bv, bv1, bv2: VAR bvec[N]

  bv_OR  : LEMMA (bv1 OR bv2)^i = (bv1^i OR bv2^i)

  bv_AND : LEMMA (bv1 AND bv2)^i = (bv1^i AND bv2^i)

  bv_IFF : LEMMA (bv1 IFF bv2)^i = (bv1^i IFF bv2^i)

  bv_XOR : LEMMA XOR(bv1,bv2)^i = XOR(bv1^i,bv2^i)

  bv_NOT : LEMMA (NOT bv)^i = NOT(bv^i)

END bv_bitwise


%------------------------------------------------------------------------
%
% Interpretation of a bitvector as a natural number
% -------------------------------------------------
%
%   Defines:
%  
%       bv2nat: function[bvec[N] -> below(exp2(N))] 
%       nat2bv: function[below(exp2(N)) -> bvec[N]]
%
%   Establishes:
%  
%       bv2nat_bij: THEOREM bijective?(bv2nat)
%       bv2nat_inv: THEOREM bv2nat(nat2bv(val)) = val  
%
%  
%   See note at end of theory for reasons why nat2bv should be avoided
%   in specifications.
%
%------------------------------------------------------------------------
bv_nat[N: nat]: THEORY
BEGIN

% === Interpretation of a bit vector as a natural number ================

  bv2nat_rec(n: upto(N), bv:bvec[N]): RECURSIVE nat =
      IF n = 0 THEN 0
      ELSE exp2(n-1) * bv^(n-1) + bv2nat_rec(n - 1, bv)
      ENDIF
    MEASURE n

  bv_lem: LEMMA FORALL (n: below(N), bv: bvec[N]): bv(n) = FALSE OR bv(n) = TRUE 

  bv2nat_rec_bound: LEMMA FORALL (n: upto(N), bv: bvec[N]): 
                                  bv2nat_rec(n, bv) < exp2(n)

  bv2nat(bv:bvec[N]): below(exp2(N)) = bv2nat_rec(N, bv)

% ===== Properties of bv2nat ===================================

  n           : VAR upto(N)
  val         : VAR below(exp2(N))
  bv, bv1, bv2: VAR bvec[N]

  bv2nat_inj_rec: LEMMA
      bv2nat_rec(n, bv1) = bv2nat_rec(n, bv2)
        <=>  (FORALL (m: below(N)): (m < n) IMPLIES bv1(m) = bv2(m))

  bv2nat_surj_rec: LEMMA FORALL (y:below(exp2(n))): 
                                   EXISTS bv:bv2nat_rec(n, bv) = y

  bv2nat_inj     : THEOREM % injective?(bv2nat)
                            (FORALL (x, y: bvec[N]): (bv2nat(x) = bv2nat(y) 
                                      IMPLIES (x = y)))

  bv2nat_surj    : THEOREM % surjective?(bv2nat)
                              (FORALL (y: below(exp2(N))): 
                                     (EXISTS (x: bvec[N]): bv2nat(x) = y))


  bv2nat_bij        : THEOREM bijective?(bv2nat)    

  bv2nat_rec_fill_F : LEMMA bv2nat_rec(n, fill[N](FALSE)) = 0
  bv2nat_rec_fill_T : LEMMA bv2nat_rec(n, fill[N](TRUE)) = exp2(n)-1

  bv2nat_fill_F     : LEMMA bv2nat(fill[N](FALSE)) = 0
  bv2nat_fill_T     : LEMMA bv2nat(fill[N](TRUE)) = exp2(N)-1

  bv2nat_eq0        : LEMMA bv2nat(bv) = 0     IMPLIES (bv = fill[N](FALSE))
  bv2nat_eq_max     : LEMMA bv2nat(bv) = exp2(N)-1 IMPLIES bv = (fill[N](TRUE)) 

  bv2nat_top_bit    : THEOREM N > 0 IMPLIES
                             IF bv2nat(bv) < exp2(N-1) THEN bv^(N-1) = FALSE
                                                       ELSE bv^(N-1) = TRUE ENDIF

  bv2nat_topbit    : THEOREM N > 0 IMPLIES bv^(N-1) = (bv2nat(bv) >= exp2(N-1))

% =================== Properties of nat2bv ===================================

  nat2bv(val: below(exp2(N))): {bv: bvec[N] | bv2nat(bv) = val}

  nat2bv_def       : LEMMA  nat2bv = inverse(bv2nat)

  nat2bv_bij       : THEOREM bijective?[below(exp2(N)),bvec[N]](nat2bv)

  nat2bv_inv       : THEOREM nat2bv(bv2nat(bv)) = bv

  nat2bv_rew       : LEMMA nat2bv(val) = bv IFF bv2nat(bv) = val

  bv2nat_inv       : THEOREM bv2nat(nat2bv(val)) = val

END bv_nat


empty_bv: THEORY

BEGIN

  empty_bv: [below[0] -> bool] = (LAMBDA (x: below[0]): TRUE) ;

END empty_bv


bv_caret[N: nat]: THEORY
%-----------------------------------------------------------------------
% An extractor operation decomposes a bvec into smaller
% bit vectors.  In the following, we define a number of
% extractors for bvec. 
%
%    Introduces:
%       bv^(m,n) creates bv[m-n+1] from bits m through n
%    
%       bv^^(m,n) can return an empty bitvector if n > m
%  
%-----------------------------------------------------------------------  
BEGIN

  %-----------------------------------------------------------------------  
  % The following operator (^) extracts a contiguous fragment of
  % a bit vector between two given positions.
  %-----------------------------------------------------------------------  

%  ^(bv: bvec[N], p:[m: below(N), upto(m)]): bvec[LET (m, n) = p IN m - n + 1] =
%             LET (m, n) = p IN
%                (LAMBDA  (ii: below(m - n + 1)): bv(ii + n)) ;


   ^(bv: bvec[N], sp:[i1: below(N), upto(i1)]): bvec[proj_1(sp)-proj_2(sp)+1] =
       (LAMBDA  (ii: below(proj_1(sp) - proj_2(sp) + 1)):
		  bv(ii + proj_2(sp))) ;

%  ^^(bv: bvec[N], p:[m:below[N],nat]): bvec[LET (m, n) = p IN 
%                                        IF m < n THEN 0 ELSE m - n + 1 ENDIF] =
%      LET (m, n) = p IN
%        IF m < n THEN empty_bv 
%        ELSE (LAMBDA (ii: below[m-n+1]): bv(ii + n))
%        ENDIF


% === Useful Lemmas and Theorems =========================================

  bv: VAR bvec[N]

  bv_caret_all  : LEMMA N > 0 IMPLIES bv^(N-1, 0) = bv

%    dcaret_lem : LEMMA (FORALL (i: below(N), j: upto(i)): bv^^(i,j) = bv^(i,j))

  bv_caret_ii_0 : LEMMA (FORALL (i: below(N)): bv^(i,i)^0 = bv^i)

  bv_caret_elim : LEMMA (FORALL (i: below(N), j: upto(i), k: below(i-j+1)): 
                               bv ^ (i, j) ^ k = bv^(j+k))

END bv_caret 


mod: THEORY
%------------------------------------------------------------------------
%
%  mod function as defined in `Concrete Mathematics'
%  The Ada definition of mod is consistent with this definition
%
% Author:
%   Paul S. Miner                | email: p.s.miner@larc.nasa.gov
%   1 South Wright St. / MS 130  |   fax: (804) 864-4234
%   NASA Langley Research Center | phone: (804) 864-6201
%   Hampton, Virginia 23681      |
%------------------------------------------------------------------------

  BEGIN

  i,k,cc: VAR int
  m: VAR posnat
  n,a,b,c,x: VAR nat   

  j: VAR nonzero_integer

  ml3: LEMMA abs(i -  m * floor(i/m)) < m
  ml4: LEMMA abs(i +  m * floor(-i/m)) < m

  mod(i,j): {k| abs(k) < abs(j)} = i-j*floor(i/j)

  mod_pos:  LEMMA mod(i,m) >= 0 AND mod(i,m) < m

  JUDGEMENT mod(i:int, m: posnat) HAS_TYPE below(m)


  mod_even   : LEMMA integer_pred(i/j) IMPLIES mod(i,j) = 0

  mod_neg    : LEMMA mod(-i,j) = IF integer_pred(i/j) THEN 0
                                 ELSE j - mod(i,j)
                                 ENDIF

  mod_neg_d  : LEMMA mod(i,-j) = IF integer_pred(i/j) THEN 0
                                 ELSE mod(i,j) - j 
                                 ENDIF

  mod_eq_arg : LEMMA mod(j,j) = 0
  
  mod_lt     : LEMMA abs(i) < abs(j) IMPLIES mod(i,j) = 
                                  IF sgn(i) = sgn(j) OR i = 0 THEN i
                                  ELSE i + j
                                  ENDIF

  mod_lt_nat : LEMMA n < m IMPLIES mod(n,m) = n

  mod_lt_int : LEMMA -m < i AND i < m IMPLIES mod(i,m) = 
                                  IF i >= 0 THEN i ELSE i + m
                                  ENDIF


  mod_sum_pos: LEMMA mod(i+k*m,m) = mod(i,m)

  mod_gt : LEMMA m <= i AND i < 2*m IMPLIES mod(i,m) = i-m 

  mod_sum    : LEMMA mod(i+k*j,j) = mod(i,j)

  mod_sum_nat: LEMMA (FORALL (n1,n2: below(m)):
                          mod(n1+n2,m) = IF n1 + n2 < m THEN n1+n2
                                         ELSE n1 + n2 - m
                                         ENDIF)

  mod_it_is  : LEMMA a = b + m*c AND b < m IMPLIES
                        b = mod(a,m)

  mod_zero   : LEMMA mod(0,j) = 0

  mod_one    : LEMMA mod(1,j) = IF abs(j) = 1 THEN 0
                                   ELSIF j > 0 THEN 1
                                   ELSE j + 1
                                   ENDIF

  mod_of_mod     : LEMMA mod(i + mod(k,m), m) = mod(i+k, m)

  mod_of_mod_neg : LEMMA mod(i - mod(k,m), m) = mod(i-k, m)

  mod_inj_plus       : LEMMA a < m AND n < m AND c < m AND
                       mod(a + n, m) = mod(a + c, m) IMPLIES n = c

  mod_inj_minus       : LEMMA a < m AND n < m AND c < m AND
                        mod(a - n, m) = mod(a - c, m) IMPLIES n = c


  mod_wrap_around: LEMMA n < m AND (c <= m AND c >= m-n) IMPLIES
                              mod(n+c, m) = n - (m-c)

  mod_wrap2:       LEMMA c < m IMPLIES mod(m+c, m) = c

% Injection-like properties for mod for limited ranges

  mod_inj1:        LEMMA x < m AND n < m AND c < m AND
                       mod(x + n, m) = mod(x + c, m) IMPLIES n = c

  mod_inj2:        LEMMA x < m AND n < m AND c < m AND
                        mod(x - n, m) = mod(x - c, m) IMPLIES n = c

  mod_wrap_inj: LEMMA n < m AND a < m AND b < m AND a > 0 AND
                            mod(n + a, m) = mod(n - b, m) IMPLIES a + b = m

  mod_wrap_inj_eq: LEMMA
      x < m AND a < m AND b < m AND a > 0
        IMPLIES (mod(x + a, m) = mod(x - b, m)) = (a + b = m)

  kk, vv: VAR nat
  mod_neg_limited: LEMMA 0 <= kk AND  kk < m AND vv < m AND
                      vv - kk < 0 IMPLIES mod(vv - kk,m) = m + vv - kk


  odd_mod: LEMMA even?(m) => (odd?(mod(i, m)) IFF odd?(i))

  even_mod: LEMMA even?(m) => (even?(mod(i, m)) IFF even?(i))

  mj: VAR posnat
  mod_mult      : LEMMA mod(mod(i,mj*m),m) = mod(i,m)

END mod


%------------------------------------------------------------------------
%
% Natural arithmetic over bitvectors
% -----------------------------------------
%
%   Defines:
%  
%         > :  bv1 > bv2
%         >=:  bv1 >= bv2
%         < :  bv1 < bv2
%         <=:  bv1 <= bv1
%         + :  bv1 + i
%         - :  bv1 - i
%         + :  bv1 + bv2
%         * :  bv1 * bv2
%
%   Establishes:
%   
%     bv_plus    : LEMMA bv2nat(bv + i) = mod(bv2nat(bv) + i, exp2(N))) 
%					       
%     bv_add     : LEMMA bv2nat(bv1 + bv2) =
%			       IF bv2nat(bv1) + bv2nat(bv2) < exp2(N)
%			       THEN bv2nat(bv1) + bv2nat(bv2)
%			       ELSE bv2nat(bv1) + bv2nat(bv2) - exp2(N) ENDIF
%   
%	
%  Note that + has been defined in a manner that avoids the introduction of 
%  nat2bv.  Also nat2bv has been defined in a manner that prevents GRIND from
%  expanding nat2bv into an expression involving "epsilon".  The needed
%  property about nat2bv is available in its type.
% 
%------------------------------------------------------------------------
bv_arith_nat_defs[N: nat]: THEORY
BEGIN

% ----- Definition of inequalities over the bit vectors.

  < (bv1: bvec[N], bv2: bvec[N]): bool = bv2nat(bv1) <  bv2nat(bv2) ;
  <=(bv1: bvec[N], bv2: bvec[N]): bool = bv2nat(bv1) <= bv2nat(bv2) ;

  > (bv1: bvec[N], bv2: bvec[N]): bool = bv2nat(bv1) >  bv2nat(bv2) ;
  >=(bv1: bvec[N], bv2: bvec[N]): bool = bv2nat(bv1) >= bv2nat(bv2) ;


% ----- Increments a bit vector by a integer modulo 2**N 


  +(bv: bvec[N], i: int): {bvn: bvec[N] | 
                             bv2nat(bvn) = mod(bv2nat(bv) + i, exp2(N))}

  bv_plus : LEMMA (FORALL (bv: bvec[N], i: int):
                         bv2nat(bv + i) = mod(bv2nat(bv) + i, exp2(N))) ;

% ----- Decrements a bit vector by a integer modulo 2**N.

  -(bv: bvec[N], i: int): bvec[N] = bv + (-i) ;

  bv_minus     : LEMMA (FORALL (bv: bvec[N], i: int):
                    bv2nat(bv - i) = mod(bv2nat(bv) - i, exp2(N))) ;


% ----- Addition of two bit vectors interpreted as natural numbers.

  +(bv1: bvec[N], bv2: bvec[N]): {bv: bvec[N] | bv2nat(bv) = 
                              IF bv2nat(bv1) + bv2nat(bv2) < exp2(N)
                              THEN bv2nat(bv1) + bv2nat(bv2)
                              ELSE bv2nat(bv1) + bv2nat(bv2) - exp2(N) 
                              ENDIF}

% ----- Multiplication of two bit vectors interpreted as natural numbers.

  *(bv1: bvec[N], bv2: bvec[N]): {bv: bvec[2*N] | bv2nat(bv) = 
                                          bv2nat(bv1) * bv2nat(bv2)} ;

% Properties of these functions are in the bitvector library

END bv_arith_nat_defs


%------------------------------------------------------------------------
%
% Integer interpretation of a bitvector (twos-complement interpretation)
% ----------------------------------------------------------------------
%
%   Defines:
%
%      minint: int = -exp2(N-1)
%      maxint: int =  exp2(N-1) - 1
%      rng_2s_comp: TYPE = {i: int | minint <= i AND i <= maxint}
%      bv2int : [bvec[N] -> rng_2s_comp] 
%      int2bv : [rng_2s_comp  -> bvec[N]]
%  
%   Establishes:
%  
%      bv2int_bij : THEOREM bijective?(bv2int)
%      bv2int_inv : THEOREM bv2int(int2bv(iv))=iv
%
%   Note.  The int2bv should be avoided in practice.  It is necessary
%          for the development of the library.
%
%------------------------------------------------------------------------
bv_int_defs[N: posnat]: THEORY
BEGIN

  minint: int = -exp2(N-1)
  maxint: int =  exp2(N-1) - 1

  bv_maxint_to_minint:    LEMMA maxint = -minint - 1
  bv_minint_to_maxint:    LEMMA minint = -maxint - 1

  in_rng_2s_comp(i: int): bool = (minint <= i AND i <= maxint)
  rng_2s_comp: TYPE = {i: int | minint <= i AND i <= maxint}

  bv2int(bv: bvec[N]): rng_2s_comp = IF bv2nat(bv) < exp2(N-1) THEN bv2nat(bv)
                                     ELSE bv2nat(bv) - exp2(N) ENDIF

  int2bv(iv: rng_2s_comp): {bv: bvec[N] | bv2int(bv) = iv}

END bv_int_defs


%------------------------------------------------------------------------
%
% Defines functions over bit vectors interpreted as natural numbers.
%
%    Introduces:
%
%         - :  bv1 - bv2
%         - : [bvec -> bvec] 
%         - : [bvec, bvec -> bvec]
%         overflow    : [bvec, bvec -> bool]  
%         underflow   : [bvec, bvec -> bool]  
%   
%------------------------------------------------------------------------
bv_arithmetic_defs[N: posnat]: THEORY
BEGIN

  bv, bv1, bv2 : VAR bvec[N]

% ---------- 2's complement negation of a bit vector ----------
    
  -(bv: bvec[N]): { bvn: bvec[N] | bv2int(bvn) = 
                              IF bv2int(bv) = minint[N] THEN bv2int(bv)
                              ELSE -(bv2int(bv)) ENDIF}

% ---------- 2's complement subtraction of two bit vectors ----------
    
  -(bv1, bv2): bvec[N] = (bv1 + (-bv2))

% ---------- Define conditions for 2's complement overflow  ----------

    
  overflow(bv1,bv2): bool = (bv2int(bv1) + bv2int(bv2)) > maxint[N]
                             OR (bv2int(bv1) + bv2int(bv2)) < minint[N]

% === Properties of these additional functions in lib/bitvectors/bv_arithmetic=

% ---------- Integer <, <=, >, >=, +, * ----------
% Note that we can't use these operators, as they will always be ambiguous

  bv_slt(bv1, bv2): bool = bv2int(bv1) < bv2int(bv2)

  bv_sle(bv1, bv2): bool = bv2int(bv1) <= bv2int(bv2)

  bv_sgt(bv1, bv2): bool = bv2int(bv1) > bv2int(bv2)

  bv_sge(bv1, bv2): bool = bv2int(bv1) >= bv2int(bv2)

  bv_splus(bv1, (bv2 | NOT overflow(bv1, bv2))): bvec[N] =
    int2bv(bv2int(bv1) + bv2int(bv2))

  mult_overflow(bv1, bv2): bool = (bv2int(bv1) * bv2int(bv2)) > maxint[N]
                               OR (bv2int(bv1) * bv2int(bv2)) < minint[N]

  bv_stimes(bv1, (bv2 | NOT mult_overflow(bv1, bv2))): bvec[N] =
    int2bv(bv2int(bv1) * bv2int(bv2))

END bv_arithmetic_defs


bv_extend_defs [N: posnat] : THEORY
% -----------------------------------------------------------------------  
%    Introduces:
%	zero_extend: [k: above(N) -> [bvec[N] -> bvec[l]]] =
%	sign_extend: [k: above(N) -> [bvec[N] -> bvec[l]]] =
%	zero_extend_lsend: [k: above(N) -> [bvec[N] -> bvec[l]]]
%	lsb_extend: [k: above(N) -> [bvec[N] -> bvec[l]]] =
%   
% -----------------------------------------------------------------------  

BEGIN
    
  bv: VAR bvec[N]
  k: VAR above(N)
  
  %------------------------------------------------------------------------
  % zero_extend returns a function that extends a bit vector to length k 
  % such that its interpretation as a natural number remains unchanged.
  % That is, it fills 0's at the most significant positions.
  %------------------------------------------------------------------------

  zero_extend(k: above(N)): [bvec[N] -> bvec[k]] =
       (LAMBDA bv: fill[k-N](FALSE) o bv )

  %------------------------------------------------------------------------
  % sign_extend returns a function that extends a bit vector to length k
  % by repeating the most significant bit of the given bit vector.
  % The set above(N) contains nat numbers greater than n.
  %------------------------------------------------------------------------

  sign_extend(k: above(N)): [bvec[N] -> bvec[k]] =
       (LAMBDA bv:  fill[k-N](bv^(N-1)) o bv )
  %------------------------------------------------------------------------
  % zero_extend_lsend returns a function that extends a bit vector to
  % length k by padding 0's at the least significant end of bvec.
  % That is, the bv2nat interpretation of the argument increases by 2**(k-N)
  %------------------------------------------------------------------------
    
  zero_extend_lsend(k: above(N)): [bvec[N] -> bvec[k]] =
        (LAMBDA bv:  bv o fill[k-N](FALSE))
  
  %------------------------------------------------------------------------
  % lsb_extend returns a function that extends a bit vector to
  % length k by repeating the lsb of the bit vector at its
  % least significant end.
  %------------------------------------------------------------------------
    
  lsb_extend(k: above(N)): [bvec[N] -> bvec[k]] =
       (LAMBDA bv: bv o fill[k-N](bv^0))

  pad_left(k: above(N), b: bit)(bv): bvec[k] = fill[k-N](b) o bv

  pad_right(k: above(N), b: bit)(bv): bvec[k] = bv o fill[k-N](b)

END bv_extend_defs


%-------------------------------------------------------------------------
%
%  Basic properties of infinite sets.  This theory defines infinite sets
%  as the subtype of set[T] that satisfies the predicate "is_infinite".
%  This predicate states that there is no injective function into below[N]
%  for any N.
%
%  ---------------------------------------------------------------------
%      Author: Jerry James (jamesj@acm.org), University of Kansas
%
%-------------------------------------------------------------------------
infinite_sets_def[T: TYPE]: THEORY
 BEGIN

  S, R: VAR set[T]


  % ==========================================================================
  % The main definition
  % ==========================================================================

  is_infinite(S): MACRO bool = NOT is_finite(S)  
  infinite_set: TYPE = {S | NOT is_finite(S)}

  Inf: VAR infinite_set
  Fin: VAR finite_set[T]
  t: VAR T


  % ==========================================================================
  % Properties of infinite sets
  % ==========================================================================

  infinite_nonempty: JUDGEMENT infinite_set SUBTYPE_OF (nonempty?[T])

  infinite_add: JUDGEMENT add(t, Inf) HAS_TYPE infinite_set

  infinite_remove: JUDGEMENT remove(t, Inf) HAS_TYPE infinite_set

  infinite_superset: THEOREM FORALL Inf, S: subset?(Inf, S) => is_infinite(S)

  infinite_union_left: JUDGEMENT union(Inf, S) HAS_TYPE infinite_set
  infinite_union_right: JUDGEMENT union(S, Inf) HAS_TYPE infinite_set

  infinite_union: THEOREM
    FORALL S, R:
      is_infinite(union(S, R)) => is_infinite(S) OR is_infinite(R)

  infinite_intersection: THEOREM
    FORALL S, R:
      is_infinite(intersection(S, R)) => is_infinite(S) AND is_infinite(R)

  infinite_difference: JUDGEMENT difference(Inf, Fin) HAS_TYPE infinite_set

  infinite_rest: JUDGEMENT rest(Inf) HAS_TYPE infinite_set

  infinite_fullset: THEOREM
    (EXISTS S: is_infinite(S)) => is_infinite(fullset[T])

 END infinite_sets_def


finite_sets_of_sets[T: TYPE]: THEORY
 BEGIN

  a: VAR set[T]

  powerset_natfun_rec(A: finite_set[T],
                      n: upto(card(A)),
                      f: (bijective?[(A), below(card(A))]),
                      B: (powerset(A))):
        RECURSIVE nat =
   IF n = 0 THEN 0
   ELSE LET nval = exp2(n-1) * IF member(inverse(f)(n-1), B) THEN 1 ELSE 0 ENDIF
         IN nval + powerset_natfun_rec(A, n-1, f, B)
   ENDIF
  MEASURE n

  powerset_natfun_rec_bound: LEMMA
   FORALL (A: finite_set[T],
           n: upto(card(A)),
           f: (bijective?[(A), below(card(A))]),
           B: (powerset(A))):
     powerset_natfun_rec(A, n, f, B) < exp2(n)

  powerset_natfun(A: finite_set[T])(B: (powerset(A))): below(exp2(card(A))) =
    LET f = choose(bijective?[(A), below(card(A))])
     IN powerset_natfun_rec(A, card(A), f, B)

  powerset_natfun_inj_rec: LEMMA
   FORALL (A: finite_set[T],
           n: upto(card(A)),
           f: (bijective?[(A), below(card(A))]),
           B1, B2: (powerset(A))):
    powerset_natfun_rec(A, n, f, B1) = powerset_natfun_rec(A, n, f, B2)
      <=> (FORALL (m: upto(card(A))):
            (m < n) IMPLIES
               (member(inverse(f)(m), B1) IFF member(inverse(f)(m), B2)))

  powerset_natfun_inj: LEMMA
    FORALL (A: finite_set[T]):
      FORALL (B1, B2: (powerset(A))):
        powerset_natfun(A)(B1) = powerset_natfun(A)(B2) IMPLIES B1 = B2

  powerset_finite: JUDGEMENT
    powerset(A: finite_set[T]) HAS_TYPE finite_set[set[T]]

  SS: VAR setofsets[T]

  Union_finite: THEOREM
    FORALL SS: is_finite(Union(SS)) IFF is_finite(SS) AND every(is_finite)(SS)

  finite_Union_finite: LEMMA is_finite(SS) AND every(is_finite[T])(SS) IMPLIES
                             is_finite(Union(SS))

  Union_infinite: COROLLARY
    FORALL SS:
      is_infinite(Union(SS)) IFF is_infinite(SS) OR some(is_infinite)(SS)

  Intersection_finite: THEOREM
    FORALL SS:
      nonempty?(SS) AND every(is_finite)(SS) => is_finite(Intersection(SS))

  Intersection_infinite: COROLLARY
    FORALL SS: is_infinite(Intersection(SS)) => every(is_infinite)(SS)

  Complement_finite: THEOREM
    FORALL SS: is_finite(Complement(SS)) IFF is_finite(SS)

  Complement_is_finite: JUDGEMENT
    Complement(SS: finite_set[set[T]]) HAS_TYPE finite_set[set[T]]

  Complement_infinite: COROLLARY
    FORALL SS: is_infinite(Complement(SS)) IFF is_infinite(SS)

  Complement_is_infinite: JUDGEMENT
    Complement(SS: infinite_set[set[T]]) HAS_TYPE infinite_set[set[T]]

 END finite_sets_of_sets


% Equivalence classes, quotient types, etc.
%
% Bart Jacobs, Dep. Comp. Sci., Univ. Nijmegen.
% 
% With suggestions and remarks from: 
% Ulrich Hensel, Marieke Huisman, Hendrik Tews.
%
% With modifications and additions by Sam Owre, SRI International

EquivalenceClosure[T : TYPE] : THEORY
BEGIN

  R, S : VAR PRED[[T, T]]
  x, y : VAR T

  % Higher order definition of equivalence relation closure.

  EquivClos(R) : equivalence[T] =
    { (x, y) | FORALL(S : equivalence[T]) : subset?(R, S) IMPLIES S(x, y) }

  EquivClosSuperset : LEMMA
    subset?(R, EquivClos(R))

  EquivClosMonotone : LEMMA
    subset?(R, S) IMPLIES subset?(EquivClos(R), EquivClos(S))

  EquivClosLeast : LEMMA
    equivalence?(S) AND subset?(R, S) IMPLIES subset?(EquivClos(R), S)

  EquivClosIdempotent : LEMMA
    EquivClos(EquivClos(R)) = EquivClos(R)

  EquivalenceCharacterization : LEMMA
    equivalence?(S) IFF (S = EquivClos(S))

END EquivalenceClosure


QuotientDefinition[T : TYPE] : THEORY
BEGIN

  R : VAR set[[T, T]]
  S : VAR equivalence[T]
  x, y, z : VAR T

  EquivClass(R)(x) : set[T] = { z | R(x, z) }

  EquivClassNonEmpty : LEMMA
    nonempty?[T](EquivClass(S)(x))

  EquivClassEq : LEMMA
    EquivClass(S)(x) = EquivClass(S)(y) IFF S(x, y)

  repEC(S)(x): T = choose(EquivClass(S)(x))

  % The next two lemmas facilitate working with representatives.

  EquivClassChoose : LEMMA
    S(x, repEC(S)(x))

  ChooseEquivClassChoose : LEMMA
    EquivClass(S)(repEC(S)(x)) = EquivClass(S)(x)

  Quotient(S) : TYPE =
    { P : set[T] | EXISTS x : P = EquivClass(S)(x) }

  rep(S)(P: Quotient(S)): T = choose(P)

  rep_is_repEC: LEMMA
     rep(S)(EquivClass(S)(x)) = repEC(S)(x)

  rep_lemma: LEMMA
     EquivClass(S)(x)(rep(S)(EquivClass(S)(x)))

  % Note that quotient_map has a different range type than EquivClass,
  % and EquivClass(S) is not surjective.
  quotient_map(S)(x) : Quotient(S) =
    EquivClass(S)(x)

  quotient_map_surjective : LEMMA
    surjective?(quotient_map(S))

  % Quotients are also defined for arbitrary relations, via the
  % Equivalence Closure (EC).

  ECQuotient(R) : TYPE =              
    Quotient(EquivClos(R))

  ECquotient_map(R)(x) : ECQuotient(R) =
    quotient_map(EquivClos(R))(x)

END QuotientDefinition

KernelDefinition[X: TYPE, X1: TYPE FROM X, Y : TYPE] : THEORY
BEGIN

  f : VAR [X1 -> Y]
  R : VAR PRED[[X, X]]
  x1, x2 : VAR X1

  % The following definition would be part of the 'functions' theory
  % but equivalence is defined afterwards.

  EquivalenceKernel(f) : equivalence[X1] =
    { (x1, x2) | f(x1) = f(x2) }

  PreservesEq(R)(f) : bool =
    subset?(restrict[[X, X],[X1, X1], bool](R), EquivalenceKernel(f))

  PreservesEqClosure : LEMMA
    PreservesEq(R) = PreservesEq(EquivClos[X1](R))

  PreservesEq_is_preserving: LEMMA
    PreservesEq(R) = preserves(restrict[[X, X], [X1, X1], bool](R), =[Y])

END KernelDefinition

QuotientKernelProperties[X : TYPE, X1: TYPE FROM X] : THEORY
BEGIN

  S : VAR equivalence[X1]
  R : VAR PRED[[X, X]]

  Kernel_quotient_map : LEMMA
    EquivalenceKernel[X, X1, Quotient(S)](quotient_map(S)) = S

  PreservesEq_quotient_map : LEMMA
    PreservesEq[X, X1, Quotient(S)](S)(quotient_map(S))

  quotient_map_is_Quotient_EqivalenceRespecting: JUDGEMENT
    quotient_map(S) HAS_TYPE (PreservesEq[X, X1, Quotient(S)](S))

  Kernel_ECquotient_map : LEMMA
    EquivalenceKernel[X, X1, ECQuotient(S)](quotient_map(S)) = S

  PreservesEq_ECquotient_map : LEMMA
    PreservesEq[X, X1, ECQuotient(S)](S)(quotient_map(S))

  quotient_map_is_ECQuotient_EqivalenceRespecting: JUDGEMENT
    quotient_map(S) HAS_TYPE (PreservesEq[X, X1, ECQuotient(S)](S))

END QuotientKernelProperties


QuotientSubDefinition[X : TYPE, X1: TYPE FROM X] : THEORY
BEGIN

  x: VAR X1
  S : VAR {R: equivalence[X] | PreservesEq[X, X, bool](R)(X1_pred)}

  QuotientSub(S) : TYPE =
    { P :set[X] | EXISTS x : P = EquivClass(S)(x) }

  quotient_sub_map(S)(x) : QuotientSub(S) =
    EquivClass(S)(x)

END QuotientSubDefinition


QuotientExtensionProperties[X : TYPE, X1: TYPE FROM X, Y : TYPE] : THEORY
BEGIN

  S : VAR {R: equivalence[X] | PreservesEq[X, X, bool](R)(X1_pred)}

  lift(S)
      (g : (PreservesEq[X, X1, Y](S)))
      (P : QuotientSub[X, X1](S))
      : Y
    = g(rep(S)(P))

  lift_commutation : LEMMA
    FORALL (S),
           (g : (PreservesEq[X, X1, Y](S))) : 
      lift(S)(g) o quotient_sub_map[X, X1](S) = g

  lift_unicity : LEMMA
    FORALL (S | PreservesEq[X, X, bool](S)(X1_pred)),
           (g : (PreservesEq[X, X1, Y](S))) :
      FORALL(h : [QuotientSub[X, X1](S) -> Y]) :
        h o quotient_sub_map[X, X1](S) = g IMPLIES h = lift(S)(g)

END QuotientExtensionProperties


QuotientDistributive[X, Y: TYPE] : THEORY
BEGIN

% This theory makes clear that quotients commute with products: there is an isomorphism 
%
%     [X/S, Y] iso [X,Y]/EqualityExtension(S)
%
% given by the canonical map (from right to left). Such distributivity
% results can be used to define functions with several parameters on a
% quotient.  In the presence of function types, this can also be done
% via Currying.  The result is included here mainly as a test for the
% formalisation of quotients.
%

  S : VAR equivalence[X]
  z, w : VAR [X, Y]

  EqualityExtension(S) : set[[[X, Y], [X, Y]]] =
    { (z, w) | S(z`1, w`1) AND z`2 = w`2  }

  EqualityExtension_is_equivalence: JUDGEMENT
    EqualityExtension(S) HAS_TYPE equivalence[[X, Y]]

  EqualityExtensionPreservesEq : LEMMA
    PreservesEq[[X, Y], [X, Y], [Quotient(S), Y]]
        (EqualityExtension(S))
        (LAMBDA(x : X, y : Y) : (quotient_map(S)(x), y))

  QuotientDistributive : LEMMA
    bijective?[Quotient(EqualityExtension(S)), [Quotient(S), Y]]
      (lift[[X, Y], [X, Y], [Quotient(S), Y]](EqualityExtension(S))
           (LAMBDA(x : X, y : Y) : (quotient_map(S)(x), y)))

  %     [X/S, Y/R] iso [X,Y]/RelExtension(S,R)

  R: VAR equivalence[Y]

  RelExtension(S, R) : equivalence[[X, Y]] =
    { (z, w) | S(z`1, w`1) AND R(z`2, w`2) }

  RelExtensionPreservesEq : LEMMA
    PreservesEq[[X, Y], [X, Y], [Quotient(S), Quotient(R)]]
        (RelExtension(S,R))
        (LAMBDA(x : X, y : Y) : (quotient_map(S)(x), quotient_map(R)(y)))

  RelQuotientDistributive : LEMMA
    bijective?[Quotient(RelExtension(S,R)), [Quotient(S), Quotient(R)]]
      (lift[[X, Y], [X, Y], [Quotient(S), Quotient(R)]](RelExtension(S, R))
           (LAMBDA(x : X, y : Y) : (quotient_map(S)(x), quotient_map(R)(y))))

  %     F: [X -> equivalence[Y]]
  %        [x:X -> Y/F(x)] iso [X -> Y]/FunExtension(F)
  F: VAR [X -> equivalence[Y]]
  f, g : VAR [X -> Y]

  FunExtension(F) : equivalence[[X -> Y]] =
    { (f, g) | FORALL (x : X) : F(x)(f(x), g(x)) }

  FunExtensionPreservesEq : LEMMA
    PreservesEq[[X -> Y], [X -> Y], [x : X -> Quotient(F(x))]]
        (FunExtension(F))
        (LAMBDA(f : [X -> Y]) : LAMBDA (x : X) : quotient_map(F(x))(f(x)))

  FunQuotientDistributive : LEMMA
    bijective?[Quotient(FunExtension(F)), [x : X -> Quotient(F(x))]]
      (lift[[X -> Y], [X -> Y], [x : X -> Quotient(F(x))]](FunExtension(F))
           (LAMBDA(f : [X -> Y]) : (LAMBDA (x : X): (quotient_map(F(x))(f(x))))))

END QuotientDistributive


QuotientIteration[X : TYPE] : THEORY
BEGIN

% In this theory it will be shown how successive quotients can be reduced
% to a single quotient:
% 
%     (X/S)/R iso X/action(S)(R)
%
% again via the canonical map.

  S : VAR equivalence[X]
  x, y : VAR X

  action(S)(R : equivalence[Quotient(S)])(x, y) : bool =
    R(EquivClass(S)(x), EquivClass(S)(y))

  action_equivalence_is_equivalence: JUDGEMENT
    action(S)(R : equivalence[Quotient(S)]) HAS_TYPE equivalence[X]

  QuotientAction : LEMMA
    FORALL(R : equivalence[Quotient(S)]) :
      bijective?[Quotient(R), Quotient(action(S)(R))]
        (lift[Quotient(S), Quotient(S), Quotient(action(S)(R))](R)
           (lift[X, X, Quotient(action(S)(R))](S)
              (quotient_map[X](action(S)(R)))))

END QuotientIteration


% The following theory illustrates how the lift type can be used
% to describe partial functions from X to Y, namely as total
% functions from X to lift[Y].

PartialFunctionDefinitions[X, Y : TYPE] : THEORY
BEGIN

% Two representations of partial functions are described,
% and shown to be isomorphic. In practice, the formulation
% based on lift is more convenient, because definitions
% are easier and fewer TCCs are generated.

  SubsetPartialFunction : TYPE =
    [# dom : PRED[X], fun : [(dom) -> Y] #]

  LiftPartialFunction : TYPE =
    [X -> lift[Y]]

  f : VAR LiftPartialFunction
  g : VAR SubsetPartialFunction
  h : VAR [X -> Y]

  SPartFun_appl(g): [(dom(g)) -> Y] = g`fun

  SPartFun_to_LPartFun(g) : LiftPartialFunction =
    LAMBDA(x : X) : IF dom(g)(x) THEN up(fun(g)(x)) ELSE bottom ENDIF

  LPartFun_to_SPartFun(f) : SubsetPartialFunction =
    (# 
      dom := {x : X | up?(f(x))}, 
      fun := LAMBDA(y : {x : X | up?(f(x))}) : down(f(y))
    #)

  TotalFun_to_SPartFun(h): SubsetPartialFunction =
    (#
      dom := {x : X | TRUE},
      fun := h
    #)

  TotalFun_to_LPartFun(h): LiftPartialFunction =
    LAMBDA(x : X) : up(h(x))

  CONVERSION SPartFun_appl,
             SPartFun_to_LPartFun, LPartFun_to_SPartFun,
             TotalFun_to_SPartFun, TotalFun_to_LPartFun

  SPartFun_to_LPartFun_to_SPartFun : LEMMA
    LPartFun_to_SPartFun(SPartFun_to_LPartFun(g)) = g

  LPartFun_to_SPartFun_to_LPartFun : LEMMA
    SPartFun_to_LPartFun(LPartFun_to_SPartFun(f)) = f

END PartialFunctionDefinitions


PartialFunctionComposition[X, Y, Z : TYPE] : THEORY
BEGIN

  f : VAR LiftPartialFunction[X, Y]
  g : VAR LiftPartialFunction[Y, Z]

  o(g, f) : LiftPartialFunction[X, Z] =
    LAMBDA(x : X) :
      CASES f(x) OF
        bottom : bottom,
        up(y) : g(y)
      ENDCASES

  h : VAR SubsetPartialFunction[X, Y]
  k : VAR SubsetPartialFunction[Y, Z]

  CompDom(k, h) : PRED[X] =
    { x : X | dom(h)(x) AND dom(k)(fun(h)(x)) };

  o(k, h) : SubsetPartialFunction[X, Z] =
    (# 
      dom := CompDom(k, h),
      fun := LAMBDA(x : (CompDom(k, h))) : fun(k)(fun(h)(x))
    #)

  SPartFun_to_LPartFun_CompositionPreservation : LEMMA
    SPartFun_to_LPartFun(k o h)
      = SPartFun_to_LPartFun(k) o SPartFun_to_LPartFun(h)

  LPartFun_to_SPartFun_CompositionPreservation : LEMMA
    LPartFun_to_SPartFun(g o f)
      = LPartFun_to_SPartFun(g) o LPartFun_to_SPartFun(f)

END PartialFunctionComposition

% PVSio support theories

%% stdlang 
%% Basic language definitions

stdlang : THEORY
BEGIN

  void : TYPE = bool

  skip : void = true
  fail : void = false

  try(s1:void,s2:void)  : MACRO void = s1 OR s2
  try(s:void)           : MACRO void = try(s,skip)
  ifthen(b:bool,s:void) : MACRO void = IF b THEN s ELSE skip ENDIF
  ifelse(b:bool,s:void) : MACRO void = IF b THEN skip ELSE s ENDIF

  Dummy : TYPE  = bool
  dummy : MACRO Dummy = false

END stdlang


%% stdexc.pvs
%% Definition of exception type

stdexc[T:TYPE+] : THEORY
BEGIN

  ExceptionTag : TYPE = string

  Exception : TYPE = [#
    tag : ExceptionTag,
    val : T
  #]

  make_exc(e:ExceptionTag,t:T) : Exception = (#
    tag := e,
    val := t
  #)

END stdexc

%% stdcatch.pvs
%% Definition of catch and throw 

stdcatch[T1,T2:TYPE+] : THEORY
BEGIN

  catch_lift(tag:ExceptionTag[T2],
             t1:[Dummy->T1],
             t2:[Exception[T2]->T1]) : T1

  catch(tag:ExceptionTag[T2],
        t1:T1,
        t2:[Exception[T2]->T1]) : MACRO T1 =
    catch_lift(tag,LAMBDA(d:Dummy):t1,t2)

  catch_list_lift(l:list[ExceptionTag[T2]],
                  f1:[Dummy->T1],
                  f2:[Exception[T2]->T1]) : RECURSIVE T1 =
    CASES l OF
      null : f1(dummy),
      cons(e,r) : catch_lift(e,
                             LAMBDA(d:Dummy):catch_list_lift(r,f1,f2),
                             f2)
    ENDCASES
    MEASURE l BY <<  

  catch(l:list[ExceptionTag[T2]],t1:T1,t2:[Exception[T2]->T1]) : MACRO T1 =
    catch_list_lift(l,LAMBDA(d:Dummy):t1,t2)

  throw(tag:ExceptionTag[T2],e:Exception[T2]):T1

  throw(tag:ExceptionTag[T2],val:T2) : MACRO T1 =
    throw(tag,make_exc(tag,val))

END stdcatch

%% stdprog.pvs
%% Imperative aspects such as errors, exceptions, mutables and loops

stdprog[T:TYPE+] : THEORY
BEGIN

  prog(s:void,t:T): T = t

  % Errors and Exceptions
  error(mssg:string):T 

  % Exists the current evaluation and returns to the Ground Evaluator
  exit : T

  catch(tag:ExceptionTag[void],t1,t2:T): MACRO T =
    catch_lift[T,void](tag,LAMBDA(d:Dummy):t1,LAMBDA(e:Exception[void]):t2)

  throw(tag:ExceptionTag[void]): MACRO T = throw[T,void](tag,fail)

  catch(l:list[ExceptionTag[void]],t1,t2:T) : MACRO T =
    catch_list_lift[T,void](l,LAMBDA(d:Dummy):t1,LAMBDA(e:Exception[void]):t2)

  % Mutable data
  UndefinedMutableVariable : ExceptionTag[void] = "UndefinedMutableVariable"

  Mutable : TYPE+

  ref(t:T) : Mutable

  new      : Mutable 

  undef(v:Mutable)    : bool

  val_lisp(v:Mutable) : T

  val(v:Mutable): T = 
    if undef(v) then 
      throw(UndefinedMutableVariable)
    else val_lisp(v)
    endif

  def(v:Mutable,t:T) : T = t

  set(v:Mutable,t:T) : void = 
    let nt = def(v,t) in
    skip

  CONVERSION val

  % Global uninitilized variables
  Global : TYPE+ = Mutable

  % Loops
  loop_lift(f:[Dummy->void]): T

  loop(s:void): MACRO T = loop_lift(LAMBDA(d:Dummy):s)

  return(t:T):void = fail

  % Formatting text
  format(s:string,t:T):string

END stdprog

%% stdglobal.pvs
%% Global variables

stdglobal[T:TYPE+,t:T] : THEORY
BEGIN

  Global  : TYPE+ = Mutable[T]

END stdglobal

%% stdpvs.pvs
%% PVS printing and parsing

stdpvs[T:TYPE+] : THEORY
BEGIN

  typeof(t:T) : string

  str2pvs(s:string):T 

  pvs2str_lisp(t:T) : string

  pvs2str(t:T) : MACRO string =
    catch("cant-translate",	
	  pvs2str_lisp(t),
          LAMBDA(e:Exception[void]):"<?>")

  Slisp : TYPE+  

  slisp(l:list[T]) : Slisp

  {||}(l:list[T]) : Slisp = slisp(l)

END stdpvs

%% stdstr.pvs
%% String operations

stdstr: THEORY
BEGIN

  NotARealNumber : ExceptionTag[string] = "NotARealNumber"
  NotAnInteger   : ExceptionTag[string] = "NotAnInteger"

% Char from code n
  charcode(n:nat) : string

% Prints standard table of characters
  chartable       : void 

  emptystr      : string = ""
  space         : string = " "
  newline       : string 
  tab           : string 
  doublequote   : string = charcode(34)
  singlequote   : string = "'"
  backquote     : string = "`"
  spaces(n:nat) : string 

  upcase(s:string):string
  downcase(s:string):string
  capitalize(s:string):string
  
  %% Index of leftmost occurrence of s1 in s2, 2, starting from 0, or -1 if none
  strfind(s1,s2:string):int

  %% Substring S[i..j] if i <= j, S[j..i] if j <= i, empty if indices are out of range
  substr(s:string,i,j:nat):string

  %% Real to string
  real2str(r:real): string

  %% Bool to string
  bool2str(b:bool): string =
    IF b THEN "TRUE" ELSE "FALSE" ENDIF

  tostr(r:real): MACRO string = real2str(r)
  tostr(b:bool): MACRO string = bool2str(b)

  %% String to real
  str2real(s:string): rat
  str2int(s:string) : int 
  str2bool(s,answer:string):bool = downcase(s) = downcase(answer)

  %% String is a number
  number?(s:string):bool

  %% String is an integer
  int?(s:string):bool

 %% String concatenation
  concat(s1:string,s2:string):string = s1 o s2
  ;+(s1:string,s2:string): MACRO string = concat(s1,s2)
  ;+(r:real,s:string): MACRO string = concat(tostr(r),s)
  ;+(s:string,r:real): MACRO string = concat(s,tostr(r))
  ;+(b:bool,s:string): MACRO string = concat(tostr(b),s)
  ;+(s:string,b:bool): MACRO string = concat(s,tostr(b))

  %% Concatenates n-times string s
  pad(n:nat,s:string): RECURSIVE string = 
    IF n=0 THEN emptystr
    ELSE s+pad(n-1,s)
    ENDIF
    MEASURE n

  %% String comparison
  %% = 0 if s1 = s2
  %% < 0 if s1 < s2
  %% > 0 if s1 > s2

  strcmp(s1,s2:string,sensitive:bool):int

  strcmp(s1,s2:string): MACRO int = 
    strcmp(s1,s2,true)

  %% A substring of s2, with all the characters in s1 stripped of 
  %% the beginning and end
  strtrim(s1,s2:string):string

  %% Returns a substring of s2, with all the characters in s1 stripped of 
  %% the beginning 
  strtrim_left(s1,s2:string):string

  %% Returns a substring of s2, with all the characters in s1 stripped of 
  %% the end
  strtrim_right(s1,s2:string):string

  %% Returns a substring of s, with all the space characters stripped of 
  %% the beginning and end
  trim(s:string):string

  %% Returns a substring of s, with all the space characters stripped of 
  %% the beginning 
  trim_left(s:string):string

  %% Returns a substring of s, with all the space characters stripped of 
  %% the end
  trim_right(s:string):string

  %% Returns the name part of a file name
  filename(s:string):string

  %% Returns the directory part of a file name
  directory(s:string):string

END stdstr

%% stdio.pvs
%% Input/output operations

stdio : THEORY
BEGIN

%% I/O Exceptions
  FileNotFound          : ExceptionTag[string] = "FileNotFound"
  FileAlreadyExists     : ExceptionTag[string] = "FileAlreadyExists"
  WrongInputStreamMode  : ExceptionTag[string] = "WrongInputStreamMode"
  WrongOutputStreamMode : ExceptionTag[string] = "WrongOutputStreamMode"
  ClosedStream          : ExceptionTag[string] = "ClosedStream"
  EndOfFile             : ExceptionTag[string] = "EndOfFile"
  
  IOExceptionTags : list[ExceptionTag[string]] = 
    (: NotARealNumber, NotAnInteger, FileNotFound,
       FileAlreadyExists, WrongInputStreamMode, WrongOutputStreamMode,
       ClosedStream, EndOfFile :)

%% Assert
  assert(b:bool,str:string):void =
    try(b,error[void]("[Assertion Failure] "+str) & fail)

%% While loops
  break : MACRO void = return(skip)

  while(b:bool,s:void) : MACRO void =
    loop(if b then s else break endif)

  for(si:void,b:bool,sinc,s:void): MACRO void =
    si & while(b,s&sinc)

%% Print, Println 

  %% Prints lisp format of string s
  printstr(s:string):void = skip

  print(s:string)   : MACRO void = printstr(s)
  print(r:real)     : MACRO void = print(r+emptystr)
  print(b:bool)     : MACRO void = print(b+emptystr)
  println(s:string) : MACRO void = print(s+newline)
  println(r:real)   : MACRO void = print(r+newline)
  println(b:bool)   : MACRO void = print(b+newline)

%% Reading

  % Querying from stdin with prompt message
  query_token(mssg,s:string): string
  query_word(mssg:string)   : MACRO string = query_token(mssg,emptystr) 
  query_line(mssg:string)   : string
  query_real(mssg:string)   : rat
  query_int(mssg:string)    : int
  query_bool(mssg,answer:string):bool = str2bool(query_word(mssg),answer)

  % Reads from stdin
  read_token(s:string): MACRO string = query_token(emptystr,s) 
  read_word : MACRO string = query_word(emptystr)
  read_line : MACRO string = query_line(emptystr)
  read_real : MACRO rat    = query_real(emptystr)
  read_int  : MACRO int    = query_int(emptystr)
  read_bool(answer:string): MACRO bool   = query_bool(emptystr,answer)

%% Character Streams
  Stream   : TYPE+ 
  IStream  : TYPE+ FROM Stream
  OStream  : TYPE+ FROM Stream

  fclose(f:Stream)     :void =   %% Close an stream
    skip
  fexists(s:string)    :bool     %% true IFF file s exists
  fopen?(f:Stream)     :bool     %% Open stream?
  strstream?(f:Stream) :bool     %% String stream?
  filestream?(f:Stream):bool     %% File stream?
  sdtstream?(f:Stream) :bool =   %% Standard stream?
    NOT (filestream?(f) OR strstream?(f))
  finput?(f:Stream)    :bool     %% Input stream?
  foutput?(f:Stream)   :bool     %% Output stream?

%% Standard I/O Steams
  stdin    : IStream 
  stdout   : OStream 
  stderr   : OStream

  Mode   : TYPE = {input,output,create,append,overwrite,rename,str}

  mode2str(m:Mode) : string = CASES m OF
    input     : "input",
    output    : "output",
    create    : "create",
    append    : "append",
    overwrite : "overwrite",
    rename    : "rename",
    str       : "str"
  ENDCASES

  tostr(m:Mode) : MACRO string = mode2str(m) 

  fopenin_lisp(s:string)        : IStream 
  fopenout_lisp(s:string,n:nat) : OStream 
  sopenin(s:string)             : IStream
  sopenout(s:string)            : OStream

  % Opens a file as an input stream
  fopenin(m:Mode,s:string) : IStream = 
    IF    m = input AND length(s)=0 THEN stdin 
    ELSIF m = input THEN fopenin_lisp(s)
    ELSIF m = str   THEN sopenin(s)
    ELSE throw(WrongInputStreamMode,tostr(m))
    ENDIF

  fopenin(s:string) : IStream = 
    fopenin_lisp(s)

  % Opens a file as an oput stream
  fopenout(m:Mode,s:string): OStream =
    IF    m = output AND length(s)=0 THEN stdout
    ELSIF m = output    THEN fopenout_lisp(s,0) 
    ELSIF m = create    THEN fopenout_lisp(s,1)
    ELSIF m = append    THEN fopenout_lisp(s,2)
    ELSIF m = overwrite THEN fopenout_lisp(s,3)
    ELSIF m = rename    THEN fopenout_lisp(s,4)
    ELSIF m = str       THEN sopenout(s)
    ELSE throw(WrongOutputStreamMode,tostr(m))
    ENDIF

  fopenout(s:string) : OStream =
    fopenout(output,s)

  fname_lisp(f:Stream)           :string
  fgetstr_lisp(f:OStream)        :string
  eof_lisp(f:IStream)            :bool           
  flength_lisp(f:Stream)         :nat          
  fgetpos_lisp(f:Stream)         :nat         
  fsetpos_lisp(f:Stream,n:nat)   :void   
  fprint_lisp(f:OStream,s:string):void = skip

  % Gets the full name of a file string
  fname(f:Stream):string =
    if filestream?(f) then 
      fname_lisp(f)
    else
      emptystr
    endif

  % Gets string from an *output string* stream 
  fgetstr(f:OStream):string =
    if fopen?(f) then fgetstr_lisp(f)
    else throw(ClosedStream,fname(f))
    endif

  %% true IFF f has reached EOF
  eof?(f:IStream):bool =
    if fopen?(f) then eof_lisp(f)
    else throw(ClosedStream,fname(f))
    endif

  %% Length of stream 
  flength(f:Stream):nat =
    if fopen?(f) then flength_lisp(f)
    else throw(ClosedStream,fname(f))
    endif

  %% Gets current position 
  fgetpos(f:Stream):nat = 
    if fopen?(f) then fgetpos_lisp(f)
    else throw(ClosedStream,fname(f))
    endif

  % Prints to a stream
  fprint(f:OStream,s:string):void = 
    if fopen?(f) then fprint_lisp(f,s)
    else throw(ClosedStream,fname(f))
    endif
  fprint(f:OStream,r:real)    : MACRO void = fprint(f,r+emptystr)
  fprint(f:OStream,b:bool)    : MACRO void = fprint(f,b+emptystr)
  
  %% Sets current positions
  fsetpos(f:Stream,n:nat):void =
    if fopen?(f) then fsetpos_lisp(f,n)
    else throw(ClosedStream,fname(f))
    endif

  % Printlns to a stream
  fprintln(f:OStream,s:string): MACRO void = fprint(f,s+newline)
  fprintln(f:OStream,r:real)  : MACRO void = fprint(f,r+newline)
  fprintln(f:OStream,b:bool)  : MACRO void = fprint(f,b+newline)

  % Prints to a stream and echoes to stdout
  echo(f:OStream,s:string): MACRO void = print(s) & fprint(f,s) 
  echo(f:OStream,r:real)  : MACRO void = print(r) & fprint(f,r) 
  echo(f:OStream,b:bool)  : MACRO void = print(b) & fprint(f,b) 

  % Printlns to a stream and echo to stdout
  echoln(f:OStream,s:string): MACRO void = println(s) & fprintln(f,s)
  echoln(f:OStream,r:real)  : MACRO void = println(r) & fprintln(f,r) 
  echoln(f:OStream,b:bool)  : MACRO void = println(b) & fprintln(f,b) 

%% Input functions
  fread_token_lisp(f:IStream,s:string) : string
  fread_line_lisp(f:IStream) : string
  fread_real_lisp(f:IStream) : rat
  fread_int_lisp(f:IStream)  : int

  fcheck(f:IStream): bool = 
    (fopen?(f)   OR throw(ClosedStream,fname(f))) AND
    (NOT eof?(f) OR throw(EndOfFile,fname(f)))

  % Reads a token from f separated by characters in s from a stream
  fread_token(f:IStream,s:string) : string =
    prog(fcheck(f),fread_token_lisp(f,s))

  % Reads a word from f
  fread_word(f:IStream) : MACRO string =
    fread_token(f,emptystr)

  % Reads a line from f
  fread_line(f:IStream) : string =
    prog(fcheck(f),fread_line_lisp(f))

  % Reads a real number from f
  fread_real(f:IStream) : rat =
    prog(fcheck(f),fread_real_lisp(f))

  % Reads an integer from f
  fread_int(f:IStream)  : int =
    prog(fcheck(f),fread_int_lisp(f))

  % Reads a boolean from f
  fread_bool(f:IStream,answer:string): MACRO bool=
    str2bool(fread_word(f),answer)

END stdio

%% stdmath.pvs
%% PVSio math library 

stdmath : THEORY
BEGIN

  %% sq is available in the theory reals@sq
  %% div and mod are available through the theories
  %% ints@div_nat, ints@mod_nat

%% Math Exceptions
  MathExceptions : list[ExceptionTag[string]] = 
   (: NotARealNumber, NotAnInteger :)

  PI                : posreal 
  SIN(x:real)       : {x:real| -1 <= x and x <= 1}
  COS(y:real)       : {x:real| -1 <= x and x <= 1}
  EXP(x:real)       : posreal
  RANDOM            : {y:nnreal | 0 <= y AND y <= 1} 
  NRANDOM(n:posnat) : {y:nat | 0 <= y AND y < n} 

  sqrt_lisp(x:nnreal) : nnreal
  log_lisp(x:posreal) : real
  atan_lisp(x,y:real) : real
  asin_lisp(x:real)   : real    
  acos_lisp(x:real)   : real    

  SQRT(x:real):nnreal =
    IF x < 0 THEN 
      throw[nnreal,string](NotARealNumber,"SQRT("+x+")")
    ELSE sqrt_lisp(x)
    ENDIF

  LOG(x:real):real =
    IF x <= 0 THEN 
      throw[real,string](NotARealNumber,"LOG("+x+")")
    ELSE log_lisp(x)
    ENDIF

  TAN(x:real):real =
    LET d = COS(x) IN
    IF d = 0 THEN 
      throw[real,string](NotARealNumber,"TAN("+x+"0)")
    ELSE
      SIN(x)/COS(x)
    ENDIF

  ATAN(y,x:real):real = 
    IF x=0 AND y= 0 THEN 
      throw[real,string](NotARealNumber,"ATAN(0,0)")
    ELSE atan_lisp(y,x)
    ENDIF

  ASIN(x:real) : real =
    IF x < -1 OR x > 1 THEN
      throw[real,string](NotARealNumber,"ASIN("+x+")")
    ELSE asin_lisp(x)
    ENDIF
 
  ACOS(x:real) : real =
    IF x < -1 OR x > 1 THEN
      throw[real,string](NotARealNumber,"ACOS("+x+")")
    ELSE acos_lisp(x)
    ENDIF

  BRANDOM: bool =
    (NRANDOM(2) = 0)

END stdmath

%% stdfmap.pvs
%% Iterations on files and format printing

stdfmap[T:TYPE+] : THEORY
BEGIN

  % Iterates a function on a file n times
  fmap(f:IStream,fread:[IStream->string],t:T,st:[[string,T]->T],n:nat) : 
    RECURSIVE T =
    IF fopen?(f) THEN
      IF n=0 or eof?(f) THEN t
      ELSE
        LET s  = fread(f) IN
        LET nt = st(s,t) IN
          fmap(f,fread,nt,st,n-1)
      ENDIF
    ELSE throw(ClosedStream,fname(f))
    ENDIF
    MEASURE n

  % Iterates a function on a file n times until the end
  fmap(f:IStream,fread:[IStream->string],t:T,st:[[string,T]->T]) : T =
    let l  = flength(f) in
    let nt = fmap(f,fread,t,st,l) in  
    prog(fclose(f),nt)

  % Iterates a function on a file line by line until the end
  fmap_line(f:IStream,t:T,st:[[string,T]->T]):T =
    let l  = flength(f) in
    let nt = fmap(f,fread_line,t,st,l) in  
    prog(fclose(f),nt)

  % Formatting text to a file
  printf(s:string,t:T): MACRO void =
    print(format(s,t))

  fprintf(f:OStream,s:string,t:T): MACRO void =
    fprint(f,format(s,t))

END stdfmap

%% stdindent.pvs
%% Indentations

stdindent : THEORY
BEGIN

  %% Indentation
  Indent : TYPE+
  
  create_indent(n:nat,s:string):Indent
  push_indent(i:Indent,n:nat):void
  pop_indent(i:Indent):void
  top_indent(i:Indent):nat
  get_indent(i:Indent):nat
  set_indent(i:Indent,n:nat):void
  get_prefix(i:Indent):string
  set_prefix(i:Indent,s:string):void

  create_indent(n:nat)         : MACRO Indent = create_indent(n,emptystr)
  open_block(i:Indent,n:nat)   : MACRO void = push_indent(i,n)
  open_block(i:Indent)         : MACRO void = push_indent(i,get_indent(i))
  close_block(i:Indent)        : MACRO void = pop_indent(i)

  indent(i:Indent):string = get_prefix(i)+spaces(top_indent(i))
  indent(i:Indent,s:string):string = indent(i)+s

  prindent(i:Indent,s:string):void = print(indent(i,s))
  prindentln(i:Indent,s:string):void = println(indent(i,s))

  fprindent(f:OStream,i:Indent,s:string):void = fprint(f,indent(i,s))
  fprindentln(f:OStream,i:Indent,s:string):void = fprintln(f,indent(i,s))

  %% Formatting functions

  center(col:nat,s:string):string = format("~"+col+":@<~a~>",s)

  flushleft(col:nat,s:string) :string = format("~"+col+"a",s)

  flushright(col:nat,s:string):string = format("~"+col+"@a",s)

END stdindent

%% stdtokenizer.pvs
%% Tokenizers

stdtokenizer : THEORY
BEGIN
 
  %% Error Codes
  %% Negative numbers are user defined
  NoError           : nat = 0
  FileNotFound      : nat = 1
  EndOfTokenizer    : nat = 2
  InvalidToken      : nat = 3
  ExpectingWord     : nat = 4
  ExpectingTestWord : nat = 5
  ExpectingInt      : nat = 6
  ExpectingTestInt  : nat = 7
  ExpectingReal     : nat = 8
  ExpectingTestReal : nat = 9

  Tokenizer : TYPE = [#
    stream   : ARRAY[nat->string],
    lines    : ARRAY[nat->nat],
    casesen  : boolean,
    separ    : string,
    error    : int,
    val_int  : int,
    val_real : real,
    length   : nat,
    pos      : upto(length)
  #]

  init_tokenizer(casesen:bool,separ:string) : Tokenizer = (#
    stream   := LAMBDA(x:nat):emptystr,
    lines    := LAMBDA(x:nat):0,
    casesen  := casesen,
    separ    := separ,
    error    := NoError,
    val_int  := 0,
    val_real := 0, 
    length   := 0,
    pos      := 0
  #)

  empty_tokenizer : Tokenizer = (#
    stream   := LAMBDA(x:nat):emptystr,
    lines    := LAMBDA(x:nat):0,
    casesen  := true,
    separ    := emptystr,
    error    := NoError,
    val_int  := 0,
    val_real := 0, 
    length   := 0,
    pos      := 0
  #)

  TokenizerOfLength(l:int): TYPE = {t:Tokenizer | t`length = l}

  set_casesen(t:Tokenizer,c:bool):Tokenizer =
    t with [`casesen := c]

  %% Error?
  error?(t:Tokenizer): MACRO bool = 
    t`error /= NoError
 
  %% Set Error
  set_error(t:Tokenizer,code:int): TokenizerOfLength(t`length) =
    t WITH [`error:= code]

  %% Last processed token
  last_token(t:Tokenizer): string =
    IF t`pos = 0 THEN emptystr
    ELSE t`stream(t`pos-1)
    ENDIF

  %% Peek token at current position + offset n
  peek(t:Tokenizer,n:posnat): MACRO string = t`stream(t`pos+n-1)

  %% Next unprocessed token
  next_token(t:Tokenizer) : MACRO string = peek(t,1)

  tostr(t:Tokenizer,i:upto(t`length)) : RECURSIVE string =
    IF i = t`length THEN emptystr
    ELSIF i=t`pos THEN
      "["+t`stream(i)+"] " + tostr(t,i+1)
    ELSE 
      t`stream(i)+space + tostr(t,i+1)
    ENDIF
    MEASURE t`length - i

  add_token(s:string,t:Tokenizer,l:nat): MACRO Tokenizer =
    LET n = t`length IN
    t WITH [`stream(n) := s,
            `lines(n)  := l,
            `length    := n + 1]

  read_token(t:Tokenizer)(f:IStream):string = fread_token(f,t`separ)

  line_tokenizer(s:string,tl:[Tokenizer,nat]):[Tokenizer,nat] =
    LET (t,l) = tl IN
    LET g = fopenin(str,s) IN
    LET f = (LAMBDA(mys:string,myt:Tokenizer):add_token(mys,myt,l)) IN
    LET nt = fmap(g,read_token(t),t,f,length(s)) IN
    prog(fclose(g),(nt,l+1))

  %% Create a tokenizer from a files
  file2tokenizer(s:string,t:Tokenizer):Tokenizer=
    IF fexists(s) THEN 
      LET (nt,l) = fmap_line(fopenin(s),
                             (t,1),
                             line_tokenizer) IN 
        nt
    ELSE	
      LET filename = s IN
      t WITH [
        `stream(0) := filename,
        `error     := FileNotFound
      ]
    ENDIF

  file2tokenizer(s:string):Tokenizer=
    file2tokenizer(s,empty_tokenizer)

  %% Create a tokenizer from a string
  str2tokenizer(s:string,t:Tokenizer):Tokenizer= 
    LET f = fopenin(str,s) IN
    LET (nt,l) = fmap_line(f,(t,1),line_tokenizer) IN 
        nt

  str2tokenizer(s:string):Tokenizer=
    str2tokenizer(s,empty_tokenizer)

  % EOT = End of tokenizer
  eot?(t:Tokenizer) : bool = t`pos = t`length

  % Current line
  get_line(t:Tokenizer) : MACRO nat = t`lines(t`pos) 

  consume(t:Tokenizer,n:posnat) : TokenizerOfLength(t`length) = 
    IF error?(t) THEN t
    ELSIF eot?(t) OR t`pos + n > t`length THEN 
      t WITH [`error := EndOfTokenizer]
    ELSE 
      t WITH [`pos := t`pos + n]
    ENDIF

  go_next(t:Tokenizer) : MACRO TokenizerOfLength(t`length) = 
    consume(t,1)
 
  go_back(t:Tokenizer) : TokenizerOfLength(t`length) = 
    IF t`pos = 0 THEN t
    ELSE 
      t WITH [`pos := t`pos - 1]
    ENDIF

  pos_go_next : LEMMA
    FORALL (t1:Tokenizer):
      LET t2= go_next(t1) IN
      NOT error?(t2) IMPLIES t2`pos = t1`pos+1

  %% Accepts a word that satisfies test
  accept_word(t:Tokenizer,test:[string->bool])
    : TokenizerOfLength(t`length) =
    IF error?(t) THEN t
    ELSIF eot?(t) THEN 
      t WITH [`error := EndOfTokenizer]
    ELSIF number?(t`stream(t`pos)) THEN
      t WITH [`error := ExpectingWord]
    ELSIF test(t`stream(t`pos)) THEN 
      t WITH [`pos := t`pos + 1]
    ELSE 
      t WITH [`error := ExpectingTestWord]
    ENDIF

  pos_accept_word : LEMMA
    FORALL (t1:Tokenizer,test:[string->bool]):
      LET t2= accept_word(t1,test) IN
      NOT error?(t2) IMPLIES t2`pos = t1`pos+1

  the_word(s:string)(token:string):bool =
     str2bool(s,token)

  %% Accepts the word s
  accept_word(t:Tokenizer,s:string) : MACRO TokenizerOfLength(t`length) =
    accept_word(t,the_word(s))

  any_word(token:string):bool = TRUE

  %% Accepts any word
  accept_word(t:Tokenizer) : MACRO TokenizerOfLength(t`length) =
    accept_word(t,any_word)

  %% Accepts an integer that satisfies test
  accept_int(t:Tokenizer,test:[int->bool])
    : TokenizerOfLength(t`length) =
    IF error?(t) THEN t
    ELSIF eot?(t) THEN 
      t WITH [`error := EndOfTokenizer]
    ELSIF NOT int?(t`stream(t`pos)) THEN
      t WITH [`error := ExpectingInt]
    ELSE
      LET i = str2int(t`stream(t`pos)) IN
      IF test(i) THEN 
        t WITH [`pos := t`pos + 1, `val_int := i, `val_real := i]

      ELSE 
        t WITH [`error := ExpectingTestInt]
      ENDIF
    ENDIF

  pos_accept_int : LEMMA
    FORALL (t1:Tokenizer,test:[int->bool]):
      LET t2= accept_int(t1,test) IN
      NOT error?(t2) IMPLIES t2`pos = t1`pos+1

  any_int(i:int):bool = TRUE

  %% Accepts any integer 
  accept_int(t:Tokenizer) : MACRO TokenizerOfLength(t`length) =
    accept_int(t,any_int)

  %% Accepts a real that satisfies test
  accept_real(t:Tokenizer,test:[real->bool])
    : TokenizerOfLength(t`length) =
    IF error?(t) THEN t
    ELSIF eot?(t) THEN 
      t WITH [`error := EndOfTokenizer]
    ELSIF NOT number?(t`stream(t`pos)) THEN
      t WITH [`error := ExpectingReal]
    ELSE
      LET r = str2real(t`stream(t`pos)) IN
      IF test(r) THEN 
        t WITH [`pos := t`pos + 1, `val_real := r]

      ELSE 
        t WITH [`error := ExpectingTestReal]
      ENDIF
    ENDIF

  pos_accept_real : LEMMA
    FORALL (t1:Tokenizer,test:[real->bool]):
      LET t2= accept_real(t1,test) IN
      NOT error?(t2) IMPLIES t2`pos = t1`pos+1

  any_real(r:real):bool = TRUE

  %% Accepts any real
  accept_real(t:Tokenizer) : MACRO TokenizerOfLength(t`length) =
    accept_real(t,any_real)

  %% Messengers

  Messenger : TYPE = [int->string]

  std_mssg(t:Tokenizer)(code:int):string =
    IF    code <= 0 THEN emptystr
    ELSIF code = FileNotFound THEN
      "File not found: "+doublequote+next_token(t)+doublequote+"."
    ELSIF code = EndOfTokenizer THEN
      "Found EOT."
    ELSIF code = InvalidToken THEN 
      "Invalid Token: "+doublequote+next_token(t)+doublequote+"."
    ELSIF code = ExpectingWord THEN 
      "Expecting a word. Found: "+doublequote+next_token(t)+doublequote+"."
    ELSIF code = ExpectingTestWord THEN 
      "Expecting word that satisfies test. Found: "+doublequote+next_token(t)+
      doublequote+"."
    ELSIF code = ExpectingInt THEN 
      "Expecting an integer. Found: "+doublequote+next_token(t)+doublequote+"."
    ELSIF code = ExpectingTestInt THEN 
      "Expecting integer that satisfies test. Found: "+
      doublequote+next_token(t)+doublequote+"."
    ELSIF code = ExpectingReal THEN 
      "Expecting a real. Found: "+doublequote+next_token(t)+doublequote+"."
    ELSIF code = ExpectingTestReal THEN 
      "Expecting real that satisfies test. Found: "+doublequote+next_token(t)+
      doublequote+"."
    ELSE
      emptystr
    ENDIF

  print_error(t:Tokenizer,m:Messenger): MACRO void = 
    IF error?(t) THEN  
      println("Syntax Error. "+"Line "+get_line(t)+": "+m(error(t))) &
      fail
    ELSE
      skip
    ENDIF

  print_error(t:Tokenizer): MACRO void = 
    print_error(t,std_mssg(t))

  ;*(m:Messenger,cs:[int,string]) : Messenger =
    LET (code,s) = cs IN m WITH [`code := s]
  
  tokenizer2str(t:Tokenizer):string = 
    tostr(t,0)+newline+std_mssg(t)(t`error)

  CONVERSION tokenizer2str

  tostr(t:Tokenizer): MACRO string = tokenizer2str(t)

END stdtokenizer

%% stdpvsio.pvs
%% PVSio interface

stdpvsio : THEORY
BEGIN

  help_pvs_attachment(s:string) : void 

  help_pvs_theory_attachments(s:string) : void 

  pvsio_version : string

  set_promptin(s:string) : void

  set_promptout(s:string) : void

END stdpvsio

%% stdsys.pvs
%% PVSio sytem library 

stdsys : THEORY
BEGIN
  
  Time : TYPE = [# 
    second : below(60),
    minute : below(60),
    hour   : below(24),
    day    : subrange(1,31),
    month  : subrange(1,12),
    year   : nat,
    dow    : below(7), % Day of week: 0 Monday
    dst    : bool,     % Dayligth saving time?
    tz     : {x:rat| -24 <= x AND x <= 24} % Time zone 
  #]

  tinybang : Time = (#
    day    := 1,
    dow    := 0,
    dst    := false,
    hour   := 0,
    minute := 0,
    month  := 1,
    second := 0,
    tz     := 0,
    year   := 0
  #)

  days_of_week(dow:below(7)):string =
    if    dow=0 then "Monday"
    elsif dow=1 then "Tuesday"
    elsif dow=2 then "Wednesday"
    elsif dow=3 then "Thursday" 
    elsif dow=4 then "Friday"
    elsif dow=5 then "Saturday"
    else  "Sunday"
    endif
 
  months(month:subrange(1,12)):string =
    if    month=1  then "January"
    elsif month=2  then "February"
    elsif month=3  then "March"
    elsif month=4  then "April"
    elsif month=5  then "May"
    elsif month=6  then "June"
    elsif month=7  then "July"
    elsif month=8  then "August"
    elsif month=9  then "September"
    elsif month=10 then "October"
    elsif month=11 then "November"
    else "December"
    endif

  tostr(t:Time): string =
    days_of_week(dow(t))+" "+months(month(t))+
    format(" ~2,'0d ",day(t))+year(t)+
    format(", ~2,'0d:~2,'0d:~2,'0d ",(hour(t),minute(t),second(t)))+
    format("(GMT~@d)",-tz(t))

  get_time : Time     % Get current time

  today : string =
    let t = get_time in
    format("~d/~2,'0d/~d",(month(t),day(t),year(t)))

  date : string =
    let t = get_time in
     tostr(get_time)

  sleep(n:nat): void  % Sleep n seconds 

  get_env(name,default:string):string % Get environment variable (default)

  get_env(name:string): MACRO string = get_env(name,emptystr)

  printf(s:string) : MACRO void =
    print(format(s,""))

  fprintf(f:OStream,s:string): MACRO void =
    fprint(f,format(s,""))

  Blisp : TYPE+  

  blisp(b:bool) : Blisp

  {||}(b:bool) : Blisp = blisp(b)

END stdsys