(* ---------------------------------------------------------------------- *)
(*  Reals                                                                 *)
(* ---------------------------------------------------------------------- *)
let real_ty = `:real`;;
let rx = `x:real`;;
let ry = `y:real`;;
let rz = `z:real`;;
let rzero = `&0`;;
let req = `(=):real->real->bool`;;
let rneq = `(<>):real->real->bool`;;
let rlt = `(<):real->real->bool`;;
let rgt = `(>):real->real->bool`;;
let rle = `(<=):real->real->bool`;;
let rge = `(>=):real->real->bool`;;
let rm = `( * ):real->real->real`;;
let rs = `(-):real->real->real`;;
let rn = `(--):real->real`;;
let rd = `(/):real->real->real`;;
let rp = `(+):real->real->real`;;
let rzero = `&0`;;
let rone = `&1`;;
let rlast = `LAST:(real) list -> real`;;
let rappend = `APPEND:(real) list -> real list -> real list`;;
let mk_rlist l = mk_list (l,real_ty);;

let diffl_tm = `(diffl)`;;
let dest_diffl tm =
  try
    let l,var = dest_comb tm in
    let dp,p' = dest_comb l in
    let d,p = dest_comb dp in
    if not (d = diffl_tm) then failwith "dest_diffl: not a diffl" else
    let _,bod = dest_abs p in
      bod,p'
  with _ -> failwith "dest_diffl";;

let dest_mult =
  try
    dest_binop rm
  with _ -> failwith "dest_mult";;

let mk_mult = mk_binop rm;;

let pow_tm = `(pow)`;;
let dest_pow =
  try
    dest_binop pow_tm
  with _ -> failwith "dest_pow";;

let mk_plus = mk_binop rp;;
let mk_negative = curry mk_comb rn;;

let dest_plus =
  try
    dest_binop rp
  with _ -> failwith "dest_plus";;

