Require Import BenB.
Require Import BenB2.

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

(*
   Title:
   ======
   Authors:
   Mart Brennenraedts, s1078038
   Daan Weessies, s1063758
   Wessel van der Lans, s1084461
   Mees Ephraim, s1085936
*)

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

(*
   [
     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:
   ====================

 ____ ___ ____   _____ ____  _____ ____  _   _   ________  _   _ _____
| __ )_ _/ ___| |  ___|  _ \| ____/ ___|| | | | |__  / _ \| \ | | ____|
|  _ \| | |  _  | |_  | |_) |  _| \___ \| |_| |   / / | | |  \| |  _|
| |_) | | |_| | |  _| |  _ <| |___ ___) |  _  |  / /| |_| | |\  | |___
|____/___\____| |_|   |_| \_\_____|____/|_| |_| /____\___/|_| \_|_____|

/%%&&&&&&&&&%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%###############((((/////****
(&&&&&&&&&&%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&
(&&&&&&&&&&&%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/%%((&&&
(&&&&&&&&&&&%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&
(&&&&&&&&&&&%&&%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%&&&&&&&&&&&&&&&@@@@@@&&&&
(&&&/**/(%&&&&&%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%#((((((((((####(########@@@@&&&&
(&&&%*/.@%&&&&%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%(..... . ...............@@@@&&&&
(&&&*,*(.%&&&&%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%(...,*****,,,,,,*****,*.&@@@&&&&
(&&&&&&&&&&&&&%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%/....................,,.&@@@&&&&
(&&&&&&&&&&&&&&%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%/.,,      . ....      ..&@@@&&&&
(&&&&&&&&&&&&&&%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%(,.........,...,.,,,,,,,@@@@&&&&
(&&&&&&&&&&&&&&%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%%*,,,,,***,,,,,,*****,,,@@@@&&&&
(&&&&&&&&&&&&&&&%&%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%/      .................@@@@&&&&
(&&&&&&&&&&&&&&&%&&%%%%%%%%%%%%%%%%%%%%%@%%%%%%%/    ...................&@@@&&&&
(&&&&&&&&&&&&&&&%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%/   ...  ...,......,,,..&@@@&&&&
(&&&&&&&&&&&&&&&%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%/ .**,**,.*/,,**.,/*,*/,@@@@&&&&
(&&&&&&&&&&&&&&%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%/,,**,//*/**,,//.,/**//*&@@@&&&&
(&&&&&&&&&&&&&&&%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%/............... .,....,@@@@&&&&
(&&&&&&&&&&&&&&&%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%/.............., .,....,@@@@&&&&
(&&&&&&&&&&&&&&&%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%%/ ......,......, .,....,@@@@&&&&
(&&&&&&&&&%%%%%%%%(##%########%%%%%%%%%%@%%%%%%%(,,..,,,,,,,,,,,,,,,,*,,@@@@&&&&
(&&&&&&&@@%%%%%%%%%%%%%#######%@@%%%%%%%@%%%%%%%(... .  .,.............,&@@@&&&&
(&&&&&&&@@%#%%%%%%%%%%########%@@%%%%%%%@%%%%%%%#,..    .,.....,..,,,..,&@@@&&&&
(&&&&&&&@@%%%%%%%%%%%%#####((#&@@%%%%%%%@%%%%%%%#/  .....,,. ..,..,,  ..@@@@&&&&
(&&&&&&&@@@@@@&&%%%%%%%%%&%%&&&@@%%%%%%%@%%%%%%%(........ ,....,..,,,...@@@@&&&&
(&&&&&&&@@@@@&&&&%%%%%%%&&%%&&&@@%%%%%%%@%%%%%%%#**,,////(,[*[***//[**[*&@@@&&&&
(&&&&&&&@@@@@&&&&#(/////&&&%%@@@@%%%%%%%@%%%%%%%((/,**/(###///(//*//(/#(@@@@&&&
(&&&&&&&@@@@@&&%%%&&&&&&%&%%%@@@@%%%%%%%@%%%%%%%#*, .****/[*,**[*/*[*.*/@@@@&&&&
(&&&&&&&@@@@@&&%%%%%%%%%%&%%%&&&&%%%%%%%@%%%%%%%[*. ,,/,/**,..,**/*/,..*@@@@&&&&
(&&&&&&&@@@@@&&%%%%%%%%%%%%%#&&&&%%%%%%%@%%%%%%%#*. ,,*.//*,..,/.*//,..*@@@@&&&&
(&&&&&&&@@@@@%%%%%&&%%%%&%%%#&&&&%%%%%%%@%%%%%%%#*. ,,*,//,. .,/.*//,..*@@@@&&&&
(&&&&&&&@@@@@&%%%%%%%%%%%%%%#&&&&%%%%%%%@%%%%%%%(                       @@@@&&&&
(&&&&&&&@@@@@&%%%%%%%%%%%%%%#&&&&%%%%%%%@%%%%%%%%%%%%&&&&&&&&&&&&&@@@@@@@@@@&&&&
(&&&&&&&@@@@@&%%%%%%%%%%%%%%#&&&&%%%%%%%@%%%%%%%%%%%%&&&&&&&&&&&&&&@@@@@@@@@&&&&
(&&&&&&&@@&&@&&&&&&&&&&&&&&&%&&&&%%%%%%%@%%%%%%%#%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&%&&&&&
 &%@@#&&@&%&&@@@@@@@@@@@@@@@@@@@@@@@@@@@@%##&@@@@@@@@@@@@@@@@@@@@@@&@&%&&@@@&&#%
(@&&#&&&&&&&&&&&&&&&&%%%&%&%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&@#&&&
(&&&###((((////****,,,,,,,..............@.............,,,,,,,*****///(((((##%&&&
(&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%&&&%%%&&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&%&%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%&%&&%&&&&&&&&&&&&
(@&&&&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%&%&&&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%&@%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%&%%&&&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%&%%%%%%%%@%%%%%%%%%%%%%%%%%%&%&%&&&&&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%&&%%%%%@%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&@%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&
(@&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&@%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&
(@&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&@%%%%%%%%%&&%&%%%&&&&&&&&&&&&&&&&&&&&&&&
(@&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&@%%%%%%%%&%%&&%%&&&&&&&&&&&&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&@&&&&&&&%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&@&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&@&&&&%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&@&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
(&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&@&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
(@&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
  @@@@                                                                    /@@@#

  As accurately pictured above, the fragment of reality is an LG smart fridge. Even more accurate pictures can be found in
  the pdf file.

   Perspective:
   ============
   We focus on the cooling of the food, the light in the fridge and the creation of ice.
*)

(*
   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.
   ]

*)

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

(* Domain model *)

Definition Time := R.
(* Time in seconds in reals*)
Definition Temp := R.
(*meaning: temperature in degrees celcius in reals*)
Definition Food := Set.
(* Set of all food *)

(* Constants (including their meaning) *)

Definition MaximumFridgeTemp := 4.
(* The minimum temperature at which the FridgeTempSensor gives off the FridgeTempTooHigh signal *)
Definition MaximumFreezerTemp := -5.
(* The minimum temperature at which the FreezerTempSensor gives off the FreezerTempTooHigh signal *)

(* Functions *)
Variable TimeToTemp: Time -> Temp.

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

Variable FoodPutInFreezer: Food -> Time -> Prop.
(* meaning: food x is put in the freezer at time y *)
(* measurement: look in the freezer *)
Variable FoodPutInFridge: Food -> Time -> Prop.
(* meaning: food x is put in the fridge at time y *)
(* measurement : look in the fridge *)
Variable FoodIsCool: Food -> Time -> Prop.
(* meaning: the temperature of food x is within a margin of error of the MaximumFridgeTemp *)
(* measurement: measure the food's temperature with a thermometer *)
Variable FoodIsFrozen: Food -> Time -> Prop.
(* meaning: the temperature of food x is within a margin of error of the MaximumFreezerTemp *)
(* measurement: measure the food's temperature with a thermometer *)

(* Inputs and outputs of the SmartFridge *)

Variable PowerIn: Time -> Prop.
(* power is supplied to the smartfridge's powersupply *)
Variable FridgeDoorROpen: Time -> Prop.
(* the right fridge door is opened *)
Variable FridgeDoorLOpen: Time -> Prop.
(* the left fridge door is opened *)
Variable FreezerDoorROpen: Time -> Prop.
(* the right freezer door is opened *)
Variable FreezerDoorLOpen: Time -> Prop.
(* the left freezer door is opened *)
Variable ColdWater: Time -> Prop.
(* cold water is dispensed by the dispenser *)
Variable CubedIce: Time -> Prop.
(* ice cubes are dispensed by the dispenser *)
Variable CrushedIce: Time -> Prop.
(* crushed ice is dispensed by the dispenser *)
Variable WaterIn: Time -> Prop.
(* water is supplied to the IceMakerTray and to the WaterCooler *)
Variable FridgeLightOn: Time -> Prop.
(* the light inside the fridge is emitting *)
Variable WaterButtonIn: Time -> Prop.
(* The option to receive cold water is selected on the ControlPanel *)
Variable CrushedIceButtonIn: Time -> Prop.
(* The option to receive crushed ice is selected on the ControlPanel *)
Variable CubedIceButtonIn: Time -> Prop.
(* The option to receive cubed ice is selected on the ControlPanel *)

(* Internal components for the SmartFridge *)

Variable Pow1: Time -> Prop.
(* Power flows between the Powersupply and the FridgePowerHub *)
Variable Pow2: Time -> Prop.
(* Power flows between the Powersupply and the FreezerPowerHub *)

(* Internal IceMaker variables *)

Variable PowIceMakerTray: Time -> Prop.
(* Power flows between the icemaker and the icemaker powerhub *)
Variable ReservoirFull: Time -> Prop.
(* The ice reservoir is full *)
Variable PowIceReservoir: Time -> Prop.
(* Power is flowing between the ice reservoir and the icemaker powerhub *)
Variable CrushIce: Time -> Prop.
(* There is ice that needs to be crushed *)
Variable FillReservoir: Time -> Prop.
(* There is room in the IceReservoir*)
Variable DispenceIce: Time -> Prop.
(* The dispenser requires ice *)
Variable PowDispenser: Time -> Prop.
(* Power is flowing between the IceMakerPowerHub and the Dispenser *)
Variable PowIceCrusher: Time -> Prop.
(* Power flows between the IceCrusher and the IceMakerPowerHub *)
Variable DispenseWater: Time -> Prop.
(* The dispenser requires ice from the WaterCooler *)
Variable PowWaterCooler: Time -> Prop.
(* Power flows between the WaterCooler and the IceMakerPowerHub *)
Variable DispenseCrushedIce: Time -> Prop.
(* The dispenser requires crushed ice *)

(* FridgeDoorL variables *)

Variable PowIceMaker: Time -> Prop.
(* Power flows between the FridgeDoorLPowerHub and IceMaker *)
Variable PowControlPanel: Time -> Prop.
(* power is supplied to the ControlPanelPowerHub if and only if power is supplied to the FridgeDoorLPowerHub *)
Variable ColdWaterSignal: Time -> Prop.
(* WaterButton is being pressed *)
Variable CrushedIceSignal: Time -> Prop.
(* CrushedIceButton is being pressed *)
Variable CubedIceSignal: Time -> Prop.
(* CubedIceButton is being pressed *)
Variable PowFridgeDoorSensorL: Time -> Prop.
(* The Sensor in FridgeDoorL is supplied with power *)

(* Internal components of ControlPanel *)

Variable PowWaterButton: Time -> Prop.
(* power is supplied to the button for selecting water if and only if power is supplied to the ControlPanelPowerHub *)
Variable PowCrushedIceButton: Time -> Prop.
(* power is supplied to the button for selecting crushed ice if and only if power is supplied to the ControlPanelPowerHub *)
Variable PowCubedIceButton: Time -> Prop.
(* power is supplied to the button for selecting cubed ice if and only if power is supplied to the ControlPanelPowerHub *)

(* Internal components of the freezer *)

Variable FreezerDoorSignalL: Time -> Prop.
(* The sensor in the freezerdoor senses that the left door is open *)
Variable FreezerDoorSignalR: Time -> Prop.
(* The sensor in the freezerdoor senses that the right door is open *)
Variable FreezerTempTooHigh: Time -> Prop.
(* The temperature near the freezer sensor is higher than MaximumFreezerTemp *)
Variable PowF: Time -> Prop.
(* Power is flowing between the FreezerPowerHub and FreezerDoorL *)
Variable PowG: Time -> Prop.
(* Power is flowing between the FreezerPowerHub and FreezerCooler *)
Variable PowH: Time -> Prop.
(* Power is flowing between the FreezerPowerHub and FreezerLight *)
Variable PowI: Time -> Prop.
(* Power is flowing between the FreezerPowerHub and FreezerTempSensor *)
Variable PowJ: Time -> Prop.
(* Power is flowing between the FreezerPowerHub and FreezerDoorR *)
Variable FreezerRightTemp: Time -> Prop.
(* The freezer is actively cooling *)
Variable FreezerLightOn: Time -> Prop.
(* The freezer light is turned on *)

(* Internal components of the fridge *)

Variable FridgeDoorSignalL: Time -> Prop.
(* The sensor in the Fridgedoor senses that the left door is open *)
Variable FridgeDoorSignalR: Time -> Prop.
(* The sensor in the Fridgedoor senses that the right door is open *)
Variable FridgeTempTooHigh: Time -> Prop.
(* The temperature near the Fridge sensor is higher than MaximumFridgeTemp *)
Variable PowA: Time -> Prop.
(* Power is flowing between the FridgePowerHub and FridgeDoorL *)
Variable PowB: Time -> Prop.
(* Power is flowing between the FridgePowerHub and FridgeCooler *)
Variable PowC: Time -> Prop.
(* Power is flowing between the FridgePowerHub and FridgeLight *)
Variable PowD: Time -> Prop.
(* Power is flowing between the FridgePowerHub and FridgeTempSensor *)
Variable PowE: Time -> Prop.
(* Power is flowing between the FridgePowerHub and FridgeDoorR *)
Variable FridgeRightTemp: Time -> Prop.
(* The fridge is actively cooling *)

(* Internal components of FreezerDoorL *)

Variable PowFreezerDoorSensorL: Time -> Prop.
(* power is supplied to FreezerDoorSensorL if and only if power is supplied to FreezerDoorLPowerhub *)

(* Internal components of FreezerDoorR*)

Variable PowFreezerDoorSensorR: Time -> Prop.
(* power is supplied to FreezerDoorSensorR if and only if power is supplied to FreezerDoorRPowerHub *)

(* Internal components of FridgeDoorR *)

Variable PowFridgeDoorSensorR: Time -> Prop.
(* power is supplied to FridgeDoorSensorR if and only if power is supplied to FridgeDoorRPowerhub *)

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

(* 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 *)

(*
   [
     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.
   ]
*)

(* Specifications of devices in ControlPanel *)

Definition WaterButton :=
  forall t:Time,
        WaterButtonIn t
      /\
        PowWaterButton t
    <->
      ColdWaterSignal t
.
(* meaning: if and only if the button to select cold water is pressed and power is supplied to the button to select cold water then the signal for cold water is sent *)

Definition CrushedIceButton :=
  forall t:Time,
        CrushedIceButtonIn t
      /\
        PowCrushedIceButton t
    <->
      CrushedIceSignal t
.
(* meaning: if and only if the button to select crushed ice is pressed and power is supplied to the button to select crushed ice then the signal for crushed ice is sent *)

Definition CubedIceButton :=
  forall t:Time,
        CubedIceButtonIn t
      /\
        PowCubedIceButton t
    <->
        CubedIceSignal t
.
(* meaning: if and only if the button to select cubed ice is pressed and power is supplied to the button to select cubed ice then the signal for cubed ice is sent *)

Definition ControlPanelPowerHub :=
  forall t:Time,
      PowControlPanel t
    <->
        PowWaterButton t
      /\
        PowCrushedIceButton t
      /\
        PowCubedIceButton t

.
(* meaning: if and only if power is supplied to the control panel then power is supplied to the water button, crushed ice button, and cubed ice button *)

(* specification of devices in FridgeDoorL *)

Definition FridgeDoorLPowerHub :=
  forall t:Time,
        PowIceMaker t
      /\
        PowControlPanel t
      /\
        PowFridgeDoorSensorL t
    <->
      PowA t
.
(* meaning: power is supplied to PowIceMaker, PowControlPanel and PowFridgeDoorSensorL if and only if power is supplied to PowA *)

Definition FridgeDoorSensorL :=
  forall t:Time,
      FridgeDoorLOpen t
    /\
      PowFridgeDoorSensorL t
  <->
    FridgeDoorSignalL t
.
(* meaning: The signal FridgeDoorSignalL is sent if and only if FridgeDoorLOpen and PowFridgeDoorSensorL are received *)

(* Specifications of devices in IceMaker *)

Definition IceMakerTray :=
  forall t:Time,
        WaterIn t
      /\
        PowIceMakerTray t
      /\
        ~ReservoirFull t
    <->
      FillReservoir t
.
(* meaning: iff there's water pressure on the water line, and power is being supplied to the IceMakerTray, and the IceReservoir  is not full, then the IceReservoir is being filled *)

Definition WaterCooler :=
  forall t:Time,
        PowWaterCooler t
      /\
        WaterIn t
      /\
        ColdWaterSignal t
    <->
      DispenseWater t
.
(* meaning: iff power is being supplied to the WaterCooler, and there is water pressure on the water line, and WaterCooler is receiving a cold water signal from WaterButton, then water flows from the WaterCooler to the Dispenser*)

Definition IceReservoir :=
  forall t:Time,
          PowIceReservoir t
        /\
          CrushedIceSignal t
      <->
        CrushIce t
    /\
          PowIceReservoir t
        /\
          CubedIceSignal t
      <->
        CubedIce t
.
(* meaning: *)

Definition IceMakerPowerHub :=
  forall t:Time,
      PowIceMaker t
    <->
        PowIceMakerTray t
      /\
        PowIceCrusher t
      /\
        PowWaterCooler t
      /\
        PowIceReservoir t
      /\
        PowDispenser t
.
(* iff power is supplied to the IceMakerPowerHub, then power is supplied to the IceMakerTray,
IceCrusher, WaterCooler, IceReservoir and Dispenser *)

Definition IceCrusher :=
  forall t:Time,
        PowIceCrusher t
      /\
        CrushedIce t
    <->
      DispenseCrushedIce t
.
(* iff power is supplied to the IceCrusher and ice is received, then it will send crushed ice to the dispenser  *)

Definition Dispenser :=
  forall t:Time,
      (
            PowDispenser t
          /\
            DispenseWater t
        <->
          ColdWater t
      )
    /\
      (
            PowDispenser t
          /\
            DispenceIce t
        <->
          CubedIce t
      )
    /\
      (
            PowDispenser t
          /\
            DispenseCrushedIce t
        <->
          CrushedIce t
      )
.
(*  iff power is supplied to the Dispenser and water is received from the WaterCooler, then ColdWater is dispensed
and iff power is supplied to the Dispenser and cubed ice is received from the IceReservoir, then CubedIce is dispensed
and iff power is supplied to the Dispenser and crushed ice is received from the IceCrusher, then CrushedIce is dispensed*)

(* Specifications of devices in FridgeDoorR *)

Definition FridgeDoorSensorR :=
  forall t:Time,
        FridgeDoorROpen t
      /\
        PowFridgeDoorSensorR t
    <->
      FridgeDoorSignalR t
.
(* meaning: if and only if power is supplied to the sensor in the right-side fridge door and the right-side fridge door is opened, then a signal is sent from the right-side fridge door *)

Definition FridgeDoorRPowerHub :=
  forall t:Time,
        PowE t
      <->
        PowFridgeDoorSensorR t
.
(* meaning: iff power is being supplied to FridgeDoorRPowerHub, then it is supplying power to FridgeDoorSensorR *)

(* Specifications of devices in FreezerDoorR *)

Definition FreezerDoorSensorR :=
  forall t:Time,
        FreezerDoorROpen t
      /\
        PowFreezerDoorSensorR t
    <->
      FreezerDoorSignalR t
.
(* meaning: if and only if power is supplied to the sensor in the right-side freezer door and the right-side freezer door is opened, then a signal is sent from the right-side freezer door *)

Definition FreezerDoorRPowerHub :=
  forall t:Time,
      PowJ t
    <->
      PowFreezerDoorSensorR t
.
(* meaning: iff power is supplied to PowJ, then power is supplied to the right-side freezer door sensor *)

(* Specifications of devices in FreezerDoorL *)

Definition FreezerDoorSensorL :=
  forall t:Time,
        FreezerDoorLOpen t
      /\
        PowFreezerDoorSensorL t
    <->
      FreezerDoorSignalL t
.
(* meaning: iff power is supplied to the sensor in the left-side freezer door and the left-side freezer door is opened, then a signal is sent from the left-side freezer door *)

Definition FreezerDoorLPowerhub :=
  forall t:Time,
      PowF t
    <->
      PowFreezerDoorSensorL t
.
(* meaning: iff power is supplied to PowF, then power is supplied to the left-side freezer door sensor *)

(* Specifications of devices in SmartFridge *)

Definition Powersupply :=
  forall t:Time,
        PowerIn t
    <->
        Pow1 t
      /\
        Pow2 t
.
(* Meaning: DC Power is supplied to FridgePowerhub and FreezerPowerhub iff AC Power is supplied to the Powersupply *)

(* Specifications of devices in Fridge *)

Definition FridgeLight :=
  forall t:Time,
          FridgeDoorSignalL t
        \/
          FridgeDoorSignalR t
      /\
        PowC t
    <->
      FridgeLightOn t
.
(* Meaning: The light of the fridge is turned on if and only if it receives power through PowC and one or more of its doors is open *)

Definition FridgeCooler :=
  forall t:Time,
        FridgeTempTooHigh t
      /\
        PowB t
    <->
      (
        exists d:Time,
          FridgeRightTemp (t + d)
      )
.
(* Meaning: When both power is delivered through PowB and the FridgeTempTooHigh signal is received, then the cooler will turn on *)

Definition FridgeTempSensor :=
    forall t:Time,
          TimeToTemp( t ) > MaximumFridgeTemp
        /\
          PowD t
      <->
        FridgeTempTooHigh t
.
(* Meaning: For any moment in time if and only if the temperature is greater than the MaximumFridgeTemp and PowD supplies power then the signal FridgeTempTooHigh is send *)

Definition FridgePowerHub :=
  forall t:Time,
      Pow1 t
    <->
        PowA t
      /\
        PowB t
      /\
        PowC t
      /\
        PowD t
      /\
        PowE t
.
(* Meaning: if and only if power is supplied to Pow1, then power is supplied to PowA, PowB, PowC, PowD and PowE *)

Definition FridgeStorage :=
  forall t:Time,
    forall f:Food,
          FoodPutInFridge f t
        /\
          FridgeRightTemp t
      <->
        (
          exists d:Time,
            FoodIsCool f (t+d)
        )

.
(* Iff a certain food f is in the fridge and the fridge cooler has been turned on at time t, then the food wil be cool at time t+d *)

(* Specifications of devices in Freezer *)

Definition FreezerPowerHub :=
  forall t:Time,
      Pow2 t
    <->
        PowF t
      /\
        PowG t
      /\
        PowH t
      /\
        PowI t
      /\
        PowJ t
.
(* meaning: iff power is supplied to Pow2, then power is supplied to PowF, PowG, PowH, PowI, and PowJ *)

Definition FreezerCooler :=
  forall t:Time,
          PowG t
        /\
          FreezerTempTooHigh t
      <->
        (
          exists d:Time,
            FreezerRightTemp ( t + d )
        )
.
(* Iff power is supplied to PowG and the temperature inside the freezer is too high, then the cooler inside the freezer will turn on *)

Definition FreezerTempSensor :=
  forall t:Time,
        TimeToTemp t > MaximumFreezerTemp
      /\
        PowI t
    <->
      FreezerTempTooHigh t
.
(* Iff the temperature at time t is higher than the maximum allowed freezer temperature and power is supplied to PowI, then a signal is sent that the freezer temperature is too high *)

Definition FreezerLight :=
  forall t:Time,
        PowH t
      /\
        FreezerDoorSignalL t
    <->
      FreezerLightOn t
.
(* Iff power is supplied to PowH at time t and there is a signal coming from the left-side freezer door at time t, then the freezer light will be turned on at time t *)

Definition FreezerStorage :=
  forall t:Time,
      forall f:Food,
          FoodPutInFreezer f t
        /\
          FreezerRightTemp t
      <->
        (
          exists d:Time,
            FoodIsFrozen f ( t + d )
        )
.
(* For any moment in time if and only if food is in the freezer and the freezer is at the right temp, then the food is frozen at some later moment of time *)

Definition FridgeDoorR :=
    forall t: Time,
            PowE t
          /\
            FridgeDoorROpen t
      <->
          FridgeDoorSignalR t
.
(* For any moment in time iff power is supplied to PowE and the right-side fridge door is opened at time t, then a signal is sent from the right-side
fridge door *)

Definition FreezerDoorR :=
  forall t:Time,
        PowJ t
      /\
        FreezerDoorROpen t
    <->
      FreezerDoorSignalR t
.
(* For any moment in time iff power is supplied to PowJ and the right-side freezer door is opened at time t, then a signal is sent from the right-side
freezer door *)

Definition FreezerDoorL :=
  forall t:Time,
          FreezerDoorLOpen t
        /\
          PowF t
      <->
        FreezerDoorSignalL t
.
(* For any moment in time iff power is supplied to PowF and the left-side freezer door is opened at time t, then a signal is sent from the left-side
freezer door *)

Definition ControlPanel :=
  forall t:Time,
      (
            PowControlPanel t
          /\
            WaterButtonIn t
        <->
          ColdWaterSignal t
      )
    /\
      (
            PowControlPanel t
          /\
            CrushedIceButtonIn t
        <->
          CrushedIceSignal t
      )
    /\
      (
            PowControlPanel t
          /\
            CubedIceButtonIn t
        <->
          CubedIceSignal t
      )
.
(* For any moment in time, iff power is supplied to PowControlPanel and the button to select water is pressed, then a cold-water signal is sent, and
iff power is supplied to PowControlPanel and the button to select crushed ice is pressed, then a crushed-ice signal is sent, and
iff power is supplied to PowControlPanel and the button to select cubed ice is pressed, then a cubed-ice signal is sent *)

Definition FridgeDoorL :=
  forall t:Time,
        (
            FridgeDoorLOpen t
          /\
            PowA t
        <->
          FridgeDoorSignalL t
        )
    /\
      (
            PowA t
          /\
            WaterIn t
          /\
            WaterButtonIn t
        <->
          ColdWater t
      )
    /\
      (
            PowA t
          /\
            WaterIn t
          /\
            CrushedIceButtonIn t
        <->
          CrushedIce t
      )
    /\
      (
            PowA t
          /\
            WaterIn t
          /\
            CubedIceButtonIn t
        <->
          CubedIce t
      )
.
(* For any moment in time, if and only if power is supplied to PowA and the fridgedoor is open then, FridgeDoorSignalL is send.
And for any moment in time, if and only if power is supplied to PowA and waterpressure is supplied to WaterIn and the WaterButton is pressed then, ColdWater is delivered to the dispenser.
And for any moment in time, if and only if power is supplied to PowA and waterpressure is supplied to WaterIn and the CrushedIceButton is pressed then, crushed ice is dispensed.
And for any moment in time, if and only if power is supplied to PowA and waterpressure is supplied to WaterIn and the CubedIceButton is pressed then, cubed ice is dispensed. *)

Definition IceMaker :=
  forall t:Time,
      (
            PowIceMaker t
          /\
            WaterIn t
          /\
            ColdWaterSignal t
        <->
          ColdWater t
      )
    /\
      (
            PowIceMaker t
          /\
            WaterIn t
          /\
            CrushedIceSignal t
        <->
          CrushedIce t
      )
    /\
      (
            PowIceMaker t
          /\
            WaterIn t
          /\
            CubedIceSignal t
        <->
          CubedIce t
      )
.

(*
   iff power is being supplied to the IceMaker and there's water pressure and there's a signal coming from the WaterButton
   then cold water is flowing out of the dispenser.

   iff power is being supplied to the IceMaker and there's water pressure and there's a signal coming frm the CrushedIceButton,
   then crushed ice is being dispensed.

   iff power is being supplied to the IceMaker and there's water pressure and there's a signal coming from the CubedIceButton,
   then CubedIce is being dispensed.
*)

Definition Freezer :=
  forall t: Time,
    forall f:Food,
        (
              Pow2 t
            /\
              FoodPutInFreezer f t
          <->
              (
                exists d: Time,
                  FoodIsFrozen f (d+t)
              )
        )
      /\
        (
              Pow2 t
            /\
              (
                  FreezerDoorLOpen t
                \/
                  FreezerDoorROpen t
              )
          <->
            FreezerLightOn t
        )
.
(* For any moment in time, it holds that iff power is supplied to the freezer
   and food is put in the freezer, then at a later moment in time, that food wil be frozen.
   And iff power is supplied to the freezer and either the right or the left door is open,
   then the freezer light will be on.*)

Definition Fridge :=
  forall t:Time,
    forall f:Food,
        (
              Pow1 t
            /\
                FridgeDoorROpen t
              \/
                FridgeDoorLOpen t
          <->
            FridgeLightOn t
        )
      /\
        (
              Pow1 t
            /\
              FoodPutInFreezer f t
          <->
            (
              exists d:Time,
                FoodIsCool f (t+d)
            )
        )
      /\
        (
              Pow1 t
            /\
              WaterButtonIn t
            /\
              WaterIn t
          <->
            ColdWater t
        )
      /\
        (
              Pow1 t
            /\
              CrushedIceButtonIn t
            /\
              WaterIn t
          <->
            CrushedIce t
        )
      /\
        (
              Pow1 t
            /\
              CubedIceButtonIn t
            /\
              WaterIn t
          <->
            CubedIce t
        )
.
(* for any moment in time,
iff power is supplied to Pow1 at time t and either the right-side or the left-side fridge door is opened at time t, then the fridge light is on at time t, and
iff power is supplied to Pow1 at time t and food has been put in the freezer at time t, then the food will be cool at time (t+d), and
iff power is supplied to Pow1 at time t, and the button to select water is pressed at time t, and the waterline is connected at time t, then cold water is dispensed at time t, and
iff power is supplied to Pow1 at time t, and the button to select crushed ice is pressed at time t, and the waterline is connected at time t, then crushed ice is dispensed at time t, and
iff power is supplied to Pow1 at time t, and the button to select cubed ice is pressed at time t, and the waterline is connected at time t, then cubed ice is dispensed at time t
 *)

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

(* Specification of the overall system *)

(*
   [
     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.
   ]
*)

(*
Outputs:
    ColdWater
      WaterIn
      PowerIn
      WaterButtonIn
    CubedIce
      WaterIn
      PowerIn
      CubedIceButtonIn
    CrushedIce
      WaterIn
      PowerIn
      CrushedIceButtonIn
    FoodIsFrozen
      PowerIn
      FoodPutInFreezer
    FoodIsCool
      PowerIn
      FoodPutInFridge
    FreezerLightOn
      PowerIn
      FreezerDoorROpen \/ FreezerDoorLOpen
    FridgeLightOn
      PowerIn
      FridgeDoorROpen \/ FridgeDoorLOpen

Inputs:
    WaterIn
    PowerIn
    FreezerDoorROpen
    FreezerDoorLOpen
    WaterButtonIn
    CrushedIceButtonIn
    CubedIceButtonIn
    FridgeDoorLOpen
    FridgeDoorROpen
    FoodPutInFridge
    FoodPutInFreezer
*)

Definition SmartFridge :=
  forall t:Time,
      (
            WaterIn t
          /\
            PowerIn t
          /\
            WaterButtonIn t
        ->
          ColdWater t
      )
    /\
      (
            WaterIn t
          /\
            PowerIn t
          /\
            CubedIceButtonIn t
        ->
          CubedIce t
      )
    /\
      (
            WaterIn t
          /\
            PowerIn t
          /\
            CrushedIceButtonIn t
        ->
          CrushedIce t
      )
    /\
      (
        forall f:Food,
          exists d:Time,
                PowerIn t
              /\
                FoodPutInFreezer f t
            ->
              FoodIsFrozen f ( t + d )
      )
    /\
      (
        forall f:Food,
          exists d:Time,
                PowerIn t
              /\
                FoodPutInFridge f t
            ->
              FoodIsCool f ( t + d )
      )
    /\
      (
            (
                FreezerDoorROpen t
              \/
                FreezerDoorLOpen t
            )
          /\
            PowerIn t
        ->
          FreezerLightOn t
      )
    /\
      (
            (
                FridgeDoorROpen t
              \/
                FridgeDoorLOpen t
            )
          /\
            PowerIn t
        ->
          FridgeLightOn t
      )
.

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

(* 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.
   ]
*)

(*

Theorem CorFreezerDoorR :
      FreezerDoorSensorR
    /\
      FreezerDoorRPowerHub
  ->
    FreezerDoorR
.

Theorem CorFreezerDoorL :
      FreezerDoorLPowerhub
    /\
      FreezerDoorSensorL
  ->
    FreezerDoorL
.

Theorem CorFridgeDoorR :
      FridgeDoorSensorR
    /\
      FridgeDoorRPowerHub
  ->
    FridgeDoorR
.

Theorem CorFridgeDoorL :
      FridgeDoorLPowerHub
    /\
      FridgeDoorSensorL
    /\
      ControlPanel
    /\
      IceMaker
  ->
    FridgeDoorL
.

Theorem CorControlpanel :
      WaterButton
    /\
      CrushedIceButton
    /\
      CubedIceButton
    /\
      ControlPanelPowerHub
  ->
    ControlPanel
.

Theorem CorFridge :
      FridgeStorage
    /\
      FridgeLight
    /\
      FridgeDoorL
    /\
      FridgeCooler
    /\
      FridgeTempSensor
    /\
      FridgeDoorR
    /\
      FridgePowerHub
  ->
    Fridge
.

Theorem CorFreezer :
      FreezerTempSensor
    /\
      FreezerPowerHub
    /\
      FreezerCooler
    /\
      FreezerDoorL
    /\
      FreezerDoorR
    /\
      FreezerLight
    /\
      FreezerStorage
  ->
    Freezer
.

*)

(* 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 :
        Freezer
      /\
        Powersupply
      /\
         Fridge
    ->
      SmartFridge
.

Proof.
unfold WaterButton.
unfold CrushedIceButton.
unfold CubedIceButton.
unfold ControlPanelPowerHub.
unfold FridgeDoorLPowerHub.
unfold FridgeDoorSensorL.
unfold IceMakerTray.
unfold WaterCooler.
unfold IceReservoir.
unfold IceMakerPowerHub.
unfold IceCrusher.
unfold Dispenser.
unfold FridgeDoorSenso rR.
unfold FridgeDoorRPowerHub.
unfold FreezerDoorSensorR.
unfold FreezerDoorRPowerHub.
unfold FreezerDoorSensorL.
unfold FreezerDoorLPowerhub.
unfold Powersupply.
unfold FridgeLight.
unfold FridgeCooler.
unfold FridgeTempSensor.
unfold FridgePowerHub.
unfold FridgeStorage.
unfold FreezerPowerHub.
unfold FreezerCooler.
unfold FreezerTempSensor.
unfold FreezerLight.
unfold FreezerStorage.
tauto.
Qed.
*)

(*

*)


Require Import compcert.common.Memory.
Require Import VST.msl.Coqlib2.
Require Import VST.msl.eq_dec.
Require Import VST.msl.seplog.
Require Import VST.msl.ageable.
Require Import VST.msl.age_to.
Require Import VST.veric.coqlib4.
Require Import VST.veric.compcert_rmaps.

Set Bullet Behavior "Strict Subproofs".

Lemma pred_hered {A} {_ : ageable A} (P : pred A) : hereditary age (app_pred P).
Proof.
  destruct P; auto.
Qed.

Lemma hereditary_necR {phi phi' : rmap} {P} :
  necR phi phi' ->
  hereditary age P ->
  P phi -> P phi'.
Proof.
  intros N H; induction N; auto.
  apply H; auto.
Qed.

Lemma anti_hereditary_necR {phi phi' : rmap} {P} :
  necR phi phi' ->
  hereditary (fun x y => age y x) P ->
  P phi' -> P phi.
Proof.
  intros N H; induction N; auto.
  apply H; auto.
Qed.

Lemma app_pred_age {R} {phi phi' : rmap} :
  age phi phi' ->
  app_pred R phi ->
  app_pred R phi'.
Proof.
  destruct R as [R HR]; simpl.
  apply HR.
Qed.

Lemma age_yes_sat {Phi Phi' phi phi' l z sh sh'} (R : pred rmap) :
  level Phi = level phi ->
  age Phi Phi' ->
  age phi phi' ->
  app_pred R phi ->
  Phi  @ l = YES sh sh' (LK z) (SomeP rmaps.Mpred (fun _ => R)) ->
  app_pred (approx (S (level phi')) R) phi' /\
  Phi' @ l = YES sh sh' (LK z) (SomeP rmaps.Mpred (fun _ => approx (level Phi') R)).
Proof.
  intros L A Au SAT AT.
  pose proof (app_pred_age Au SAT) as SAT'.
  split.
  - split.
    + apply age_level in A; apply age_level in Au. omega.
    + apply SAT'.
  - apply (necR_YES _ Phi') in AT.
    + rewrite AT.
      reflexivity.
    + constructor. assumption.
Qed.

Lemma age_resource_at {phi phi' loc} :
  age phi phi' ->
  phi' @ loc = resource_fmap (approx (level phi')) (approx (level phi')) (phi @ loc).
Proof.
  intros A.
  rewrite <- (age1_resource_at _ _ A loc (phi @ loc)).
  - reflexivity.
  - rewrite resource_at_approx. reflexivity.
Qed.

Lemma age_to_resource_at phi n loc : age_to n phi @ loc = resource_fmap (approx n) (approx n) (phi @ loc).
Proof.
  assert (D : (n <= level phi \/ n >= level phi)%nat) by omega.
  destruct D as [D | D]; swap 1 2.
  - rewrite age_to_ge; auto.
    rewrite <-resource_at_approx.
    match goal with
      |- _ = ?map ?f1 ?f2 (?map ?g1 ?g2 ?r) => transitivity (map (f1 oo g1) (g2 oo f2) r)
    end; swap 1 2.
    + destruct (phi @ loc); unfold "oo"; simpl; auto.
      * destruct p; auto.
        rewrite preds_fmap_fmap; auto.
      * destruct p; auto.
        rewrite preds_fmap_fmap; auto.
    + f_equal. rewrite approx'_oo_approx; auto.
      rewrite approx_oo_approx'; auto.
  - generalize (age_to_ageN n phi).
    generalize (age_to n phi); intros phi'.
    replace n with (level phi - (level phi - n))%nat at 2 3 by omega.
    generalize (level phi - n)%nat; intros k. clear n D.
    revert phi phi'; induction k; intros phi phi'.
    + unfold ageN in *; simpl.
      injection 1 as <-.
      simpl; replace (level phi - 0)%nat with (level phi) by omega.
      symmetry.
      apply resource_at_approx.
    + change (ageN (S k) phi) with
      (match age1 phi with Some w' => ageN k w' | None => None end).
      destruct (age1 phi) as [o|] eqn:Eo. 2:congruence.
      intros A; specialize (IHk _ _ A).
      rewrite IHk.
      pose proof age_resource_at Eo (loc := loc) as R.
      rewrite R.
      clear A R.
      rewrite (age_level _ _ Eo).
      simpl.
      match goal with
        |- ?map ?f1 ?f2 (?map ?g1 ?g2 ?r) = _ => transitivity (map (f1 oo g1) (g2 oo f2) r)
      end.
      * destruct (phi @ loc); unfold "oo"; simpl; auto.
        -- destruct p; auto.
           rewrite preds_fmap_fmap; auto.
        -- destruct p; auto.
           rewrite preds_fmap_fmap; auto.
      * f_equal. rewrite approx_oo_approx'; auto.
        omega.
        rewrite approx'_oo_approx; auto.
        omega.
Qed.


Require Import VST.floyd.proofauto.
Local Open Scope logic.
Require Import List. Import ListNotations.
Require Import ZArith.
Local Open Scope Z.
Require Import tweetnacl20140427.tweetNaclBase.
Require Import tweetnacl20140427.Salsa20.
Require Import tweetnacl20140427.verif_salsa_base.
Require Import tweetnacl20140427.tweetnaclVerifiableC.
Require Import tweetnacl20140427.Snuffle.
Require Import tweetnacl20140427.spec_salsa.

Require Import tweetnacl20140427.verif_fcore_jbody.

Opaque Snuffle.Snuffle.

Lemma SnuffleS i l: Snuffle (S i) l = bind (Snuffle i l) (Snuffle 1). reflexivity. Qed.

Fixpoint WcontI (xs: list int) (j:nat) (l:list val):Prop :=
   match j with O => Zlength l = 16
   | (S n) => Zlength l = 16 /\
              exists t0 t1 t2 t3,
              Znth ((5 * (Z.of_nat n) + 4 * 0) mod 16) (map Vint xs) = Vint t0 /\
              Znth ((5 * (Z.of_nat n) + 4 * 1) mod 16) (map Vint xs) = Vint t1 /\
              Znth ((5 * (Z.of_nat n) + 4 * 2) mod 16) (map Vint xs) = Vint t2 /\
              Znth ((5 * (Z.of_nat n) + 4 * 3) mod 16) (map Vint xs) = Vint t3 /\
              exists wl, WcontI xs n wl /\
                match Wcopyspec t0 t1 t2 t3 with
                 (s0,s1,s2,s3) => wlistJ' wl (Z.of_nat n) s0 s1 s2 s3 l
                end
  end.

Lemma WcontI_Zlength xs j l: WcontI xs j l -> Zlength l=16.
Proof. intros. destruct j; eapply H. Qed.

Lemma WWI r w (W: WcontI r 4 w) (R:Zlength r = 16):
      exists wi, w=map Vint wi /\ snuffleRound r = Some wi.
Proof.
apply listD16 in R.
destruct R as [x0 [x1 [x2 [x3 [x4 [x5 [x6 [x7
              [x8 [x9 [x10 [x11 [x12 [x13 [x14 [x15 XX]]]]]]]]]]]]]]]]. subst r.
destruct W as [HW H1].
destruct H1 as [t0 [t1 [t2 [t3 [T0 [T1 [T2 [T3 [w1 [[_ H1] W1]]]]]]]]]]. simpl in T0, T1, T2, T3.
rewrite Z.mod_small in T0. 2: lia.
rewrite Zmod_eq in T1. 2: lia.
rewrite Zmod_eq in T2. 2: lia.
rewrite Zmod_eq in T3. 2: lia. simpl in T0, T1, T2, T3.
destruct H1 as [t4 [t5 [t6 [t7 [T4 [T5 [T6 [T7 [w2 [[_ H1] W2]]]]]]]]]]. simpl in T4, T5, T6, T7.
rewrite Zmod_eq in T4. 2: lia.
rewrite Zmod_eq in T5. 2: lia.
rewrite Zmod_eq in T6. 2: lia.
rewrite Zmod_eq in T7. 2: lia. simpl in T4, T5, T6, T7.
destruct H1 as [t8 [t9 [t10 [t11 [T8 [T9 [T10 [T11 [w3 [[_ H1] W3]]]]]]]]]]. simpl in T8, T9, T10, T11.
rewrite Z.mod_small in T8. 2: lia.
rewrite Z.mod_small in T9. 2: lia.
rewrite Zmod_eq in T10. 2: lia.
rewrite Zmod_eq in T11. 2: lia. simpl in T8, T9, T10, T11.
destruct H1 as [t12 [t13 [t14 [t15 [T12 [T13 [T14 [T15 [w4 [L4 W4]]]]]]]]]]. simpl in T12, T13, T14, T15.
rewrite Z.mod_small in T12. 2: lia.
rewrite Z.mod_small in T13. 2: lia.
rewrite Z.mod_small in T14. 2: lia.
rewrite Z.mod_small in T15. 2: lia.
unfold Znth in *. simpl in  T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15.
symmetry in T0; inv T0. symmetry in T1; inv T1. symmetry in T2; inv T2. symmetry in T3; inv T3.
symmetry in T4; inv T4. symmetry in T5; inv T5. symmetry in T6; inv T6. symmetry in T7; inv T7.
symmetry in T8; inv T8. symmetry in T9; inv T9. symmetry in T10; inv T10. symmetry in T11; inv T11.
symmetry in T12; inv T12. symmetry in T13; inv T13. symmetry in T14; inv T14. symmetry in T15; inv T15.
red in L4.
simpl in W4.
remember (Int.xor x4 (Int.rol (Int.add x0 x12) (Int.repr 7))) as z1.
remember (Int.xor x8 (Int.rol (Int.add z1 x0) (Int.repr 9))) as z2.
remember (Int.xor x12 (Int.rol (Int.add z2 z1) (Int.repr 13))) as z3.
remember (Int.xor x0 (Int.rol (Int.add z3 z2) (Int.repr 18))) as z0.
apply listD16 in L4.
destruct L4 as [y0 [y1 [y2 [y3 [y4 [y5 [y6 [y7
               [y8 [y9 [y10 [y11 [y12 [y13 [y14 [y15 XX]]]]]]]]]]]]]]]]. subst w4.
destruct W4 as [_ W4]; simpl in W4.
(*rewrite Z.mod_small in W4. 2: lia.
rewrite Z.mod_small in W4. 2: lia.
rewrite Z.mod_small in W4. 2: lia.
rewrite Z.mod_small in W4. 2: lia.*)
unfold upd_Znth, sublist in W4; simpl in W4. subst w3.
simpl in W3.
remember (Int.xor x9 (Int.rol (Int.add x5 x1) (Int.repr 7))) as z6.
remember (Int.xor x13 (Int.rol (Int.add z6 x5) (Int.repr 9))) as z7.
remember (Int.xor x1 (Int.rol (Int.add z7 z6) (Int.repr 13))) as z4.
remember (Int.xor x5 (Int.rol (Int.add z4 z7) (Int.repr 18))) as z5.
destruct W3 as [_ W3]; simpl in W3.
unfold upd_Znth, sublist in W3; simpl in W3. subst w2.
destruct W2 as [_ W2]. simpl in W2.
remember (Int.xor x14 (Int.rol (Int.add x10 x6) (Int.repr 7))) as z11.
remember (Int.xor x2 (Int.rol (Int.add z11 x10) (Int.repr 9))) as z8.
remember (Int.xor x6 (Int.rol (Int.add z8 z11) (Int.repr 13))) as z9.
remember (Int.xor x10 (Int.rol (Int.add z9 z8) (Int.repr 18))) as z10.
unfold upd_Znth, sublist in W2; simpl in W2. subst w1.
destruct W1 as [_ W1]; simpl in W1.
remember (Int.xor x3 (Int.rol (Int.add x15 x11) (Int.repr 7))) as z12.
remember (Int.xor x7 (Int.rol (Int.add z12 x15) (Int.repr 9))) as z13.
remember (Int.xor x11 (Int.rol (Int.add z13 z12) (Int.repr 13))) as z14.
remember (Int.xor x15 (Int.rol (Int.add z14 z13) (Int.repr 18))) as z15.
unfold upd_Znth, sublist in W1; simpl in W1. subst w. clear HW.
exists [z0; z1; z2; z3; z4; z5; z6; z7;
        z8; z9; z10; z11; z12; z13; z14; z15].
split. reflexivity.
rewrite Int.add_commut in Heqz0, Heqz2, Heqz3, Heqz4, Heqz5, Heqz7, Heqz8,
  Heqz9, Heqz10, Heqz13, Heqz14, Heqz15.
subst z0 z1 z2 z3 z4 z5 z6 z7 z8 z9 z10 z11 z12 z13 z14 z15. reflexivity.
Qed.

Definition array_copy3_statement:=
Sfor (Sset _m (Econst_int (Int.repr 0) tint))
     (Ebinop Olt (Etempvar _m tint) (Econst_int (Int.repr 16) tint) tint)
     (Ssequence
        (Sset _t'19
           (Ederef
              (Ebinop Oadd (Evar _w (tarray tuint 16)) (Etempvar _m tint)
                 (tptr tuint)) tuint))
        (Sassign
           (Ederef
              (Ebinop Oadd (Evar _x (tarray tuint 16)) (Etempvar _m tint)
                 (tptr tuint)) tuint) (Etempvar _t'19 tuint)))
     (Sset _m
        (Ebinop Oadd (Etempvar _m tint) (Econst_int (Int.repr 1) tint) tint)).

Lemma array_copy3 Espec:
forall FR c k h nonce out
       i w x y t (xlist wlist:list val)
       (WZ: forall m, 0<=m<16 -> exists mval, Znth m wlist =Vint mval),
@semax CompSpecs Espec
  (func_tycontext f_core SalsaVarSpecs SalsaFunSpecs nil)
  (PROP  ()
   LOCAL  (temp _j (Vint (Int.repr 4)); temp _i (Vint (Int.repr i)); lvar _t (tarray tuint 4) t;
   lvar _y (tarray tuint 16) y; lvar _x (tarray tuint 16) x;
   lvar _w (tarray tuint 16) w; temp _out out; temp _in nonce; temp _k k; temp _c c;
   temp _h (Vint (Int.repr h)))
   SEP  (FR; data_at Tsh (tarray tuint 16) wlist w;
         data_at Tsh (tarray tuint 16) xlist x))
 array_copy3_statement
  (normal_ret_assert
  (PROP  ()
   LOCAL  (temp _j (Vint (Int.repr 4)); temp _i (Vint (Int.repr i)); lvar _t (tarray tuint 4) t;
      lvar _y (tarray tuint 16) y; lvar _x (tarray tuint 16) x;
      lvar _w (tarray tuint 16) w; temp _out out; temp _in nonce; temp _k k; temp _c c;
      temp _h (Vint (Int.repr h)))
   SEP  (FR; data_at Tsh (tarray tuint 16) wlist w;
         data_at Tsh (tarray tuint 16) wlist x))).
Proof. intros. abbreviate_semax.
Time assert_PROP (Zlength wlist = 16 /\ Zlength xlist = 16) as WXL by entailer!. (*1.4 versus 5.4*)
destruct WXL as [WL XL].
unfold array_copy3_statement.
Time forward_for_simple_bound 16 (EX m:Z,
  (PROP  ()
   LOCAL  (temp _j (Vint (Int.repr 4)); temp _i (Vint (Int.repr i)); lvar _t (tarray tuint 4) t;
   lvar _y (tarray tuint 16) y; lvar _x (tarray tuint 16) x;
   lvar _w (tarray tuint 16) w; temp _out out; temp _in nonce; temp _k k; temp _c c;
   temp _h (Vint (Int.repr h)))
   SEP  (FR; data_at Tsh (tarray tuint 16) wlist w;
         EX mlist:_, !!(forall mm, 0<=mm<m -> Znth mm mlist = Znth mm wlist)
                && data_at Tsh (tarray tuint 16) mlist x))).
  (*1.2 versus 2.7*)
{ Exists xlist. Time entailer!. (*2.6 versus 6.7*) }
{ Intros mlist. rename H into M. rename i0 into m. rename H0 into HM.
  destruct (WZ _ M) as [mval MVAL].
  freeze [0;2] FR1.
  Time forward; change (@Znth val Vundef) with (@Znth val _); rewrite MVAL. (*3.5 versus 8.7*)
  Time solve[entailer!]. (*0.9 versus 3.3*)
  thaw FR1.
  Time assert_PROP (Zlength mlist = 16) as ML by entailer!. (*1.2 versus 3.5*)
  Time forward. (*3.2 versus 9*)
   { Exists (upd_Znth m mlist (Vint mval)).
     Time entailer!. (*2.8 versus 5.6*)
     intros mm ?.
     destruct (zeq mm m); subst.
     + rewrite MVAL, upd_Znth_same; trivial. lia.
     + rewrite <- HM. 2: lia.
       apply upd_Znth_diff; trivial; lia. }
}
{ Time entailer!. (*1.8 versus 4.3*)
  Intros mlist.
  assert_PROP (Zlength mlist = 16) as ML by entailer.
  apply derives_refl'. f_equal.
  eapply Znth_extensional. lia.
  intros kk K. apply H2. lia. }
Time Qed. (*June 4th, 2017 (laptop): 1s*)

Definition f_core_loop3_statement :=
Sfor (Sset _i (Econst_int (Int.repr 0) tint))
     (Ebinop Olt (Etempvar _i tint) (Econst_int (Int.repr 20) tint) tint)
     (Ssequence
        (Sfor (Sset _j (Econst_int (Int.repr 0) tint))
           (Ebinop Olt (Etempvar _j tint) (Econst_int (Int.repr 4) tint) tint)
           (Ssequence
              (Sfor (Sset _m (Econst_int (Int.repr 0) tint))
                 (Ebinop Olt (Etempvar _m tint)
                    (Econst_int (Int.repr 4) tint) tint)
                 (Ssequence
                    (Sset _t'33
                       (Ederef
                          (Ebinop Oadd (Evar _x (tarray tuint 16))
                             (Ebinop Omod
                                (Ebinop Oadd
                                   (Ebinop Omul
                                      (Econst_int (Int.repr 5) tint)
                                      (Etempvar _j tint) tint)
                                   (Ebinop Omul
                                      (Econst_int (Int.repr 4) tint)
                                      (Etempvar _m tint) tint) tint)
                                (Econst_int (Int.repr 16) tint) tint)
                             (tptr tuint)) tuint))
                    (Sassign
                       (Ederef
                          (Ebinop Oadd (Evar _t (tarray tuint 4))
                             (Etempvar _m tint) (tptr tuint)) tuint)
                       (Etempvar _t'33 tuint)))
                 (Sset _m
                    (Ebinop Oadd (Etempvar _m tint)
                       (Econst_int (Int.repr 1) tint) tint)))
              (Ssequence
                 (Ssequence
                    (Ssequence
                       (Sset _t'31
                          (Ederef
                             (Ebinop Oadd (Evar _t (tarray tuint 4))
                                (Econst_int (Int.repr 0) tint) (tptr tuint))
                             tuint))
                       (Ssequence
                          (Sset _t'32
                             (Ederef
                                (Ebinop Oadd (Evar _t (tarray tuint 4))
                                   (Econst_int (Int.repr 3) tint)
                                   (tptr tuint)) tuint))
                          (Scall (Some _t'5)
                             (Evar _L32
                                (Tfunction (Tcons tuint (Tcons tint Tnil))
                                   tuint cc_default))
                             [Ebinop Oadd (Etempvar _t'31 tuint)
                                (Etempvar _t'32 tuint) tuint;
                             Econst_int (Int.repr 7) tint])))
                    (Ssequence
                       (Sset _t'30
                          (Ederef
                             (Ebinop Oadd (Evar _t (tarray tuint 4))
                                (Econst_int (Int.repr 1) tint) (tptr tuint))
                             tuint))
                       (Sassign
                          (Ederef
                             (Ebinop Oadd (Evar _t (tarray tuint 4))
                                (Econst_int (Int.repr 1) tint) (tptr tuint))
                             tuint)
                          (Ebinop Oxor (Etempvar _t'30 tuint)
                             (Etempvar _t'5 tuint) tuint))))
                 (Ssequence
                    (Ssequence
                       (Ssequence
                          (Sset _t'28
                             (Ederef
                                (Ebinop Oadd (Evar _t (tarray tuint 4))
                                   (Econst_int (Int.repr 1) tint)
                                   (tptr tuint)) tuint))
                          (Ssequence
                             (Sset _t'29
                                (Ederef
                                   (Ebinop Oadd (Evar _t (tarray tuint 4))
                                      (Econst_int (Int.repr 0) tint)
                                      (tptr tuint)) tuint))
                             (Scall (Some _t'6)
                                (Evar _L32
                                   (Tfunction (Tcons tuint (Tcons tint Tnil))
                                      tuint cc_default))
                                [Ebinop Oadd (Etempvar _t'28 tuint)
                                   (Etempvar _t'29 tuint) tuint;
                                Econst_int (Int.repr 9) tint])))
                       (Ssequence
                          (Sset _t'27
                             (Ederef
                                (Ebinop Oadd (Evar _t (tarray tuint 4))
                                   (Econst_int (Int.repr 2) tint)
                                   (tptr tuint)) tuint))
                          (Sassign
                             (Ederef
                                (Ebinop Oadd (Evar _t (tarray tuint 4))
                                   (Econst_int (Int.repr 2) tint)
                                   (tptr tuint)) tuint)
                             (Ebinop Oxor (Etempvar _t'27 tuint)
                                (Etempvar _t'6 tuint) tuint))))
                    (Ssequence
                       (Ssequence
                          (Ssequence
                             (Sset _t'25
                                (Ederef
                                   (Ebinop Oadd (Evar _t (tarray tuint 4))
                                      (Econst_int (Int.repr 2) tint)
                                      (tptr tuint)) tuint))
                             (Ssequence
                                (Sset _t'26
                                   (Ederef
                                      (Ebinop Oadd (Evar _t (tarray tuint 4))
                                         (Econst_int (Int.repr 1) tint)
                                         (tptr tuint)) tuint))
                                (Scall (Some _t'7)
                                   (Evar _L32
                                      (Tfunction
                                         (Tcons tuint (Tcons tint Tnil))
                                         tuint cc_default))
                                   [Ebinop Oadd (Etempvar _t'25 tuint)
                                      (Etempvar _t'26 tuint) tuint;
                                   Econst_int (Int.repr 13) tint])))
                          (Ssequence
                             (Sset _t'24
                                (Ederef
                                   (Ebinop Oadd (Evar _t (tarray tuint 4))
                                      (Econst_int (Int.repr 3) tint)
                                      (tptr tuint)) tuint))
                             (Sassign
                                (Ederef
                                   (Ebinop Oadd (Evar _t (tarray tuint 4))
                                      (Econst_int (Int.repr 3) tint)
                                      (tptr tuint)) tuint)
                                (Ebinop Oxor (Etempvar _t'24 tuint)
                                   (Etempvar _t'7 tuint) tuint))))
                       (Ssequence
                          (Ssequence
                             (Ssequence
                                (Sset _t'22
                                   (Ederef
                                      (Ebinop Oadd (Evar _t (tarray tuint 4))
                                         (Econst_int (Int.repr 3) tint)
                                         (tptr tuint)) tuint))
                                (Ssequence
                                   (Sset _t'23
                                      (Ederef
                                         (Ebinop Oadd
                                            (Evar _t (tarray tuint 4))
                                            (Econst_int (Int.repr 2) tint)
                                            (tptr tuint)) tuint))
                                   (Scall (Some _t'8)
                                      (Evar _L32
                                         (Tfunction
                                            (Tcons tuint (Tcons tint Tnil))
                                            tuint cc_default))
                                      [Ebinop Oadd (Etempvar _t'22 tuint)
                                         (Etempvar _t'23 tuint) tuint;
                                      Econst_int (Int.repr 18) tint])))
                             (Ssequence
                                (Sset _t'21
                                   (Ederef
                                      (Ebinop Oadd (Evar _t (tarray tuint 4))
                                         (Econst_int (Int.repr 0) tint)
                                         (tptr tuint)) tuint))
                                (Sassign
                                   (Ederef
                                      (Ebinop Oadd (Evar _t (tarray tuint 4))
                                         (Econst_int (Int.repr 0) tint)
                                         (tptr tuint)) tuint)
                                   (Ebinop Oxor (Etempvar _t'21 tuint)
                                      (Etempvar _t'8 tuint) tuint))))
                          (Sfor (Sset _m (Econst_int (Int.repr 0) tint))
                             (Ebinop Olt (Etempvar _m tint)
                                (Econst_int (Int.repr 4) tint) tint)
                             (Ssequence
                                (Sset _t'20
                                   (Ederef
                                      (Ebinop Oadd (Evar _t (tarray tuint 4))
                                         (Etempvar _m tint) (tptr tuint))
                                      tuint))
                                (Sassign
                                   (Ederef
                                      (Ebinop Oadd
                                         (Evar _w (tarray tuint 16))
                                         (Ebinop Oadd
                                            (Ebinop Omul
                                               (Econst_int (Int.repr 4) tint)
                                               (Etempvar _j tint) tint)
                                            (Ebinop Omod
                                               (Ebinop Oadd
                                                  (Etempvar _j tint)
                                                  (Etempvar _m tint) tint)
                                               (Econst_int (Int.repr 4) tint)
                                               tint) tint) (tptr tuint))
                                      tuint) (Etempvar _t'20 tuint)))
                             (Sset _m
                                (Ebinop Oadd (Etempvar _m tint)
                                   (Econst_int (Int.repr 1) tint) tint))))))))
           (Sset _j
              (Ebinop Oadd (Etempvar _j tint) (Econst_int (Int.repr 1) tint)
                 tint)))
        (Sfor (Sset _m (Econst_int (Int.repr 0) tint))
           (Ebinop Olt (Etempvar _m tint) (Econst_int (Int.repr 16) tint)
              tint)
           (Ssequence
              (Sset _t'19
                 (Ederef
                    (Ebinop Oadd (Evar _w (tarray tuint 16))
                       (Etempvar _m tint) (tptr tuint)) tuint))
              (Sassign
                 (Ederef
                    (Ebinop Oadd (Evar _x (tarray tuint 16))
                       (Etempvar _m tint) (tptr tuint)) tuint)
                 (Etempvar _t'19 tuint)))
           (Sset _m
              (Ebinop Oadd (Etempvar _m tint) (Econst_int (Int.repr 1) tint)
                 tint))))
     (Sset _i
        (Ebinop Oadd (Etempvar _i tint) (Econst_int (Int.repr 1) tint) tint)).

Lemma f_core_loop3: forall (Espec : OracleKind) FR
c k h nonce out w x y t (xI:list int),
@semax CompSpecs Espec
  (func_tycontext f_core SalsaVarSpecs SalsaFunSpecs nil)
  (PROP  ()
   LOCAL  (temp _i (Vint (Int.repr 16)); lvar _t (tarray tuint 4) t;
   lvar _y (tarray tuint 16) y; lvar _x (tarray tuint 16) x;
   lvar _w (tarray tuint 16) w; temp _out out; temp _in nonce; temp _k k; temp _c c;
   temp _h (Vint (Int.repr h)))
   SEP  (FR; data_at_ Tsh (tarray tuint 4) t;
         data_at_ Tsh (tarray tuint 16) w;
         data_at Tsh (tarray tuint 16) (map Vint xI) x))
 f_core_loop3_statement
  (normal_ret_assert
  (PROP  ()
   LOCAL  (temp _i (Vint (Int.repr 20)); lvar _t (tarray tuint 4) t; lvar _y (tarray tuint 16) y;
       lvar _x (tarray tuint 16) x; lvar _w (tarray tuint 16) w; temp _out out; temp _in nonce;
       temp _k k; temp _c c; temp _h (Vint (Int.repr h)))
   SEP (FR; data_at_ Tsh (tarray tuint 4) t; data_at_ Tsh (tarray tuint 16) w;
        EX r:_, !!(Snuffle 20 xI = Some r) &&
           data_at Tsh (tarray tuint 16) (map Vint r) x))).
Proof. intros. abbreviate_semax.
unfold f_core_loop3_statement.
freeze [0;1;2] FR1.
Time assert_PROP (Zlength (map Vint xI) = 16) as XIZ by entailer!. (*0.9*)
thaw FR1.
rewrite Zlength_map in XIZ.
drop_LOCAL 0%nat.
Time forward_for_simple_bound 20 (EX i:Z,
  (PROP  ()
   LOCAL  (lvar _t (tarray tuint 4) t;
   lvar _y (tarray tuint 16) y; lvar _x (tarray tuint 16) x;
   lvar _w (tarray tuint 16) w; temp _out out; temp _in nonce; temp _k k; temp _c c;
   temp _h (Vint (Int.repr h)))
   SEP  (FR; data_at_ Tsh (tarray tuint 4) t; data_at_ Tsh (tarray tuint 16) w;
         EX r:_, !!(Snuffle (Z.to_nat i) xI = Some r) &&
             data_at Tsh (tarray tuint 16) (map Vint r) x))). (*0.9*)
{ Exists xI. Time entailer!. (*2.6*) }

{ rename H into I. Intros r. rename H into R.
  assert (XI: length xI = 16%nat). eapply (Zlength_length _ _ 16). lia. trivial.
  assert (RL:= Snuffle_length _ _ _ R XI).
  assert (RZL: Zlength r = 16). rewrite Zlength_correct, RL; reflexivity.

  Time forward_for_simple_bound 4 (EX j:Z,
  (PROP  ()
   LOCAL  (temp _i (Vint (Int.repr i)); lvar _t (tarray tuint 4) t;
   lvar _y (tarray tuint 16) y; lvar _x (tarray tuint 16) x;
   lvar _w (tarray tuint 16) w; temp _out out; temp _in nonce; temp _k k; temp _c c;
   temp _h (Vint (Int.repr h)))
   SEP  (FR; data_at_ Tsh (tarray tuint 4) t;
      EX l:_, !!(WcontI r (Z.to_nat j) l) && data_at Tsh (tarray tuint 16) l w;
      data_at Tsh (tarray tuint 16) (map Vint r) x))). (*1.5*)
  { Time entailer!. (*2.5*) Exists (repeat Vundef 16). Time entailer!. (*0.1*) }
  { rename H into J. rename i0 into j.
    Intros wlist. rename H into WCONT.
    destruct (Znth_mapVint r ((5 * j + 4 * 0) mod 16)) as [t0 T0].
      rewrite RZL; apply Z_mod_lt; lia.
    destruct (Znth_mapVint r ((5 * j + 4 * 1) mod 16)) as [t1 T1].
      rewrite RZL; apply Z_mod_lt; lia.
    destruct (Znth_mapVint r ((5 * j + 4 * 2) mod 16)) as [t2 T2].
      rewrite RZL; apply Z_mod_lt; lia.
    destruct (Znth_mapVint r ((5 * j + 4 * 3) mod 16)) as [t3 T3].
      rewrite RZL; apply Z_mod_lt; lia.
    eapply semax_post_flipped'.
    apply (Jbody _ FR c k h nonce out w x y t i j r I J wlist _ _ _ _ T0 T1 T2 T3).
    Intros W. Exists W.
    Time entailer!. (*6.1*)
    rewrite Z.add_comm, Z2Nat.inj_add; try lia.
    assert (X: (Z.to_nat 1 + Z.to_nat j = S (Z.to_nat j))%nat) by reflexivity.
    rewrite X. simpl. split. assumption.
    exists t0, t1, t2, t3. simpl in T0, T1, T2, T3. rewrite Z2Nat.id, T0, T1, T2, T3.
    repeat split; trivial.
    exists wlist. split; trivial. lia. }

  Intros wlist. rename H into HW.
  destruct (WWI _ _ HW RZL) as [wints [WI SNUFF]]. subst wlist.
  freeze [0;1] FR2.
  eapply semax_post_flipped'.
  apply (array_copy3 _ (FRZL FR2) c k h nonce out
                  i w x y t (map Vint r) (map Vint wints)); trivial.
           intros. apply Znth_mapVint.
              destruct (snuffleRound_length _ _ SNUFF) as [WL _].
              rewrite Zlength_correct, WL; simpl; lia.
  Exists wints. rewrite Z.add_comm, Z2Nat.inj_add; try lia.
  Time entailer!. (*4.3*)
  rewrite SnuffleS, R; trivial.
  thaw FR2; cancel. }
 apply ENTAIL_refl.
Time Qed. (*June4th, 2017 (laptop): Finished transaction in 1.781 secs (1.072u,0.028s) (successful)*)


Require Import Bool.
Require Import List.

From sflib Require Import sflib.
From Paco Require Import paco.

From PromisingLib Require Import Basic.
Require Import Event.
From PromisingLib Require Import Language.
Require Import Time.
Require Import View.
Require Import Cell.
Require Import Memory.
Require Import TView.
Require Import Local.
Require Import Thread.
Require Import Configuration.
Require Import Progress.

Require Import SimMemory.
Require Import SimPromises.
Require Import SimLocal.
Require Import Compatibility.
Require Import SimThread.

Require Import Syntax.
Require Import Semantics.

Set Implicit Arguments.

Lemma intro_load_sim_stmts
      r loc ord:
  sim_stmts (RegFile.eq_except (RegSet.singleton r))
            []
            [Stmt.instr (Instr.load r loc ord)]
            (RegFile.eq_except (RegSet.singleton r)).
Proof.
  pcofix CIH. ii. subst. pfold. ii. splits; try done; i.
  { exploit SimPromises.cap; try apply LOCAL; eauto. }
  { right. esplits; eauto.
    eapply sim_local_memory_bot; eauto.
  }
  ii. right.
  inv STEP_TGT; inv STEP; try (inv STATE; inv INSTR); ss.
  - (* promise *)
    exploit sim_local_promise; eauto. i. des.
    esplits; try apply SC; eauto; ss.
    econs 2. econs 1; eauto. econs; eauto. eauto.
  - (* load *)
    destruct e_tgt; ss. esplits; eauto; ss.
    + econs 1.
    + ss.
    + by inv LOCAL0.
    + by inv LOCAL0.
    + left. eapply paco9_mon; [apply sim_stmts_nil|]; ss.
      * etrans; [eauto|].
        symmetry. apply RegFile.eq_except_singleton.
      * inv LOCAL. inv LOCAL0. inv LOCAL. econs; ss.
        etrans; eauto. apply TViewFacts.read_tview_incr.
Qed.


Unset Automatic Introduction.

Require Import RezkCompletion.pathnotations.
Require Import Foundations.hlevel2.hSet.
        Import RezkCompletion.pathnotations.PathNotations.

Fixpoint c (m n:nat) : UU.
  intros [|m] [|n].
  * exact unit.
  * exact empty.
  * exact empty.
  * exact (c m n).
Defined.

Fixpoint encode (m n:nat) : m == n -> c m n.
  intros [|m] [|n] p.
  * exact tt.
  * destruct p. exact tt.
  * destruct p.


(*

  Copyright 2014 Cornell University
  Copyright 2015 Cornell University
  Copyright 2016 Cornell University

  This file is part of VPrl (the Verified Nuprl project).

  VPrl is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  VPrl is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with VPrl.  If not, see <http://www.gnu.org/licenses/>.

  Websites: http://nuprl.org/html/verification/
            http://nuprl.org/html/Nuprl2Coq
            https://github.com/vrahli/NuprlInCoq

  Authors: Abhishek Anand & Vincent Rahli

*)

Require Import approx_star0.

Lemma extensional_sleep {p} : extensional_op (@NCan p NSleep).
Proof.
  introv Hpra Hprt Hprt' Hcv Has Hi.

  applydup @isprogram_sleep_implies in Hprt; exrepnd; subst; cpx.
  applydup @isprogram_sleep_implies in Hprt'; exrepnd; subst; cpx.
  allrw @fold_sleep.

  unfold lblift_sub in Has; simpl in Has; repnd; GC.
  repeat(approxrelbtd); show_hyps.
  allapply @approx_star_bterm_nobnd2.

  apply computes_to_val_like_in_max_k_steps_sleep_implies in Hcv; exrepnd; cpx.
  unfold extensional_op_ind in Hi.
  applydup @computes_to_val_like_in_max_k_steps_preserves_program in Hcv2; auto.
  apply Hi with (v := t0) in Hcv2; auto; clear Hi.

  dorn Hcv1; exrepnd; subst.

  - apply howe_lemma2 in Hcv2; auto; prove_isprogram; exrepnd.
    unfold approx_starbts, lblift_sub in Hcv2; simpl in Hcv2; repnd; cpx.
    allrw @fold_integer.
    apply approx_open_implies_approx_star.
    apply approx_implies_approx_open.
    apply reduces_to_implies_approx_eauto; prove_isprogram.
    apply reduces_to_trans with (b := mk_sleep (mk_integer z)).
    { apply reduces_to_prinarg; auto.
      destruct Hcv1; auto. }
    { apply reduces_to_if_step; reflexivity. }

  - apply isexc_implies in Hcv3; auto; exrepnd; subst; GC.
    apply howe_lemma2_exc in Hcv2; auto; exrepnd.
    apply approx_star_open_trans with (b := mk_exception a' e').
    apply approx_star_exception; auto.
    apply approx_implies_approx_open.
    apply reduces_to_implies_approx_eauto; prove_isprogram.
    apply reduces_to_trans with (b := mk_sleep (mk_exception a' e')).
    { apply reduces_to_prinarg; auto. }
    { apply reduces_to_if_step; reflexivity. }
Qed.

(*
*** Local Variables:
*** coq-load-path: ("." "../util/" "../terms/" "../computation/")
*** End:
*)