Add LoadPath ".".
Load BenB.

Load BenB2.

(* ====================================================================== *)

(*
Praktika super TL artifact
======
Authors:
Jozef Coldenhoff      s1017656
Charlotte Frenzen     s4739760
Nils Golembiewski     s1019649
Noah van der Vleuten  s1018323
*)

(* ====================================================================== *)

(*
[
    This file has to be a valid script, meaning that it
    can be executed by Coq.
    Therefore, explanations in natural language have to be between
    comment markers.

    In this project template, text within square brackets (within
    comment markers) is intended to clarify what needs to be
    written where.

    In the final version, we expect that all these blocks have been
    replaced by (your) proper content.
]

*)

(*
Abstract:
=========
[
    Explain whether you managed to prove the correctness theorem.
    And how did that go: did you have to change a lot compared to
    the original model as it was before you started with the proof,
    or could you use your formalization without many modifications?
]

*)

(*
Focus:

Modeling Goal:
==============
[Verification model]

Fragment of reality:
====================
[
Our model will focus on the user of the camera, the camera itself,
and the visual representation of the world within the scope of the lens
and the film inside of the camera.
]

Perspective:
============
[
Save selected visual state of world within the scope of the lens on film.
]

*)

(*
Abstractions or simplifications:
================================
[
    Depending on the chosen focus, you may simplify certain aspects of
    your artifact.
    If you are modeling some kind of home automation system, it is not
    unreasonable to assume that the net power is constant, although this
    is not exactly the case in reality. However, if you are modeling an
    artifact that protects against high peaks of power, these fluctuations
    should be part of the model.

    Write down explicitly which assumptions you have made to simplify
    the artifact.
]

We have chosen not to model the viewfinder of the camera.
We did this because we thought it would add unnecessary complexity that doesn't really help prove what we're trying to prove.
*)

(* ====================================================================== *)

(* Domain model *)

(* Domains (including their meaning) *)
Definition T :=     R
.
(* Time as minutes in real numbers. *)

Variable SS : Set.
(* Set of all shutter speed settings. *)

Variable AS : Set.
(* All aperture settings. *)

Variable FS : Set.
(* All focus settings. *)

(* Constants (including their meaning) *)

(* Functions (including their meaning) *)
Variable ShutterTime : SS -> T.
(* Gives the associated amount of time of the shutter setting. *)

(* Predicates (including their meaning and measurements) *)

Variable hasPowerAt (* t *) : T -> Prop.
(* Components have power at time t. *)
(* Check if the internal spring in the camera is loaded. *)

Variable focusSelectorTurnedToAt (* fs t *) : FS -> T -> Prop.
(* The focus selector was turned to setting fs at time t. *)
(* Ask the user of the camera if he/she turned the focus selector. *)

Variable apertureSelectorTurnedToAt (* as t *) : AS -> T -> Prop.
(* The aperture selector was turned to setting as at time t. *)
(* Ask the user of the camera if he/she turned the aperture selector. *)

Variable shutterSelectorTurnedToAt (* ss t *) : SS -> T -> Prop.
(* The shutter speed selector was turned to setting ss at time t. *)
(* Ask the user of the camera if he/she turned the shutter speed selector. *)

Variable leverPulledAt (* t *) : T -> Prop.
(* The lever was pulled at time t. *)
(* Ask the user if and when the lever was pulled. *)

Variable visualWorldStateInScopeAt (* t *) : T -> Prop .
(* There is light entering the lens at time t. *)
(* Look through the viewfinder. *)

Variable shutterButtonPressedAt (* t *) : T -> Prop.
(* The shutter button was pressed at time t. *)
(* Ask the user of the camera if and when the shutter button was pressed. *)

Variable filmRollLoadedAt (* t *) : T -> Prop.
(* At time t the camera contains a roll of film. *)
(* Look if there is a roll of film in the camera. *)

Variable shutterSettingAt (* s t *) : SS -> T -> Prop.
(* Shutter setting s is present at time t. *)
(* Check the shutter selector wheel at time t. *)

Variable apertureSettingAt (* s t *) : AS -> T -> Prop.
(* Aperture setting s is present at time t. *)
(* Check the aperture selector ring at time t. *)

Variable focusSettingAt (* s t *) : FS -> T -> Prop.
(* Focus setting s is present at time t. *)
(* Check the focus selector ring at time t. *)

Variable apertureContractedAt (* t *) : T -> Prop.
(* The aperture is contracted at time t. *)
(* Look at the aperture at time t. *)

Variable shutterOpenAt (* t *) : T -> Prop.
(* The shutter is open at time t. *)
(* Look whether the shutter is open at time t. *)

Variable mirrorUpAt (* t *) : T -> Prop.
(* The mirror is up at time t. *)
(* Look if the viewfinder is dark. *)

Variable focussedLensLightAt (* t *) :  T -> Prop.
(* Focussed lens light is available at time t. *)
(* Look through the viewfinder to see if the picture taken is in focus. *)

Variable selectedVisualWorldStateSavedOnFilm (* fs as1 ss t *) : FS -> AS -> SS -> T -> Prop.
(* The selected picture negative was saved on film at time t. *)
(* Develop the film and see if it is the picture that was meant to be taken. *)

(* ====================================================================== *)

(* Auxiliary predicates (including their meaning) *)

(*
[
    At this place within this template you may define as many
    auxiliary predicates as you want, but do not forget to include
    their meaning.
]
*)

(* ====================================================================== *)

(* Components *)
Definition Lever :=
    forall t : T,
            leverPulledAt t
        ->
            (
                forall d : T,
                            shutterButtonPressedAt (t+d)
                        /\
                            d > 0
                        /\
                            (
                                forall d2 : T,
                                        d2 in [t, t+d)
                                    ->
                                        ~shutterButtonPressedAt d2
                            )
                    ->
                        (
                            forall i : T,
                                    i in [t, t+d]
                                ->
                                    hasPowerAt i
                        )
            )
.

(*
Meaning Lever:
If at any point in time the lever is pulled, then,
the first time the shutter button is pressed after that point,
the components have power from the time the lever is pulled
until the time the shutter button is pressed.
*)

Definition ShutterSpeedSelectorWheel :=
    forall (t : T) (ss : SS),
            shutterSelectorTurnedToAt ss t
        ->
            (
                forall d: T,
                            d >= 0
                            (* possibly replace by t2 where t2 = t+d *)
                        /\
                            (
                                forall ss2 : SS,
                                        ss <> ss2
                                    ->
                                        (
                                            forall x: T,
                                                    x in [t, t+d]
                                                ->
                                                    ~shutterSelectorTurnedToAt ss2 x
                                        )
                            )
                    ->
                        shutterSettingAt ss (t+d)
            )
.

(*
Meaning ShutterSpeedSelectorWheel:

If at any point in time the shutter time selector is turned to a specific (first) setting,
then, if there is any point in the future at which the shutter time selector is
turned to another setting, the shutter setting is set to the first setting until that point
at which the shutter time selector is turned to the other setting. And, if there is no
point in the future at which the shutter time selector is turned to another setting,
the setting will always be the first setting in the future.
*)

Definition ApertureSelectorRing :=
    forall (t : T) (as1 : AS),
            apertureSelectorTurnedToAt as1 t
        ->
            (
                forall d: T,
                            d >= 0
                        /\
                            (
                                forall as2 : AS,
                                        as1 <> as2
                                    ->
                                        (
                                            forall x: T,
                                                    x in [t, t+d]
                                                ->
                                                    ~apertureSelectorTurnedToAt as2 x
                                        )
                            )
                    ->
                        apertureSettingAt as1 (t+d)
            )
.

(*
Meaning ApertureSelectorRing:

If at any point in time the aperture selector is turned to a specific (first) setting,
then, if there is any point in the future at which the aperture selector is
turned to another setting, the aperture setting is set to the first setting until that point
at which the aperture selector is turned to the other setting. And, if there is no
point in the future at which the aperture selector is turned to another setting,
the setting will always be the first setting in the future.
*)

Definition FocusSelectorRing :=
    forall (t : T) (fs : FS),
            focusSelectorTurnedToAt fs t
        ->
            (
                forall d: T,
                            d >= 0
                        /\
                            (
                                forall fs2 : FS,
                                        fs <> fs2
                                    ->
                                        (
                                            forall x: T,
                                                    x in [t, t+d]
                                                ->
                                                    ~focusSelectorTurnedToAt fs2 x
                                        )
                            )
                    ->
                        focusSettingAt fs (t+d)
            )
.

(*
Meaning FocusSelectorRing:

If at any point in time the focus selector is turned to a specific (first) setting,
then, if there is any point in the future at which the focus selector is
turned to another setting, the focus setting is set to the first setting until that point
at which the focus selector is turned to the other setting. And, if there is no
point in the future at which the focus selector is turned to another setting,
the setting will always be the first setting in the future.
*)

Definition Lens:=
    forall (t : T) (fs : FS),
                focusSettingAt fs t
            /\
                visualWorldStateInScopeAt t
        ->
            focussedLensLightAt t
.
(*
Meaning Lens:

If at any point in time the focus setting is set to a specific setting,
and the visual state of the world is in scope at that time,
then there is focussed lens light at that time.
*)

Definition Shutter :=
    forall (t : T) (ss : SS),
                shutterSettingAt ss t
            /\
                hasPowerAt t
            /\
                shutterButtonPressedAt t
        ->
            (
                forall d :T,
                        d in [t, t + ShutterTime ss]
                    ->
                        shutterOpenAt d
            )
.

(*
Meaning Shutter:

If at any point in time, the shutter setting is a specific setting, and the components have power
and the shutter button is pressed, then the shutter is open from that point time, until that
point in time plus the interval specified by the shutter setting.
*)

Definition Aperture:=
    forall (t : T) (ss : SS) (as1 : AS),
                shutterSettingAt ss t
            /\
                hasPowerAt t
            /\
                shutterButtonPressedAt t
            /\
                apertureSettingAt as1 t
        ->
            (
                forall d :T,
                        d in [t, t + ShutterTime ss]
                    ->
                        apertureContractedAt d
            )
.

(*
Meaning Aperture:

If at any point in time, the shutter setting is a specific setting, the components have power,
the shutter button is pressed and the aperture setting is a specific setting,
then the aperture is contracted from that point time, until that
point in time plus the interval specified by the shutter setting.
*)

Definition Mirror :=
    forall (t : T) (ss : SS),
                shutterSettingAt ss t
            /\
                hasPowerAt t
            /\
                shutterButtonPressedAt t
        ->
            (
                forall d :T,
                        d in [t, t + ShutterTime ss]
                    ->
                        mirrorUpAt d
            )
.

(*
Meaning Mirror:

If at any point in time, the shutter setting is a specific setting, the components have power,
the shutter button is pressed,
then the mirror is up from that point time, until that
point in time plus the interval specified by the shutter setting.
*)

Definition Film :=
    forall (t : T) (ss : SS) (as1 : AS) (fs : FS),
                filmRollLoadedAt t
            /\
                shutterSettingAt ss t
            /\
                focusSettingAt fs t
            /\
                apertureSettingAt as1 t
            /\
                (
                    forall i: T,
                            i in [t, t + (ShutterTime ss))
                        ->
                                shutterOpenAt i
                            /\
                                mirrorUpAt i
                            /\
                                apertureContractedAt i
                            /\
                                focussedLensLightAt i
                )
        ->
            selectedVisualWorldStateSavedOnFilm fs as1 ss (t + (ShutterTime ss))
.