let REAL_DENSE = 
prove( `!x y. x < y ==> ?z. x < z /\ z < y`,
(* {{{ Proof *) REPEAT STRIP_TAC THEN CLAIM `&0 < y - x` THENL [REWRITE_TAC[REAL_LT_SUB_LADD;REAL_ADD_LID] THEN POP_ASSUM MATCH_ACCEPT_TAC; DISCH_THEN (ASSUME_TAC o (MATCH_MP REAL_DOWN)) THEN POP_ASSUM MP_TAC THEN STRIP_TAC THEN EXISTS_TAC `e + x` THEN STRIP_TAC THENL [ONCE_REWRITE_TAC[REAL_ADD_SYM] THEN CONV_TAC (LAND_CONV (ONCE_REWRITE_CONV[GSYM REAL_ADD_RID])) THEN MATCH_MP_TAC REAL_LET_ADD2 THEN STRIP_TAC THENL [MATCH_ACCEPT_TAC REAL_LE_REFL; FIRST_ASSUM MATCH_ACCEPT_TAC]; MATCH_EQ_MP_TAC ((GEN `y:real` (GEN `z:real` (ISPECL [`y:real`;`z:real`;`-- x`] REAL_LT_RADD)))) THEN REWRITE_TAC[GSYM REAL_ADD_ASSOC;REAL_ADD_RINV;REAL_ADD_RID] THEN REWRITE_TAC[GSYM real_sub] THEN FIRST_ASSUM MATCH_ACCEPT_TAC]]);;
(* }}} *)
let REAL_LT_EXISTS = 
prove( `!x. ?y. x < y`,
(* {{{ Proof *) GEN_TAC THEN EXISTS_TAC `x + &1` THEN REAL_ARITH_TAC);;
(* }}} *)
let REAL_GT_EXISTS = 
prove( `!x. ?y. y < x`,
(* {{{ Proof *) GEN_TAC THEN EXISTS_TAC `x - &1` THEN REAL_ARITH_TAC);;
(* }}} *)
let REAL_DIV_DISTRIB_L = 
prove_by_refinement( `!x y z. x / (y * z) = (x / y) * (&1 / z)`,
(* {{{ Proof *) [ REWRITE_TAC[real_div;REAL_INV_MUL]; REAL_ARITH_TAC; ]);;
(* }}} *)
let REAL_DIV_DISTRIB_R = 
prove_by_refinement( `!x y z. x / (y * z) = (&1 / y) * (x / z)`,
(* {{{ Proof *) [ REWRITE_TAC[real_div;REAL_INV_MUL]; REAL_ARITH_TAC; ]);;
(* }}} *)
let REAL_DIV_DISTRIB_2 = 
prove_by_refinement( `!x y z. (x * w) / (y * z) = (x / y) * (w / z)`,
(* {{{ Proof *) [ REWRITE_TAC[real_div;REAL_INV_MUL]; REAL_ARITH_TAC; ]);;
(* }}} *)
let REAL_DIV_ADD_DISTRIB = 
prove_by_refinement( `!x y z. (x + y) / z = (x / z) + (y / z)`,
(* {{{ Proof *) [ REWRITE_TAC[real_div;REAL_INV_MUL]; REAL_ARITH_TAC; ]);;
(* }}} *)
let DIV_ID = 
prove_by_refinement( `!x. ~(x = &0) ==> (x / x = &1)`,
(* {{{ Proof *) [ REPEAT STRIP_TAC; REWRITE_TAC[real_div]; ASM_MESON_TAC[REAL_MUL_LINV;REAL_MUL_SYM]; ]);;
(* }}} *)
let POS_POW = 
prove_by_refinement( `!c x. &0 < c /\ &0 < x ==> &0 < c * x pow k`,
(* {{{ Proof *) [ MESON_TAC[REAL_POW_LT;REAL_LT_MUL] ]);;
(* }}} *)
let POS_NAT_POW = 
prove_by_refinement( `!c n. 0 < n /\ &0 < c ==> &0 < c * &n pow k`,
(* {{{ Proof *) [ MESON_TAC[REAL_POW_LT;REAL_LT_MUL;REAL_LT;] ]);;
(* }}} *)
let REAL_NUM_LE_0 = 
prove_by_refinement( `!n. &0 <= (&n)`,
(* {{{ Proof *) [ INDUCT_TAC; REAL_ARITH_TAC; REWRITE_TAC[REAL]; REAL_ARITH_TAC; ]);;
(* }}} *)
let REAL_ARCH_SIMPLE_LT = 
prove_by_refinement( `!x. ?n. x < &n`,
(* {{{ Proof *) [ STRIP_TAC; CHOOSE_THEN ASSUME_TAC (ISPEC `x:real` REAL_ARCH_SIMPLE); EXISTS_TAC `SUC n`; REWRITE_TAC[REAL]; POP_ASSUM MP_TAC THEN REAL_ARITH_TAC; ]);;
(* }}} *)
let BINOMIAL_LEMMA_LT = 
prove_by_refinement( `!x y. &0 < x /\ &0 < y ==> !n. 0 < n ==> x pow n + y pow n <= (x + y) pow n`,
(* {{{ Proof *) [ REPEAT GEN_TAC; STRIP_TAC; INDUCT_TAC; ARITH_TAC; REWRITE_TAC[real_pow]; STRIP_TAC; CASES_ON `n = 0`; ASM_REWRITE_TAC[real_pow;REAL_MUL_RID;REAL_LE_REFL]; CLAIM `0 < n`; POP_ASSUM MP_TAC THEN ARITH_TAC; DISCH_THEN (fun x -> FIRST_ASSUM (fun y -> ASSUME_TAC (MATCH_MP y x))); MATCH_MP_TAC REAL_LE_TRANS; EXISTS_TAC `(x + y) * (x pow n + y pow n)`; STRIP_TAC; REWRITE_TAC[REAL_ADD_RDISTRIB]; MATCH_MP_TAC REAL_LE_ADD2; CONJ_TAC; MATCH_MP_TAC REAL_LE_LMUL; STRIP_TAC; FIRST_ASSUM (fun x -> MP_TAC x THEN ARITH_TAC); MATCH_MP_TAC (REAL_ARITH `&0 <= y ==> x <= x + y`); MATCH_MP_TAC REAL_POW_LE; FIRST_ASSUM (fun x -> MP_TAC x THEN ARITH_TAC); REWRITE_TAC[REAL_ADD_LDISTRIB]; MATCH_MP_TAC (REAL_ARITH `&0 <= y ==> x <= y + x`); MATCH_MP_TAC REAL_LE_MUL; CONJ_TAC; FIRST_ASSUM (fun x -> MP_TAC x THEN REAL_ARITH_TAC); MATCH_MP_TAC (REAL_ARITH `x < y ==> x <= y`); MATCH_MP_TAC REAL_POW_LT; FIRST_ASSUM MATCH_ACCEPT_TAC; MATCH_MP_TAC REAL_LE_LMUL; CONJ_TAC; EVERY_ASSUM MP_TAC THEN REAL_ARITH_TAC; FIRST_ASSUM MATCH_ACCEPT_TAC; ]);;
(* }}} *)
let BINOMIAL_LEMMA = 
prove_by_refinement( `!x y. &0 <= x /\ &0 <= y ==> !n. 0 < n ==> x pow n + y pow n <= (x + y) pow n`,
(* {{{ Proof *) [ REPEAT GEN_TAC; STRIP_TAC; CASES_ON `(x = &0) \/ (y = &0)`; POP_ASSUM DISJ_CASES_TAC; ASM_REWRITE_TAC[real_pow;REAL_ADD_LID;POW_0]; REPEAT STRIP_TAC; CLAIM `n = SUC (PRE n)`; POP_ASSUM MP_TAC THEN ARITH_TAC; STRIP_TAC; ONCE_ASM_REWRITE_TAC[]; ASM_REWRITE_TAC[POW_0;REAL_ADD_LID;real_pow;REAL_LE_REFL]; REPEAT STRIP_TAC; CLAIM `n = SUC (PRE n)`; POP_ASSUM MP_TAC THEN ARITH_TAC; STRIP_TAC; ONCE_ASM_REWRITE_TAC[]; ASM_REWRITE_TAC[POW_0;REAL_ADD_LID;REAL_ADD_RID;real_pow;REAL_LE_REFL]; POP_ASSUM MP_TAC THEN REWRITE_TAC[DE_MORGAN_THM] THEN STRIP_TAC; MATCH_MP_TAC BINOMIAL_LEMMA_LT; EVERY_ASSUM MP_TAC THEN REAL_ARITH_TAC; ]);;
(* }}} *)
let NEG_ABS = 
prove_by_refinement( `!x. -- (abs x) <= &0`,
(* {{{ Proof *) [ REAL_ARITH_TAC; ]);;
(* }}} *)
let REAL_MUL_LT = 
prove_by_refinement( `!x y. x * y < &0 <=> (x < &0 /\ &0 < y) \/ (&0 < x /\ y < &0)`,
(* {{{ Proof *) [ REPEAT STRIP_TAC; EQ_TAC; REPEAT STRIP_TAC; CCONTR_TAC; REWRITE_ASSUMS ([REAL_NOT_LT;DE_MORGAN_THM;] @ !REAL_REWRITES); POP_ASSUM MP_TAC THEN STRIP_TAC; CLAIM `x = &0`; EVERY_ASSUM MP_TAC THEN REAL_ARITH_TAC; DISCH_THEN (REWRITE_ASSUMS o list); REWRITE_ASSUMS !REAL_REWRITES; ASM_MESON_TAC !REAL_REWRITES; CLAIM `&0 * &0 <= x * y`; MATCH_MP_TAC REAL_LE_MUL2; EVERY_ASSUM MP_TAC THEN REAL_ARITH_TAC; REAL_SIMP_TAC; EVERY_ASSUM MP_TAC THEN REAL_ARITH_TAC; CLAIM `&0 * &0 <= --x * --y`; MATCH_MP_TAC REAL_LE_MUL2; REAL_SIMP_TAC; EVERY_ASSUM MP_TAC THEN REAL_ARITH_TAC; REAL_SIMP_TAC; EVERY_ASSUM MP_TAC THEN REAL_ARITH_TAC; CLAIM `y = &0`; EVERY_ASSUM MP_TAC THEN REAL_ARITH_TAC; DISCH_THEN (REWRITE_ASSUMS o list); REWRITE_ASSUMS !REAL_REWRITES; ASM_REWRITE_TAC[]; EVERY_ASSUM MP_TAC THEN ARITH_TAC; (* save *) REPEAT STRIP_TAC; CLAIM `&0 < --x`; EVERY_ASSUM MP_TAC THEN ARITH_TAC; STRIP_TAC; CLAIM `&0 * &0 < --x * y`; MATCH_MP_TAC REAL_LT_MUL2; REAL_SIMP_TAC; EVERY_ASSUM MP_TAC THEN REAL_ARITH_TAC; REAL_SIMP_TAC; REWRITE_TAC[REAL_ARITH `--y * x = --(y * x)`]; REAL_ARITH_TAC; CLAIM `&0 < --y`; EVERY_ASSUM MP_TAC THEN REAL_ARITH_TAC; STRIP_TAC; CLAIM `&0 * &0 < x * --y`; MATCH_MP_TAC REAL_LT_MUL2; REAL_SIMP_TAC; ASM_REWRITE_TAC[]; REAL_SIMP_TAC; REWRITE_TAC[REAL_ARITH `x * --y = --(x * y)`]; REAL_ARITH_TAC; ]);;
(* }}} *)
let REAL_MUL_GT = 
prove_by_refinement( `!x y. &0 < x * y <=> (x < &0 /\ y < &0) \/ (&0 < x /\ &0 < y)`,
(* {{{ Proof *) [ REPEAT STRIP_TAC; EQ_TAC; REPEAT STRIP_TAC; ONCE_REWRITE_ASSUMS[ARITH_RULE `x < y <=> -- y < -- x`]; REWRITE_ASSUMS[GSYM REAL_MUL_RNEG]; REWRITE_ASSUMS[REAL_ARITH `-- &0 = &0`; REAL_MUL_LT]; POP_ASSUM MP_TAC THEN REPEAT STRIP_TAC; DISJ1_TAC; EVERY_ASSUM MP_TAC THEN REAL_ARITH_TAC; EVERY_ASSUM MP_TAC THEN REAL_ARITH_TAC; REPEAT STRIP_TAC; ONCE_REWRITE_TAC [ARITH_RULE `x * y = --x * --y`]; ONCE_REWRITE_TAC [ARITH_RULE `&0 = &0 * &0`]; MATCH_MP_TAC REAL_LT_MUL2; EVERY_ASSUM MP_TAC THEN REAL_ARITH_TAC; ONCE_REWRITE_TAC [ARITH_RULE `&0 = &0 * &0`]; MATCH_MP_TAC REAL_LT_MUL2; EVERY_ASSUM MP_TAC THEN REAL_ARITH_TAC; ]);;
(* }}} *)
let REAL_DIV_INV = 
prove_by_refinement( `!y z. &0 < y /\ y < z ==> &1 / z < &1 / y`,
(* {{{ Proof *) [ REPEAT STRIP_TAC; REWRITE_TAC[real_div]; REAL_SIMP_TAC; MATCH_MP_TAC REAL_LT_INV2; ASM_MESON_TAC[]; ]);;
(* }}} *)
let REAL_DIV_DENOM_LT = 
prove_by_refinement( `!x y z. &0 < x /\ &0 < y /\ y < z ==> x / z < x / y`,
(* {{{ Proof *) [ REPEAT STRIP_TAC; MATCH_MP_TAC REAL_LT_LCANCEL_IMP; EXISTS_TAC `inv x`; REPEAT STRIP_TAC; REAL_SOLVE_TAC; REWRITE_TAC[real_div]; ASM_SIMP_TAC[REAL_LT_IMP_NZ;REAL_MUL_ASSOC;REAL_MUL_LINV;]; REAL_SIMP_TAC; MATCH_MP_TAC (REWRITE_RULE [REAL_MUL_LID;real_div] REAL_DIV_INV); ASM_MESON_TAC[]; ]);;
(* }}} *)
let REAL_DIV_DENOM_LE = 
prove_by_refinement( `!x y z. &0 <= x /\ &0 < y /\ y <= z ==> x / z <= x / y`,
(* {{{ Proof *) [ REPEAT STRIP_TAC; CASES_ON `x = &0`; ASM_REWRITE_TAC[]; REWRITE_TAC[real_div;REAL_MUL_LZERO;REAL_LE_REFL]; MATCH_MP_TAC REAL_LE_LCANCEL_IMP; EXISTS_TAC `inv x`; REPEAT STRIP_TAC; MATCH_MP_TAC REAL_LT_INV; ASM_MESON_TAC[REAL_LT_LE]; REWRITE_TAC[real_div]; ASM_SIMP_TAC[REAL_LT_IMP_NZ;REAL_MUL_ASSOC;REAL_MUL_LINV;]; REAL_SIMP_TAC; MATCH_MP_TAC REAL_LE_INV2; ASM_REWRITE_TAC[]; ]);;
(* }}} *)
let REAL_NEG_DIV = 
prove_by_refinement( `!x y. -- x / -- y = x / y`,
(* {{{ Proof *) [ REWRITE_TAC[real_div]; REWRITE_TAC[REAL_INV_NEG]; REAL_ARITH_TAC; ]);;
(* }}} *)
let REAL_GT_IMP_NZ = 
prove( `!x. x < &0 ==> ~(x = &0)`,
(* {{{ Proof *) REAL_ARITH_TAC);;
(* }}} *)
let REAL_NEG_NZ = 
prove( `!x. x < &0 ==> ~(x = &0)`,
(* {{{ Proof *) REAL_ARITH_TAC);;
(* }}} *)
let PARITY_POW_LT = 
prove_by_refinement( `!a n. a < &0 ==> (EVEN n ==> a pow n > &0) /\ (ODD n ==> a pow n < &0)`,
(* {{{ Proof *) [ STRIP_TAC; INDUCT_TAC; REWRITE_TAC[EVEN;ODD;real_pow]; REAL_ARITH_TAC; DISCH_THEN (fun x -> REWRITE_ASSUMS[x] THEN ASSUME_TAC x); REWRITE_TAC[EVEN;ODD;real_pow;NOT_EVEN;NOT_ODD]; DISJ_CASES_TAC (ISPEC `n:num` EVEN_OR_ODD); ASM_REWRITE_TAC[]; REPEAT STRIP_TAC; ASM_REWRITE_TAC[real_gt;REAL_MUL_GT]; ASM_MESON_TAC[EVEN_AND_ODD]; ASM_REWRITE_TAC[real_gt;REAL_MUL_LT]; ASM_MESON_TAC[real_gt]; ASM_REWRITE_TAC[]; ASM_REWRITE_TAC[real_gt;REAL_MUL_LT;REAL_MUL_GT]; REPEAT STRIP_TAC; ASM_MESON_TAC[]; ASM_MESON_TAC[EVEN_AND_ODD]; ]);;
(* }}} *)
let EVEN_ODD_POW = 
prove_by_refinement( `!a n. a <> &0 ==> (EVEN n ==> a pow n > &0) /\ (ODD n ==> a < &0 ==> a pow n < &0) /\ (ODD n ==> a > &0 ==> a pow n > &0)`,
(* {{{ Proof *) [ REWRITE_TAC[NEQ]; REPEAT_N 2 STRIP_TAC; CLAIM `a < &0 \/ a > &0 \/ (a = &0)`; REAL_ARITH_TAC; ASM_REWRITE_TAC[]; STRIP_TAC; REPEAT STRIP_TAC; ASM_MESON_TAC[PARITY_POW_LT]; ASM_MESON_TAC[PARITY_POW_LT]; ASM_MESON_TAC[REAL_POW_LT;real_gt]; REPEAT STRIP_TAC; ASM_MESON_TAC[REAL_POW_LT;real_gt]; EVERY_ASSUM MP_TAC THEN REAL_ARITH_TAC; ASM_MESON_TAC[REAL_POW_LT;real_gt]; ASM_REWRITE_TAC[]; ]);;
(* }}} *)