File size: 32,943 Bytes
5ee1fb5
8397e00
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
(* The levelization of a netlist given in blocks (Lemma 2.5 applied): the depth of every signal, the last layer that
   reads it and the width of every layer, from claims that are checked block by block. *)
From Stdlib Require Import List Bool Arith ZArith Lia.
Import ListNotations.
From TH Require Import TwoHead Net Lev.

(* ---- finite sums -------------------------------------------------------------------------------------------- *)
Fixpoint nsum (k : nat) (g : nat -> nat) : nat := match k with O => 0 | S k' => nsum k' g + g k' end.

Lemma nsum_ext : forall k g h, (forall i, i < k -> g i = h i) -> nsum k g = nsum k h.
Proof. induction k as [|k IH]; intros g h H; cbn [nsum]; [reflexivity|]. rewrite (IH g h), (H k); [reflexivity | lia |]. intros i Hi. apply H. lia. Qed.

Lemma nsum_const : forall k c, nsum k (fun _ => c) = k * c.
Proof. induction k as [|k IH]; intro c; cbn [nsum]; [reflexivity|]. rewrite IH. lia. Qed.

Lemma nsum_add : forall k g h, nsum k (fun i => g i + h i) = nsum k g + nsum k h.
Proof. induction k as [|k IH]; intros g h; cbn [nsum]; [reflexivity|]. rewrite IH. lia. Qed.

Lemma nsum_split : forall a b g, nsum (a + b) g = nsum a g + nsum b (fun i => g (a + i)).
Proof.
  intros a b g. induction b as [|b IH]; [rewrite Nat.add_0_r; cbn [nsum]; lia|].
  replace (a + S b) with (S (a + b)) by lia. cbn [nsum]. rewrite IH. lia.
Qed.

Lemma nsum_mul : forall c s g, nsum (c * s) g = nsum c (fun j => nsum s (fun r => g (s * j + r))).
Proof.
  induction c as [|c IH]; intros s g; cbn [nsum]; [reflexivity|]. replace (S c * s) with (c * s + s) by lia.
  rewrite nsum_split, IH. f_equal. apply nsum_ext. intros r Hr. f_equal. lia.
Qed.

Lemma nsum_le : forall k g h, (forall i, i < k -> g i <= h i) -> nsum k g <= nsum k h.
Proof. induction k as [|k IH]; intros g h H; cbn [nsum]; [lia|]. pose proof (IH g h (fun i Hi => H i ltac:(lia))). pose proof (H k ltac:(lia)). lia. Qed.

Lemma nsum_zero : forall k g, (forall i, i < k -> g i = 0) -> nsum k g = 0.
Proof. intros k g H. rewrite (nsum_ext k g (fun _ => 0)) by exact H. rewrite nsum_const. lia. Qed.

Lemma nsum_one : forall k g j, j < k -> (forall i, i < k -> i <> j -> g i = 0) -> nsum k g = g j.
Proof.
  induction k as [|k IH]; intros g j Hj H; [lia|]. cbn [nsum]. destruct (Nat.eq_dec j k) as [->|E].
  - rewrite nsum_zero; [lia|]. intros i Hi. apply H; lia.
  - rewrite (IH g j) by (lia || (intros i Hi Hne; apply H; lia)). rewrite (H k) by lia. lia.
Qed.

Lemma nsum_swap : forall a b (f : nat -> nat -> nat), nsum a (fun i => nsum b (f i)) = nsum b (fun j => nsum a (fun i => f i j)).
Proof.
  induction a as [|a IH]; intros b f; cbn [nsum].
  - symmetry. apply nsum_zero. reflexivity.
  - rewrite IH, <- nsum_add. reflexivity.
Qed.

Lemma nsum_eq_split : forall k a u v, a < k -> nsum k (fun j => if Nat.eqb j a then u else v) = (k - 1) * v + u.
Proof.
  induction k as [|k IH]; intros a u v Ha; [lia|]. cbn [nsum]. destruct (Nat.eqb_spec k a) as [<-|E].
  - rewrite (nsum_ext k _ (fun _ => v)) by (intros i Hi; destruct (Nat.eqb_spec i k); [lia | reflexivity]).
    rewrite nsum_const. lia.
  - rewrite IH by lia. destruct k; [lia|]. lia.
Qed.

Lemma nsum_lt_split : forall k a u v, a <= k -> nsum k (fun j => if j <? a then u else v) = a * u + (k - a) * v.
Proof.
  intros k a u v Ha. replace k with (a + (k - a)) at 1 by lia. rewrite nsum_split.
  rewrite (nsum_ext a _ (fun _ => u)) by (intros i Hi; destruct (Nat.ltb_spec i a); [reflexivity | lia]).
  rewrite (nsum_ext (k - a) _ (fun _ => v)) by (intros i Hi; destruct (Nat.ltb_spec (a + i) a); [lia | reflexivity]).
  rewrite !nsum_const. lia.
Qed.

Lemma nsum_succ : forall k g, nsum (S k) g = nsum k g + g k.
Proof. reflexivity. Qed.

Lemma nsum_mulc : forall k f c, nsum k (fun i => f i * c) = nsum k f * c.
Proof. induction k as [|k IH]; intros f c; cbn [nsum]; [reflexivity|]. rewrite IH. lia. Qed.

Lemma fold_nsum_seq : forall m (f : nat -> nat), fold_right Nat.add 0 (map f (seq 0 m)) = nsum m f.
Proof.
  assert (G : forall l c, fold_right Nat.add c l = fold_right Nat.add 0 l + c)
    by (induction l as [|y l IHl]; intro c; cbn [fold_right]; [reflexivity | rewrite IHl; lia]).
  induction m as [|m IH]; intro f; [reflexivity|]. rewrite seq_S, map_app, fold_right_app. cbn [map fold_right nsum].
  rewrite G, IH. cbn [Nat.add]. lia.
