open import 1Lab.Path.Reasoning
open import 1Lab.Prelude
open import Algebra.Monoid.Category
open import Algebra.Group
open import Data.List hiding (lookup)
open import Data.Fin
open import Data.Fin.Closure
open is-iso
module Hats where
module _ (n-1 : Nat) where
  private
    n = suc n-1
    Person = Fin n
    Hat = Fin n
    Hats = Person → Hat
  record Strategy : Type where
    
    
    field
      guess : Person → (Fin n-1 → Hat) → Hat
    
    
    _✓_ : Hats → Person → Type
    hats ✓ i = guess i (delete hats i) ≡ hats i
    
    
    field
      one-right : ∀ hats → Σ Person λ i → hats ✓ i
    
    
    
    
    
    
    
    exactly-one-right : ∀ hats → is-contr (Σ Person λ i → hats ✓ i)
    exactly-one-right hats = Equiv→is-hlevel 0 (Fibre-equiv _ hats e⁻¹) (p-is-equiv .is-eqv hats)
      where
        probability : ∀ i → Iso (Σ Hats (_✓ i)) (Fin n-1 → Hat)
        probability i .fst (hats , _) = delete hats i
        probability i .snd .from other .fst = other [ i ≔ guess i other ]
        probability i .snd .from other .snd =
          guess i ⌜ delete (other [ i ≔ guess i other ]) i ⌝ ≡⟨ ap! (funext (delete-insert _ i _)) ⟩
          guess i other                                      ≡˘⟨ insert-lookup _ i _ ⟩
          (other [ i ≔ guess i other ]) i                    ∎
        probability i .snd .rinv _ = funext (delete-insert _ i _)
        probability i .snd .linv (hats , r) = Σ-prop-path! (funext (insert-delete _ i _ (sym r)))
        only-one : Σ Hats (λ hats → Σ Person (hats ✓_)) ≃ Hats
        only-one =
          Σ _ (λ hats → Σ _ λ i → hats ✓ i) ≃⟨ Σ-swap₂ ⟩
          Σ _ (λ i → Σ _ λ hats → hats ✓ i) ≃⟨ Σ-ap-snd (Iso→Equiv ∘ probability) ⟩
          Fin n × (Fin n-1 → Fin n)         ≃˘⟨ Fin-suc-Π ⟩
          (Fin n → Fin n)                   ≃∎
        p : Σ Hats (λ hats → Σ Person (hats ✓_)) → Hats
        p = fst
        p-is-equiv : is-equiv p
        p-is-equiv = Finite-surjection→equiv (inc only-one) p
          λ other → inc ((other , one-right other) , refl)
  open Strategy public
  
  
  Hypercube : Nat → Type
  Hypercube m = (Fin n → Fin m) → Fin m
  
  
  
  is-latin : ∀ {m} → Hypercube m → Type
  is-latin {m} h = ∀ (i : Fin n) (xs : Fin n-1 → Fin m) → is-equiv λ x → h (xs [ i ≔ x ])
  Latin-hypercube : Nat → Type
  Latin-hypercube m = Σ (Hypercube m) is-latin
  
  
  latin→strategy : Latin-hypercube n → Strategy
  latin→strategy (h , lat) .guess i other = equiv→inverse (lat i other) i
  latin→strategy (h , lat) .one-right hats =
    h hats , Equiv.from (eqv (h hats)) refl
    where
      module L i = Equiv (_ , lat i (delete hats i))
      eqv : ∀ (i : Fin n) → (L.from i i ≡ hats i) ≃ (h hats ≡ i)
      eqv i =
        L.from i i ≡ hats i ≃⟨ Equiv.adjunct (L.inverse i) ⟩
        i ≡ L.to i (hats i) ≃⟨ sym-equiv ⟩
        L.to i (hats i) ≡ i ≃⟨ ∙-pre-equiv (sym (ap h (funext (insert-delete hats i _ refl)))) ⟩
        h hats ≡ i          ≃∎
  
  
  group→latin : Group-on (Fin n) → Latin-hypercube n
  group→latin G = mul , mul-equiv
    where
      open Group-on G hiding (magma-hlevel)
      mul : ∀ {m} → (Fin m → Fin n) → Fin n
      mul xs = fold underlying-monoid (tabulate xs)
      mul-equiv : ∀ {m} (i : Fin (suc m)) (xs : Fin m → Fin n)
                → is-equiv (λ x → mul (xs [ i ≔ x ]))
      mul-equiv i xs with fin-view i
      mul-equiv _ xs | zero = ⋆-equivr _
      mul-equiv {suc m} _ xs | suc i = ∘-is-equiv (⋆-equivl _) (mul-equiv i (xs ∘ fsuc))
  group→strategy : Group-on (Fin n) → Strategy
  group→strategy = latin→strategy ∘ group→latin