(*
Meaning film:

If at some point in time, the shutter setting is set to a specific setting,
and the forcus setting is set to a specific setting, and the aperture setting is
set to a specific setting and, the mirror is up, and the aperture is contracted,
and there is focused lens light from the point in time until as long as the shutter
time setting specifies, then the selected visual world state at the point in time plus
the shutter time is saved on film with the shutter setting, focus setting and shutter setting.
*)

(*
[
    For each component you have to specify the following information:

    OUTSIDE comment markers:
    - The 'Definition' to be read by Coq, in a readable layout that
    matches the mathematical structure of the formula.

    WITHIN comment markers:
    - The specification of the component in natural language. Obviously,
    this specification should be consistent with the formula used
    by Coq.
    - If appropriate, a short explanation in natural language about
    the choices that have been made.
]
*)

(* ====================================================================== *)

(* Specification of the overall system *)
Definition Camera :=
    forall (t1 : T) (t2 : T) (t3 : T) (ss : SS) (as1 : AS) (fs : FS),
                t2 > t1
            /\
                t3 > t2
            /\
                filmRollLoadedAt t3
            /\
                ShutterTime ss > 0
            /\
                (
                    forall t : T,
                            t in [t1, t3 + ShutterTime ss]
                        ->
                            (
                                    (
                                        forall as2 : AS,
                                                as1 <> as2
                                            ->
                                                ~apertureSelectorTurnedToAt as2 t
                                    )
                                /\
                                    (
                                        forall ss2 : SS,
                                                ss <> ss2
                                            ->
                                                ~shutterSelectorTurnedToAt ss2 t
                                    )
                                /\
                                    (
                                        forall fs2 : FS,
                                                fs <> fs2
                                            ->
                                                ~focusSelectorTurnedToAt fs2 t
                                    )
                            )
                )
            /\
                (
                    exists y1 : T,
                            y1 in [t1, t2 )
                        /\
                            leverPulledAt y1
                )
            /\
                (
                    forall t : T,
                            t in [t1, t3)
                        ->
                            ~shutterButtonPressedAt t
                )
            /\
                (
                    exists y2 : T,
                            y2 in [t2, t3 )
                        /\
                            focusSelectorTurnedToAt fs y2
                        /\
                            apertureSelectorTurnedToAt as1 y2
                        /\
                            shutterSelectorTurnedToAt ss  y2
                )
            /\
                (
                    forall y3: T,
                            y3 in [t3 , t3 + (ShutterTime ss))
                        ->
                            visualWorldStateInScopeAt y3
                )
            /\
                shutterButtonPressedAt t3
        ->
            selectedVisualWorldStateSavedOnFilm fs as1 ss (t3 + (ShutterTime ss))
.

(*
Meaning Camera:

For any three subsequent points in time and any shutter, aperture and focus setting,
if at the first point in time the lever is pulled, and at the second point in time the aperture,
shutter and, focus settings are set and are not changed until the shutter button is pressed,
and if we press the shutter button no earlier or later than the third point in time,
then the photo is saved on film after a short time after the third point in time that is associated with the chosen shutter speed setting.
*)

(*
[
    Here you have to specify:

    OUTSIDE comment markers:
    - The 'Definition' to be read by Coq, in a readable layout that
    matches the mathematical structure of the formula.

    WITHIN comment markers:
    - The specification of the overall system in natural language.
    Obviously, this specification should be consistent with the
    formula used by Coq.
    - If appropriate, a short explanation in natural language about
    the choices that have been made.
]
*)

(* ====================================================================== *)

(* Extras *)

(*
[
    It is very likely that you do not need any extras!

    However, if it turns out during your proof that you have to prove
    several times (almost) the same, then you may define a 'Lemma' at
    this place, followed by its proof. And in the proof of the correctness
    theorem, you may apply this lemma several times.
    Note that it is always allowed to add lemmas to this script!

    Sometimes it happens that Coq has troubles with 'trivial' properties
    of numbers, that cannot be solve easily using 'lin_solve'.
    In such situations, you may contact your supervisor and discuss
    whether this may be solved by adding an 'Axiom', which can also be
    applied later on within the proof of the correctness theorem.
]
*)

(* Correctness theorem *)

(* ====================================================================== *)

(*
[
    Write down your correctness theorem in the usual notation:
    Theorem CorTheorem:
    Component1 /\ Component2 /\ ... /\ ComponentN -> SpecOfTheOverallSystem.

    Note that as long as you don't know what natural deduction is
    and you cannot start with the proof yet, you should keep this
    theorem within comment markers, otherwise you will get a red cross
    for stating a theorem without a proof.

    For the final version you obviously have to remove these comment
    markers and provide a real proof!

    Note that even if your proof is correct, you won't be able to
    get a green check mark, but only an orange flag, for technical
    reasons. But that is no problem.
]
*)

Theorem CorTheorem:
            Lever
        /\
            Lens
        /\
            Mirror
        /\
            Aperture
        /\
            Shutter
        /\
            Film
        /\
            ShutterSpeedSelectorWheel
        /\
            ApertureSelectorRing
        /\
            FocusSelectorRing
    ->
        Camera
.
Proof.
unfold Lever.
unfold Lens.
unfold Mirror.
unfold Aperture.
unfold Shutter.
unfold Film.
unfold ShutterSpeedSelectorWheel.
unfold ApertureSelectorRing.
unfold FocusSelectorRing.
unfold Camera.
imp_i a1.
destruct a1.
destruct H0.
destruct H1.
destruct H2.
destruct H3.
destruct H4.
destruct H5.
destruct H6.
all_i t1_strict.
all_i t2_strict.
all_i t3_strict.
all_i ss_strict.
all_i as1_strict.
all_i fs_strict.
imp_i a2.
destruct a2.
destruct H9.
destruct H10.
destruct H11.
destruct H12.
destruct H13.
destruct H14.
destruct H15.
destruct H16.
exi_e (
    exists y1 : T,
            y1 in [t1_strict, t2_strict)
        /\
            leverPulledAt y1
)
y1_strict a3
.
hyp H13.
destruct a3.
exi_e (
    exists y2 : T,
            y2 in [t2_strict, t3_strict)
        /\
            focusSelectorTurnedToAt fs_strict y2
        /\
            apertureSelectorTurnedToAt as1_strict y2
        /\
            shutterSelectorTurnedToAt ss_strict y2
)
y2_strict a4
.
hyp H15.
destruct a4.
destruct H21.
destruct H22.
exi_e (
    exists d:T,
        d = (t3_strict - y2_strict)
)
d_strict a5.
exi_i (t3_strict - y2_strict).
lin_solve.
imp_e (
        filmRollLoadedAt t3_strict
    /\
        shutterSettingAt ss_strict t3_strict
    /\
        focusSettingAt fs_strict t3_strict
    /\
        apertureSettingAt as1_strict t3_strict
    /\
        (
            forall i : T,
                    i in [t3_strict, t3_strict + ShutterTime ss_strict)
                ->
                        shutterOpenAt i
                    /\
                        mirrorUpAt i
                    /\
                        apertureContractedAt i
                    /\
                        focussedLensLightAt i
        )
)
.
all_e (
    forall (fs : FS),
                filmRollLoadedAt t3_strict
            /\
                shutterSettingAt ss_strict t3_strict
            /\
                focusSettingAt fs t3_strict
            /\
                apertureSettingAt as1_strict t3_strict
            /\
                (
                    forall i : T,
                            i in [t3_strict, t3_strict + ShutterTime ss_strict)
                        ->
                                shutterOpenAt i
                            /\
                                mirrorUpAt i
                            /\
                                apertureContractedAt i
                            /\
                                focussedLensLightAt i
                )
        ->
            selectedVisualWorldStateSavedOnFilm fs as1_strict ss_strict (t3_strict + ShutterTime ss_strict)
)
fs_strict.
all_e (
    forall (as1 : AS)(fs : FS),
                filmRollLoadedAt t3_strict
            /\
                shutterSettingAt ss_strict t3_strict
            /\
                focusSettingAt fs t3_strict
            /\
                apertureSettingAt as1 t3_strict
            /\
                (
                    forall i : T,
                            i in [t3_strict, t3_strict + ShutterTime ss_strict)
                        ->
                                shutterOpenAt i
                            /\
                                mirrorUpAt i
                            /\
                                apertureContractedAt i
                            /\
                                focussedLensLightAt i
                )
        ->
            selectedVisualWorldStateSavedOnFilm fs as1 ss_strict (t3_strict + ShutterTime ss_strict)
)
as1_strict
.
all_e (
    forall (ss : SS) (as1 : AS) (fs : FS),
                filmRollLoadedAt t3_strict
            /\
                shutterSettingAt ss t3_strict
            /\
                focusSettingAt fs t3_strict
            /\
                apertureSettingAt as1 t3_strict
            /\
                (
                    forall i : T,
                            i in [t3_strict, t3_strict + ShutterTime ss)
                        ->
                                shutterOpenAt i
                            /\
                                mirrorUpAt i
                            /\
                                apertureContractedAt i
                            /\
                                focussedLensLightAt i
                )
        ->
            selectedVisualWorldStateSavedOnFilm fs as1 ss (t3_strict + ShutterTime ss)
)
ss_strict
.
all_e (
    forall (t:T) (ss : SS) (as1 : AS)(fs : FS),
                filmRollLoadedAt t
            /\
                shutterSettingAt ss t
            /\
                focusSettingAt fs t
            /\
                apertureSettingAt as1 t
            /\
                (
                    forall i : T,
                            i in [t, t + ShutterTime ss)
                        ->
                                shutterOpenAt i
                            /\
                                mirrorUpAt i
                            /\
                                apertureContractedAt i
                            /\
                                focussedLensLightAt i
                )
        ->
            selectedVisualWorldStateSavedOnFilm fs as1 ss (t + ShutterTime ss)
)
t3_strict
.
hyp H4.
con_i.
hyp H10.

imp_e (
    apertureSettingAt as1_strict t3_strict
)
.
imp_i a_aperture_setting.

imp_e (
    focusSettingAt fs_strict t3_strict
)
.
imp_i a_focus_setting.

imp_e (
    shutterSettingAt ss_strict t3_strict
)
.
imp_i a_shutter_setting.

imp_e (
    hasPowerAt t3_strict
)
.
imp_i a_has_power.
(*
imp_e (forall t:T, t in [t1_strict, t3_strict] -> t in [t1_strict, t3_strict  + ShutterTime ss_strict]).
imp_i a_interval1.
*)
con_i.
hyp a_shutter_setting.
con_i.
hyp a_focus_setting.
con_i.
hyp a_aperture_setting.
all_i i_strict.
imp_i ab1.
con_i.

(*=============shutteropenedat=========*)

imp_e (
    i_strict in [t3_strict, t3_strict + ShutterTime ss_strict]
)
.
all_e (
    forall d : T,
            d in [t3_strict, t3_strict + ShutterTime ss_strict]
        ->
            shutterOpenAt d
)
i_strict.
imp_e (
        shutterSettingAt ss_strict t3_strict
    /\
        hasPowerAt t3_strict
    /\
        shutterButtonPressedAt t3_strict
)
.
all_e (
    forall (ss : SS),
                shutterSettingAt ss t3_strict
            /\
                hasPowerAt t3_strict
            /\
                shutterButtonPressedAt t3_strict
        ->
            (
                forall d : T,
                        d in [t3_strict, t3_strict + ShutterTime ss]
                    ->
                        shutterOpenAt d
            )
)
ss_strict
.
all_e (
    forall (t : T) (ss : SS),
                shutterSettingAt ss t
            /\
                hasPowerAt t
            /\
                shutterButtonPressedAt t
        ->
            (
                forall d : T,
                        d in [t, t + ShutterTime ss]
                    ->
                        shutterOpenAt d
            )
)
t3_strict
.
hyp H3.
con_i.
hyp a_shutter_setting.
con_i.
hyp a_has_power.
hyp H17.
interval.
imp_e (
        t3_strict <= i_strict
    /\
        i_strict < t3_strict + ShutterTime ss_strict
)
.
imp_i ab2.
con_i.
con_e1 (
    i_strict < t3_strict + ShutterTime ss_strict
)
.
hyp ab2.
imp_e (
    i_strict < t3_strict + ShutterTime ss_strict
)
.
imp_i abc1.
lin_solve.
con_e2 (
    t3_strict <= i_strict
)
.
hyp ab2.
hyp ab1.