Qed.

Lemma length_filter_seq : forall (f : nat -> bool) a k, length (filter f (seq a k)) = nsum k (fun i => Nat.b2n (f (a + i))).
Proof.
  intros f a k. revert a. induction k as [|k IH]; intro a; [reflexivity|].
  rewrite seq_S, filter_app, length_app, IH. cbn [nsum filter]. destruct (f (a + k)); cbn [length Nat.b2n]; lia.
Qed.

Definition usu (bs : list block) (x : sref) : su := match x with SIn _ => su0 | SRef b j r => sunit bs b j r end.

(* ---- the rows of a layered map by their sources ------------------------------------------------------------ *)
Definition isUn (c : src) : bool := match c with Un _ => true | Cp _ => false end.
Definition rowlen (us : list tu) (n : nat) (c : src) : nat := match c with Cp _ => 1 | Un s => length (tin (nth (s - n) us tu0)) end.

Lemma count_Un : forall (L : list src) n m, (forall s, In (Un s) L -> n <= s < n + m) ->
  length (filter isUn L) = nsum m (fun k => length (filter (isun (n + k)) L)).
Proof.
  induction L as [|c t IH]; intros n m H; cbn [filter length].
  - symmetry. apply nsum_zero. reflexivity.
  - rewrite (nsum_ext m _ (fun k => (if isun (n + k) c then 1 else 0) + length (filter (isun (n + k)) t)))
      by (intros k _; cbn [filter]; destruct (isun (n + k) c); reflexivity).
    rewrite nsum_add, <- IH by (intros s Hs; apply H; right; exact Hs).
    destruct c as [s|s]; cbn [isUn isun].
    + rewrite nsum_zero by reflexivity. lia.
    + specialize (H s (or_introl eq_refl)). rewrite (nsum_one m _ (s - n)); [| lia |].
      * replace (n + (s - n)) with s by lia. rewrite Nat.eqb_refl. cbn [length]. lia.
      * intros k Hk E. destruct (Nat.eqb_spec s (n + k)); [lia | reflexivity].
Qed.

Lemma sum_rowlen : forall (L : list src) us n m, (forall s, In (Un s) L -> n <= s < n + m) ->
  fold_right Nat.add 0 (map (rowlen us n) L)
  = length (filter (fun c => negb (isUn c)) L) + nsum m (fun k => length (filter (isun (n + k)) L) * length (tin (nth k us tu0))).
Proof.
  induction L as [|c t IH]; intros us n m H; cbn [map fold_right filter length].
  - rewrite nsum_zero by reflexivity. reflexivity.
  - rewrite (nsum_ext m _ (fun k => (if isun (n + k) c then length (tin (nth k us tu0)) else 0)
                                     + length (filter (isun (n + k)) t) * length (tin (nth k us tu0))))
      by (intros k _; cbn [filter]; destruct (isun (n + k) c); cbn [length]; lia).
    rewrite nsum_add, (IH us n m) by (intros s Hs; apply H; right; exact Hs).
    destruct c as [s|s]; cbn [isUn isun negb rowlen].
    + rewrite (nsum_zero m (fun _ => 0)) by reflexivity. cbn [length]. lia.
    + specialize (H s (or_introl eq_refl)).
      rewrite (nsum_one m (fun k => if Nat.eqb s (n + k) then length (tin (nth k us tu0)) else 0) (s - n)); [| lia |].
      * replace (n + (s - n)) with s by lia. rewrite Nat.eqb_refl. lia.
      * intros k Hk E. destruct (Nat.eqb_spec s (n + k)); [lia | reflexivity].
Qed.

Lemma fold_add : forall (l : list nat) c, fold_right Nat.add c l = fold_right Nat.add 0 l + c.
Proof. induction l as [|a t IH]; intro c; cbn [fold_right]; [reflexivity|]. rewrite IH. lia. Qed.

Lemma fold_add_app : forall (l1 l2 : list nat), fold_right Nat.add 0 (l1 ++ l2) = fold_right Nat.add 0 l1 + fold_right Nat.add 0 l2.
Proof. intros l1 l2. rewrite fold_right_app, fold_add. lia. Qed.

(* the rows of Lev(N) and their entries, by their sources *)
Lemma lev_rowcount : forall n us nx, length (concat (lev n us nx)) = length (concat (map snd (lsrc n us nx))).
Proof.
  intros n us nx. unfold lev. induction (lsrc n us nx) as [|[l cs] P IH]; [reflexivity|].
  cbn [map concat fst snd]. rewrite !length_app, length_map, IH. reflexivity.
Qed.

Lemma lev_rowlens : forall n us nx,
  fold_right Nat.add 0 (map (fun row => length (tin row)) (concat (lev n us nx)))
  = fold_right Nat.add 0 (map (rowlen us n) (concat (map snd (lsrc n us nx)))).
Proof.
  intros n us nx. unfold lev. induction (lsrc n us nx) as [|[l cs] P IH]; [reflexivity|].
  cbn [map concat fst snd]. rewrite !map_app, !fold_add_app, IH. f_equal. f_equal. rewrite map_map.
  apply map_ext. intros [s|s]; cbn [mkrow rowlen idr reidx tin]; [reflexivity|]. apply length_map.
Qed.

