\import Algebra.Meta
\import Algebra.Monoid
\import Arith.Rat
\import Arith.Real
\import Arith.Real.Field
\import Function()
\import Function.Meta
\import Logic
\import Logic.Meta
\import Meta
\import Operations
\import Order.Lattice
\import Order.LinearOrder
\import Order.PartialOrder
\import Order.StrictOrder
\import Paths
\import Paths.Meta
\import Set.Filter
\import Set.Subset
\import Topology.CoverSpace
\import Topology.CoverSpace.Complete
\import Topology.CoverSpace.Product
\import Topology.MetricSpace
\import Topology.NormedAbGroup.Real
\import Topology.NormedAbGroup.Real.Functions
\import Topology.TopAbGroup.Product
\import Topology.TopSpace
\import Topology.TopSpace.Product
\import Topology.UniformSpace
\import Topology.UniformSpace.Complete

\func dense-metric-lift {X Y : MetricSpace} {Z : CompleteMetricSpace} (f : IsometricMap X Y) (fd : f.IsDense) (g : MetricMap X Z) : MetricMap Y Z \cowith
  | UniformMap => dense-uniform-lift f (f.dense->uniformEmbedding fd) g
  | func-dist {y} {y'} =>
    \have | g~ : UniformMap Y Z => dense-uniform-lift f (f.dense->uniformEmbedding fd) g
          | g-char {x} : g~ (f x) = g x => dense-lift-char (fd, f.embedding->coverEmbedding (f.dense->uniformEmbedding fd).2) x
    \in Function.id {dist (g~ y) (g~ y') <= dist y y'} $ RealAbGroup.meet_<=' $ dense-lift-unique (prod f f) (prod.isDense fd fd) (real-meet-uniform  tuple (dist-uniform-map  prod g~ g~) dist-uniform-map) (dist-uniform-map  prod g~ g~)
          (\lam (x,x') => Function.id {dist (g~ (f x)) (g~ (f x'))  dist (f x) (f x') = dist (g~ (f x)) (g~ (f x'))} $ rewrite (g-char,g-char) $ RealAbGroup.meet_<= $ later $ rewrite f.func-isometry g.func-dist) (y,y')
  \where {
    \open ProductTopSpace
    \open ContMap
  }

\instance MetricCompletion (X : PseudoMetricSpace) : CompleteMetricSpace
  | CompleteUniformSpace => UniformCompletion X
  | dist x y => dist-cover (x,y)
  | dist-refl => dist-refl-lem
  | dist-symm {y} {y'} => dense-lift-unique (prod completion completion) (prod.isDenseEmbedding completion.isDenseEmbedding completion.isDenseEmbedding).1 dist-cover (dist-cover  tuple proj2 proj1) (\lam x => dist-char *> dist-symm *> inv dist-char) (y,y')
  | dist-triang => filter-dist-triang _ _ _
  | dist-uniform => dist-uniform-lem
  | dist-ext {F} {G} FG=0 => isSeparatedCoverSpace $ UniformSpace.uniform-separated.2 $ later \case dist-uniform-lem.1 __ \with {
    | inP (eps,eps>0,h) => later \case h F \with {
      | inP (U,CU,g) => inP (U, CU, (g $ rewrite dist-refl-lem eps>0, g $ rewrite FG=0 eps>0))
    }
  }
  \where {
    \open ProductCoverSpace
    \open CoverMap

    \sfunc dist-cover : CoverMap (Completion X  Completion X) RealNormed
      => dense-lift (prod completion completion) (prod.isDenseEmbedding completion.isDenseEmbedding completion.isDenseEmbedding) dist-uniform-map

    \lemma dist-char {x y : X} : dist-cover (pointCF x, pointCF y) = dist x y
      => rewrite (\peval dist-cover) $ dense-lift-char (prod.isDenseEmbedding completion.isDenseEmbedding completion.isDenseEmbedding) (x,y)

    \private \lemma dist-refl-lem {F : RegularCauchyFilter X} : dist-cover (F, F) = (0 : Real)
      => dense-lift-unique completion completion.isDenseEmbedding.1 (dist-cover  tuple id id) (const 0) (\lam x => dist-char *> dist-refl) F

    \private \lemma dist-uniform-lem {C : Set (Set (Completion X))} : isUniform C <->  (eps : Real) (0 < eps)  x  (U : C)  {y} (dist-cover (x,y) < eps -> U y)
      => (later \lam (inP (D,Du,g)) => \case dist-uniform.1 Du \with {
        | inP (eps,eps>0,h) => inP (halfs 2 eps, halfs>0 2 eps>0, \lam F => \case isCauchyFilter (X.makeCauchy $ X.makeUniform (halfs>0 4 eps>0)) \with {
          | inP (_, inP (x,idp), Fx) => \case h x \with {
            | inP (V,DV,Vd) => \case g DV \with {
              | inP (U,CU,VU) => inP (U, CU, \lam {G} d => \case isCauchyFilter (X.makeCauchy $ X.makeUniform (halfs>0 4 eps>0)) \with {
                | inP (_, inP (y,idp), Gy) => VU $ filter-mono (later \lam {z} d' => Vd
                    \have | t => dist-neighborhood (pointCF x) F (halfs>0 2 eps>0) (OBall-center_<=< $ halfs>0 4 eps>0) Fx
                          | s => dist-neighborhood G (pointCF y) (halfs>0 2 eps>0) Gy (OBall-center_<=< $ halfs>0 4 eps>0)
                    \in rewriteI dist-char $ rewriteI dist-char at d' $ linarith (filter-dist-triang (pointCF x) F (pointCF z), filter-dist-triang F G (pointCF z), filter-dist-triang G (pointCF y) (pointCF z))) Gy
              })
            }
          }
        })
      }, \lam (inP (eps,eps>0,h)) => inP (_, X.makeUniform {half (half eps)} $ half>0 $ half>0 eps>0, \lam {_} (inP (x,idp)) => \case h (pointCF x) \with {
        | inP (U,CU,g) => inP (U, CU, \lam {F} Fx => g $ dist-neighborhood _ _ eps>0 {x} (OBall-center_<=< $ half>0 $ half>0 eps>0) Fx)
      }))

    \lemma filter-dist-triang (x y z : RegularCauchyFilter X) : dist-cover (x,z) <= dist-cover (x,y) + dist-cover (y,z)
      => RealAbGroup.meet_<=' $ dense-lift-unique (prod (prod completion completion) completion) (prod.isDenseEmbedding (prod.isDenseEmbedding completion.isDenseEmbedding completion.isDenseEmbedding) completion.isDenseEmbedding).1
          (real-meet-uniform  tuple (dist-cover  prod proj1 id) (+-uniform  tuple (dist-cover  proj1) (dist-cover  prod proj2 id)))
          (dist-cover  prod proj1 id)
          (\lam ((x,y),z) => Function.id {dist-cover (pointCF x, pointCF z)  (dist-cover (pointCF x, pointCF y) + dist-cover (pointCF y, pointCF z)) = dist-cover (pointCF x, pointCF z)} $
            rewrite (dist-char,dist-char,dist-char) $ RealAbGroup.meet_<= dist-triang)
          ((x,y),z)

    \lemma dist-neighborhood (F G : RegularCauchyFilter X) {eps : Real} (eps>0 : 0 < eps) {x : X} (Fx : F (OBall (half (half eps)) x)) (Gx : G (OBall (half (half eps)) x)) : dist-cover (F,G) < eps
      => rewrite (\peval dist-cover) $ simplify in RealAbGroup.abs>=_- <∘r <=<_<= ((completion-lift-neighborhood2 (dist-uniform-map {X}) F G (OBall eps 0)).2 $ inP
          (OBall (half eps) 0, <=*_<=< $ OBall_<=* (half<id eps>0), _, Fx, _, Gx, \lam {z1} d1 {z2} d2 => RealAbGroup.abs_-_< (linarith (dist>=0 {X} {z1}, half>0 eps>0)) $ simplify $ halving d1 d2)) idp
  }