(*===============mirrorupat=================*)
con_i.
imp_e (
    i_strict in [t3_strict, t3_strict + ShutterTime ss_strict]
)
.
all_e (
    forall d : T,
            d in [t3_strict, t3_strict + ShutterTime ss_strict]
        ->
            mirrorUpAt d
)
i_strict.
imp_e (
        shutterSettingAt ss_strict t3_strict
    /\
        hasPowerAt t3_strict
    /\
        shutterButtonPressedAt t3_strict
)
.
all_e (
    forall (ss : SS),
                shutterSettingAt ss t3_strict
            /\
                hasPowerAt t3_strict
            /\
                shutterButtonPressedAt t3_strict
        ->
            (
                forall d : T,
                        d in [t3_strict, t3_strict + ShutterTime ss]
                    ->
                        mirrorUpAt d
            )
)
ss_strict
.
all_e (
    forall (t : T) (ss : SS),
                shutterSettingAt ss t
            /\
                hasPowerAt t
            /\
                shutterButtonPressedAt t
        ->
            (
                forall d : T,
                        d in [t, t + ShutterTime ss]
                    ->
                        mirrorUpAt d
            )
)
t3_strict
.
hyp H1.
con_i.
hyp a_shutter_setting.
con_i.
hyp a_has_power.
hyp H17.
interval.
imp_e (
        t3_strict <= i_strict
    /\
        i_strict < t3_strict + ShutterTime ss_strict
)
.
imp_i ab2.
con_i.
con_e1 (
    i_strict < t3_strict + ShutterTime ss_strict
)
.
hyp ab2.
imp_e (
    i_strict < t3_strict + ShutterTime ss_strict
)
.
imp_i abc1.
lin_solve.
con_e2 (
    t3_strict <= i_strict
)
.
hyp ab2.
hyp ab1.

(*=======aperturecontractedat========*)
con_i.
imp_e (
    i_strict in [t3_strict, t3_strict + ShutterTime ss_strict]
)
.
all_e (
    forall d : T,
            d in [t3_strict, t3_strict + ShutterTime ss_strict]
        ->
            apertureContractedAt d
)
i_strict.
imp_e (
        shutterSettingAt ss_strict t3_strict
    /\
        hasPowerAt t3_strict
    /\
        shutterButtonPressedAt t3_strict
    /\
        apertureSettingAt as1_strict t3_strict
)
.
all_e (
    forall (as1 : AS),
                shutterSettingAt ss_strict t3_strict
            /\
                hasPowerAt t3_strict
            /\
                shutterButtonPressedAt t3_strict
            /\
                apertureSettingAt as1 t3_strict
        ->
            (
                forall d : T,
                        d in [t3_strict, t3_strict + ShutterTime ss_strict]
                    ->
                        apertureContractedAt d
            )
)
as1_strict
.
all_e (
    forall (ss:SS) (as1 : AS),
                shutterSettingAt ss t3_strict
            /\
                hasPowerAt t3_strict
            /\
                shutterButtonPressedAt t3_strict
            /\
                apertureSettingAt as1 t3_strict
        ->
            (
                forall d : T,
                        d in [t3_strict, t3_strict + ShutterTime ss]
                    ->
                        apertureContractedAt d
            )
)
ss_strict
.
all_e (
    forall (t:T) (ss:SS) (as1 : AS),
                shutterSettingAt ss t
            /\
                hasPowerAt t
            /\
                shutterButtonPressedAt t
            /\
                apertureSettingAt as1 t
        ->
            (
                forall d : T,
                        d in [t, t + ShutterTime ss]
                    ->
                        apertureContractedAt d
            )
)
t3_strict
.

hyp H2.
con_i.
hyp a_shutter_setting.
con_i.
hyp a_has_power.
con_i.
hyp H17.
hyp a_aperture_setting.
interval.
imp_e (
        t3_strict <= i_strict
    /\
        i_strict < t3_strict + ShutterTime ss_strict
)
.
imp_i ab2.
con_i.
con_e1 (
    i_strict < t3_strict + ShutterTime ss_strict
)
.
hyp ab2.
imp_e (
    i_strict < t3_strict + ShutterTime ss_strict
)
.
imp_i abc1.
lin_solve.
con_e2 (
    t3_strict <= i_strict
)
.
hyp ab2.
hyp ab1.

(*=========focussedlenslightat======*)
imp_e (
        focusSettingAt fs_strict i_strict
    /\
        visualWorldStateInScopeAt i_strict
)
.
all_e (
    forall (fs : FS),
                focusSettingAt fs i_strict
            /\
                visualWorldStateInScopeAt i_strict
        ->
            focussedLensLightAt i_strict
)
fs_strict
.
all_e (
    forall (t : T) (fs : FS),
                focusSettingAt fs t
            /\
                visualWorldStateInScopeAt t
        ->
            focussedLensLightAt t
)
i_strict.
hyp H0.
con_i.
exi_e (
    exists d:T,
        y2_strict + d = i_strict
)
d2_strict ab3.
exi_i (i_strict - y2_strict).
lin_solve.

replace i_strict with (y2_strict + d2_strict).

imp_e (
        d2_strict >= 0
    /\
        (
            forall fs2 : FS,
                    fs_strict <> fs2
                ->
                    forall x : T,
                            x in [y2_strict, (y2_strict + d2_strict)]
                        ->
                            ~focusSelectorTurnedToAt fs2 x
        )
)
.
all_e (
    forall d : T,
                d >= 0
            /\
                (
                    forall fs2 : FS,
                            fs_strict <> fs2
                        ->
                            forall x : T,
                                    x in [y2_strict, y2_strict + d]
                                ->
                                    ~focusSelectorTurnedToAt fs2 x
                )
        ->
            focusSettingAt fs_strict (y2_strict + d)
)
d2_strict
.
imp_e (
    focusSelectorTurnedToAt fs_strict y2_strict
)
.
all_e (
    forall (fs : FS),
            focusSelectorTurnedToAt fs y2_strict
        ->
            (
                forall d: T,
                            d >= 0
                            (* possibly replace by t2 where t2 = t+d ?????????????????????????????*)
                        /\
                            (
                                forall fs2 : FS,
                                        fs <> fs2
                                    ->
                                        (
                                            forall x: T,
                                                    x in [y2_strict, y2_strict + d]
                                                ->
                                                    ~focusSelectorTurnedToAt fs2 x
                                        )
                            )
                    ->
                        focusSettingAt fs (y2_strict + d)
            )
)
fs_strict
.
all_e (
    forall (t : T) (fs : FS),
            focusSelectorTurnedToAt fs t
        ->
            (
                forall d: T,
                            d >= 0
                            (* possibly replace by t2 where t2 = t+d ?????????????????????????????*)
                        /\
                            (
                                forall fs2 : FS,
                                        fs <> fs2
                                    ->
                                        (
                                            forall x: T,
                                                    x in [t, t + d]
                                                ->
                                                    ~focusSelectorTurnedToAt fs2 x
                                        )
                            )
                    ->
                        focusSettingAt fs (t + d)
            )
)
y2_strict
.
hyp H7.
hyp H21.
con_i.
replace d2_strict with (i_strict - y2_strict).
imp_e (
    i_strict >= y2_strict
)
.
imp_i ab4.
lin_solve.
imp_e (
        t3_strict <= i_strict
    /\
        y2_strict <= t3_strict
)
.
imp_i ab4.
imp_e (
    t3_strict <= i_strict
)
.
imp_i ab5.
imp_e (
    y2_strict <= t3_strict
)
.
imp_i ab6.
lin_solve.
con_e2 (
    t3_strict <= i_strict
)
.
hyp ab4.
con_e1 (
    y2_strict <= t3_strict
)
.
hyp ab4.
con_i.
con_e1 (
    i_strict < t3_strict + ShutterTime ss_strict
)
.
hyp ab1.
imp_e (
    y2_strict < t3_strict
)
.
imp_i ab4.
lin_solve.
con_e2 (
    t2_strict <= y2_strict
)
.
hyp H20.
replace i_strict with (y2_strict + d2_strict).
lin_solve.
all_i fs2_strict.
imp_i ab5.
all_i x_strict.
imp_i ab6.
imp_e (
    fs_strict <> fs2_strict
)
.
all_e (
    forall fs2 : FS,
            fs_strict <> fs2
        ->
            ~focusSelectorTurnedToAt fs2 x_strict
)
fs2_strict.
con_e2 (
    forall ss2 : SS,
            ss_strict <> ss2
        ->
            ~shutterSelectorTurnedToAt ss2 x_strict
)
.
con_e2 (
    forall as2 : AS,
            as1_strict <> as2
        ->
            ~apertureSelectorTurnedToAt as2 x_strict
)
.
imp_e (
    x_strict in [t1_strict, t3_strict + ShutterTime ss_strict]
)
.
all_e (
    forall t : T,
            t in [t1_strict, t3_strict + ShutterTime ss_strict]
        ->
                (
                    forall as2 : AS,
                            as1_strict <> as2
                        ->
                            ~apertureSelectorTurnedToAt as2 t
                )
            /\
                (
                    forall ss2 : SS,
                            ss_strict <> ss2
                        ->
                            ~shutterSelectorTurnedToAt ss2 t
                )
            /\
                (
                    forall fs2 : FS,
                            fs_strict <> fs2
                        ->
                            ~focusSelectorTurnedToAt fs2 t
                )
)
x_strict.
hyp H12.
interval.
imp_e (
        y2_strict <= x_strict
    /\
        x_strict <= y2_strict + d2_strict
)
.
imp_i ab7.
imp_e (
        t3_strict <= i_strict
    /\
        i_strict <  t3_strict + ShutterTime ss_strict
)
.
imp_i ab8.
imp_e (
        t2_strict <= y2_strict
    /\
        y2_strict < t3_strict
)
.
imp_i ab9.
imp_e (
    t2_strict <= y2_strict
)
.
imp_i ab10.
imp_e (
    y2_strict <= x_strict
)
.
imp_i ab11.
imp_e (
    x_strict <= y2_strict + d2_strict
)
.
imp_i ab12.
imp_e (
        t3_strict <= i_strict
    /\
        i_strict < t3_strict + ShutterTime ss_strict
)
.
imp_i ab13.
imp_e (
    i_strict < t3_strict + ShutterTime ss_strict
)
.
imp_i ab14.
con_i.
lin_solve.
lin_solve.
con_e2 (
    t3_strict <= i_strict
)
.
hyp ab8.
hyp ab1.
con_e2 (
    y2_strict <= x_strict
)
.
hyp ab7.
con_e1 (
    x_strict <= y2_strict + d2_strict
)
.
hyp ab6.
con_e1 (
    y2_strict < t3_strict
)
.
hyp ab9.
hyp H20.
hyp ab1.
hyp ab6.
hyp ab5.
imp_e (
    i_strict in [t3_strict, t3_strict + ShutterTime ss_strict)
)
.
all_e (
    forall y3 : T,
            y3 in [t3_strict, t3_strict + ShutterTime ss_strict)
        ->
            visualWorldStateInScopeAt y3
)
i_strict.
hyp H16.
hyp ab1.