Lemma boff_S_gen : forall bs b, b < length bs -> boff bs (S b) = boff bs b + bcount (nth b bs blk0) * bsize (nth b bs blk0).
Proof.
  induction bs as [|blk t IH]; intros b Hb; cbn [length] in Hb; [lia|].
  destruct b as [|b]; cbn [boff nth]; [assert (boff t 0 = 0) by (destruct t; reflexivity); lia|]. rewrite IH by lia. lia.
Qed.

Definition isref (x : sref) : bool := match x with SIn _ => false | SRef _ _ _ => true end.

Section BL.
Variable bs : list block.
Variable n : nat.
Variable out : nat -> sref.
Hypothesis Hwf : wf bs n.
Hypothesis Hout : forall i, i < n -> valid bs n (out i).

Definition fus : list tu := compile n bs.
Definition fnx : list nat := map (fun i => idx n bs (out i)) (seq 0 n).
Definition nun : nat := boff bs (length bs).

Definition cnt (b : nat) : nat := bcount (nth b bs blk0).
Definition sz (b : nat) : nat := bsize (nth b bs blk0).

(* the sum of g over the units, block by block *)
Definition bsum (g : sref -> nat) : nat := nsum (length bs) (fun b => nsum (cnt b) (fun j => nsum (sz b) (fun r => g (SRef b j r)))).

Lemma shaped_all : Forall shaped bs.
Proof. exact (proj1 Hwf). Qed.

Lemma fus_len : length fus = nun.
Proof. unfold fus, nun. apply length_compile. exact shaped_all. Qed.

Lemma fwfn : wfn n fus.
Proof. intros k Hk. exact (wf_reads_below bs n Hwf k Hk). Qed.

Lemma fnx_len : length fnx = n.
Proof. unfold fnx. rewrite length_map, length_seq. reflexivity. Qed.

Lemma boff_S : forall b, b < length bs -> boff bs (S b) = boff bs b + cnt b * sz b.
Proof. intros b Hb. unfold cnt, sz. apply boff_S_gen. exact Hb. Qed.

Lemma nsum_units : forall (g : nat -> nat), nsum nun g = bsum (fun x => match x with SRef b j r => g (boff bs b + sz b * j + r) | SIn _ => 0 end).
Proof.
  intro g. unfold nun, bsum.
  assert (G : forall k, k <= length bs -> nsum (boff bs k) g
            = nsum k (fun b => nsum (cnt b) (fun j => nsum (sz b) (fun r => g (boff bs b + sz b * j + r))))).
  { induction k as [|k IH]; intro Hk; [destruct bs; reflexivity|].
    rewrite boff_S by lia. cbn [nsum]. rewrite nsum_split, IH by lia. f_equal.
    rewrite (Nat.mul_comm (cnt k) (sz k)) at 1. rewrite Nat.mul_comm, nsum_mul. apply nsum_ext. intros j Hj.
    apply nsum_ext. intros r Hr. f_equal. lia. }
  apply G. lia.
Qed.

Lemma nsum_bsum_swap : forall m (g : nat -> sref -> nat), nsum m (fun l => bsum (g l)) = bsum (fun x => nsum m (fun l => g l x)).
Proof.
  intros m g. unfold bsum. rewrite nsum_swap. apply nsum_ext. intros b _. rewrite nsum_swap. apply nsum_ext.
  intros j _. apply nsum_swap.
Qed.

Lemma bsum_ext : forall g1 g2, (forall b j r, valid bs n (SRef b j r) -> g1 (SRef b j r) = g2 (SRef b j r)) ->
  bsum g1 = bsum g2.
Proof.
  intros g1 g2 H. unfold bsum. apply nsum_ext. intros b Hb. apply nsum_ext. intros j Hj. apply nsum_ext. intros r Hr.
  apply H. split; [exact Hb | split; [exact Hj | exact Hr]].
Qed.

