fin_maps.v 17.5 KB
Newer Older
1
2
3
4
5
6
(* Copyright (c) 2012, Robbert Krebbers. *)
(* This file is distributed under the terms of the BSD license. *)
(** Finite maps associate data to keys. This file defines an interface for
finite maps and collects some theory on it. Most importantly, it proves useful
induction principles for finite maps and implements the tactic [simplify_map]
to simplify goals involving finite maps. *)
Robbert Krebbers's avatar
Robbert Krebbers committed
7
8
Require Export prelude.

9
10
(** * Axiomatization of finite maps *)
(** We require Leibniz equality to be extensional on finite maps. This of
11
12
13
14
15
course limits the space of finite map implementations, but since we are mainly
interested in finite maps with numbers as indexes, we do not consider this to
be a serious limitation. The main application of finite maps is to implement
the memory, where extensionality of Leibniz equality is very important for a
convenient use in the assertions of our axiomatic semantics. *)
16
17
18
19
20
21
22
(** Finiteness is axiomatized by requiring each map to have a finite domain.
Since we may have multiple implementations of finite sets, the [dom] function is
parametrized by an implementation of finite sets over the map's key type. *)
(** Finite map implementations are required to implement the [merge] function
which enables us to give a generic implementation of [union_with],
[intersection_with], and [difference_with]. *)
Class FinMap K M `{ A, Empty (M A)} `{Lookup K M} `{FMap M}
23
    `{PartialAlter K M} `{Dom K M} `{Merge M} := {
24
25
26
27
28
29
30
31
32
33
34
35
  finmap_eq {A} (m1 m2 : M A) :
    ( i, m1 !! i = m2 !! i)  m1 = m2;
  lookup_empty {A} i :
    ( : M A) !! i = None;
  lookup_partial_alter {A} f (m : M A) i :
    partial_alter f i m !! i = f (m !! i);
  lookup_partial_alter_ne {A} f (m : M A) i j :
    i  j  partial_alter f i m !! j = m !! j;
  lookup_fmap {A B} (f : A  B) (m : M A) i :
    (f <$> m) !! i = f <$> m !! i;
  elem_of_dom C {A} `{Collection K C} (m : M A) i :
    i  dom C m  is_Some (m !! i);
36
  merge_spec {A} f `{!PropHolds (f None None = None)}
Robbert Krebbers's avatar
Robbert Krebbers committed
37
38
39
    (m1 m2 : M A) i : merge f m1 m2 !! i = f (m1 !! i) (m2 !! i)
}.

40
41
42
43
44
(** * Derived operations *)
(** All of the following functions are defined in a generic way for arbitrary
finite map implementations. These generic implementations do not cause a
significant enough performance loss to make including them in the finite map
axiomatization worthwhile. *)
45
46
47
48
Instance finmap_alter `{PartialAlter K M} : Alter K M := λ A f,
  partial_alter (fmap f).
Instance finmap_insert `{PartialAlter K M} : Insert K M := λ A k x,
  partial_alter (λ _, Some x) k.
49
Instance finmap_delete `{PartialAlter K M} : Delete K M := λ A,
50
  partial_alter (λ _, None).
51
Instance finmap_singleton `{PartialAlter K M} {A}
52
  `{Empty (M A)} : Singleton (K * A) (M A) := λ p, <[fst p:=snd p]>.
Robbert Krebbers's avatar
Robbert Krebbers committed
53

54
55
Definition list_to_map `{Insert K M} {A} `{Empty (M A)}
  (l : list (K * A)) : M A := insert_list l .
Robbert Krebbers's avatar
Robbert Krebbers committed
56

57
58
59
60
61
62
Instance finmap_union `{Merge M} : UnionWith M := λ A f,
  merge (union_with f).
Instance finmap_intersection `{Merge M} : IntersectionWith M := λ A f,
  merge (intersection_with f).
Instance finmap_difference `{Merge M} : DifferenceWith M := λ A f,
  merge (difference_with f).
Robbert Krebbers's avatar
Robbert Krebbers committed
63

64
(** * General theorems *)
Robbert Krebbers's avatar
Robbert Krebbers committed
65
Section finmap.
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
Context `{FinMap K M} `{ i j : K, Decision (i = j)} {A : Type}.

Global Instance finmap_subseteq: SubsetEq (M A) := λ m n,
   i x, m !! i = Some x  n !! i = Some x.
Global Instance: BoundedPreOrder (M A).
Proof. split. firstorder. intros m i x. rewrite lookup_empty. discriminate. Qed.

Lemma lookup_subseteq_Some (m1 m2 : M A) i x :
  m1  m2  m1 !! i = Some x  m2 !! i = Some x.
Proof. auto. Qed.
Lemma lookup_subseteq_None (m1 m2 : M A) i :
  m1  m2  m2 !! i = None  m1 !! i = None.
Proof. rewrite !eq_None_not_Some. firstorder. Qed.
Lemma lookup_ne (m : M A) i j : m !! i  m !! j  i  j.
Proof. congruence. Qed.

Lemma not_elem_of_dom C `{Collection K C} (m : M A) i :
  i  dom C m  m !! i = None.
Proof. now rewrite (elem_of_dom C), eq_None_not_Some. Qed.

Lemma finmap_empty (m : M A) : ( i, m !! i = None)  m = .
Proof. intros Hm. apply finmap_eq. intros. now rewrite Hm, lookup_empty. Qed.
Lemma dom_empty C `{Collection K C} : dom C ( : M A)  .
Proof.
  split; intro.
  * rewrite (elem_of_dom C), lookup_empty. simplify_is_Some.
92
  * solve_elem_of.
93
94
95
96
Qed.
Lemma dom_empty_inv C `{Collection K C} (m : M A) : dom C m    m = .
Proof.
  intros E. apply finmap_empty. intros. apply (not_elem_of_dom C).
97
  rewrite E. solve_elem_of.
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
Qed.

Lemma lookup_empty_not i : ¬is_Some (( : M A) !! i).
Proof. rewrite lookup_empty. simplify_is_Some. Qed.
Lemma lookup_empty_Some i (x : A) : ¬ !! i = Some x.
Proof. rewrite lookup_empty. discriminate. Qed.

Lemma partial_alter_compose (m : M A) i f g :
  partial_alter (f  g) i m = partial_alter f i (partial_alter g i m).
Proof.
  intros. apply finmap_eq. intros ii. case (decide (i = ii)).
  * intros. subst. now rewrite !lookup_partial_alter.
  * intros. now rewrite !lookup_partial_alter_ne.
Qed.
Lemma partial_alter_comm (m : M A) i j f g :
  i  j 
 partial_alter f i (partial_alter g j m) = partial_alter g j (partial_alter f i m).
Proof.
  intros. apply finmap_eq. intros jj.
  destruct (decide (jj = j)).
  * subst. now rewrite lookup_partial_alter_ne,
     !lookup_partial_alter, lookup_partial_alter_ne.
  * destruct (decide (jj = i)).
    + subst. now rewrite lookup_partial_alter,
       !lookup_partial_alter_ne, lookup_partial_alter by congruence.
    + now rewrite !lookup_partial_alter_ne by congruence.
Qed.
Lemma partial_alter_self_alt (m : M A) i x :
  x = m !! i  partial_alter (λ _, x) i m = m.
Proof.
  intros. apply finmap_eq. intros ii.
  destruct (decide (i = ii)).
  * subst. now rewrite lookup_partial_alter.
  * now rewrite lookup_partial_alter_ne.
Qed.
Lemma partial_alter_self (m : M A) i : partial_alter (λ _, m !! i) i m = m.
Proof. now apply partial_alter_self_alt. Qed.

Lemma lookup_insert (m : M A) i x : <[i:=x]>m !! i = Some x.
Proof. unfold insert. apply lookup_partial_alter. Qed.
Lemma lookup_insert_rev (m : M A) i x y : <[i:= x ]>m !! i = Some y  x = y.
Proof. rewrite lookup_insert. congruence. Qed.
Lemma lookup_insert_ne (m : M A) i j x : i  j  <[i:=x]>m !! j = m !! j.
Proof. unfold insert. apply lookup_partial_alter_ne. Qed.
Lemma insert_comm (m : M A) i j x y :
  i  j  <[i:=x]>(<[j:=y]>m) = <[j:=y]>(<[i:=x]>m).
Proof. apply partial_alter_comm. Qed.

Lemma lookup_insert_Some (m : M A) i j x y :
  <[i:=x]>m !! j = Some y  (i = j  x = y)  (i  j  m !! j = Some y).
Proof.
  split.
  * destruct (decide (i = j)); subst;
      rewrite ?lookup_insert, ?lookup_insert_ne; intuition congruence.
  * intros [[??]|[??]].
    + subst. apply lookup_insert.
    + now rewrite lookup_insert_ne.
Qed.
Lemma lookup_insert_None (m : M A) i j x :
  <[i:=x]>m !! j = None  m !! j = None  i  j.
Proof.
  split.
  * destruct (decide (i = j)); subst;
      rewrite ?lookup_insert, ?lookup_insert_ne; intuition congruence.
  * intros [??]. now rewrite lookup_insert_ne.
Qed.

Lemma lookup_singleton_Some i j (x y : A) :
  {[(i, x)]} !! j = Some y  i = j  x = y.
Proof.
  unfold singleton, finmap_singleton.
  rewrite lookup_insert_Some, lookup_empty. simpl.
  intuition congruence.
Qed.
Lemma lookup_singleton_None i j (x : A) :
  {[(i, x)]} !! j = None  i  j.
Proof.
  unfold singleton, finmap_singleton.
  rewrite lookup_insert_None, lookup_empty. simpl. tauto.
Qed.

Lemma lookup_singleton i (x : A) : {[(i, x)]} !! i = Some x.
Proof. rewrite lookup_singleton_Some. tauto. Qed.
Lemma lookup_singleton_ne i j (x : A) : i  j  {[(i, x)]} !! j = None.
Proof. now rewrite lookup_singleton_None. Qed.

Lemma lookup_delete (m : M A) i : delete i m !! i = None.
Proof. apply lookup_partial_alter. Qed.
Lemma lookup_delete_ne (m : M A) i j : i  j  delete i m !! j = m !! j.
Proof. apply lookup_partial_alter_ne. Qed.

Lemma lookup_delete_Some (m : M A) i j y :
  delete i m !! j = Some y  i  j  m !! j = Some y.
Proof.
  split.
  * destruct (decide (i = j)); subst;
      rewrite ?lookup_delete, ?lookup_delete_ne; intuition congruence.
  * intros [??]. now rewrite lookup_delete_ne.
Qed.
Lemma lookup_delete_None (m : M A) i j :
  delete i m !! j = None  i = j  m !! j = None.
Proof.
  destruct (decide (i = j)).
  * subst. rewrite lookup_delete. tauto.
  * rewrite lookup_delete_ne; tauto.
Qed.

Lemma delete_empty i : delete i ( : M A) = .
Proof. rewrite <-(partial_alter_self ) at 2. now rewrite lookup_empty. Qed.
Lemma delete_singleton i (x : A) : delete i {[(i, x)]} = .
Proof. setoid_rewrite <-partial_alter_compose. apply delete_empty. Qed.
Lemma delete_comm (m : M A) i j : delete i (delete j m) = delete j (delete i m).
Proof. destruct (decide (i = j)). now subst. now apply partial_alter_comm. Qed.
Lemma delete_insert_comm (m : M A) i j x :
  i  j  delete i (<[j:=x]>m) = <[j:=x]>(delete i m).
Proof. intro. now apply partial_alter_comm. Qed.

Lemma delete_notin (m : M A) i : m !! i = None  delete i m = m.
Proof.
  intros. apply finmap_eq. intros j.
  destruct (decide (i = j)).
  * subst. now rewrite lookup_delete.
  * now apply lookup_delete_ne.
Qed.

Lemma delete_partial_alter (m : M A) i f :
  m !! i = None  delete i (partial_alter f i m) = m.
Proof.
226
  intros. unfold delete, finmap_delete. rewrite <-partial_alter_compose.
227
228
  rapply partial_alter_self_alt. congruence.
Qed.
229
Lemma delete_partial_alter_dom C `{Collection K C} (m : M A) i f :
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
  i  dom C m  delete i (partial_alter f i m) = m.
Proof. rewrite (not_elem_of_dom C). apply delete_partial_alter. Qed.
Lemma delete_insert (m : M A) i x : m !! i = None  delete i (<[i:=x]>m) = m.
Proof. apply delete_partial_alter. Qed.
Lemma delete_insert_dom C `{Collection K C} (m : M A) i x :
  i  dom C m  delete i (<[i:=x]>m) = m.
Proof. rewrite (not_elem_of_dom C). apply delete_partial_alter. Qed.
Lemma insert_delete (m : M A) i x : m !! i = Some x  <[i:=x]>(delete i m) = m.
Proof.
  intros Hmi. unfold delete, finmap_delete, insert, finmap_insert.
  rewrite <-partial_alter_compose. unfold compose. rewrite <-Hmi.
  now apply partial_alter_self_alt.
Qed.

Lemma elem_of_dom_delete C `{Collection K C} (m : M A) i j :
  i  dom C (delete j m)  i  j  i  dom C m.
Proof.
  rewrite !(elem_of_dom C). unfold is_Some.
  setoid_rewrite lookup_delete_Some. firstorder auto.
Qed.
Lemma not_elem_of_dom_delete C `{Collection K C} (m : M A) i :
  i  dom C (delete i m).
Proof. apply (not_elem_of_dom C), lookup_delete. Qed.
Robbert Krebbers's avatar
Robbert Krebbers committed
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
(** * Induction principles *)
(** We use the induction principle on finite collections to prove the
following induction principle on finite maps. *)
Lemma finmap_ind_alt C (P : M A  Prop) `{FinCollection K C} :
  P  
  ( i x m, i  dom C m  P m  P (<[i:=x]>m)) 
   m, P m.
Proof.
  intros Hemp Hinsert m.
  apply (collection_ind (λ X,  m, dom C m  X  P m)) with (dom C m).
  * solve_proper.
  * clear m. intros m Hm. rewrite finmap_empty.
    + easy.
    + intros. rewrite <-(not_elem_of_dom C), Hm.
      now solve_elem_of.
  * clear m. intros i X Hi IH m Hdom.
    assert (is_Some (m !! i)) as [x Hx].
    { apply (elem_of_dom C).
      rewrite Hdom. clear Hdom.
      now solve_elem_of. }
    rewrite <-(insert_delete m i x) by easy.
    apply Hinsert.
    { now apply (not_elem_of_dom_delete C). }
    apply IH. apply elem_of_equiv. intros.
    rewrite (elem_of_dom_delete C).
    esolve_elem_of.
  * easy.
Qed.

(** We use the [listset] implementation to prove an induction principle that
does not mention the map's domain. *)
Lemma finmap_ind (P : M A  Prop) :
  P  
  ( i x m, m !! i = None  P m  P (<[i:=x]>m)) 
   m, P m.
Proof.
  setoid_rewrite <-(not_elem_of_dom (listset _)).
  apply (finmap_ind_alt (listset _) P).
Qed.

(** * Deleting and inserting multiple elements *)
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
Lemma lookup_delete_list (m : M A) is j :
  In j is  delete_list is m !! j = None.
Proof.
  induction is as [|i is]; simpl; [easy |].
  intros [?|?].
  * subst. now rewrite lookup_delete.
  * destruct (decide (i = j)).
    + subst. now rewrite lookup_delete.
    + rewrite lookup_delete_ne; auto.
Qed.
Lemma lookup_delete_list_notin (m : M A) is j :
  ¬In j is  delete_list is m !! j = m !! j.
Proof.
  induction is; simpl; [easy |].
  intros. rewrite lookup_delete_ne; tauto.
Qed.

Lemma delete_list_notin (m : M A) is :
  Forall (λ i, m !! i = None) is  delete_list is m = m.
Proof.
  induction 1; simpl; [easy |].
  rewrite delete_notin; congruence.
Qed.
Lemma delete_list_insert_comm (m : M A) is j x :
  ¬In j is  delete_list is (<[j:=x]>m) = <[j:=x]>(delete_list is m).
Proof.
  induction is; simpl; [easy |].
  intros. rewrite IHis, delete_insert_comm; tauto.
Qed.

325
326
Lemma lookup_insert_list (m : M A) l1 l2 i x :
  (y, ¬In (i,y) l1)  insert_list (l1 ++ (i,x) :: l2) m !! i = Some x.
327
Proof.
328
329
330
331
332
333
334
335
336
  induction l1 as [|[j y] l1 IH]; simpl.
  * intros. now rewrite lookup_insert.
  * intros Hy. rewrite lookup_insert_ne; naive_solver.
Qed.

Lemma lookup_insert_list_not_in (m : M A) l i :
  (y, ¬In (i,y) l)  insert_list l m !! i = m !! i.
Proof.
  induction l as [|[j y] l IH]; simpl.
337
  * easy.
338
  * intros Hy. rewrite lookup_insert_ne; naive_solver.
339
340
Qed.

341
(** * Properties of the merge operation *)
342
343
344
345
Section merge.
  Context (f : option A  option A  option A).

  Global Instance: LeftId (=) None f  LeftId (=)  (merge f).
Robbert Krebbers's avatar
Robbert Krebbers committed
346
  Proof.
347
348
    intros ??. apply finmap_eq. intros.
    now rewrite !(merge_spec f), lookup_empty, (left_id None f).
Robbert Krebbers's avatar
Robbert Krebbers committed
349
  Qed.
350
  Global Instance: RightId (=) None f  RightId (=)  (merge f).
Robbert Krebbers's avatar
Robbert Krebbers committed
351
  Proof.
352
353
    intros ??. apply finmap_eq. intros.
    now rewrite !(merge_spec f), lookup_empty, (right_id None f).
Robbert Krebbers's avatar
Robbert Krebbers committed
354
  Qed.
355
356
357
358
359
360
361
  Global Instance: Idempotent (=) f  Idempotent (=) (merge f).
  Proof. intros ??. apply finmap_eq. intros. now rewrite !(merge_spec f). Qed.

  Context `{!PropHolds (f None None = None)}.

  Lemma merge_spec_alt m1 m2 m :
    ( i, m !! i = f (m1 !! i) (m2 !! i))  merge f m1 m2 = m.
Robbert Krebbers's avatar
Robbert Krebbers committed
362
  Proof.
363
364
365
    split; [| intro; subst; apply (merge_spec _) ].
    intros Hlookup. apply finmap_eq. intros. rewrite Hlookup.
    apply (merge_spec _).
Robbert Krebbers's avatar
Robbert Krebbers committed
366
  Qed.
367
368
369
370
371
372
373
374
375

  Lemma merge_comm m1 m2 :
    ( i, f (m1 !! i) (m2 !! i) = f (m2 !! i) (m1 !! i)) 
    merge f m1 m2 = merge f m2 m1.
  Proof. intros. apply finmap_eq. intros. now rewrite !(merge_spec f). Qed.
  Global Instance: Commutative (=) f  Commutative (=) (merge f).
  Proof. intros ???. apply merge_comm. intros. now apply (commutative f). Qed.

  Lemma merge_assoc m1 m2 m3 :
376
377
    ( i, f (m1 !! i) (f (m2 !! i) (m3 !! i)) =
          f (f (m1 !! i) (m2 !! i)) (m3 !! i)) 
378
379
380
381
382
383
    merge f m1 (merge f m2 m3) = merge f (merge f m1 m2) m3.
  Proof. intros. apply finmap_eq. intros. now rewrite !(merge_spec f). Qed.
  Global Instance: Associative (=) f  Associative (=) (merge f).
  Proof. intros ????. apply merge_assoc. intros. now apply (associative f). Qed.
End merge.

384
(** * Properties of the union and intersection operation *)
385
386
387
388
Section union_intersection.
  Context (f : A  A  A).

  Lemma finmap_union_merge m1 m2 i x y :
389
390
391
    m1 !! i = Some x 
    m2 !! i = Some y 
    union_with f m1 m2 !! i = Some (f x y).
392
  Proof.
393
394
    intros Hx Hy. unfold union_with, finmap_union.
    now rewrite (merge_spec _), Hx, Hy.
395
  Qed.
396
397
  Lemma finmap_union_l m1 m2 i x :
    m1 !! i = Some x  m2 !! i = None  union_with f m1 m2 !! i = Some x.
398
  Proof.
399
400
    intros Hx Hy. unfold union_with, finmap_union.
    now rewrite (merge_spec _), Hx, Hy.
401
  Qed.
402
403
  Lemma finmap_union_r m1 m2 i y :
    m1 !! i = None  m2 !! i = Some y  union_with f m1 m2 !! i = Some y.
Robbert Krebbers's avatar
Robbert Krebbers committed
404
  Proof.
405
406
    intros Hx Hy. unfold union_with, finmap_union.
    now rewrite (merge_spec _), Hx, Hy.
Robbert Krebbers's avatar
Robbert Krebbers committed
407
  Qed.
408
409
  Lemma finmap_union_None m1 m2 i :
    union_with f m1 m2 !! i = None  m1 !! i = None  m2 !! i = None.
Robbert Krebbers's avatar
Robbert Krebbers committed
410
  Proof.
411
412
    unfold union_with, finmap_union. rewrite (merge_spec _).
    destruct (m1 !! i), (m2 !! i); compute; intuition congruence.
Robbert Krebbers's avatar
Robbert Krebbers committed
413
414
  Qed.

415
416
417
418
419
420
421
422
423
  Global Instance: LeftId (=)  (union_with f : M A  M A  M A) := _.
  Global Instance: RightId (=)  (union_with f : M A  M A  M A) := _.
  Global Instance:
    Commutative (=) f  Commutative (=) (union_with f : M A  M A  M A) := _.
  Global Instance:
    Associative (=) f  Associative (=) (union_with f : M A  M A  M A) := _.
  Global Instance:
    Idempotent (=) f  Idempotent (=) (union_with f : M A  M A  M A) := _.
End union_intersection.
Robbert Krebbers's avatar
Robbert Krebbers committed
424
End finmap.
425

426
427
428
429
(** * The finite map tactic *)
(** The tactic [simplify_map by tac] simplifies finite map expressions
occuring in the conclusion and assumptions. It uses [tac] to discharge generated
inequalities. *)
430
431
Tactic Notation "simplify_map" "by" tactic(T) := repeat
  match goal with
432
  | _ => progress simplify_equality
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
  | H : context[  !! _ ] |- _ => rewrite lookup_empty in H
  | H : context[ (<[_:=_]>_) !! _ ] |- _ => rewrite lookup_insert in H
  | H : context[ (<[_:=_]>_) !! _ ] |- _ => rewrite lookup_insert_ne in H by T
  | H : context[ (delete _ _) !! _ ] |- _ => rewrite lookup_delete in H
  | H : context[ (delete _ _) !! _ ] |- _ => rewrite lookup_delete_ne in H by T
  | H : context[ {[ _ ]} !! _ ] |- _ => rewrite lookup_singleton in H
  | H : context[ {[ _ ]} !! _ ] |- _ => rewrite lookup_singleton_ne in H by T
  | |- context[  !! _ ] => rewrite lookup_empty
  | |- context[ (<[_:=_]>_) !! _ ] => rewrite lookup_insert
  | |- context[ (<[_:=_]>_) !! _ ] => rewrite lookup_insert_ne by T
  | |- context[ (delete _ _) !! _ ] => rewrite lookup_delete
  | |- context[ (delete _ _) !! _ ] => rewrite lookup_delete_ne by T
  | |- context[ {[ _ ]} !! _ ] => rewrite lookup_singleton
  | |- context[ {[ _ ]} !! _ ] => rewrite lookup_singleton_ne by T
  end.
Tactic Notation "simplify_map" := simplify_map by auto.