(*==============haspowerat===============*)

exi_e (
    exists d3:T,
        y1_strict + d3 = t3_strict
)
d3_strict ab1.
exi_i (t3_strict - y1_strict).
lin_solve.
replace t3_strict with (y1_strict + d3_strict).
imp_e (
    (y1_strict + d3_strict) in [y1_strict, t3_strict]
)
.
all_e all_e (forall i : T, i in [y1_strict, t3_strict] -> hasPowerAt i) (y1_strict + d3_strict).
imp_e (
        shutterButtonPressedAt (y1_strict + d3_strict)
    /\
        d3_strict > 0
    /\
        (
            forall d2 : T,
                    d2 in [y1_strict, (y1_strict + d3_strict))
                ->
                    ~shutterButtonPressedAt d2
        )
)
.
replace t3_strict with (y1_strict + d3_strict).
all_e (
    forall d : T,
                shutterButtonPressedAt (y1_strict + d)
            /\
                d > 0
            /\
                (
                    forall d2 : T,
                            d2 in [y1_strict, y1_strict + d)
                        ->
                            ~shutterButtonPressedAt d2
                )
        ->
            (
                forall i : T,
                        i in [y1_strict, y1_strict + d]
                    ->
                        hasPowerAt i
            )
)
d3_strict
.
imp_e (
    leverPulledAt y1_strict
)
.
all_e (
    forall t : T,
            leverPulledAt t
        ->
            forall d : T,
                        shutterButtonPressedAt (t + d)
                    /\
                        d > 0
                    /\
                        (
                            forall d2 : T,
                                    d2 in [t, t + d)
                                ->
                                    ~shutterButtonPressedAt d2
                        )
                ->
                    forall i : T,
                            i in [t, t + d]
                        ->
                            hasPowerAt i
)
y1_strict
.
hyp H.
hyp H19.
con_i.
replace (y1_strict + d3_strict) with t3_strict.
hyp H17.
con_i.
imp_e (
        t1_strict <= y1_strict
    /\
        y1_strict < t2_strict
)
.
imp_i ab2.
imp_e (
    y1_strict < t2_strict
)
.
imp_i ab4.
lin_solve.
con_e2 (
    t1_strict <= y1_strict
)
.
hyp ab2.
hyp H18.
all_i d2_strict.
imp_i ab2.
imp_e (
    d2_strict in [t1_strict, t3_strict)
)
.
all_e (
    forall t : T,
            t in [t1_strict, t3_strict)
        ->
            ~shutterButtonPressedAt t
)
d2_strict.
hyp H14.
interval.
(*need ab2, H18, *)
imp_e (
        y1_strict <= d2_strict
    /\
        d2_strict < y1_strict + d3_strict
)
.
imp_i ab3.
imp_e (
        t1_strict <= y1_strict
    /\
        y1_strict < t2_strict
)
.
imp_i ab4.
imp_e (
    y1_strict <= d2_strict
)
.
imp_i ab5.
imp_e (
    t1_strict <= y1_strict
)
.
imp_i ab6.
imp_e (
    d2_strict < y1_strict + d3_strict
)
.
imp_i ab7.
con_i.
lin_solve.
replace t3_strict with (y1_strict + d3_strict).
lin_solve.
con_e2 (
    y1_strict <= d2_strict
)
.
hyp ab3.
con_e1 (
    y1_strict < t2_strict
)
.
hyp ab4.
con_e1 (
    d2_strict < y1_strict + d3_strict
)
.
hyp ab3.
hyp H18.
hyp ab2.
replace (y1_strict + d3_strict) with t3_strict.
imp_e (
        t1_strict <= y1_strict
    /\
        y1_strict < t2_strict
)
.
imp_i ab2.
interval.
con_i.
imp_e (
    y1_strict < t2_strict
)
.
imp_i ab3.
lin_solve.
con_e2 (
    t1_strict <= y1_strict
)
.
hyp ab2.
lin_solve.
hyp H18.

(* ======================= *)
(* SHUTTERSETTING *)
(* ======================= *)

replace t3_strict with ((t3_strict - d_strict) + d_strict).
imp_e (
        d_strict >= 0
    /\
        (
            forall ss2 : SS,
                    ss_strict <> ss2
                ->
                    forall x : T,
                            x in [(t3_strict - d_strict), (t3_strict - d_strict) + d_strict]
                        ->
                            ~shutterSelectorTurnedToAt ss2 x
        )
)
.
all_e (
    forall d : T,
                d >= 0
            /\
                (
                    forall ss2 : SS,
                            ss_strict <> ss2
                        ->
                            forall x : T,
                                    x in [(t3_strict - d_strict), (t3_strict - d_strict) + d]
                                ->
                                    ~shutterSelectorTurnedToAt ss2 x
                )
        ->
            shutterSettingAt ss_strict ((t3_strict - d_strict) + d)
)
d_strict
.
imp_e (
    shutterSelectorTurnedToAt ss_strict (t3_strict - d_strict)
)
.
all_e (
    forall (ss : SS),
            shutterSelectorTurnedToAt ss (t3_strict - d_strict)
        ->
            (
                forall d: T,
                            d >= 0
                            (* possibly replace by t2 where t2 = t+d ?????????????????????????????*)
                        /\
                            (
                                forall ss2 : SS,
                                        ss <> ss2
                                    ->
                                        (
                                            forall x: T,
                                                    x in [(t3_strict - d_strict), (t3_strict - d_strict) + d]
                                                ->
                                                    ~shutterSelectorTurnedToAt ss2 x
                                        )
                            )
                    ->
                        shutterSettingAt ss ((t3_strict - d_strict) + d)
            )
)
ss_strict
.
all_e (
    forall (t : T) (ss : SS),
            shutterSelectorTurnedToAt ss t
        ->
            (
                forall d: T,
                            d >= 0
                            (* possibly replace by t2 where t2 = t+d ?????????????????????????????*)
                        /\
                            (
                                forall ss2 : SS,
                                        ss <> ss2
                                    ->
                                        (
                                            forall x: T,
                                                    x in [t, t + d]
                                                ->
                                                    ~shutterSelectorTurnedToAt ss2 x
                                        )
                            )
                    ->
                        shutterSettingAt ss (t + d)
            )
)
 (t3_strict - d_strict)
.
hyp H5.
replace (t3_strict - d_strict) with y2_strict.
hyp H23.
replace d_strict with (t3_strict - y2_strict).
lin_solve.
con_i.
replace d_strict with (t3_strict - y2_strict).
imp_e (
        t2_strict <= y2_strict
    /\
        y2_strict < t3_strict
)
.
imp_i a6.
imp_e (
    t2_strict <= y2_strict
)
.
imp_i ab7.
imp_e (
    y2_strict < t3_strict
)
.
imp_i ab8.
lin_solve.
con_e2 (
    t2_strict <= y2_strict
)
.
hyp a6.
con_e1 (
    y2_strict < t3_strict
)
.
hyp a6.
hyp H20.
replace (t3_strict - d_strict + d_strict) with t3_strict.
replace (t3_strict - d_strict) with y2_strict.
all_i ss2_strict.
imp_i a7.
all_i x_strict.
imp_i a8.
imp_e (
    ss_strict <> ss2_strict
)
.
all_e (
    forall ss2 : SS,
            ss_strict <> ss2
        ->
            ~shutterSelectorTurnedToAt ss2 x_strict
)
ss2_strict.
con_e1 (
    forall fs2 : FS,
            fs_strict <> fs2
        ->
            ~focusSelectorTurnedToAt fs2 x_strict
)
.
con_e2 (
    forall as2 : AS,
            as1_strict <> as2
        ->
            ~apertureSelectorTurnedToAt as2 x_strict
)
.
imp_e (
    x_strict in [t1_strict, t3_strict + ShutterTime ss_strict]
)
.
all_e (
    forall t : T,
            t in [t1_strict, t3_strict + ShutterTime ss_strict]
        ->
                (
                    forall as2 : AS,
                            as1_strict <> as2
                        ->
                            ~apertureSelectorTurnedToAt as2 t
                )
            /\
                (
                    forall ss2 : SS,
                            ss_strict <> ss2
                        ->
                            ~shutterSelectorTurnedToAt ss2 t
                )
            /\
                (
                    forall fs2 : FS,
                            fs_strict <> fs2
                        ->
                            ~focusSelectorTurnedToAt fs2 t
                )
)
x_strict.
hyp H12.

interval.
imp_e (
        y2_strict <= x_strict
    /\
        x_strict <= t3_strict
)
.
imp_i ab7.
imp_e (
        t2_strict <= y2_strict
    /\
        y2_strict < t3_strict
)
.
imp_i ab9.
imp_e (
    t2_strict <= y2_strict
)
.
imp_i ab10.
imp_e (
    y2_strict <= x_strict
)
.
imp_i ab11.
imp_e (
    x_strict <= t3_strict
)
.
imp_i ab12.
imp_e (
    x_strict < t3_strict + ShutterTime ss_strict
)
.
imp_i ab14.
con_i.
lin_solve.
lin_solve.
lin_solve.
con_e2 (
    y2_strict <= x_strict
)
.
hyp ab7.
con_e1 (
    x_strict <= t3_strict
)
.
hyp a8.
con_e1 (
    y2_strict < t3_strict
)
.
hyp ab9.
hyp H20.
hyp a8.
hyp a7.
replace d_strict with (t3_strict - y2_strict).
lin_solve.
lin_solve.
lin_solve.

(* =========================================== *)
(* FOCUSSETTING *)
(* =========================================== *)

replace t3_strict with ((t3_strict - d_strict) + d_strict).
imp_e (
        d_strict >= 0
    /\
        (
            forall fs2 : FS,
                    fs_strict <> fs2
                ->
                    forall x : T,
                            x in [(t3_strict - d_strict), (t3_strict - d_strict) + d_strict]
                        ->
                            ~focusSelectorTurnedToAt fs2 x
        )
)
.
all_e (
    forall d : T,
                d >= 0
            /\
                (
                    forall fs2 : FS,
                            fs_strict <> fs2
                        ->
                            forall x : T,
                                    x in [(t3_strict - d_strict), (t3_strict - d_strict) + d]
                                ->
                                    ~focusSelectorTurnedToAt fs2 x
                )
        ->
            focusSettingAt fs_strict ((t3_strict - d_strict) + d)
)
d_strict
.
imp_e (
    focusSelectorTurnedToAt fs_strict (t3_strict - d_strict)
)
.
all_e (
    forall (fs : FS),
            focusSelectorTurnedToAt fs (t3_strict - d_strict)
        ->
            (
                forall d: T,
                            d >= 0
                            (* possibly replace by t2 where t2 = t+d ?????????????????????????????*)
                        /\
                            (
                                forall fs2 : FS,
                                        fs <> fs2
                                    ->
                                        (
                                            forall x: T,
                                                    x in [(t3_strict - d_strict), (t3_strict - d_strict) + d]
                                                ->
                                                    ~focusSelectorTurnedToAt fs2 x
                                        )
                            )
                    ->
                        focusSettingAt fs ((t3_strict - d_strict) + d)
            )
)
fs_strict
.
all_e (
    forall (t : T) (fs : FS),
            focusSelectorTurnedToAt fs t
        ->
            (
                forall d: T,
                            d >= 0
                            (* possibly replace by t2 where t2 = t+d ?????????????????????????????*)
                        /\
                            (
                                forall fs2 : FS,
                                        fs <> fs2
                                    ->
                                        (
                                            forall x: T,
                                                    x in [t, t + d]
                                                ->
                                                    ~focusSelectorTurnedToAt fs2 x
                                        )
                            )
                    ->
                        focusSettingAt fs (t + d)
            )
)
 (t3_strict - d_strict)