(* locating a unit *)
Lemma loc : forall k, k < nun -> exists b j r, valid bs n (SRef b j r) /\ k = boff bs b + sz b * j + r.
Proof.
  unfold nun, sz, valid. pose proof shaped_all as Hsh. clear -Hsh. intros k Hk. revert k Hk.
  induction Hsh as [|blk t Hs Ht IH]; intros k Hk; cbn [boff length] in Hk; [lia|].
  destruct (Nat.lt_ge_cases k (bcount blk * bsize blk)) as [Hl|Hg].
  - exists 0, (k / bsize blk), (k mod bsize blk).
    assert (bsize blk <> 0) by (intro E; rewrite E in Hl; lia).
    cbn [nth length boff]. split; [split; [lia|]; split; [apply Nat.Div0.div_lt_upper_bound; nia | apply Nat.mod_upper_bound; exact H]|].
    rewrite (Nat.div_mod k (bsize blk)) at 1 by exact H. lia.
  - destruct (IH (k - bcount blk * bsize blk) ltac:(lia)) as (b & j & r & (Hb & Hj & Hr) & Hk').
    exists (S b), j, r. cbn [nth length boff]. split; [split; [lia|]; split; assumption|]. lia.
Qed.

Lemma idx_ref : forall b j r, idx n bs (SRef b j r) = n + (boff bs b + sz b * j + r).
Proof. intros b j r. cbn [idx]. unfold sz. lia. Qed.

Lemma fus_nth : forall b j r, valid bs n (SRef b j r) ->
  nth (boff bs b + sz b * j + r) fus tu0 = flat_unit n bs (sunit bs b j r).
Proof. intros b j r (Hb & Hj & Hr). unfold fus, sz. apply compile_nth; [exact shaped_all | exact Hb | exact Hj | exact Hr]. Qed.

Lemma unit_lt : forall b j r, valid bs n (SRef b j r) -> boff bs b + sz b * j + r < nun.
Proof.
  intros b j r Hv. pose proof (idx_lt_total bs n (SRef b j r) Hv) as H. rewrite idx_ref in H. unfold nun. lia.
Qed.

(* the units and the edges, block by block *)
Lemma units_bsum : length fus = bsum (fun _ => 1).
Proof.
  rewrite fus_len. rewrite <- (Nat.mul_1_r nun) at 1. rewrite <- nsum_const, nsum_units.
  apply bsum_ext. reflexivity.
Qed.

Lemma edges_bsum : nsum (length fus) (fun k => length (tin (nth k fus tu0))) = bsum (fun x => length (sin (usu bs x))).
Proof.
  rewrite fus_len, nsum_units. apply bsum_ext. intros b j r Hv. cbn [usu].
  rewrite fus_nth by exact Hv. unfold flat_unit. cbn [tin]. apply length_map.
Qed.

(* the predecessors of a unit are valid signals with smaller indices *)
Lemma pred_ok : forall b j r x z, valid bs n (SRef b j r) -> In (x, z) (sin (sunit bs b j r)) ->
  valid bs n x /\ idx n bs x < idx n bs (SRef b j r).
Proof.
  intros b j r x z Hv Hin. destruct Hv as (Hb & Hj & Hr). destruct Hwf as [Hsh Hrd].
  pose proof (Hrd b j r x z Hb Hj Hr Hin) as Hbk. destruct x as [i|b' j' r']; cbn [reads_back valid] in *.
  - split; [exact Hbk|]. cbn [idx]. lia.
  - destruct Hbk as [(Hb' & Hj' & Hr')|(-> & -> & Hr')].
    + split; [split; [lia|]; split; assumption|].
      pose proof (boff_mono bs b b' Hb' ltac:(lia)) as Hm. cbn [idx]. nia.
    + split; [split; [lia|]; split; lia|]. cbn [idx]. lia.
Qed.

Lemma idx_inj : forall x y, valid bs n x -> valid bs n y -> idx n bs x = idx n bs y -> x = y.
Proof.
  assert (L : forall b j r b' j' r', valid bs n (SRef b j r) -> valid bs n (SRef b' j' r') -> b < b' ->
            idx n bs (SRef b j r) < idx n bs (SRef b' j' r')).
  { intros b j r b' j' r' (_ & Hj & Hr) (Hb' & _ & _) Hlt.
    pose proof (boff_mono bs b' b Hlt ltac:(lia)) as Hm. cbn [idx]. nia. }
  intros [i|b j r] [i'|b' j' r'] Hx Hy E; cbn [idx valid] in E, Hx, Hy.
  - congruence.
  - lia.
  - lia.
  - destruct (Nat.lt_trichotomy b b') as [Hlt|[<-|Hgt]].
    + pose proof (L b j r b' j' r' Hx Hy Hlt). cbn [idx] in *. lia.
    + assert (Hs : bsize (nth b bs blk0) * j + r = bsize (nth b bs blk0) * j' + r') by lia.
      destruct Hx as (_ & _ & Hr), Hy as (_ & _ & Hr').
      destruct (Nat.lt_trichotomy j j') as [Hj|[<-|Hj]]; [nia | | nia].
      assert (r = r') by lia. subst. reflexivity.
    + pose proof (L b' j' r' b j r Hy Hx Hgt). cbn [idx] in *. lia.
Qed.

(* ---- depths ----------------------------------------------------------------------------------------- *)
Lemma dep_su : forall b j r, valid bs n (SRef b j r) ->
  dep n fus (idx n bs (SRef b j r)) = S (maxl (map (fun p => dep n fus (idx n bs (fst p))) (sin (sunit bs b j r)))).
Proof.
  intros b j r Hv. rewrite idx_ref. rewrite dep_unit; [| exact fwfn | rewrite fus_len; exact (unit_lt b j r Hv)].
  rewrite fus_nth by exact Hv. unfold flat_unit. cbn [tin]. rewrite map_map. reflexivity.
Qed.

(* the depth recursion from bounds on each unit: every predecessor lies below it, one of them just below *)
Lemma cd_rec : forall (cd : sref -> nat) (u : su) c,
  (forall p z, In (p, z) (sin u) -> S (cd p) <= c) -> (exists p z, In (p, z) (sin u) /\ S (cd p) = c) ->
  c = S (maxl (map (fun p => cd (fst p)) (sin u))).
Proof.
  intros cd u c Hle (p0 & z0 & Hin0 & E0). apply Nat.le_antisymm.
  - rewrite <- E0. apply le_n_S. apply maxl_ge. apply in_map_iff. exists (p0, z0). split; [reflexivity | exact Hin0].
  - assert (maxl (map (fun p => cd (fst p)) (sin u)) <= c - 1); [|lia].
    apply maxl_le. intros a Ha. apply in_map_iff in Ha as ([p z] & <- & Hin). specialize (Hle p z Hin). cbn [fst]. lia.
Qed.

Section Depth.
Variable cd : sref -> nat.
Hypothesis Hcd0 : forall i, cd (SIn i) = 0.
Hypothesis Hcd : forall b j r, valid bs n (SRef b j r) ->
  cd (SRef b j r) = S (maxl (map (fun p => cd (fst p)) (sin (sunit bs b j r)))).

Theorem dep_cd : forall x, valid bs n x -> dep n fus (idx n bs x) = cd x.
Proof.
  assert (G : forall k x, idx n bs x < k -> valid bs n x -> dep n fus (idx n bs x) = cd x).
  { induction k as [|k IH]; intros x Hk Hv; [lia|]. destruct x as [i|b j r].
    - cbn [idx]. rewrite Hcd0. apply dep_state. exact Hv.
    - rewrite dep_su by exact Hv. rewrite Hcd by exact Hv. f_equal. f_equal. apply map_ext_in. intros [p z] Hin.
      cbn [fst]. destruct (pred_ok b j r p z Hv Hin) as [Hvp Hlt]. apply IH; [lia | exact Hvp]. }
  intros x Hv. exact (G (S (idx n bs x)) x (Nat.lt_succ_diag_r _) Hv).
Qed.

Theorem dmax_cd : forall D, (forall b j r, valid bs n (SRef b j r) -> cd (SRef b j r) <= D) ->
  (exists b j r, valid bs n (SRef b j r) /\ cd (SRef b j r) = D) -> dmax n fus = D.
Proof.
  intros D Hle (b0 & j0 & r0 & Hv0 & E0). apply Nat.le_antisymm.
  - apply maxl_le. intros a Ha. apply in_map_iff in Ha as (k & <- & Hk). apply in_seq in Hk. rewrite fus_len in Hk.
    destruct (loc k ltac:(lia)) as (b & j & r & Hv & ->). rewrite <- idx_ref, dep_cd by exact Hv. apply Hle. exact Hv.
  - rewrite <- E0, <- (dep_cd _ Hv0), idx_ref. apply maxl_ge. apply in_map_iff. exists (boff bs b0 + sz b0 * j0 + r0).
    split; [reflexivity|]. apply in_seq. rewrite fus_len. pose proof (unit_lt b0 j0 r0 Hv0). lia.
Qed.

End Depth.

(* ---- every unit contributes to a next value ---------------------------------------------------------- *)
Section Need.
Hypothesis Hrd : forall b j r, valid bs n (SRef b j r) -> (exists i, i < n /\ out i = SRef b j r) \/
  (exists b' j' r' z, valid bs n (SRef b' j' r') /\ In (SRef b j r, z) (sin (sunit bs b' j' r'))).

Theorem need_all : forall b j r, valid bs n (SRef b j r) -> need n fus fnx (idx n bs (SRef b j r)) = true.
Proof.
  assert (G : forall k b j r, n + nun - idx n bs (SRef b j r) < k -> valid bs n (SRef b j r) ->
            need n fus fnx (idx n bs (SRef b j r)) = true).
  { induction k as [|k IH]; intros b j r Hk Hv; [lia|].
    destruct (Hrd b j r Hv) as [(i & Hi & Ei)|(b' & j' & r' & z & Hvr & Hin)].
    - apply need_nx. unfold fnx. apply in_map_iff. exists i. split; [rewrite Ei; reflexivity | apply in_seq; lia].
    - destruct (pred_ok b' j' r' (SRef b j r) z Hvr Hin) as [_ Hlt].
      assert (Hn' : need n fus fnx (idx n bs (SRef b' j' r')) = true).
      { apply IH; [| exact Hvr]. pose proof (idx_lt_total bs n _ Hvr). unfold nun in *. lia. }
      rewrite idx_ref in Hn'. apply (need_pred n fus fnx fwfn (boff bs b' + sz b' * j' + r') (idx n bs (SRef b j r)) z).
      + rewrite fus_len. exact (unit_lt b' j' r' Hvr).
      + exact Hn'.
      + rewrite fus_nth by exact Hvr. unfold flat_unit. cbn [tin]. apply in_map_iff. exists (SRef b j r, z).
        split; [reflexivity | exact Hin]. }
  intros b j r Hv. apply (G (S (n + nun)) b j r); [lia | exact Hv].
Qed.

End Need.

(* ---- the last layer that reads each signal ------------------------------------------------------------- *)
Lemma reads_su : forall b j r x, valid bs n (SRef b j r) -> valid bs n x ->
  reads fus (boff bs b + sz b * j + r) (idx n bs x) = true <-> exists z, In (x, z) (sin (sunit bs b j r)).
Proof.
  intros b j r x Hv Hx. rewrite reads_in. rewrite fus_nth by exact Hv. unfold flat_unit. cbn [tin]. split.
  - intros (z & Hin). apply in_map_iff in Hin as ([p z'] & E & Hin). cbn [fst snd] in E. injection E as Ep <-.
    exists z'. destruct (pred_ok b j r p z' Hv Hin) as [Hvp _]. rewrite (idx_inj p x Hvp Hx Ep) in Hin. exact Hin.
  - intros (z & Hin). exists z. apply in_map_iff. exists (x, z). split; [reflexivity | exact Hin].
Qed.

Section Lam.
Variable cd : sref -> nat.
Hypothesis Hdep : forall x, valid bs n x -> dep n fus (idx n bs x) = cd x.
Hypothesis Hneed : forall b j r, valid bs n (SRef b j r) -> need n fus fnx (idx n bs (SRef b j r)) = true.
Variable D : nat.
Hypothesis HD : dmax n fus = D.
Variable cl : sref -> nat.
Hypothesis Hup : forall b j r p z, valid bs n (SRef b j r) -> In (p, z) (sin (sunit bs b j r)) -> cd (SRef b j r) <= cl p.
Hypothesis Hcl_out : forall i, i < n -> cl (out i) = S D.
Hypothesis Hlow : forall x, valid bs n x -> (forall i, i < n -> out i <> x) ->
  cl x = 0 \/ exists b j r z, valid bs n (SRef b j r) /\ In (x, z) (sin (sunit bs b j r)) /\ cd (SRef b j r) = cl x.

Lemma unit_dep_le : forall k, k < nun -> dep n fus (n + k) <= D.
Proof. intros k Hk. rewrite <- HD. apply dep_le_dmax. rewrite fus_len. exact Hk. Qed.

Theorem lam_cl : forall x, valid bs n x -> lam n fus fnx (idx n bs x) = cl x.
Proof.
  intros x Hx. unfold lam.
  (* the readers *)
  assert (Hr : maxl (map (fun k => if need n fus fnx (n + k) && reads fus k (idx n bs x) then dep n fus (n + k) else 0)
                         (seq 0 (length fus))) <= cl x).
  { apply maxl_le. intros a Ha. apply in_map_iff in Ha as (k & <- & Hk). apply in_seq in Hk. rewrite fus_len in Hk.
    destruct (loc k ltac:(lia)) as (b & j & r & Hv & ->).
    destruct (need n fus fnx _ && reads fus _ (idx n bs x)) eqn:E; [|lia].
    apply andb_true_iff in E as [_ E]. apply (reads_su b j r x Hv Hx) in E as (z & Hin).
    rewrite <- idx_ref, Hdep by exact Hv. exact (Hup b j r x z Hv Hin). }
  destruct (existsb (Nat.eqb (idx n bs x)) fnx) eqn:Eo.
  - apply existsb_exists in Eo as (s & Hs & E). apply Nat.eqb_eq in E. unfold fnx in Hs.
    apply in_map_iff in Hs as (i & Ei & Hi). apply in_seq in Hi. rewrite <- Ei in E.
    apply (idx_inj x (out i) Hx (Hout i ltac:(lia))) in E. subst x. rewrite Hcl_out by lia. rewrite HD.
    apply Nat.max_l. eapply Nat.le_trans; [|apply Nat.le_succ_diag_r]. apply maxl_le. intros a Ha.
    apply in_map_iff in Ha as (k & <- & Hk). apply in_seq in Hk. rewrite fus_len in Hk.
    destruct (_ && _); [apply unit_dep_le; lia | lia].
  - cbn [Nat.max]. apply Nat.le_antisymm; [exact Hr|].
    assert (Hno : forall i, i < n -> out i <> x).
    { intros i Hi E. subst x. assert (existsb (Nat.eqb (idx n bs (out i))) fnx = true) as C.
      { apply existsb_exists. exists (idx n bs (out i)). split; [|apply Nat.eqb_refl].
        unfold fnx. apply in_map_iff. exists i. split; [reflexivity | apply in_seq; lia]. }
      rewrite C in Eo. discriminate. }
    destruct (Hlow x Hx Hno) as [Z|(b & j & r & z & Hv & Hin & E)]; [rewrite Z; lia|].
    rewrite <- E. apply maxl_ge. apply in_map_iff. exists (boff bs b + sz b * j + r).
    split; [|apply in_seq; rewrite fus_len; pose proof (unit_lt b j r Hv); lia].
    rewrite <- idx_ref, Hneed by exact Hv.
    replace (reads fus (boff bs b + sz b * j + r) (idx n bs x)) with true
      by (symmetry; apply (reads_su b j r x Hv Hx); exists z; exact Hin).
    cbn [andb]. apply Hdep. exact Hv.
Qed.

(* the rows of layer l: the units of depth l and the copies of the signals of smaller depth read after l *)
Theorem live_cl : forall l x, valid bs n x ->
  live n fus fnx l (idx n bs x) = isref x && Nat.eqb (cd x) l || Nat.ltb (cd x) l && Nat.ltb l (cl x).
Proof.
  intros l x Hx. unfold live, isunit. rewrite Hdep, lam_cl by exact Hx. destruct x as [i|b j r].
  - cbn [idx isref valid] in *. destruct (Nat.leb_spec n i); [lia|]. reflexivity.
  - rewrite Hneed by exact Hx. rewrite idx_ref. destruct (Nat.leb_spec n (n + (boff bs b + sz b * j + r))); [|lia].
    cbn [isref andb]. rewrite andb_true_r. reflexivity.
Qed.

Theorem width_bsum : forall l, 1 <= l ->
  width n fus fnx l = nsum n (fun i => Nat.b2n (Nat.ltb (cd (SIn i)) l && Nat.ltb l (cl (SIn i))))
                      + bsum (fun x => Nat.b2n (Nat.eqb (cd x) l || Nat.ltb (cd x) l && Nat.ltb l (cl x))).
Proof.
  intros [|l] Hl; [lia|]. cbn [width]. unfold NS. rewrite fus_len, seq_app, filter_app, length_app.
  rewrite !length_filter_seq. f_equal.
  - apply nsum_ext. intros i Hi. cbn [Nat.add]. replace i with (idx n bs (SIn i)) at 1 by reflexivity.
    rewrite live_cl by (cbn [valid]; exact Hi). reflexivity.
  - rewrite nsum_units. unfold bsum. apply nsum_ext. intros b Hb. apply nsum_ext. intros j Hj. apply nsum_ext.
    intros r Hr. assert (Hv : valid bs n (SRef b j r)) by (split; [exact Hb | split; [exact Hj | exact Hr]]).
    replace (0 + n + (boff bs b + sz b * j + r)) with (idx n bs (SRef b j r)) by (rewrite idx_ref; lia).
    rewrite live_cl by exact Hv. reflexivity.
Qed.

End Lam.

(* ---- netlists with units that contribute to nothing ------------------------------------------------------ *)
Section Need2.
Variable nd : sref -> bool.
Hypothesis Hnd_up : forall b j r, valid bs n (SRef b j r) -> nd (SRef b j r) = true ->
  (exists i, i < n /\ out i = SRef b j r) \/
  (exists b' j' r' z, valid bs n (SRef b' j' r') /\ nd (SRef b' j' r') = true /\ In (SRef b j r, z) (sin (sunit bs b' j' r'))).
Hypothesis Hnd_down : forall b j r, valid bs n (SRef b j r) -> nd (SRef b j r) = false ->
  (forall i, i < n -> out i <> SRef b j r) /\
  forall b' j' r' z, valid bs n (SRef b' j' r') -> In (SRef b j r, z) (sin (sunit bs b' j' r')) -> nd (SRef b' j' r') = false.

Theorem need_eq : forall b j r, valid bs n (SRef b j r) -> need n fus fnx (idx n bs (SRef b j r)) = nd (SRef b j r).
Proof.
  assert (G : forall m b j r, n + nun - idx n bs (SRef b j r) < m -> valid bs n (SRef b j r) ->
            need n fus fnx (idx n bs (SRef b j r)) = nd (SRef b j r)).
  { induction m as [|m IH]; intros b j r Hm Hv; [lia|]. pose proof (idx_lt_total bs n _ Hv) as Hlt.
    destruct (nd (SRef b j r)) eqn:Hnd.
    - destruct (Hnd_up b j r Hv Hnd) as [(i & Hi & Ei)|(b' & j' & r' & z & Hv' & Hn' & Hin)].
      + apply need_nx. unfold fnx. apply in_map_iff. exists i. split; [rewrite Ei; reflexivity | apply in_seq; lia].
      + destruct (pred_ok b' j' r' (SRef b j r) z Hv' Hin) as [_ Hl].
        pose proof (idx_lt_total bs n _ Hv').
        assert (Hn'' : need n fus fnx (idx n bs (SRef b' j' r')) = true)
          by (rewrite (IH b' j' r'); [exact Hn' | unfold nun in *; lia | exact Hv']).
        rewrite idx_ref in Hn''. apply (need_pred n fus fnx fwfn (boff bs b' + sz b' * j' + r') _ z).
        * rewrite fus_len. exact (unit_lt b' j' r' Hv').
        * exact Hn''.
        * rewrite fus_nth by exact Hv'. unfold flat_unit. cbn [tin]. apply in_map_iff. exists (SRef b j r, z).
          split; [reflexivity | exact Hin].
    - destruct (need n fus fnx (idx n bs (SRef b j r))) eqn:E; [|reflexivity]. exfalso.
      destruct (Hnd_down b j r Hv Hnd) as [Hno Hrd].
      destruct (needf_step fus n _ _ E) as [H0|(k & z & Hk & Hk2 & Hin)].
      + apply existsb_exists in H0 as (s & Hs & Es). apply Nat.eqb_eq in Es. unfold fnx in Hs.
        apply in_map_iff in Hs as (i & Ei & Hi). apply in_seq in Hi. rewrite <- Ei in Es.
        apply (Hno i ltac:(lia)). symmetry. exact (idx_inj _ _ Hv (Hout i ltac:(lia)) Es).
      + rewrite fus_len in Hk. destruct (loc k Hk) as (b' & j' & r' & Hv' & ->).
        rewrite fus_nth in Hin by exact Hv'. unfold flat_unit in Hin. cbn [tin] in Hin.
        apply in_map_iff in Hin as ([p z'] & Ep & Hin). cbn [fst snd] in Ep. injection Ep as Ep _.
        destruct (pred_ok b' j' r' p z' Hv' Hin) as [Hvp Hl].
        rewrite (idx_inj _ _ Hvp Hv Ep) in Hin, Hl.
        pose proof (Hrd b' j' r' z' Hv' Hin) as Hf. pose proof (idx_lt_total bs n _ Hv').
        change (need n fus fnx (n + (boff bs b' + sz b' * j' + r')) = true) in Hk2.
        rewrite <- idx_ref, (IH b' j' r') in Hk2; [rewrite Hf in Hk2; discriminate | unfold nun in *; lia | exact Hv']. }
  intros b j r Hv. apply (G (S (n + nun)) b j r); [lia | exact Hv].
Qed.

End Need2.

Section Lam2.
Variable cd : sref -> nat.
Hypothesis Hdep : forall x, valid bs n x -> dep n fus (idx n bs x) = cd x.
Variable nd : sref -> bool.
Hypothesis Hneed : forall b j r, valid bs n (SRef b j r) -> need n fus fnx (idx n bs (SRef b j r)) = nd (SRef b j r).
Variable D : nat.
Hypothesis HD : dmax n fus = D.
Variable cl : sref -> nat.
Hypothesis Hup : forall b j r p z, valid bs n (SRef b j r) -> nd (SRef b j r) = true ->
  In (p, z) (sin (sunit bs b j r)) -> cd (SRef b j r) <= cl p.
Hypothesis Hcl_out : forall i, i < n -> cl (out i) = S D.
Hypothesis Hlow : forall x, valid bs n x -> (forall i, i < n -> out i <> x) ->
  cl x = 0 \/ exists b j r z, valid bs n (SRef b j r) /\ nd (SRef b j r) = true /\ In (x, z) (sin (sunit bs b j r))
                             /\ cd (SRef b j r) = cl x.

Theorem lam_cl2 : forall x, valid bs n x -> lam n fus fnx (idx n bs x) = cl x.
Proof.
  intros x Hx. unfold lam.
  assert (Hr : maxl (map (fun k => if need n fus fnx (n + k) && reads fus k (idx n bs x) then dep n fus (n + k) else 0)
                         (seq 0 (length fus))) <= cl x).
  { apply maxl_le. intros a Ha. apply in_map_iff in Ha as (k & <- & Hk). apply in_seq in Hk. rewrite fus_len in Hk.
    destruct (loc k ltac:(lia)) as (b & j & r & Hv & ->).
    destruct (need n fus fnx _ && reads fus _ (idx n bs x)) eqn:E; [|lia].
    apply andb_true_iff in E as [E1 E]. apply (reads_su b j r x Hv Hx) in E as (z & Hin).
    rewrite <- idx_ref, Hneed in E1 by exact Hv.
    rewrite <- idx_ref, Hdep by exact Hv. exact (Hup b j r x z Hv E1 Hin). }
  destruct (existsb (Nat.eqb (idx n bs x)) fnx) eqn:Eo.
  - apply existsb_exists in Eo as (s & Hs & E). apply Nat.eqb_eq in E. unfold fnx in Hs.
    apply in_map_iff in Hs as (i & Ei & Hi). apply in_seq in Hi. rewrite <- Ei in E.
    apply (idx_inj x (out i) Hx (Hout i ltac:(lia))) in E. subst x. rewrite Hcl_out by lia. rewrite HD.
    apply Nat.max_l. eapply Nat.le_trans; [|apply Nat.le_succ_diag_r]. apply maxl_le. intros a Ha.
    apply in_map_iff in Ha as (k & <- & Hk). apply in_seq in Hk. rewrite fus_len in Hk.
    destruct (_ && _); [rewrite <- HD; apply dep_le_dmax; rewrite fus_len; lia | lia].
  - cbn [Nat.max]. apply Nat.le_antisymm; [exact Hr|].
    assert (Hno : forall i, i < n -> out i <> x).
    { intros i Hi E. subst x. assert (existsb (Nat.eqb (idx n bs (out i))) fnx = true) as C.
      { apply existsb_exists. exists (idx n bs (out i)). split; [|apply Nat.eqb_refl].
        unfold fnx. apply in_map_iff. exists i. split; [reflexivity | apply in_seq; lia]. }
      rewrite C in Eo. discriminate. }
    destruct (Hlow x Hx Hno) as [Z|(b & j & r & z & Hv & Hn & Hin & E)]; [rewrite Z; lia|].
    rewrite <- E. apply maxl_ge. apply in_map_iff. exists (boff bs b + sz b * j + r).
    split; [|apply in_seq; rewrite fus_len; pose proof (unit_lt b j r Hv); lia].
    rewrite <- idx_ref, Hneed, Hn by exact Hv.
    replace (reads fus (boff bs b + sz b * j + r) (idx n bs x)) with true
      by (symmetry; apply (reads_su b j r x Hv Hx); exists z; exact Hin).
    cbn [andb]. apply Hdep. exact Hv.
Qed.

Theorem live_cl2 : forall l x, valid bs n x ->
  live n fus fnx l (idx n bs x) = isref x && nd x && Nat.eqb (cd x) l || Nat.ltb (cd x) l && Nat.ltb l (cl x).
Proof.
  intros l x Hx. unfold live, isunit. rewrite Hdep, lam_cl2 by exact Hx. destruct x as [i|b j r].
  - cbn [idx isref valid] in *. destruct (Nat.leb_spec n i); [lia|]. reflexivity.
  - rewrite Hneed by exact Hx. rewrite idx_ref. destruct (Nat.leb_spec n (n + (boff bs b + sz b * j + r))); [|lia].
    cbn [isref andb]. destruct (nd (SRef b j r)), (Nat.eqb (cd (SRef b j r)) l); reflexivity.
Qed.

(* the rows of the layers 1 .. d - 1, signal by signal *)
Theorem rows_bsum : forall m, fold_right Nat.add 0 (map (width n fus fnx) (seq 1 m))
  = nsum n (fun i => nsum m (fun l => Nat.b2n (Nat.ltb (cd (SIn i)) (S l) && Nat.ltb (S l) (cl (SIn i)))))
    + bsum (fun x => nsum m (fun l => Nat.b2n (nd x && Nat.eqb (cd x) (S l) || Nat.ltb (cd x) (S l) && Nat.ltb (S l) (cl x)))).
Proof.
  intro m. rewrite <- (seq_shift m 0), map_map.
  rewrite (fold_nsum_seq _ (fun l => width n fus fnx (S l))).
  rewrite (nsum_ext m _ (fun l => nsum n (fun i => Nat.b2n (Nat.ltb (cd (SIn i)) (S l) && Nat.ltb (S l) (cl (SIn i))))
                                  + bsum (fun x => Nat.b2n (nd x && Nat.eqb (cd x) (S l) || Nat.ltb (cd x) (S l) && Nat.ltb (S l) (cl x))))).
  - rewrite nsum_add, nsum_swap, nsum_bsum_swap. reflexivity.
  - intros l _. cbn [width]. unfold NS. rewrite fus_len, seq_app, filter_app, length_app, !length_filter_seq. f_equal.
    + apply nsum_ext. intros i Hi. cbn [Nat.add]. replace i with (idx n bs (SIn i)) at 1 by reflexivity.
      rewrite live_cl2 by (cbn [valid]; exact Hi). reflexivity.
    + rewrite nsum_units. unfold bsum. apply nsum_ext. intros b Hb. apply nsum_ext. intros j Hj. apply nsum_ext.
      intros r Hr. assert (Hv : valid bs n (SRef b j r)) by (split; [exact Hb | split; [exact Hj | exact Hr]]).
      replace (0 + n + (boff bs b + sz b * j + r)) with (idx n bs (SRef b j r)) by (rewrite idx_ref; lia).
      rewrite live_cl2 by exact Hv. cbn [isref andb]. reflexivity.
Qed.

End Lam2.

End BL.