module BigO
import UInt
import List
fun operator + (f : fn UInt -> UInt, g : fn UInt -> UInt) {
fun x:UInt {
f(x) + g(x)
}
}
fun operator * (f : fn UInt -> UInt, g : fn UInt -> UInt) {
fun x:UInt {
f(x) * g(x)
}
}
fun operator * (k : UInt, g : fn UInt -> UInt) {
fun x:UInt {
k * g(x)
}
}
fun operator ≲ (f : fn UInt -> UInt, g : fn UInt -> UInt) {
some n0 :UInt, c:UInt. all n:UInt. if n0 ≤ n then f(n) ≤ c * g(n)
}
fun operator ≈ (f : fn UInt -> UInt, g : fn UInt -> UInt) {
(f ≲ g) and (g ≲ f)
}
fun log(f : fn UInt -> UInt) {
fun x:UInt { log(f(x)) }
}
fun O_1(n:UInt) { 1 }
fun O_log(n:UInt) { log(n) }
fun O_n(n:UInt) { n }
fun O_n2(n:UInt) { n * n }
theorem bigo_refl: all f : fn UInt -> UInt. f ≲ f
proof
arbitrary f : fn UInt -> UInt
expand operator≲
choose 0, 1
arbitrary n:UInt
assume prem
show f(n) ≤ 1 * f(n)
uint_less_equal_refl
end
theorem bigo_trans: all f : fn UInt -> UInt, g : fn UInt -> UInt, h : fn UInt -> UInt.
if f ≲ g and g ≲ h then f ≲ h
proof
arbitrary f : fn UInt -> UInt, g : fn UInt -> UInt, h : fn UInt -> UInt
assume prem
have fg: f ≲ g by prem
obtain n1, c1 where fg_all: all n:UInt. if n1 ≤ n then f(n) ≤ c1 * g(n)
from expand operator≲ in fg
have gh: g ≲ h by prem
obtain n2, c2 where gh_all: all n:UInt. if n2 ≤ n then g(n) ≤ c2 * h(n)
from expand operator≲ in gh
expand operator≲
choose n1+n2, c1*c2
arbitrary n:UInt
assume n12_n
show f(n) ≤ (c1 * c2) * h(n)
have n1_n: n1 ≤ n by {
have n1_n12: n1 ≤ n1 + n2 by uint_less_equal_add
apply uint_less_equal_trans to n1_n12, n12_n
}
have fg_n: f(n) ≤ c1 * g(n) by apply fg_all to n1_n
have n2_n: n2 ≤ n by {
have n2_n21: n2 ≤ n1 + n2 by { replace uint_add_commute uint_less_equal_add }
apply uint_less_equal_trans to n2_n21, n12_n
}
have gh_n: g(n) ≤ c2 * h(n) by apply gh_all to n2_n
have c1g_c12_h: c1 * g(n) ≤ c1 * c2 * h(n)
by apply uint_mult_mono_le[c1, g(n), c2*h(n)] to gh_n
conclude f(n) ≤ c1 * c2 * h(n)
by apply uint_less_equal_trans to fg_n, c1g_c12_h
end
theorem bigo_add: all f: fn UInt->UInt. f + f ≲ f
proof
arbitrary f: fn UInt->UInt
expand operator≲
choose 0, 2
arbitrary n:UInt
assume prem
expand operator+
show f(n) + f(n) ≤ 2 * f(n)
replace uint_two_mult.
end
theorem bigo_const_mult: all f: fn UInt -> UInt, k:UInt.
k * f ≲ f
proof
arbitrary f: fn UInt -> UInt
define P = fun c:UInt { c * f ≲ f }
have base: P(0) by {
expand P | operator≲ | operator*.
}
have ind: all m:UInt. if P(m) then P(1 + m) by {
arbitrary m:UInt
expand P
assume mf_f: m * f ≲ f
obtain n0, c where mf_f_all: all n:UInt. if n0 ≤ n then m * f(n) ≤ c * f(n)
from expand operator≲ | operator* in mf_f
expand operator≲
choose n0, 2*(1 + c)
arbitrary n:UInt
assume n0_n
expand operator*
replace uint_dist_mult_add_right | uint_dist_mult_add
show f(n) + m * f(n) ≤ 2 * f(n) + 2 * c * f(n)
have fn_cfn: f(n) ≤ 2 * f(n) by replace uint_two_mult uint_less_equal_add
have mfn_2cfn: m * f(n) ≤ 2 * c * f(n) by {
have IH: m * f(n) ≤ c * f(n) by apply mf_f_all[n] to n0_n
have cfn_2cfn: c * f(n) ≤ 2 * c * f(n) by {
apply uint_mult_mono_le2[1,c*f(n),2,c*f(n)] to .
}
apply uint_less_equal_trans to IH, cfn_2cfn
}
conclude f(n) + m * f(n) ≤ 2 * f(n) + 2 * c * f(n)
by apply uint_add_mono_less_equal to fn_cfn, mfn_2cfn
}
expand P in apply uint_induction[P] to base, ind
end
theorem bigo_mult_mono:
all f1: fn UInt->UInt, f2: fn UInt->UInt, g1: fn UInt->UInt, g2: fn UInt->UInt.
if f1 ≲ g1 and f2 ≲ g2
then f1 * f2 ≲ g1 * g2
proof
arbitrary f1: fn UInt->UInt, f2: fn UInt->UInt, g1: fn UInt->UInt, g2: fn UInt->UInt
assume prem
expand operator≲ | operator*
have f1_g1: f1 ≲ g1 by prem
obtain n1, c1 where f1_c1g1_all: all n:UInt. if n1 ≤ n then f1(n) ≤ c1 * g1(n)
from expand operator≲ in f1_g1
have f2_g2: f2 ≲ g2 by prem
obtain n2, c2 where f2_c2g2_all: all n:UInt. if n2 ≤ n then f2(n) ≤ c2 * g2(n)
from expand operator≲ in f2_g2
choose n1 + n2, c1 * c2
arbitrary n:UInt
assume n12_n: n1 + n2 ≤ n
show f1(n) * f2(n) ≤ c1 * c2 * g1(n) * g2(n)
have n1_n: n1 ≤ n by {
have n1_n12: n1 ≤ n1 + n2 by uint_less_equal_add
apply uint_less_equal_trans to n1_n12, n12_n
}
have f1_c1g1: f1(n) ≤ c1 * g1(n)
by apply f1_c1g1_all to n1_n
have n2_n: n2 ≤ n by {
have n2_n12: n2 ≤ n1 + n2 by { replace uint_add_commute uint_less_equal_add }
apply uint_less_equal_trans to n2_n12, n12_n
}
have f2_c2g2: f2(n) ≤ c2 * g2(n)
by apply f2_c2g2_all to n2_n
have f12_c1g1_c2g2: f1(n) * f2(n) ≤ c1 * g1(n) * c2 * g2(n)
by apply uint_mult_mono_le2[f1(n), f2(n)] to f1_c1g1, f2_c2g2
conclude f1(n) * f2(n) ≤ c1 * c2 * g1(n) * g2(n)
by replace uint_mult_commute[g1(n),c2] in f12_c1g1_c2g2
end
theorem bigo_pointwise_le: all f:fn UInt->UInt, g:fn UInt->UInt.
if (all n:UInt. f(n) ≤ g(n)) then f ≲ g
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt
assume H
expand operator≲
choose 0, 1
arbitrary n:UInt
assume _
show f(n) ≤ 1 * g(n)
H
end
theorem bigo_self_le_add: all f:fn UInt->UInt, g:fn UInt->UInt. f ≲ f + g
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt
expand operator≲ | operator+
choose 0, 1
arbitrary n:UInt
assume _
show f(n) ≤ 1 * (f(n) + g(n))
uint_less_equal_add
end
theorem bigo_le_add_self: all f:fn UInt->UInt, g:fn UInt->UInt. g ≲ f + g
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt
expand operator≲ | operator+
choose 0, 1
arbitrary n:UInt
assume _
show g(n) ≤ 1 * (f(n) + g(n))
uint_less_equal_add_left
end
theorem bigo_equiv_refl: all f:fn UInt->UInt. f ≈ f
proof
arbitrary f:fn UInt->UInt
expand operator≈
have rf: f ≲ f by bigo_refl
rf, rf
end
theorem bigo_equiv_sym: all f:fn UInt->UInt, g:fn UInt->UInt.
if f ≈ g then g ≈ f
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt
assume H
have fg: f ≲ g by expand operator≈ in H
have gf: g ≲ f by expand operator≈ in H
expand operator≈
gf, fg
end
theorem bigo_equiv_trans:
all f:fn UInt->UInt, g:fn UInt->UInt, h:fn UInt->UInt.
if f ≈ g and g ≈ h then f ≈ h
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt, h:fn UInt->UInt
assume prem
have fg: f ≈ g by prem
have gh: g ≈ h by prem
have f_g: f ≲ g by expand operator≈ in fg
have g_f: g ≲ f by expand operator≈ in fg
have g_h: g ≲ h by expand operator≈ in gh
have h_g: h ≲ g by expand operator≈ in gh
have fh: f ≲ h by apply bigo_trans to f_g, g_h
have hf: h ≲ f by apply bigo_trans to h_g, g_f
expand operator≈
fh, hf
end
theorem bigo_add_mono:
all f1:fn UInt->UInt, f2:fn UInt->UInt, g1:fn UInt->UInt, g2:fn UInt->UInt.
if f1 ≲ g1 and f2 ≲ g2 then f1 + f2 ≲ g1 + g2
proof
arbitrary f1:fn UInt->UInt, f2:fn UInt->UInt, g1:fn UInt->UInt, g2:fn UInt->UInt
assume prem
have f1g1: f1 ≲ g1 by prem
obtain n1, c1 where f1_g1_all: all n:UInt. if n1 ≤ n then f1(n) ≤ c1 * g1(n)
from expand operator≲ in f1g1
have f2g2: f2 ≲ g2 by prem
obtain n2, c2 where f2_g2_all: all n:UInt. if n2 ≤ n then f2(n) ≤ c2 * g2(n)
from expand operator≲ in f2g2
expand operator≲ | operator+
choose n1 + n2, c1 + c2
arbitrary n:UInt
assume n12_n: n1 + n2 ≤ n
show f1(n) + f2(n) ≤ (c1 + c2) * (g1(n) + g2(n))
have n1_n12: n1 ≤ n1 + n2 by uint_less_equal_add
have n1_n: n1 ≤ n by apply uint_less_equal_trans to n1_n12, n12_n
have n2_n12: n2 ≤ n1 + n2 by uint_less_equal_add_left
have n2_n: n2 ≤ n by apply uint_less_equal_trans to n2_n12, n12_n
have f1_le: f1(n) ≤ c1 * g1(n) by apply f1_g1_all to n1_n
have f2_le: f2(n) ≤ c2 * g2(n) by apply f2_g2_all to n2_n
have c1_le: c1 ≤ c1 + c2 by uint_less_equal_add
have c2_le: c2 ≤ c1 + c2 by uint_less_equal_add_left
have g1_le: g1(n) ≤ g1(n) by .
have g2_le: g2(n) ≤ g2(n) by .
have step_c1: c1 * g1(n) ≤ (c1 + c2) * g1(n)
by apply uint_mult_mono_le2 to c1_le, g1_le
have step_c2: c2 * g2(n) ≤ (c1 + c2) * g2(n)
by apply uint_mult_mono_le2 to c2_le, g2_le
have f1_bigger: f1(n) ≤ (c1 + c2) * g1(n)
by apply uint_less_equal_trans to f1_le, step_c1
have f2_bigger: f2(n) ≤ (c1 + c2) * g2(n)
by apply uint_less_equal_trans to f2_le, step_c2
have step_add: f1(n) + f2(n) ≤ (c1 + c2) * g1(n) + (c1 + c2) * g2(n)
by apply uint_add_mono_less_equal to f1_bigger, f2_bigger
conclude f1(n) + f2(n) ≤ (c1 + c2) * (g1(n) + g2(n))
by replace uint_dist_mult_add
step_add
end
theorem bigo_add_commute: all f:fn UInt->UInt, g:fn UInt->UInt.
f + g ≈ g + f
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt
expand operator≈
have A: f + g ≲ g + f by {
expand operator≲ | operator+
choose 0, 1
arbitrary n:UInt
assume _
show f(n) + g(n) ≤ 1 * (g(n) + f(n))
replace uint_add_commute[g(n), f(n)].
}
have B: g + f ≲ f + g by {
expand operator≲ | operator+
choose 0, 1
arbitrary n:UInt
assume _
show g(n) + f(n) ≤ 1 * (f(n) + g(n))
replace uint_add_commute[f(n), g(n)].
}
A, B
end
theorem bigo_mult_commute: all f:fn UInt->UInt, g:fn UInt->UInt.
f * g ≈ g * f
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt
expand operator≈
have A: f * g ≲ g * f by {
expand operator≲ | operator*
choose 0, 1
arbitrary n:UInt
assume _
show f(n) * g(n) ≤ 1 * (g(n) * f(n))
replace uint_mult_commute[g(n), f(n)].
}
have B: g * f ≲ f * g by {
expand operator≲ | operator*
choose 0, 1
arbitrary n:UInt
assume _
show g(n) * f(n) ≤ 1 * (f(n) * g(n))
replace uint_mult_commute[f(n), g(n)].
}
A, B
end
theorem bigo_add_absorb: all f:fn UInt->UInt, g:fn UInt->UInt.
if g ≲ f then f + g ≲ f
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt
assume g_f: g ≲ f
have f_f: f ≲ f by bigo_refl
have step: f + g ≲ f + f
by apply bigo_add_mono to f_f, g_f
have ff: f + f ≲ f by bigo_add
apply bigo_trans to step, ff
end
theorem bigo_le_const_mult: all f:fn UInt->UInt, k:UInt.
if 1 ≤ k then f ≲ k * f
proof
arbitrary f:fn UInt->UInt, k:UInt
assume k_pos: 1 ≤ k
expand operator≲ | operator*
choose 0, 1
arbitrary n:UInt
assume _
show f(n) ≤ 1 * (k * f(n))
have fn_fn: f(n) ≤ f(n) by .
have step: 1 * f(n) ≤ k * f(n)
by apply uint_mult_mono_le2 to k_pos, fn_fn
step
end
theorem bigo_const_func_le_O_1: all c:UInt.
(fun n:UInt { c }) ≲ O_1
proof
arbitrary c:UInt
expand operator≲ | O_1
choose 0, c
arbitrary n:UInt
assume _
show c ≤ c * 1
.
end
theorem constant_le_log: O_1 ≲ O_log
proof
expand operator≲ | O_1 | O_log
choose 2, 1
arbitrary n:UInt
assume two_n
apply uint_log_greater_one to two_n
end
theorem log_le_linear: O_log ≲ O_n
proof
expand operator≲ | O_log | O_n
choose 0, 1
arbitrary n:UInt
assume _
uint_logn_le_n
end
theorem linear_le_quadratic: O_n ≲ O_n2
proof
expand operator≲ | O_n | O_n2
choose 1, 1
arbitrary n:UInt
assume pos
have n_n: n ≤ n by .
conclude 1 * n ≤ n * n by apply uint_mult_mono_le2 to pos, n_n
end
fun O_2n(n:UInt) { 2*n }
theorem O_n_eq_O_2n: O_n ≈ O_2n
proof
expand operator≈
have A: O_n ≲ O_2n by {
expand operator≲
choose 0, 1
arbitrary n:UInt
assume _
expand O_n | O_2n
replace uint_two_mult
uint_less_equal_add
}
have B: O_2n ≲ O_n by {
expand operator≲
choose 0, 2
arbitrary n:UInt
assume prem
expand O_n | O_2n.
}
A, B
end
fun positive(f : fn UInt -> UInt) {
all n:UInt. 0 < f(n)
}
theorem log_product_less_equal_sum:
all f:fn UInt -> UInt, g:fn UInt -> UInt.
if (all n:UInt. 1 < f(n)) and (all n:UInt. 1 < g(n))
then log(f * g) ≲ log(f) + log(g)
proof
arbitrary f:fn UInt -> UInt, g:fn UInt -> UInt
assume fg_gt_1
expand operator≲ | operator+ | operator* | log
choose 0, 2
arbitrary n:UInt
assume _
have A: log(f(n) * g(n)) ≤ 1 + log(f(n)) + log(g(n)) by uint_log_mult_le_log_add
have B: 1 + log(f(n)) + log(g(n)) ≤ 2 * (log(f(n)) + log(g(n))) by {
have log_fg_pos: 0 < log(f(n)) + log(g(n)) by {
have log_f_pos: 0 < log(f(n)) by apply uint_log_pos[f(n)] to fg_gt_1
have log_g_pos: 0 < log(g(n)) by apply uint_log_pos[g(n)] to fg_gt_1
apply uint_add_mono_less[0, 0, log(f(n)), log(g(n))] to log_f_pos, log_g_pos
}
replace uint_two_mult
apply uint_add_one_le_double to log_fg_pos
}
apply uint_less_equal_trans to A, B
end
theorem log_sum_less_equal_product:
all f:fn UInt -> UInt, g:fn UInt -> UInt.
if (all n:UInt. 0 < f(n)) and (all n:UInt. 0 < g(n))
then log(f) + log(g) ≲ log(f * g)
proof
arbitrary f:fn UInt -> UInt, g:fn UInt -> UInt
assume fg_pos
expand operator≲ | operator+ | operator* | log
choose 0, 1
arbitrary n:UInt
assume _
have f_pos: 0 < f(n) by fg_pos
have g_pos: 0 < g(n) by fg_pos
apply uint_log_add_le_log_mult to f_pos, g_pos
end
theorem log_product_equiv_sum:
all f:fn UInt -> UInt, g:fn UInt -> UInt.
if (all n:UInt. 1 < f(n)) and (all n:UInt. 1 < g(n))
then log(f * g) ≈ log(f) + log(g)
proof
arbitrary f:fn UInt -> UInt, g:fn UInt -> UInt
assume fg_gt_1
expand operator≈
have A: log(f * g) ≲ log(f) + log(g) by apply log_product_less_equal_sum[f,g] to fg_gt_1
have f_pos: all n:UInt. 0 < f(n) by {
arbitrary n:UInt
have one_lt_f: 1 < f(n) by fg_gt_1
have zero_lt_one: (0:UInt) < 1 by .
apply uint_less_trans to zero_lt_one, one_lt_f
}
have g_pos: all n:UInt. 0 < g(n) by {
arbitrary n:UInt
have one_lt_g: 1 < g(n) by fg_gt_1
have zero_lt_one: (0:UInt) < 1 by .
apply uint_less_trans to zero_lt_one, one_lt_g
}
have B: log(f) + log(g) ≲ log(f * g)
by apply log_sum_less_equal_product[f,g] to f_pos, g_pos
A, B
end
theorem bigo_pointwise_eq: all f:fn UInt->UInt, g:fn UInt->UInt.
if (all n:UInt. f(n) = g(n)) then f ≈ g
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt
assume H
expand operator≈
have fg: f ≲ g by {
expand operator≲
choose 0, 1
arbitrary n:UInt
assume _
show f(n) ≤ 1 * g(n)
have eq: f(n) = g(n) by H
replace eq.
}
have gf: g ≲ f by {
expand operator≲
choose 0, 1
arbitrary n:UInt
assume _
show g(n) ≤ 1 * f(n)
have eq: f(n) = g(n) by H
replace symmetric eq.
}
fg, gf
end
theorem bigo_add_assoc: all f:fn UInt->UInt, g:fn UInt->UInt, h:fn UInt->UInt.
(f + g) + h ≈ f + (g + h)
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt, h:fn UInt->UInt
have pointwise: all m:UInt. ((f + g) + h)(m) = (f + (g + h))(m) by {
arbitrary m:UInt
expand operator+.
}
apply bigo_pointwise_eq[(f + g) + h, f + (g + h)] to pointwise
end
theorem bigo_mult_assoc: all f:fn UInt->UInt, g:fn UInt->UInt, h:fn UInt->UInt.
(f * g) * h ≈ f * (g * h)
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt, h:fn UInt->UInt
have pointwise: all m:UInt. ((f * g) * h)(m) = (f * (g * h))(m) by {
arbitrary m:UInt
expand operator*.
}
apply bigo_pointwise_eq[(f * g) * h, f * (g * h)] to pointwise
end
theorem bigo_add_zero: all f:fn UInt->UInt.
f + (fun n:UInt { 0 }) ≈ f
proof
arbitrary f:fn UInt->UInt
have pointwise: all m:UInt. (f + (fun n:UInt { 0 }))(m) = f(m) by {
arbitrary m:UInt
expand operator+.
}
apply bigo_pointwise_eq[f + (fun n:UInt { 0 }), f] to pointwise
end
theorem bigo_mult_one_func: all f:fn UInt->UInt.
f * (fun n:UInt { 1 }) ≈ f
proof
arbitrary f:fn UInt->UInt
have pointwise: all m:UInt. (f * (fun n:UInt { 1 }))(m) = f(m) by {
arbitrary m:UInt
expand operator*.
}
apply bigo_pointwise_eq[f * (fun n:UInt { 1 }), f] to pointwise
end
theorem bigo_dist: all f:fn UInt->UInt, g:fn UInt->UInt, h:fn UInt->UInt.
f * (g + h) ≈ f * g + f * h
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt, h:fn UInt->UInt
have pointwise: all m:UInt. (f * (g + h))(m) = (f * g + f * h)(m) by {
arbitrary m:UInt
expand operator+ | operator*
show f(m) * (g(m) + h(m)) = f(m) * g(m) + f(m) * h(m)
uint_dist_mult_add
}
apply bigo_pointwise_eq[f * (g + h), f * g + f * h] to pointwise
end
theorem bigo_add_dominated_absorb_equiv: all f:fn UInt->UInt, g:fn UInt->UInt.
if g ≲ f then f + g ≈ f
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt
assume g_f: g ≲ f
expand operator≈
have A: f + g ≲ f by apply bigo_add_absorb to g_f
have B: f ≲ f + g by bigo_self_le_add
A, B
end
theorem bigo_const_mult_equiv: all f:fn UInt->UInt, k:UInt.
if 0 < k then k * f ≈ f
proof
arbitrary f:fn UInt->UInt, k:UInt
assume k_pos: 0 < k
expand operator≈
have A: k * f ≲ f by bigo_const_mult
have k_ge_1: 1 ≤ k by apply uint_pos_implies_one_le to k_pos
have B: f ≲ k * f by apply bigo_le_const_mult[f, k] to k_ge_1
A, B
end
fun max (f : fn UInt -> UInt, g : fn UInt -> UInt) {
fun n:UInt { max(f(n), g(n)) }
}
theorem bigo_max_le_add: all f:fn UInt->UInt, g:fn UInt->UInt.
max(f, g) ≲ f + g
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt
expand operator≲ | operator+ | max
choose 0, 1
arbitrary n:UInt
assume _
show max(f(n), g(n)) ≤ 1 * (f(n) + g(n))
have f_le: f(n) ≤ f(n) + g(n) by uint_less_equal_add
have g_le: g(n) ≤ f(n) + g(n) by uint_less_equal_add_left
apply uint_max_less_equal to f_le, g_le
end
theorem bigo_add_le_max: all f:fn UInt->UInt, g:fn UInt->UInt.
f + g ≲ max(f, g)
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt
expand operator≲ | operator+ | max
choose 0, 2
arbitrary n:UInt
assume _
show f(n) + g(n) ≤ 2 * max(f(n), g(n))
have f_le: f(n) ≤ max(f(n), g(n)) by uint_max_greater_left
have g_le: g(n) ≤ max(f(n), g(n)) by uint_max_greater_right
have sum_le: f(n) + g(n) ≤ max(f(n), g(n)) + max(f(n), g(n))
by apply uint_add_mono_less_equal to f_le, g_le
replace uint_two_mult[max(f(n), g(n))]
sum_le
end
fun bigo_omega(f : fn UInt -> UInt, g : fn UInt -> UInt) {
g ≲ f
}
theorem bigo_omega_refl: all f:fn UInt->UInt. bigo_omega(f, f)
proof
arbitrary f:fn UInt->UInt
expand bigo_omega
bigo_refl
end
theorem bigo_omega_trans:
all f:fn UInt->UInt, g:fn UInt->UInt, h:fn UInt->UInt.
if bigo_omega(f, g) and bigo_omega(g, h) then bigo_omega(f, h)
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt, h:fn UInt->UInt
assume prem
have gf: g ≲ f by expand bigo_omega in prem
have hg: h ≲ g by expand bigo_omega in prem
expand bigo_omega
apply bigo_trans to hg, gf
end
theorem bigo_equiv_iff_theta: all f:fn UInt->UInt, g:fn UInt->UInt.
f ≈ g ⇔ (f ≲ g and bigo_omega(f, g))
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt
have fwd: if f ≈ g then (f ≲ g and bigo_omega(f, g)) by {
assume H
have fg: f ≲ g by expand operator≈ in H
have gf: g ≲ f by expand operator≈ in H
have go: bigo_omega(f, g) by { expand bigo_omega gf }
fg, go
}
have bkwd: if (f ≲ g and bigo_omega(f, g)) then f ≈ g by {
assume H
have fg: f ≲ g by H
have gf: g ≲ f by expand bigo_omega in H
expand operator≈
fg, gf
}
fwd, bkwd
end
fun bigo_little_o(f : fn UInt -> UInt, g : fn UInt -> UInt) {
all c:UInt. if 0 < c then some n0:UInt. all n:UInt. if n0 ≤ n then c * f(n) < g(n)
}
fun bigo_little_omega(f : fn UInt -> UInt, g : fn UInt -> UInt) {
bigo_little_o(g, f)
}
theorem bigo_little_implies_big: all f:fn UInt->UInt, g:fn UInt->UInt.
if bigo_little_o(f, g) then f ≲ g
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt
assume H: bigo_little_o(f, g)
have one_pos: (0:UInt) < 1 by .
have step1: some n0:UInt. all n:UInt. if n0 ≤ n then 1 * f(n) < g(n)
by apply (expand bigo_little_o in H)[1] to one_pos
obtain n0 where step: all n:UInt. if n0 ≤ n then 1 * f(n) < g(n) from step1
expand operator≲
choose n0, 1
arbitrary n:UInt
assume n0_n: n0 ≤ n
show f(n) ≤ 1 * g(n)
have lt: f(n) < g(n) by apply step to n0_n
apply uint_less_implies_less_equal to lt
end
theorem bigo_little_irrefl: all f:fn UInt->UInt. not bigo_little_o(f, f)
proof
arbitrary f:fn UInt->UInt
assume H: bigo_little_o(f, f)
have one_pos: (0:UInt) < 1 by .
have step1: some n0:UInt. all n:UInt. if n0 ≤ n then 1 * f(n) < f(n)
by apply (expand bigo_little_o in H)[1] to one_pos
obtain n0 where step: all n:UInt. if n0 ≤ n then 1 * f(n) < f(n) from step1
have n0_n0: n0 ≤ n0 by .
have lt: f(n0) < f(n0) by apply step to n0_n0
apply uint_less_irreflexive to lt
end
theorem bigo_little_omega_swap: all f:fn UInt->UInt, g:fn UInt->UInt.
if bigo_little_omega(f, g) then bigo_little_o(g, f)
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt
assume H
expand bigo_little_omega in H
end
theorem bigo_little_omega_irrefl: all f:fn UInt->UInt. not bigo_little_omega(f, f)
proof
arbitrary f:fn UInt->UInt
assume H: bigo_little_omega(f, f)
have ff: bigo_little_o(f, f) by expand bigo_little_omega in H
apply bigo_little_irrefl to ff
end
theorem bigo_little_omega_implies_big_omega: all f:fn UInt->UInt, g:fn UInt->UInt.
if bigo_little_omega(f, g) then bigo_omega(f, g)
proof
arbitrary f:fn UInt->UInt, g:fn UInt->UInt
assume H: bigo_little_omega(f, g)
have gf: bigo_little_o(g, f) by expand bigo_little_omega in H
have gf_big: g ≲ f by apply bigo_little_implies_big to gf
expand bigo_omega
gf_big
end
fun O_n_log_n(n:UInt) { n * log(n) }
fun O_n3(n:UInt) { n * n * n }
fun O_2pow_n(n:UInt) { 2 ^ n }
theorem bigo_linear_le_n_log_n: O_n ≲ O_n_log_n
proof
expand operator≲ | O_n | O_n_log_n
choose 2, 1
arbitrary n:UInt
assume two_n: 2 ≤ n
show n ≤ 1 * (n * log(n))
have one_log: 1 ≤ log(n) by apply uint_log_greater_one to two_n
apply uint_mult_mono_le[n] to one_log
end
theorem bigo_n_log_n_le_quadratic: O_n_log_n ≲ O_n2
proof
expand operator≲ | O_n_log_n | O_n2
choose 0, 1
arbitrary n:UInt
assume _
show n * log(n) ≤ 1 * (n * n)
have logn_n: log(n) ≤ n by uint_logn_le_n
have n_n: n ≤ n by .
conclude n * log(n) ≤ 1 * (n * n) by apply uint_mult_mono_le2 to n_n, logn_n
end
theorem bigo_quadratic_le_cubic: O_n2 ≲ O_n3
proof
expand operator≲ | O_n2 | O_n3
choose 1, 1
arbitrary n:UInt
assume one_n: 1 ≤ n
show n * n ≤ 1 * (n * n * n)
apply uint_mult_mono_le[n * n] to one_n
end
theorem bigo_linear_le_2pow_n: O_n ≲ O_2pow_n
proof
expand operator≲ | O_n | O_2pow_n
choose 1, 2
arbitrary n:UInt
assume one_n: 1 ≤ n
show n ≤ 2 * 2^n
have n_pos: 0 < n by replace uint_less_is_less_equal[0, n] one_n
have lt: n < 2^(1 + log(n)) by apply less_pow_log to n_pos
have logn_n: log(n) ≤ n by uint_logn_le_n
have one_le: (1:UInt) ≤ 1 by .
have step: 1 + log(n) ≤ 1 + n by apply uint_add_mono_less_equal to one_le, logn_n
have one_le_two: (1:UInt) ≤ 2 by .
have pow_step: 2^(1 + log(n)) ≤ 2^(1 + n)
by apply (apply uint_pow_le_mono_r[1 + n, 2, 1 + log(n)] to one_le_two) to step
have lt_le: n < 2^(1 + n) by {
have or_eq: 2^(1 + log(n)) < 2^(1 + n) or 2^(1 + log(n)) = 2^(1 + n) by expand operator≤ in pow_step
cases or_eq
case lt_case { apply uint_less_trans to lt, lt_case }
case eq_case { replace eq_case in lt }
}
have expand_pow: 2^(1 + n) = 2 * 2^n by uint_pow_add_r[2, 1, n]
have lt_le2: n < 2 * 2^n by replace expand_pow in lt_le
apply uint_less_implies_less_equal to lt_le2
end
theorem bigo_pow_mono: all a:UInt, b:UInt.
if a ≤ b then (fun n:UInt { n^a }) ≲ (fun n:UInt { n^b })
proof
arbitrary a:UInt, b:UInt
assume a_le_b: a ≤ b
expand operator≲
choose 1, 1
arbitrary n:UInt
assume one_n: 1 ≤ n
show n^a ≤ 1 * n^b
apply (apply uint_pow_le_mono_r[b, n, a] to one_n) to a_le_b
end
theorem bigo_log_le_self: all f:fn UInt->UInt. log(f) ≲ f
proof
arbitrary f:fn UInt->UInt
expand operator≲ | log
choose 0, 1
arbitrary n:UInt
assume _
show log(f(n)) ≤ 1 * f(n)
uint_logn_le_n
end
theorem bigo_log_const: log(O_1) ≲ O_1
proof
bigo_log_le_self[O_1]
end
theorem bigo_log_log: log(O_log) ≲ O_log
proof
bigo_log_le_self[O_log]
end
lemma uint_quad_le_2pow: all n:UInt. if 4 ≤ n then n * n ≤ 2 ^ n
proof
define P = fun n:UInt { n * n ≤ 2 ^ n }
have base: P(4) by {
expand P
show 4 * 4 ≤ 2 ^ 4
.
}
have ind: all m:UInt. if 4 ≤ m and P(m) then P(1 + m) by {
arbitrary m:UInt
assume prem
have m_ge_4: 4 ≤ m by prem
have IH: m * m ≤ 2 ^ m by expand P in prem
expand P
show (1 + m) * (1 + m) ≤ 2 ^ (1 + m)
have expand_sq: (1 + m) * (1 + m) = 1 + 2 * m + m * m by {
replace uint_dist_mult_add_right[1, m, 1+m]
| uint_dist_mult_add[m, 1, m]
| uint_two_mult[m].
}
have m_ge_1: 1 ≤ m by {
have one_le_four: (1:UInt) ≤ 4 by .
apply uint_less_equal_trans to one_le_four, m_ge_4
}
have m_le_m: m ≤ m by .
have four_m_le_m_m: 4 * m ≤ m * m
by apply uint_mult_mono_le2 to m_ge_4, m_le_m
have one_le_2m: (1:UInt) ≤ 2 * m by {
have one_le_two: (1:UInt) ≤ 2 by .
have two_le_2m: (2:UInt) ≤ 2 * m by apply uint_mult_mono_le[2] to m_ge_1
apply uint_less_equal_trans to one_le_two, two_le_2m
}
have four_m_eq: (4:UInt) * m = 2 * m + 2 * m
by uint_dist_mult_add_right[2, 2, m]
have step_2m_le_4m: 1 + 2 * m ≤ 4 * m by {
replace four_m_eq
have two_m_le: 2 * m ≤ 2 * m by .
apply uint_add_mono_less_equal to one_le_2m, two_m_le
}
have slack: 1 + 2 * m ≤ m * m
by apply uint_less_equal_trans to step_2m_le_4m, four_m_le_m_m
have step_sum: (1 + 2 * m) + m * m ≤ m * m + m * m by {
have mm_le_mm: m * m ≤ m * m by .
apply uint_add_mono_less_equal to slack, mm_le_mm
}
have two_mm_eq: m * m + m * m = 2 * (m * m)
by symmetric uint_two_mult[m * m]
have step_2mm: (1 + 2 * m) + m * m ≤ 2 * (m * m)
by replace two_mm_eq in step_sum
have step_lhs_2mm: (1 + m) * (1 + m) ≤ 2 * (m * m) by {
replace expand_sq
step_2mm
}
have step_two_pow_m: 2 * (m * m) ≤ 2 * 2 ^ m
by apply uint_mult_mono_le[2] to IH
have step_chain: (1 + m) * (1 + m) ≤ 2 * 2 ^ m
by apply uint_less_equal_trans to step_lhs_2mm, step_two_pow_m
have pow_step: 2 ^ (1 + m) = 2 * 2 ^ m by uint_pow_add_r[2, 1, m]
replace pow_step
step_chain
}
expand P in apply uint_k_induction[P, 4] to base, ind
end
theorem bigo_quadratic_le_2pow_n: O_n2 ≲ O_2pow_n
proof
expand operator≲ | O_n2 | O_2pow_n
choose 4, 1
arbitrary n:UInt
assume four_n: 4 ≤ n
show n * n ≤ 1 * 2 ^ n
apply uint_quad_le_2pow to four_n
end
lemma uint_cube_le_2pow: all n:UInt. if 10 ≤ n then n * n * n ≤ 2 ^ n
proof
define P = fun n:UInt { n * n * n ≤ 2 ^ n }
have base: P(10) by {
expand P
show 10 * 10 * 10 ≤ 2 ^ 10
.
}
have ind: all m:UInt. if 10 ≤ m and P(m) then P(1 + m) by {
arbitrary m:UInt
assume prem
have m_ge_10: 10 ≤ m by prem
have IH: m * m * m ≤ 2 ^ m by expand P in prem
expand P
show (1 + m) * (1 + m) * (1 + m) ≤ 2 ^ (1 + m)
have m_ge_4: 4 ≤ m by {
have four_le_ten: (4:UInt) ≤ 10 by .
apply uint_less_equal_trans to four_le_ten, m_ge_10
}
have m_ge_1: 1 ≤ m by {
have one_le_four: (1:UInt) ≤ 4 by .
apply uint_less_equal_trans to one_le_four, m_ge_4
}
have m_le_m: m ≤ m by .
have expand_sq: (1 + m) * (1 + m) = 1 + 2 * m + m * m by {
replace uint_dist_mult_add_right[1, m, 1+m]
| uint_dist_mult_add[m, 1, m]
| uint_two_mult[m].
}
have step1: (1 + m) * (1 + m) * (1 + m) = (1 + 2*m + m*m) * (1 + m)
by replace expand_sq.
have step2: (1 + 2*m + m*m) * (1 + m) = (1 + 2*m + m*m) + (1 + 2*m + m*m) * m
by uint_dist_mult_add[1 + 2*m + m*m, 1, m]
have step3: (1 + 2*m + m*m) * m = m + 2*m*m + m*m*m by {
replace uint_dist_mult_add_right[1, 2*m + m*m, m]
| uint_dist_mult_add_right[2*m, m*m, m].
}
have step4: (1 + m) * (1 + m) * (1 + m)
= 1 + 2*m + m*m + m + 2*m*m + m*m*m by {
replace step1 | step2 | step3.
}
have regroup: 1 + 2*m + m*m + m + 2*m*m + m*m*m = 1 + 3*m + 3*m*m + m*m*m by {
equations
1 + 2*m + m*m + m + 2*m*m + m*m*m
= 1 + 2*m + m + m*m + 2*m*m + m*m*m by replace uint_add_commute[m*m, m].
... = 1 + (2*m + m) + (m*m + 2*m*m) + m*m*m by .
... = 1 + 3*m + (m*m + 2*m*m) + m*m*m by {
have e: 2*m + m = 3*m by symmetric uint_dist_mult_add_right[2, 1, m]
replace e.
}
... = 1 + 3*m + 3*m*m + m*m*m by {
have ee: 1 * m * m + 2 * m * m = (1 + 2) * m * m
by symmetric uint_dist_mult_add_right[1, 2, m*m]
replace ee.
}
}
have step5: (1 + m) * (1 + m) * (1 + m) = 1 + 3*m + 3*m*m + m*m*m
by transitive step4 regroup
have step_A: 1 + 3 * m ≤ 4 * m by {
have three_m_le: 3 * m ≤ 3 * m by .
have rev: 1 + 3 * m ≤ m + 3 * m by apply uint_add_mono_less_equal to m_ge_1, three_m_le
have e: m + 3 * m = 4 * m by {
have hh: (1 + 3) * m = 1 * m + 3 * m by uint_dist_mult_add_right[1, 3, m]
replace hh.
}
replace e in rev
}
have step_B: 4 * m ≤ m * m by apply uint_mult_mono_le2 to m_ge_4, m_le_m
have step_C: 1 + 3 * m ≤ m * m by apply uint_less_equal_trans to step_A, step_B
have three_mm_le: 3 * m * m ≤ 3 * m * m by .
have step_D_sum: (1 + 3 * m) + 3 * m * m ≤ m * m + 3 * m * m
by apply uint_add_mono_less_equal to step_C, three_mm_le
have e_4m: m + 3 * m = 4 * m by {
have hh: (1 + 3) * m = 1 * m + 3 * m by uint_dist_mult_add_right[1, 3, m]
replace hh.
}
have step_m3m_le_mm: m + 3 * m ≤ m * m by replace e_4m step_B
have step_dist_m: (m + 3 * m) * m ≤ m * m * m
by apply uint_mult_mono_le2 to step_m3m_le_mm, m_le_m
have dist_3mm: (m + 3 * m) * m = m * m + 3 * m * m
by uint_dist_mult_add_right[m, 3 * m, m]
have step_mm3mm_le_mmm: m * m + 3 * m * m ≤ m * m * m
by replace dist_3mm in step_dist_m
have step_F: (1 + 3 * m) + 3 * m * m ≤ m * m * m
by apply uint_less_equal_trans to step_D_sum, step_mm3mm_le_mmm
have mmm_le_mmm: m * m * m ≤ m * m * m by .
have step_sum_full: ((1 + 3 * m) + 3 * m * m) + m * m * m ≤ m * m * m + m * m * m
by apply uint_add_mono_less_equal to step_F, mmm_le_mmm
have two_mmm_eq: 2 * (m * m * m) = m * m * m + m * m * m
by uint_two_mult[m * m * m]
have step_lhs_2mmm: (1 + m) * (1 + m) * (1 + m) ≤ 2 * (m * m * m) by {
replace step5
replace two_mmm_eq
step_sum_full
}
have step_two_pow_m: 2 * (m * m * m) ≤ 2 * 2 ^ m
by apply uint_mult_mono_le[2] to IH
have step_chain: (1 + m) * (1 + m) * (1 + m) ≤ 2 * 2 ^ m
by apply uint_less_equal_trans to step_lhs_2mmm, step_two_pow_m
have pow_step: 2 ^ (1 + m) = 2 * 2 ^ m by uint_pow_add_r[2, 1, m]
replace pow_step
step_chain
}
expand P in apply uint_k_induction[P, 10] to base, ind
end
theorem bigo_cubic_le_2pow_n: O_n3 ≲ O_2pow_n
proof
expand operator≲ | O_n3 | O_2pow_n
choose 10, 1
arbitrary n:UInt
assume ten_n: 10 ≤ n
show n * n * n ≤ 1 * 2 ^ n
apply uint_cube_le_2pow to ten_n
end
lemma uint_log_pow_le: all n:UInt, k:UInt.
if 1 ≤ k then log(n^k) ≤ k * (1 + log(n))
proof
arbitrary n:UInt, k:UInt
assume one_le_k: 1 ≤ k
have k_pos: 0 < k by replace uint_less_is_less_equal[0, k] one_le_k
cases uint_zero_or_positive[n]
case n_zero: n = 0 {
have zk: (0:UInt)^k = 0 by apply uint_zero_pow to k_pos
replace n_zero | zk
show log((0:UInt)) ≤ k * (1 + log((0:UInt)))
uint_zero_le
}
case n_pos: 0 < n {
have n_lt: n < 2^(1 + log(n)) by apply less_pow_log to n_pos
have n_le: n ≤ 2^(1 + log(n)) by apply uint_less_implies_less_equal to n_lt
have step_pow: n^k ≤ (2^(1 + log(n)))^k
by apply uint_pow_le_mono_l[k, n, 2^(1 + log(n))] to n_le
have flat_pow: (2^(1 + log(n)))^k = 2^((1 + log(n)) * k)
by uint_pow_mul_r[2, 1 + log(n), k]
have step_pow2: n^k ≤ 2^((1 + log(n)) * k)
by replace flat_pow in step_pow
have step_log: log(n^k) ≤ log(2^((1 + log(n)) * k))
by apply uint_log_mono to step_pow2
have log_simp: log(2^((1 + log(n)) * k)) = (1 + log(n)) * k by log_pow
have step_final: log(n^k) ≤ (1 + log(n)) * k
by replace log_simp in step_log
show log(n^k) ≤ k * (1 + log(n))
replace uint_mult_commute[k, 1 + log(n)]
step_final
}
end
theorem bigo_log_pow: all k:UInt.
if 1 ≤ k then log((fun n:UInt { n^k })) ≈ log(O_n)
proof
arbitrary k:UInt
assume one_le_k: 1 ≤ k
expand operator≈
have upper: log((fun n:UInt { n^k })) ≲ log(O_n) by {
expand operator≲ | log | O_n
choose 2, 2 * k
arbitrary n:UInt
assume two_n: 2 ≤ n
show log(n^k) ≤ 2 * k * log(n)
have bound: log(n^k) ≤ k * (1 + log(n))
by apply uint_log_pow_le[n, k] to one_le_k
have dist: k * (1 + log(n)) = k + k * log(n)
by uint_dist_mult_add[k, 1, log(n)]
have bound2: log(n^k) ≤ k + k * log(n) by replace dist in bound
have one_logn: 1 ≤ log(n) by apply uint_log_greater_one to two_n
have k_le_klog: k ≤ k * log(n) by {
have h: k * 1 ≤ k * log(n) by apply uint_mult_mono_le[k] to one_logn
h
}
have klog_le: k * log(n) ≤ k * log(n) by .
have sum_le: k + k * log(n) ≤ k * log(n) + k * log(n)
by apply uint_add_mono_less_equal to k_le_klog, klog_le
have klog_klog_eq: k * log(n) + k * log(n) = 2 * k * log(n) by {
have assoc_eq: 2 * (k * log(n)) = 2 * k * log(n)
by symmetric uint_mult_assoc[2, k, log(n)]
replace symmetric uint_two_mult[k * log(n)]
assoc_eq
}
have sum_le2: k + k * log(n) ≤ 2 * k * log(n)
by replace klog_klog_eq in sum_le
apply uint_less_equal_trans to bound2, sum_le2
}
have lower: log(O_n) ≲ log((fun n:UInt { n^k })) by {
expand operator≲ | log | O_n
choose 1, 1
arbitrary n:UInt
assume one_n: 1 ≤ n
show log(n) ≤ 1 * log(n^k)
have n_le: n ≤ n^k
by apply (apply uint_pow_le_mono_r[k, n, 1] to one_n) to one_le_k
have log_le: log(n) ≤ log(n^k) by apply uint_log_mono to n_le
log_le
}
upper, lower
end
theorem bigo_log_pow_le: all k:UInt.
if 1 ≤ k then log((fun n:UInt { n^k })) ≲ log(O_n)
proof
arbitrary k:UInt
assume one_le_k: 1 ≤ k
have eq: log((fun n:UInt { n^k })) ≈ log(O_n)
by apply bigo_log_pow[k] to one_le_k
expand operator≈ in eq
end
theorem bigo_pow_strict_mono: all a:UInt, b:UInt.
if a < b then bigo_little_o(fun n:UInt {n^a}, fun n:UInt {n^b})
proof
arbitrary a:UInt, b:UInt
assume a_lt_b
have a_le_b: a ≤ b by apply uint_less_implies_less_equal to a_lt_b
have ba_id: a + (b ∸ a) = b by apply uint_monus_add_identity[b, a] to a_le_b
have ba_ge_1: 1 ≤ b ∸ a by {
have one_plus_a_le_b: 1 + a ≤ b by replace uint_less_is_less_equal in a_lt_b
have shaped: a + 1 ≤ b by replace uint_add_commute[1, a] in one_plus_a_le_b
replace symmetric ba_id in shaped
}
expand bigo_little_o
arbitrary c:UInt
assume _c_pos
choose 1 + c
arbitrary n:UInt
assume n_ge: 1 + c ≤ n
show c * n^a < n^b
have one_le_n: 1 ≤ n by {
have h: (1:UInt) ≤ 1 + c by uint_less_equal_add
apply uint_less_equal_trans to h, n_ge
}
have n_pos: 0 < n by replace uint_less_is_less_equal[0, n] one_le_n
have na_pos: 0 < n^a by apply uint_pow_pos[n, a] to n_pos
have c_lt_n: c < n by replace uint_less_is_less_equal[c, n] n_ge
have n_le_nba: n ≤ n^(b ∸ a)
by apply (apply uint_pow_le_mono_r[b ∸ a, n, 1] to one_le_n) to ba_ge_1
have c_lt_nba: c < n^(b ∸ a) by {
have or_n: n < n^(b ∸ a) or n = n^(b ∸ a) by expand operator≤ in n_le_nba
cases or_n
case lt { apply uint_less_trans to c_lt_n, lt }
case eq: n = n^(b ∸ a) { replace symmetric eq c_lt_n }
}
have mult_step: n^a * c < n^a * n^(b ∸ a)
by apply uint_pos_mult_both_sides_of_less to na_pos, c_lt_nba
have pow_factor: n^b = n^a * n^(b ∸ a) by {
replace symmetric ba_id
uint_pow_add_r[n, a, b ∸ a]
}
replace pow_factor
replace uint_mult_commute[c, n^a]
mult_step
end
theorem bigo_linear_closure: all a:UInt, b:UInt.
if 1 ≤ a then (fun n:UInt { a * n + b }) ≈ (fun n:UInt { n })
proof
arbitrary a:UInt, b:UInt
assume a_pos: 1 ≤ a
expand operator≈
have upper: (fun n:UInt { a * n + b }) ≲ (fun n:UInt { n }) by {
expand operator≲
choose 1, a + b
arbitrary n:UInt
assume n_ge_1: 1 ≤ n
show a * n + b ≤ (a + b) * n
have b_le_bn: b ≤ b * n by apply uint_mult_mono_le[b, 1, n] to n_ge_1
have an_le: a * n ≤ a * n by .
have sum_le: a * n + b ≤ a * n + b * n
by apply uint_add_mono_less_equal to an_le, b_le_bn
have expand_eq: (a + b) * n = a * n + b * n by uint_dist_mult_add_right
replace expand_eq
sum_le
}
have lower: (fun n:UInt { n }) ≲ (fun n:UInt { a * n + b }) by {
expand operator≲
choose 0, 1
arbitrary n:UInt
assume _
show n ≤ 1 * (a * n + b)
have n_le_n: n ≤ n by .
have n_le_an: n ≤ a * n
by apply uint_mult_mono_le2[1, n, a, n] to a_pos, n_le_n
have an_le_anb: a * n ≤ a * n + b by uint_less_equal_add
apply uint_less_equal_trans to n_le_an, an_le_anb
}
upper, lower
end
theorem bigo_quadratic_closure: all a:UInt, b:UInt, c:UInt.
if 1 ≤ a then (fun n:UInt { a * n * n + b * n + c }) ≈ (fun n:UInt { n * n })
proof
arbitrary a:UInt, b:UInt, c:UInt
assume a_pos: 1 ≤ a
expand operator≈
have upper: (fun n:UInt { a * n * n + b * n + c }) ≲ (fun n:UInt { n * n }) by {
expand operator≲
choose 1, a + b + c
arbitrary n:UInt
assume n_ge_1: 1 ≤ n
show a * n * n + b * n + c ≤ (a + b + c) * (n * n)
have n_le_n: n ≤ n by .
have n_le_nn: n ≤ n * n
by apply uint_mult_mono_le2[1, n, n, n] to n_ge_1, n_le_n
have one_le_nn: 1 ≤ n * n by apply uint_less_equal_trans to n_ge_1, n_le_nn
have bn_le_bnn: b * n ≤ b * (n * n) by apply uint_mult_mono_le[b, n, n*n] to n_le_nn
have c_le_cnn: c ≤ c * (n * n) by apply uint_mult_mono_le[c, 1, n*n] to one_le_nn
have ann_le: a * n * n ≤ a * (n * n) by .
have sum1: a * n * n + b * n ≤ a * (n * n) + b * (n * n)
by apply uint_add_mono_less_equal to ann_le, bn_le_bnn
have sum2: a * n * n + b * n + c ≤ a * (n * n) + b * (n * n) + c * (n * n)
by apply uint_add_mono_less_equal to sum1, c_le_cnn
have expand_eq: (a + b + c) * (n * n) = a * (n * n) + b * (n * n) + c * (n * n) by {
have step1: (a + b + c) * (n * n) = (a + b) * (n * n) + c * (n * n)
by uint_dist_mult_add_right[a + b, c, n * n]
have step2: (a + b) * (n * n) = a * (n * n) + b * (n * n)
by uint_dist_mult_add_right[a, b, n * n]
replace step1 | step2.
}
replace expand_eq
sum2
}
have lower: (fun n:UInt { n * n }) ≲ (fun n:UInt { a * n * n + b * n + c }) by {
expand operator≲
choose 0, 1
arbitrary n:UInt
assume _
show n * n ≤ 1 * (a * n * n + b * n + c)
have nn_le_nn: n * n ≤ n * n by .
have nn_le_ann: n * n ≤ a * n * n
by apply uint_mult_mono_le2[1, n*n, a, n*n] to a_pos, nn_le_nn
have ann_le_pluss: a * n * n ≤ a * n * n + b * n by uint_less_equal_add
have nn_le_first2: n * n ≤ a * n * n + b * n
by apply uint_less_equal_trans to nn_le_ann, ann_le_pluss
have last_le: a * n * n + b * n ≤ a * n * n + b * n + c by uint_less_equal_add
apply uint_less_equal_trans to nn_le_first2, last_le
}
upper, lower
end
theorem bigo_cubic_closure: all a:UInt, b:UInt, c:UInt, d:UInt.
if 1 ≤ a then (fun n:UInt { a * n * n * n + b * n * n + c * n + d })
≈ (fun n:UInt { n * n * n })
proof
arbitrary a:UInt, b:UInt, c:UInt, d:UInt
assume a_pos: 1 ≤ a
expand operator≈
have upper: (fun n:UInt { a * n * n * n + b * n * n + c * n + d })
≲ (fun n:UInt { n * n * n }) by {
expand operator≲
choose 1, a + b + c + d
arbitrary n:UInt
assume n_ge_1: 1 ≤ n
show a * n * n * n + b * n * n + c * n + d ≤ (a + b + c + d) * (n * n * n)
have n_le_n: n ≤ n by .
have n_le_nn: n ≤ n * n
by apply uint_mult_mono_le2[1, n, n, n] to n_ge_1, n_le_n
have one_le_nn: 1 ≤ n * n
by apply uint_less_equal_trans to n_ge_1, n_le_nn
have nn_le_nn: n * n ≤ n * n by .
have nn_le_nnn: n * n ≤ n * n * n
by apply uint_mult_mono_le2[1, n*n, n, n*n] to n_ge_1, nn_le_nn
have n_le_nnn: n ≤ n * n * n
by apply uint_less_equal_trans to n_le_nn, nn_le_nnn
have one_le_nnn: 1 ≤ n * n * n
by apply uint_less_equal_trans to n_ge_1, n_le_nnn
have annn_le: a * n * n * n ≤ a * (n * n * n) by .
have bnn_le_bnn: b * n * n ≤ b * (n * n) by .
have bnn_le_bnnn_lifted: b * (n * n) ≤ b * (n * n * n)
by apply uint_mult_mono_le[b, n*n, n*n*n] to nn_le_nnn
have bnn_le_bnnn: b * n * n ≤ b * (n * n * n)
by apply uint_less_equal_trans to bnn_le_bnn, bnn_le_bnnn_lifted
have cn_le_cnnn: c * n ≤ c * (n * n * n)
by apply uint_mult_mono_le[c, n, n*n*n] to n_le_nnn
have d_le_dnnn: d ≤ d * (n * n * n)
by apply uint_mult_mono_le[d, 1, n*n*n] to one_le_nnn
have sum1: a * n * n * n + b * n * n ≤ a * (n * n * n) + b * (n * n * n)
by apply uint_add_mono_less_equal to annn_le, bnn_le_bnnn
have sum2: a * n * n * n + b * n * n + c * n
≤ a * (n * n * n) + b * (n * n * n) + c * (n * n * n)
by apply uint_add_mono_less_equal to sum1, cn_le_cnnn
have sum3: a * n * n * n + b * n * n + c * n + d
≤ a * (n * n * n) + b * (n * n * n) + c * (n * n * n) + d * (n * n * n)
by apply uint_add_mono_less_equal to sum2, d_le_dnnn
have expand_eq: (a + b + c + d) * (n * n * n)
= a * (n * n * n) + b * (n * n * n) + c * (n * n * n) + d * (n * n * n) by {
have step1: (a + b + c + d) * (n * n * n)
= (a + b + c) * (n * n * n) + d * (n * n * n)
by uint_dist_mult_add_right[a + b + c, d, n * n * n]
have step2: (a + b + c) * (n * n * n)
= (a + b) * (n * n * n) + c * (n * n * n)
by uint_dist_mult_add_right[a + b, c, n * n * n]
have step3: (a + b) * (n * n * n)
= a * (n * n * n) + b * (n * n * n)
by uint_dist_mult_add_right[a, b, n * n * n]
replace step1 | step2 | step3.
}
replace expand_eq
sum3
}
have lower: (fun n:UInt { n * n * n })
≲ (fun n:UInt { a * n * n * n + b * n * n + c * n + d }) by {
expand operator≲
choose 0, 1
arbitrary n:UInt
assume _
show n * n * n ≤ 1 * (a * n * n * n + b * n * n + c * n + d)
have nnn_le_nnn: n * n * n ≤ n * n * n by .
have nnn_le_annn: n * n * n ≤ a * n * n * n
by apply uint_mult_mono_le2[1, n*n*n, a, n*n*n] to a_pos, nnn_le_nnn
have annn_le_p1: a * n * n * n ≤ a * n * n * n + b * n * n
by uint_less_equal_add
have nnn_le_p1: n * n * n ≤ a * n * n * n + b * n * n
by apply uint_less_equal_trans to nnn_le_annn, annn_le_p1
have p1_le_p2: a * n * n * n + b * n * n ≤ a * n * n * n + b * n * n + c * n
by uint_less_equal_add
have nnn_le_p2: n * n * n ≤ a * n * n * n + b * n * n + c * n
by apply uint_less_equal_trans to nnn_le_p1, p1_le_p2
have p2_le_p3: a * n * n * n + b * n * n + c * n
≤ a * n * n * n + b * n * n + c * n + d
by uint_less_equal_add
apply uint_less_equal_trans to nnn_le_p2, p2_le_p3
}
upper, lower
end
recursive poly_eval(List<UInt>, UInt) -> UInt {
poly_eval(empty, n) = 0
poly_eval(node(a, rest), n) = a + n * poly_eval(rest, n)
}
recursive sum_coeffs(List<UInt>) -> UInt {
sum_coeffs(empty) = 0
sum_coeffs(node(a, rest)) = a + sum_coeffs(rest)
}
lemma poly_eval_upper_bound:
all pre:List<UInt>. all a:UInt, n:UInt.
if 1 ≤ n
then poly_eval(pre ++ [a], n) ≤ (a + sum_coeffs(pre)) * n^(length(pre))
proof
induction List<UInt>
case empty {
arbitrary a:UInt, n:UInt
assume one_le_n: 1 ≤ n
expand operator++ | 2 * poly_eval | sum_coeffs | length.
}
case node(b, rest) suppose IH:
(all a:UInt, n:UInt. if 1 ≤ n
then poly_eval(rest ++ [a], n) ≤ (a + sum_coeffs(rest)) * n^(length(rest))) {
arbitrary a:UInt, n:UInt
assume one_le_n: 1 ≤ n
have ih: poly_eval(rest ++ [a], n) ≤ (a + sum_coeffs(rest)) * n^(length(rest))
by apply IH[a, n] to one_le_n
expand operator++ | poly_eval | sum_coeffs | length
show b + n * poly_eval(rest ++ [a], n)
≤ (a + (b + sum_coeffs(rest))) * n^(1 + length(rest))
have pow_next: n^(1 + length(rest)) = n * n^(length(rest))
by uint_pow_add_r[n, 1, length(rest)]
have one_le_pow_next: 1 ≤ n^(1 + length(rest))
by apply uint_pow_le_mono_l[1 + length(rest), 1, n] to one_le_n
have step_mult: n * poly_eval(rest ++ [a], n)
≤ n * ((a + sum_coeffs(rest)) * n^(length(rest)))
by apply uint_mult_mono_le[n, poly_eval(rest ++ [a], n),
(a + sum_coeffs(rest)) * n^(length(rest))]
to ih
have rearrange: n * ((a + sum_coeffs(rest)) * n^(length(rest)))
= (a + sum_coeffs(rest)) * n^(1 + length(rest)) by {
equations
n * ((a + sum_coeffs(rest)) * n^(length(rest)))
= (a + sum_coeffs(rest)) * (n * n^(length(rest)))
by replace uint_mult_commute[n, a + sum_coeffs(rest)].
... = # (a + sum_coeffs(rest)) * n^(1 + length(rest)) #
by replace pow_next.
}
have step_mult2: n * poly_eval(rest ++ [a], n)
≤ (a + sum_coeffs(rest)) * n^(1 + length(rest))
by replace rearrange in step_mult
have b_le_b_pow: b ≤ b * n^(1 + length(rest))
by apply uint_mult_mono_le[b, 1, n^(1 + length(rest))] to one_le_pow_next
have sum_bound: b + n * poly_eval(rest ++ [a], n)
≤ b * n^(1 + length(rest))
+ (a + sum_coeffs(rest)) * n^(1 + length(rest))
by apply uint_add_mono_less_equal to b_le_b_pow, step_mult2
have factor: b * n^(1 + length(rest))
+ (a + sum_coeffs(rest)) * n^(1 + length(rest))
= (a + (b + sum_coeffs(rest))) * n^(1 + length(rest)) by {
have d1: b * n^(1 + length(rest))
+ (a + sum_coeffs(rest)) * n^(1 + length(rest))
= (b + (a + sum_coeffs(rest))) * n^(1 + length(rest))
by symmetric uint_dist_mult_add_right[b, a + sum_coeffs(rest),
n^(1 + length(rest))]
have rearr: b + a = a + b by uint_add_commute[b, a]
replace d1 | rearr.
}
replace factor in sum_bound
}
end
lemma poly_eval_lower_bound:
all pre:List<UInt>. all a:UInt.
if 1 ≤ a then all n:UInt.
n^(length(pre)) ≤ poly_eval(pre ++ [a], n)
proof
induction List<UInt>
case empty {
arbitrary a:UInt
assume one_le_a: 1 ≤ a
arbitrary n:UInt
expand operator++ | 2 * poly_eval | length
one_le_a
}
case node(b, rest) suppose IH:
(all a:UInt. if 1 ≤ a then all n:UInt.
n^(length(rest)) ≤ poly_eval(rest ++ [a], n)) {
arbitrary a:UInt
assume one_le_a: 1 ≤ a
arbitrary n:UInt
have ih: n^(length(rest)) ≤ poly_eval(rest ++ [a], n)
by apply IH[a] to one_le_a
expand operator++ | poly_eval | length
show n^(1 + length(rest)) ≤ b + n * poly_eval(rest ++ [a], n)
have pow_next: n^(1 + length(rest)) = n * n^(length(rest))
by uint_pow_add_r[n, 1, length(rest)]
have step_mult: n * n^(length(rest)) ≤ n * poly_eval(rest ++ [a], n)
by apply uint_mult_mono_le[n, n^(length(rest)),
poly_eval(rest ++ [a], n)] to ih
have absorb: n * poly_eval(rest ++ [a], n)
≤ b + n * poly_eval(rest ++ [a], n) by uint_less_equal_add_left
have chain: n^(1 + length(rest))
≤ b + n * poly_eval(rest ++ [a], n) by {
replace pow_next
apply uint_less_equal_trans to step_mult, absorb
}
chain
}
end
theorem bigo_poly_closure:
all pre:List<UInt>, a:UInt.
if 1 ≤ a
then (fun n:UInt { poly_eval(pre ++ [a], n) })
≈ (fun n:UInt { n^(length(pre)) })
proof
arbitrary pre:List<UInt>, a:UInt
assume one_le_a: 1 ≤ a
expand operator≈
have upper: (fun n:UInt { poly_eval(pre ++ [a], n) })
≲ (fun n:UInt { n^(length(pre)) }) by {
expand operator≲
choose 1, a + sum_coeffs(pre)
arbitrary n:UInt
assume one_le_n: 1 ≤ n
show poly_eval(pre ++ [a], n) ≤ (a + sum_coeffs(pre)) * n^(length(pre))
apply poly_eval_upper_bound[pre][a, n] to one_le_n
}
have lower: (fun n:UInt { n^(length(pre)) })
≲ (fun n:UInt { poly_eval(pre ++ [a], n) }) by {
expand operator≲
choose 0, 1
arbitrary n:UInt
assume _
show n^(length(pre)) ≤ 1 * poly_eval(pre ++ [a], n)
have lb: all m:UInt. m^(length(pre)) ≤ poly_eval(pre ++ [a], m)
by apply poly_eval_lower_bound[pre][a] to one_le_a
lb[n]
}
upper, lower
end