.
hyp H7.
replace (t3_strict - d_strict) with y2_strict.
hyp H21.
replace d_strict with (t3_strict - y2_strict).
lin_solve.
con_i.
replace d_strict with (t3_strict - y2_strict).
imp_e (
        t2_strict <= y2_strict
    /\
        y2_strict < t3_strict
)
.
imp_i a6.
imp_e (
    t2_strict <= y2_strict
)
.
imp_i ab7.
imp_e (
    y2_strict < t3_strict
)
.
imp_i ab8.
lin_solve.
con_e2 (
    t2_strict <= y2_strict
)
.
hyp a6.
con_e1 (
    y2_strict < t3_strict
)
.
hyp a6.
hyp H20.
replace (t3_strict - d_strict + d_strict) with t3_strict.
replace (t3_strict - d_strict) with y2_strict.
all_i fs2_strict.
imp_i a7.
all_i x_strict.
imp_i a8.
imp_e (
    fs_strict <> fs2_strict
)
.
all_e (
    forall fs2 : FS,
            fs_strict <> fs2
        ->
            ~focusSelectorTurnedToAt fs2 x_strict
)
fs2_strict.
con_e2 (
    forall ss2 : SS,
            ss_strict <> ss2
        ->
            ~shutterSelectorTurnedToAt ss2 x_strict
)
.
con_e2 (
    forall as2 : AS,
            as1_strict <> as2
        ->
            ~apertureSelectorTurnedToAt as2 x_strict
)
.
imp_e (
    x_strict in [t1_strict, t3_strict + ShutterTime ss_strict]
)
.
all_e (
    forall t : T,
            t in [t1_strict, t3_strict + ShutterTime ss_strict]
        ->
                (
                    forall as2 : AS,
                            as1_strict <> as2
                        ->
                            ~apertureSelectorTurnedToAt as2 t
                )
            /\
                (
                    forall ss2 : SS,
                            ss_strict <> ss2
                        ->
                            ~shutterSelectorTurnedToAt ss2 t
                )
            /\
                (
                    forall fs2 : FS,
                            fs_strict <> fs2
                        ->
                            ~focusSelectorTurnedToAt fs2 t
                )
)
x_strict.
hyp H12.

interval.
imp_e (
        y2_strict <= x_strict
    /\
        x_strict <= t3_strict
)
.
imp_i ab7.
imp_e (
        t2_strict <= y2_strict
    /\
        y2_strict < t3_strict
)
.
imp_i ab9.
imp_e (
    t2_strict <= y2_strict
)
.
imp_i ab10.
imp_e (
    y2_strict <= x_strict
)
.
imp_i ab11.
imp_e (
    x_strict <= t3_strict
)
.
imp_i ab12.
imp_e (
    x_strict < t3_strict + ShutterTime ss_strict
)
.
imp_i ab14.
con_i.
lin_solve.
lin_solve.
lin_solve.
con_e2 (
    y2_strict <= x_strict
)
.
hyp ab7.
con_e1 (
    x_strict <= t3_strict
)
.
hyp a8.
con_e1 (
    y2_strict < t3_strict
)
.
hyp ab9.
hyp H20.
hyp a8.
hyp a7.
replace d_strict with (t3_strict - y2_strict).
lin_solve.
lin_solve.
lin_solve.

(* =========================================== *)
(* APERTURESETTING *)
(* =========================================== *)

replace t3_strict with ((t3_strict - d_strict) + d_strict).
imp_e (
        d_strict >= 0
    /\
        (
            forall as2 : AS,
                    as1_strict <> as2
                ->
                    forall x : T,
                            x in [(t3_strict - d_strict), (t3_strict - d_strict) + d_strict]
                        ->
                            ~apertureSelectorTurnedToAt as2 x
        )
)
.
all_e (
    forall d : T,
                d >= 0
            /\
                (
                    forall as2 : AS,
                            as1_strict <> as2
                        ->
                            forall x : T,
                                    x in [(t3_strict - d_strict), (t3_strict - d_strict) + d]
                                ->
                                    ~apertureSelectorTurnedToAt as2 x
                )
        ->
            apertureSettingAt as1_strict ((t3_strict - d_strict) + d)
)
d_strict
.
imp_e (
    apertureSelectorTurnedToAt as1_strict (t3_strict - d_strict)
)
.
all_e (
    forall (as1 : AS),
            apertureSelectorTurnedToAt as1 (t3_strict - d_strict)
        ->
            (
                forall d: T,
                            d >= 0
                            (* possibly replace by t2 where t2 = t+d ?????????????????????????????*)
                        /\
                            (
                                forall as2 : AS,
                                        as1 <> as2
                                    ->
                                        (
                                            forall x: T,
                                                    x in [(t3_strict - d_strict), (t3_strict - d_strict) + d]
                                                ->
                                                    ~apertureSelectorTurnedToAt as2 x
                                        )
                            )
                    ->
                        apertureSettingAt as1 ((t3_strict - d_strict) + d)
            )
)
as1_strict
.
all_e (
    forall (t : T) (as1 : AS),
            apertureSelectorTurnedToAt as1 t
        ->
            (
                forall d: T,
                            d >= 0
                            (* possibly replace by t2 where t2 = t+d ?????????????????????????????*)
                        /\
                            (
                                forall as2 : AS,
                                        as1 <> as2
                                    ->
                                        (
                                            forall x: T,
                                                    x in [t, t + d]
                                                ->
                                                    ~apertureSelectorTurnedToAt as2 x
                                        )
                            )
                    ->
                        apertureSettingAt as1 (t + d)
            )
)
 (t3_strict - d_strict)
.
hyp H6.
replace (t3_strict - d_strict) with y2_strict.
hyp H22.
replace d_strict with (t3_strict - y2_strict).
lin_solve.
con_i.
replace d_strict with (t3_strict - y2_strict).
imp_e (
        t2_strict <= y2_strict
    /\
        y2_strict < t3_strict
)
.
imp_i a6.
imp_e (
    t2_strict <= y2_strict
)
.
imp_i ab7.
imp_e (
    y2_strict < t3_strict
)
.
imp_i ab8.
lin_solve.
con_e2 (
    t2_strict <= y2_strict
)
.
hyp a6.
con_e1 (
    y2_strict < t3_strict
)
.
hyp a6.
hyp H20.
replace (t3_strict - d_strict + d_strict) with t3_strict.
replace (t3_strict - d_strict) with y2_strict.
all_i as2_strict.
imp_i a7.
all_i x_strict.
imp_i a8.
imp_e (
    as1_strict <> as2_strict
)
.
all_e (
    forall as2 : AS,
            as1_strict <> as2
        ->
            ~apertureSelectorTurnedToAt as2 x_strict
)
as2_strict.
con_e1 (
        (
            forall ss2 : SS,
                    ss_strict <> ss2
                ->
                    ~shutterSelectorTurnedToAt ss2 x_strict
        )
    /\
        (
            forall fs2 : FS,
                    fs_strict <> fs2
                ->
                    ~focusSelectorTurnedToAt fs2 x_strict
        )
)
.
imp_e (
    x_strict in [t1_strict, t3_strict + ShutterTime ss_strict]
)
.
all_e (
    forall t : T,
            t in [t1_strict, t3_strict + ShutterTime ss_strict]
        ->
                (
                    forall as2 : AS,
                            as1_strict <> as2
                        ->
                            ~apertureSelectorTurnedToAt as2 t
                )
            /\
                (
                    forall ss2 : SS,
                            ss_strict <> ss2
                        ->
                            ~shutterSelectorTurnedToAt ss2 t
                )
            /\
                (
                    forall fs2 : FS,
                            fs_strict <> fs2
                        ->
                            ~focusSelectorTurnedToAt fs2 t
                )
)
x_strict.
hyp H12.

interval.
imp_e (
        y2_strict <= x_strict
    /\
        x_strict <= t3_strict
)
.
imp_i ab7.
imp_e (
        t2_strict <= y2_strict
    /\
        y2_strict < t3_strict
)
.
imp_i ab9.
imp_e (
    t2_strict <= y2_strict
)
.
imp_i ab10.
imp_e (
    y2_strict <= x_strict
)
.
imp_i ab11.
imp_e (
    x_strict <= t3_strict
)
.
imp_i ab12.
imp_e (
    x_strict < t3_strict + ShutterTime ss_strict
)
.
imp_i ab14.
con_i.
lin_solve.
lin_solve.
lin_solve.
con_e2 (
    y2_strict <= x_strict
)
.
hyp ab7.
con_e1 (
    x_strict <= t3_strict
)
.
hyp a8.
con_e1 (
    y2_strict < t3_strict
)
.
hyp ab9.
hyp H20.
hyp a8.
hyp a7.
replace d_strict with (t3_strict - y2_strict).
lin_solve.
lin_solve.
lin_solve.
Qed.


Declare ML Module "ltac_plugin".
Local Set Default Proof Mode "Classic".
(* *)
Set Universe Polymorphism.
Unset Universe Minimization ToSet.
(* *)
Set Primitive Projections.
Unset Printing Primitive Projection Parameters.
Set Printing Projections.
Set Case Analysis Schemes.
Set Nonrecursive Elimination Schemes.
(* *)
Delimit Scope type_scope with type.
Delimit Scope function_scope with function.
Delimit Scope core_scope with core.
Bind Scope type_scope with Sortclass.
Bind Scope function_scope with Funclass.
Open Scope core_scope.
Open Scope function_scope.
Open Scope type_scope.

(** Inhabiting inductive families using only ∀ ∃ 0 1 ℕ *)

(** N-types are a TODO work-in-progress alternative to W-types, aiming to improve on both intensional properties and simplicity *)
(* Leave my types ∀10ℕ∃! *)
(* fueled types *)

(** ∀ is built-in in Coq *)
Record exi {A B} := pair { fst : A ; snd : B fst }. (* primitive projections only *)
Variant False := . (* implies anything *)
Variant True := I. (* non-empty hprop *)
Inductive nat := O | S (_ : nat). (* fuel *)

Notation "A -> B" := (forall (_:A), B) (at level 99, right associativity, B at level 200) : type_scope.
Arguments exi [_] _.
Arguments pair {_ _}.
Notation "'exists' x .. y , p" := (exi (fun x => .. (exi (fun y => p)) ..))
  (at level 200, x binder, right associativity,
   format "'[' 'exists'  '/  ' x  ..  y ,  '/  ' p ']'")
  : type_scope.
Notation "{ a  &  B }"
  := (exi (fun a => B%type))
       (at level 0, a at level 99)
     : type_scope.
Notation "{ a : A  &  B }"
  := (exi (fun a : A%type => B%type))
       (at level 0, a at level 99)
     : type_scope.
Infix "/\" := (fun A B : Type => { _ : A & B }) (at level 80, right associativity) : type_scope.
Infix "*" := (fun A B : Type => { _ : A & B }) (at level 40, left associativity) : type_scope.
Notation "( x , y , .. , z )" := (pair .. (pair x y) .. z) : core_scope.
Notation "0" := (False) : type_scope.
Notation "1" := (True) : type_scope.
Notation "0" := (O).
Notation "1" := (S 0).

Fixpoint eq_nat (a : nat) (b : nat) :=
  match a, b with
  | O, O => True
  | S a, S b => eq_nat a b
  | _, _ => False
  end.
Lemma eq_nat_refl a : eq_nat a a.
Proof. induction a; cbn; [exact I|assumption]. Defined.
Lemma eq_nat_sym {a} : forall {b}, eq_nat a b -> eq_nat b a.
Proof. induction a, b; cbn; eauto with nocore. Defined.
Lemma eq_nat_inj a b (_ : eq_nat (S a) (S b)) : eq_nat a b.
Proof. destruct a, b; eauto with nocore. Defined.
Lemma transport_nat a : forall b P (_ : eq_nat a b) (_ : P a), P b.
Proof.
  induction a, b;
         repeat match goal with
                | H : 0 |- _ => destruct H
                | H : 1 |- _ => destruct H
                | H : exi _ |- _ => destruct H
                | _ => progress intros
                | _ => progress cbn [eq_nat] in *
                | _ => assumption
                end.
  exact (IHa _ (fun n => P (S n)) X X0).
Defined.
Lemma transport_eq_nat a : forall b P (p q : eq_nat a b) (_ : P p), P q.
Proof. induction a;
         repeat match goal with
                | H : 0 |- _ => destruct H
                | H : 1 |- _ => destruct H
                | H : exi _ |- _ => destruct H
                | _ => progress intros
                | _ => progress cbn [eq_nat] in *
                | H : context [match ?n with O => _ | _ => _ end ] |- _ => destruct n
                | _ => assumption
                end.
       exact (IHa _ _ p _ X).
Defined.

(** [vec] is a one-parameter one-index family of inductive types.
    We will be implementing it in terms of 0 1 ∀ ∃ ℕ *)
Module Target.
  Section vec.
    Context {T : Type}.
    Inductive vec : nat -> Type :=
    | nil : vec 0
    | cons {n' : nat} (_ : T)  (_ : vec n') : vec (S n').
  End vec. Arguments vec : clear implicits.
End Target.

(** We can reduce inductive families to inductive types by enforcing index relationships using a recursive predicate. If equality of indices is decidable, the enforcement predicate is a hprop, allowing for the original dependent eliminator to be reconstructed without modification. *)
Module WithoutIndices.
  Section vec.
    Context {T : Type}.
    Inductive prevec : Type :=
    | prenil
    | precons (n : nat) (_ : T)  (_ : prevec).
    Fixpoint enforce (v : prevec) (n : nat) {struct v} :=
      match v with
      | prenil => eq_nat n 0
      | precons n' _ v' => eq_nat n (S n') /\ enforce v' n'
      end.
    Definition vec (n : nat) : Type := { v & enforce v n }.
    Definition nil : vec 0 := (prenil, I).
    Definition cons {n : nat} (x : T) (v : vec n) : vec (S n) := (precons n x (fst v), (@pair _ (fun _ => enforce _ _) (eq_nat_refl (S n)) (snd v))).

    Lemma vec_rect (n : nat) : forall (v : vec n) P (Pnil : P 0 nil) (Pcons : forall (n' : nat) (t : T) (v : vec n'), P n' v -> P (S n') (cons t v)), P n v.
    Proof.
      induction n; intros; destruct v as [[|n' x p'] []];
        try solve [contradiction | exact Pnil].
      pose proof (fun e' => Pcons n x (p', e') (IHn (p', e') _ Pnil Pcons)) as H; clear IHn Pcons Pnil.
      cbv [cons] in H; cbn [fst snd] in *.
      assert (eq_nat n n') as Hn by (apply eq_nat_inj; assumption).
      pattern n in H; eapply transport_nat in H; [|eapply Hn].
      specialize (H snd0).
      revert fst0; pattern n; eapply transport_nat; [exact (eq_nat_sym Hn)|]; intros.
      pattern (eq_nat_refl (S n')) in H; unshelve eapply transport_eq_nat in H; [exact fst0|].
      exact H.
    Defined.
  End vec. Arguments prevec : clear implicits. Arguments vec : clear implicits.
  Goal False.
    pose (vec nat : nat -> Set).
  Abort.
End WithoutIndices.

Module UsingNTypes.
  Section prevec.
    Context {T : Type}.
    (* Inductive prevec : Type :=
       | prenil
       | precons (n : nat) (_ : T)  (_ : prevec). *)
    Definition prevec_args (constructor : nat) (recurse : Type) : Type :=
      match constructor with
      | 0 => 1
      | 1 => nat * (T * recurse)
      | _ => 0
      end.
    Fixpoint prevec_fuel (f : nat) {struct f} : Type :=
      match f with
      | 0 => 0
      | S f => { tag & prevec_args tag (prevec_fuel f) }
      end.
    (* [canonical f pv] encodes the requirement that [f] is the
        minimum possible amount of fuel for constructing [pv] *)
    Fixpoint canonical (f : nat) {struct f} : prevec_fuel f -> Type :=
      match f with
      | 0 => fun _ => False
      | S f =>
        fun pv =>
          match fst pv as tag return prevec_args tag _ -> _ with
          | 0 => fun args => eq_nat f 0
          | 1 => fun args => canonical f (snd (snd args))
          (* recursive calls combine using propositional OR *)
          | _ => fun _ => False
          end (snd pv)
      end.
    Definition prevec := { f & { pvf : prevec_fuel f & canonical f pvf } }.
    Definition prenil : prevec := (1, ((0, I), I)).
    Definition precons (n : nat) (x : T) (pv : prevec) : prevec :=
      (* new fuel is computed as 1+max(fuels of subtrees) *)
      (S (fst pv), ((1, (n, (x, fst (snd pv)))), snd (snd pv))).
    Lemma prevec_rect p : forall
        (P : prevec -> Type)
        (Pprenil : P prenil)
        (Pprecons : forall (n : nat) (t : T) (p : prevec), P p -> P (precons n t p)),
        P p.
    Proof.
      destruct p as [f [r c]]; revert c; revert r; induction f;
        repeat match goal with
               | H : 0 |- _ => destruct H
               | H : 1 |- _ => destruct H
               | H : exi _ |- _ => destruct H
               | _ => assumption
               | H : context [match ?n with O => _ | _ => _ end ] |- _ => destruct n
               | _ => progress intros
               | _ => progress cbn [eq_nat canonical prevec_fuel prevec_args fst snd] in *
               end.
      { pose proof (eq_nat_sym c) as Hf.
        revert c. pattern f.
        eapply transport_nat; [eapply Hf|].
        intros c. pattern c.
        unshelve eapply transport_eq_nat.
        exact I.
        assumption. }
      { exact (Pprecons fst0 fst1 (_,(snd0, c)) (IHf snd0 c P ltac:(assumption) ltac:(assumption))). }
    Defined.
  End prevec.
End UsingNTypes.


Require Import msl.msl_standard.

Local Open Scope pred.

Lemma andp_TT {A}`{ageable A}: forall (P: pred A), P && TT = P.
Proof.
intros.
apply pred_ext; intros w ?.
destruct H0; auto.
split; auto.
Qed.

Lemma sepcon_andp_prop' {A}  {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{agA: ageable A}{AgeA: Age_alg A}: forall P Q R, (!!Q && P)*R = !!Q&&(P*R).
Proof.
intros.
rewrite sepcon_comm. rewrite sepcon_andp_prop.
rewrite sepcon_comm; auto.
Qed.

Hint Rewrite @sepcon_emp @emp_sepcon @TT_and @andp_TT
             @exp_sepcon1 @exp_sepcon2
               @exp_andp1 @exp_andp2
         @sepcon_andp_prop @sepcon_andp_prop'
        : normalize.

Definition pure {A}{JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{agA: ageable A}{AgeA: Age_alg A}
     (P: pred A) : Prop :=
   P |-- emp.

Lemma pure_sepcon {A} {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{CA: Canc_alg A}{agA: ageable A}{AgeA: Age_alg A}: forall (P : pred A), pure P -> P*P=P.
Proof.
pose proof I.
intros.
apply pred_ext; intros w ?.
assert ((emp * P)%pred w).
eapply sepcon_derives; try apply H1; auto.
rewrite emp_sepcon in H2.
auto.
exists w; exists w.
split; [|split]; auto.
apply H0 in H1.
do 3 red in H1. rewrite identity_unit_equiv in H1.
apply H1.
Qed.

Lemma pure_e {A}{JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{agA: ageable A}{AgeA: Age_alg A}: forall (P: pred A), pure P -> (P |-- emp).
Proof.
intros.
apply H.
Qed.

Hint Resolve @pure_e.

Lemma sepcon_pure_andp {A} {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{agA: ageable A}{AgeA: Age_alg A}:
 forall P Q, pure P -> pure Q -> ((P * Q) = (P && Q)).
Proof.
intros.
apply pred_ext; intros w ?.
destruct H1 as [w1 [w2 [? [? ?]]]].
unfold pure in *.
assert (unit_for w1 w2). apply H in H2; simpl in H2;
apply identity_unit; auto. exists w; auto.
unfold unit_for in H4.
assert (w2=w) by (apply (join_eq H4 H1)).
subst w2.
assert (join w w1 w1).
apply identity_unit; apply H0 in H3; simpl in H3; auto. exists w; auto.
assert (w1=w) by (apply (join_eq H5 (join_comm H1))).
subst w1.
split; auto.
destruct H1.
exists w; exists w; split; [|split]; auto.
apply H in H1.
do 3 red in H1.
clear dependent P. clear dependent Q.
pose proof (core_unit w); unfold unit_for in *.
pose proof (H1 _ _ (join_comm H)).
rewrite H0 in H; auto.
Qed.

Lemma pure_emp {A} {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{agA: ageable A}{AgeA: Age_alg A}: pure emp.
Proof.
intros. unfold pure; auto.
Qed.
Hint Resolve @pure_emp.

Lemma join_equiv_refl {A}: forall x:A, @join A (Join_equiv A) x x x.
Proof. split; auto. Qed.
Hint Resolve @join_equiv_refl.

Lemma pure_sepcon1'' {A} {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{agA: ageable A}{AgeA: Age_alg A}: forall P Q R, pure P -> Q |-- R -> P * Q |-- R.
Proof.
pose proof I.
intros.
intros w [w1 [w2 [? [? ?]]]].
apply H0 in H3.
apply join_unit1_e in H2; auto.
subst; auto.
Qed.

Lemma pure_existential {A} {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{CA: Canc_alg A}{agA: ageable A}{AgeA: Age_alg A}:
   forall B (P: B -> pred A),    (forall x: B , pure (P x)) -> pure (exp P).
Proof.
intros.
unfold pure in *.
intros w [x ?].
apply (H x); auto.
Qed.

Hint Resolve @pure_existential.

Lemma pure_core {A} {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{CA: Canc_alg A}{agA: ageable A}{AgeA: Age_alg A}:
  forall P w, pure P -> P w -> P (core w).
Proof.
intros.
assert (w = core w).
apply unit_core.
apply identity_unit_equiv; auto.
apply H; auto.
rewrite <- H1; auto.
Qed.

Lemma FF_sepcon {A} {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{agA: ageable A}{AgeA: Age_alg A}:
           forall P, FF * P = FF.
Proof.
intros.
apply pred_ext; intros w ?; try contradiction.
destruct H as [w1 [w2 [? [? ?]]]]; contradiction.
Qed.
Lemma sepcon_FF {A} {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{agA: ageable A}{AgeA: Age_alg A}:
            forall P, P * FF = FF.
Proof.
intros.
rewrite sepcon_comm. apply FF_sepcon.
Qed.
Hint Rewrite @FF_sepcon @sepcon_FF : normalize.

Hint Rewrite @prop_true_andp using (solve [auto]) : normalize.

Lemma true_eq {A} `{ageable A}:  forall P: Prop, P -> (!! P) = (TT: pred A).
Proof.
intros. apply pred_ext; intros ? ?; simpl in *; intuition.
Qed.
Hint Rewrite @true_eq using (solve [auto]) : normalize.

Lemma pure_con' {A} {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{CA: Canc_alg A}{agA: ageable A}{AgeA: Age_alg A}:
      forall P Q, pure P -> pure Q -> pure (P*Q).
Proof.
intros.
unfold pure in *.
rewrite <- sepcon_emp.
apply sepcon_derives; auto.
Qed.
Hint Resolve @pure_con'.

Lemma pure_intersection1: forall {A}  {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{agA: ageable A}{AgeA: Age_alg A}
       (P Q: pred A), pure P -> pure (P && Q).
Proof.
unfold pure; intros; auto.
intros w [? ?]; auto.
Qed.
Lemma pure_intersection2: forall {A} {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{agA: ageable A}{AgeA: Age_alg A}
     (P Q: pred A), pure Q -> pure (P && Q).
Proof.
unfold pure; intros; auto.
intros w [? ?]; auto.
Qed.
Hint Resolve @pure_intersection1 @pure_intersection2.

Lemma FF_andp {A} `{ageable A}:  forall P: pred A, FF && P = FF.
Proof.
unfold FF, prop, andp; intros; apply pred_ext; intros ? ?; simpl in *; intuition.
Qed.
Lemma andp_FF {A}`{ageable A}:  forall P: pred A, P && FF = FF.
Proof.
unfold FF, prop, andp; intros; apply pred_ext; intros ? ?; simpl in *; intuition.
Qed.
Hint Rewrite @FF_andp @andp_FF : normalize.

Hint Rewrite @andp_dup : normalize.

Lemma andp_emp_sepcon_TT {A} {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{CA: Canc_alg A}{agA: ageable A}{AgeA: Age_alg A}:
 forall (Q: pred A),
     (forall w1 w2, core w1 = core w2 -> Q w1 -> Q w2) ->
      (Q && emp * TT = Q).
Proof.
intros.
apply pred_ext.
intros w [w1 [w2 [? [[? ?] ?]]]].
apply H with w1; auto.
apply join_core in H0; auto.
intros w ?.
destruct (join_ex_units w) as [e ?].
exists e; exists w; split; [|split]; auto.
split; auto.
apply H with w; auto.
unfold unit_for in u; apply join_core in u. auto.
simpl; apply unit_identity with w; auto.
Qed.

Lemma sepcon_TT {A} {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{CA: Canc_alg A}{agA: ageable A}{AgeA: Age_alg A}:
   forall (P: pred A), P |-- (P * TT).
Proof.
intros.
rewrite <- (sepcon_emp P) at 1.
eapply sepcon_derives; try apply H0; auto.
Qed.
Hint Resolve @sepcon_TT.

Lemma imp_extract_exp_left {B A: Type} `{ageable A}:
    forall    (p : B -> pred A) (q: pred A),
  (forall x, p x |-- q) ->
   exp p |-- q.
Proof.
intros.
intros w [x ?].
eapply H0; eauto.
Qed.

Lemma pure_sepcon_TT_andp {A} {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{agA: ageable A}{AgeA: Age_alg A}:
  forall P Q, pure P -> (P * TT) && Q = (P*Q).
Proof.
 pose proof I.
intros.
apply pred_ext.
intros w [? ?].
destruct H1 as [w1 [w2 [? [? ?]]]].
exists w1; exists w2; split; [|split]; auto.
apply join_unit1_e in H1; auto.
subst; auto.
apply H0 in H3; auto.
apply andp_right.
apply sepcon_derives; auto.
intros w [w1 [w2 [? [? ?]]]].
apply join_unit1_e in H1; auto.
subst; auto.
apply H0 in H2; auto.
Qed.

Lemma pure_sepcon_TT_andp' {A} {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{agA: ageable A}{AgeA: Age_alg A}:
  forall P Q, pure P -> Q && (P * TT) = (Q*P).
Proof.
intros. rewrite andp_comm.
rewrite pure_sepcon_TT_andp; auto.
apply sepcon_comm.
Qed.

Hint Rewrite @pure_sepcon_TT_andp @pure_sepcon_TT_andp' using (solve [auto]): normalize.

Lemma pure_sepcon1' {A} {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{CA: Canc_alg A}{agA: ageable A}{AgeA: Age_alg A}:

  forall P Q R, pure P -> P * Q |-- P * R -> P * Q |-- R.
Proof.
intros.
eapply derives_trans; try apply H0.
apply pure_sepcon1''; auto.
Qed.

Lemma pull_right {A} {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{agA: ageable A}{AgeA: Age_alg A}:
 forall P Q R,
   (Q * P * R) = (Q * R * P).
Proof.
intros. repeat rewrite sepcon_assoc. rewrite (sepcon_comm P); auto.
Qed.

Lemma pull_right0 {A}  {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{agA: ageable A}{AgeA: Age_alg A}: forall P Q,
   (P * Q) = (Q * P).
Proof.
intros. rewrite (sepcon_comm P); auto.
Qed.

Ltac pull_left A := repeat (rewrite <- (pull_right A) || rewrite <- (pull_right0 A)).

Ltac pull_right A := repeat (rewrite (pull_right A) || rewrite (pull_right0 A)).

Lemma pure_modus {A} {JA: Join A}{PA: Perm_alg A}{SA: Sep_alg A}{agA: ageable A}{AgeA: Age_alg A}:
  forall P Q,  P |-- Q -> pure Q -> P |-- Q && P.
Proof.
intros.
intros w ?.
split; auto.
Qed.

Lemma imp_exp_right {B A : Type} `{saA: ageable A}:
  forall (x: B) (p: pred A) (q: B -> pred A),
    p |-- q x ->
    p |-- exp q.
Proof.
intros.
eapply derives_trans; try apply H.
intros w ?; exists x; auto.
Qed.

Lemma derives_extract_prop {A} `{ageable A}:
  forall (P: Prop) (Q R: pred A), (P -> Q |-- R) ->  !!P && Q |-- R.
Proof.
unfold derives, prop, andp; hnf in *; intuition.
hnf in H1; intuition.
Qed.

Lemma derives_extract_prop' {A} `{ageable A}:
  forall (P: Prop) (Q R: pred A), (P -> Q |-- R) ->  Q && !!P|-- R.
Proof.
unfold derives, prop, andp; intuition; hnf in *; intuition.
hnf in *; intuition. apply H1; auto.
Qed.

Ltac normalize1 :=
             match goal with
                | |- _ => contradiction
                | |- context [(?P && ?Q) * ?R] => rewrite (corable_andp_sepcon1 P Q R) by (auto with normalize)
                | |- context [?Q * (?P && ?R)] => rewrite (corable_sepcon_andp1 P Q R) by (auto with normalize)
                | |- context [(?Q && ?P) * ?R] => rewrite (corable_andp_sepcon2 P Q R) by (auto with normalize)
                | |- context [?Q * (?R && ?P)] => rewrite (corable_sepcon_andp2 P Q R) by (auto with normalize)
                | |- _ => progress  (autorewrite with normalize); auto with typeclass_instances
                | |- _ = ?x -> _ => intro; subst x
                | |- ?x = _ -> _ => intro; subst x
                |  |- ?ZZ -> _ => match type of ZZ with
                                               | Prop =>
                                                    let H := fresh in
                                                       ((assert (H:ZZ) by auto; clear H; intros _) || intro H)
                                               | _ => intros _
                                              end
                | |- forall _, _ => let x := fresh "x" in (intro x; normalize1; try generalize dependent x)
                | |- exp _ |-- _ => apply imp_extract_exp_left
                | |- !! _ && _ |-- _ => apply derives_extract_prop
                | |- _ && !! _ |-- _ => apply derives_extract_prop'
                | |- _ |-- !! (?x = ?y) && _ =>
                            (rewrite prop_true_andp with (P:= (x=y))
                                            by (unfold y; reflexivity); unfold y in *; clear y) ||
                            (rewrite prop_true_andp with (P:=(x=y))
                                            by (unfold x; reflexivity); unfold x in *; clear x)
                | |- _ => solve [auto with typeclass_instances]
                end.

Ltac normalize1_in Hx :=
             match type of Hx with
                | app_pred (exp _) _ => destruct Hx
                | app_pred (!! _ && _) _ => let H1 := fresh in destruct Hx as [H1 Hx]; unfold prop in H1
                | context [ !! ?P ] =>
                                    rewrite (true_eq P) in Hx by auto with typeclass_instances
                | context [ !! ?P && ?Q ] =>
                                    rewrite (prop_true_andp P Q) in Hx by auto with typeclass_instances
                | context [(?P && ?Q) * ?R] => rewrite (corable_andp_sepcon1 P Q R) in Hx by (auto with normalize)
                | context [?Q * (?P && ?R)] => rewrite (corable_sepcon_andp1 P Q R) in Hx by (auto with normalize)
                | context [(?Q && ?P) * ?R] => rewrite (corable_andp_sepcon2 P Q R) in Hx by (auto with normalize)
                | context [?Q * (?R && ?P)] => rewrite (corable_sepcon_andp2 P Q R) in Hx by (auto with normalize)
                | _ => progress  (autorewrite with normalize in Hx); auto with typeclass_instances
                end.

Ltac normalize := repeat normalize1.

Tactic Notation "normalize" "in" hyp(H) := repeat (normalize1_in H).

Definition mark {A: Type} (i: nat) (j: A) := j.

Lemma swap_mark1 {A} {JA: Join A}{PA: Perm_alg A}{agA: ageable A}{AgeA: Age_alg A}:
  forall i j Pi Pj B, (i<j)%nat -> B * mark i Pi * mark j Pj = B * mark j Pj * mark i Pi.
Proof.
intros.
repeat rewrite sepcon_assoc.
f_equal.
apply sepcon_comm.
Qed.

Lemma swap_mark0 {A} {JA: Join A}{PA: Perm_alg A}{agA: ageable A}{AgeA: Age_alg A}:
  forall i j Pi Pj,  (i<j)%nat -> mark i Pi * mark j Pj = mark j Pj * mark i Pi.
Proof.
intros.
apply sepcon_comm.
Qed.

Ltac select_left n :=
  repeat match goal with
 | |- context [(_ * mark ?i _ * mark n _)%pred] =>
      rewrite (swap_mark1 i n); [ | solve [simpl; auto]]
 | |- context [(mark ?i _ * mark n _)%pred] =>
      rewrite (swap_mark0 i n); [ | solve [simpl; auto]]
end.
Ltac select_all n := match n with
                                | O => idtac
                                | S ?n' => select_left n; select_all n'
                              end.
Ltac markem n P :=
   match P with
   | (?Y * ?Z) =>
        (match goal with H: mark _ Z = Z |- _ => idtac end
        || assert (mark n Z = Z) by auto); markem (S n) Y
   | ?Z =>  match goal with H: mark _ Z = Z |- _ => idtac end
                || assert (mark n Z = Z) by auto
  end.

Ltac prove_assoc_commut :=
 clear;
 try (match goal with |- ?F _ -> ?G _ => replace G with F; auto end);
  (repeat rewrite <- sepcon_assoc;
   match goal with |- ?P = _ => markem O P end;
   let LEFT := fresh "LEFT" in match goal with |- ?P = _ => set (LEFT := P) end;
  match goal with H: mark ?n _ = _ |- _ =>
     repeat  match goal with H: mark ?n _ = ?P |- _ => rewrite <- H; clear H end;
     select_all n;
     reflexivity
   end).

Lemma test_prove_assoc_commut {T}{JA: Join T}{PA: Perm_alg T}{agA: ageable T}{AgeA: Age_alg T} : forall A B C D E : pred T,
   D * E * A * C * B = A * B * C * D * E.
Proof.
intros.
prove_assoc_commut.
Qed.


(*
 * Copyright 2020 Youngju Song
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *)
Require Import Lists.List.
Import ListNotations.

Set Implicit Arguments.

Fixpoint filter_map A B (f: A -> option B) (l: list A): list B :=
  match l with
  | [] => []
  | hd :: tl =>
    match (f hd) with
    | Some b => b :: (filter_map f tl)
    | _ => filter_map f tl
    end
  end
.

Definition try_left A B (ab: A + B): option A :=
  match ab with
  | inl a => Some a
  | _ => None
  end
.

Definition try_right A B (ab: A + B): option B :=
  match ab with
  | inr b => Some b
  | _ => None
  end
.

Notation unwrap :=
  (fun x default => match x with
                    | Some y => y
                    | _ => default
                    end)
.

(* Notation "'unwrap' x default" := *)
(*   (match x with *)
(*    | Some y => y *)
(*    | _ => default *)
(*    end) (at level 60) *)
(* . *)

(* Definition unwrap X (x: option X) (default: X): X := *)
(*   match x with *)
(*   | Some x => x *)
(*   | _ => default *)
(*   end *)
(* . *)

Definition is_some X (x: option X): bool :=
  match x with
  | Some _ => true
  | _ => false
  end
.

Definition is_none X (x: option X): bool :=
  match x with
  | Some _ => false
  | _ => true
  end
.

Ltac apply_list f ls :=
  let rec go f ls :=
      match ls with
      | nil => f
      | ?hd :: ?tl => go (f hd) tl
      end
  in
  go f ls
.


Require Import List Common
        ADT.ADTSig ADT.Core
        ADTNotation.BuildADTSig ADTNotation.BuildADT
        Common.ilist ADTNotation.StringBound
        ADTRefinement.Core ADTRefinement.SetoidMorphisms
        ADTRefinement.GeneralRefinements
        ADTRefinement.GeneralBuildADTRefinements
        ADTRefinement.Refinements.HoneRepresentation
        ADTRefinement.BuildADTSetoidMorphisms.

(* A generic refinement and honing tactic for switching the
    representation of an ADT built from [BuildADT]. *)

Section HoneRepresentation.

  Variable oldRep : Type. (* The old representation type. *)
  Variable newRep : Type. (* The new representation type. *)

  (* The abstraction relation between old and new representations. *)
  Variable AbsR : oldRep -> newRep -> Prop.

  (* When switching representations, we can always build a default
     implementation (computation?) for the methods of an ADT with
     using the old methods. *)

  Definition absConsDef
             (Sig : consSig)
             (oldCons : @consDef oldRep Sig)
  : @consDef newRep Sig :=
    {| consBody := absConstructor AbsR (consBody oldCons) |}.

  Definition absMethDef
             (Sig : methSig)
             (oldCons : @methDef oldRep Sig)
  : @methDef newRep Sig :=
    {| methBody := absMethod AbsR (methBody oldCons) |}.

  Lemma refineADT_BuildADT_Rep_default
            (consSigs : list consSig)
            (methSigs : list methSig)
            (consDefs : ilist (@consDef oldRep) consSigs)
            (methDefs : ilist (@methDef oldRep) methSigs) :
    refineADT
      (BuildADT consDefs methDefs)
      (BuildADT (imap _ absConsDef consDefs)
                (imap _ absMethDef methDefs)).
  Proof.
    eapply refineADT_Build_ADT_Rep with (AbsR := AbsR); eauto; intros.
    - unfold getConsDef.
      rewrite <- ith_Bounded_imap.
      unfold absConsDef, absConstructor, refineConstructor, refine; simpl; intros.
      inversion_by computes_to_inv; eauto.
    - unfold getMethDef.
      rewrite <- ith_Bounded_imap.
      unfold absMethDef, absMethod, refineMethod, refine; simpl; intros.
      inversion_by computes_to_inv; eauto.
      destruct (H0 _ H) as [or' [Comp_or [AbsR_or'' or''_eq] ] ];
        subst; repeat econstructor; eauto.
      destruct v; simpl in *; subst; econstructor.
  Qed.

  (* [refine_AbsMethod] can be applied when honing methods to
     get goals in a nicer form. *)

  Lemma refine_AbsMethod :
    forall Dom Cod oldMethod nr (d : Dom) refinedMeth,
      (forall or,
         AbsR or nr ->
         refine (or' <- oldMethod or d;
                 nr' <- { nr' | AbsR (fst or') nr'};
                 ret (nr', snd or'))
                (refinedMeth nr d))
      -> refine (absMethod (Cod := Cod) AbsR oldMethod nr d)
                (refinedMeth nr d).
  Proof.
    unfold absMethod, refine; intros * H v ComputesTo_v; econstructor;
    intros or AbsR_or.
    pose proof (H _ AbsR_or v ComputesTo_v); inversion_by computes_to_inv;
    subst.
    eexists; split; eauto.
  Qed.

  Lemma refine_AbsMethod' :
    forall Dom Cod oldMethod nr (d : Dom) refinedMeth,
      refine (absMethod (Cod := Cod) AbsR oldMethod nr d)
             (refinedMeth nr d)
      -> (forall or,
            AbsR or nr ->
            refine (or' <- oldMethod or d;
                    nr' <- { nr' | AbsR (fst or') nr'};
                    ret (nr', snd or'))
                   (refinedMeth nr d)).
  Proof.
    unfold absMethod, refine; intros * H or AbsR_or v ComputesTo_v.
    pose proof (H v ComputesTo_v); inversion_by computes_to_inv;
    subst.
    destruct (H0 _ AbsR_or); intuition; subst.
    repeat econstructor; eauto.
    rewrite H4; destruct v; econstructor.
  Qed.

  (* [refine_AbsConstructor] can be applied when honing constructors to
     get goals in a nicer form. *)

  Lemma refine_AbsConstructor :
    forall Dom oldConstructor (d : Dom) refinedConstructor,
      (refine (or' <- oldConstructor d;
               { nr' | AbsR or' nr'})
                (refinedConstructor d))
      -> refine (absConstructor AbsR oldConstructor d)
                (refinedConstructor d).
  Proof.
    unfold absConstructor, refine; intros * H v ComputesTo_v; econstructor.
    pose proof (H v ComputesTo_v); inversion_by computes_to_inv;
    subst.
    eexists; split; eauto.
  Qed.

  Lemma refine_AbsConstructor' :
    forall Dom oldConstructor (d : Dom) refinedConstructor,
      refine (absConstructor AbsR oldConstructor d)
             (refinedConstructor d)
      -> (refine (or' <- oldConstructor d;
                  { nr' | AbsR or' nr'})
                 (refinedConstructor d)).
  Proof.
    unfold absConstructor, refine; intros * H v ComputesTo_v.
    pose proof (H v ComputesTo_v); inversion_by computes_to_inv;
    subst.
    repeat econstructor; eauto.
  Qed.

End HoneRepresentation.

(* Honing tactic for refining the ADT representation which provides
   default method and constructor implementations. *)

Tactic Notation "hone" "representation" "using" open_constr(AbsR') :=
  eapply SharpenStep;
  [eapply refineADT_BuildADT_Rep_default with (AbsR := AbsR') |
   compute [imap absConsDef absMethDef]; simpl ].

Tactic Notation "hone" "constructor" constr(consIdx) :=
  let A :=
      match goal with
          |- Sharpened ?A => constr:(A) end in
  let ASig := match type of A with
                  ADT ?Sig => Sig
              end in
  let consSigs :=
      match ASig with
          BuildADTSig ?consSigs _ => constr:(consSigs) end in
  let methSigs :=
      match ASig with
          BuildADTSig _ ?methSigs => constr:(methSigs) end in
  let consDefs :=
      match A with
          BuildADT ?consDefs _  => constr:(consDefs) end in
  let methDefs :=
      match A with
          BuildADT _ ?methDefs  => constr:(methDefs) end in
  let Rep' :=
      match A with
          @BuildADT ?Rep _ _ _ _ => constr:(Rep) end in
  let ConstructorIndex' := eval simpl in (ConstructorIndex ASig) in
  let MethodIndex' := eval simpl in (MethodIndex ASig) in
  let ConstructorDom' := eval simpl in (ConstructorDom ASig) in
  let consIdxB := eval simpl in
  (@Build_BoundedIndex _ (List.map consID consSigs) consIdx _) in
      eapply (@SharpenStep_BuildADT_ReplaceConstructor_eq
               Rep'  _ _ consDefs methDefs consIdxB
               (@Build_consDef Rep'
                              {| consID := consIdx;
                                 consDom := ConstructorDom' consIdxB
                              |}
                              _
             ));
    [ intros; simpl in *; set_evars; simpl in *;
      match goal with
        |  |- refine (absConstructor ?AbsR ?oldConstructor ?d)
                     (?H ?d) =>
           eapply (@refine_AbsConstructor _ _ AbsR _ oldConstructor)
        | _ => cbv [absConstructor]
      end
    |  cbv beta in *; simpl in *;
       cbv beta delta [replace_BoundedIndex replace_Index] in *;
       simpl in *].

Tactic Notation "hone" "method" constr(methIdx) :=
  let A :=
      match goal with
          |- Sharpened ?A => constr:(A) end in
  let ASig := match type of A with
                  ADT ?Sig => Sig
              end in
  let consSigs :=
      match ASig with
          BuildADTSig ?consSigs _ => constr:(consSigs) end in
  let methSigs :=
      match ASig with
          BuildADTSig _ ?methSigs => constr:(methSigs) end in
  let consDefs :=
      match A with
          BuildADT ?consDefs _  => constr:(consDefs) end in
  let methDefs :=
      match A with
          BuildADT _ ?methDefs  => constr:(methDefs) end in
  let Rep' :=
      match A with
          @BuildADT ?Rep _ _ _ _ => constr:(Rep) end in
  let ConstructorIndex' := eval simpl in (ConstructorIndex ASig) in
  let MethodIndex' := eval simpl in (MethodIndex ASig) in
  let MethodDomCod' := eval simpl in (MethodDomCod ASig) in
  let methIdxB := eval simpl in
  (@Build_BoundedIndex _ (List.map methID methSigs) methIdx _) in
      eapply (@SharpenStep_BuildADT_ReplaceMethod_eq
                Rep'  _ _ consDefs methDefs methIdxB
                (@Build_methDef Rep'
                               {| methID := methIdx;
                                  methDom := fst (MethodDomCod' methIdxB);
                                  methCod := snd (MethodDomCod' methIdxB)
                               |}
                               _
                              ));
    [ intros;
      simpl in *; set_evars; simpl in *;
      match goal with
        |  |- refine (@absMethod ?oldRep ?newRep ?AbsR ?Dom ?Cod ?oldMethod ?nr ?d)
                     (?H ?nr ?d) => eapply (@refine_AbsMethod oldRep newRep AbsR Dom Cod oldMethod)
        | _ => cbv [absMethod]
      end; intros
    |
    cbv beta in *; simpl in *;
    cbv beta delta [replace_BoundedIndex replace_Index] in *;
    simpl in *].