\import Algebra.Group
\import Algebra.Module
\import Algebra.Module.LinearMap
\import Algebra.Monoid.MonoidHom
\import Algebra.Ring
\import Algebra.Ring.RingHom
\import Paths
\import Paths.Meta

\instance LinearMapAbGroup {R : Ring} (A B : LModule R) : AbGroup (LinearMap A B)
  | zro => linearMap_zro
  | + => linearMap_+
  | zro-left => exts \lam _ => LModule.zro-left
  | zro-right => exts \lam _ => LModule.zro-right
  | +-assoc => exts \lam _ => LModule.+-assoc
  | negative => linearMap_negative
  | negative-left => exts \lam _ => LModule.negative-left
  | +-comm => exts \lam _ => LModule.+-comm

\instance LinearMapLModule {R : CRing} (A B : LModule R) : LModule R \cowith
  | AbGroup => LinearMapAbGroup A B
  | *c r f => linearMap_*c {R} {A} {B} {r} f
  | *c-assoc => exts \lam _ => B.*c-assoc
  | *c-ldistr => exts \lam _ => B.*c-ldistr
  | *c-rdistr => exts \lam _ => B.*c-rdistr
  | ide_*c => exts \lam _ => B.ide_*c

\instance LinearMapRing {R : Ring} (U : LModule R) : Ring (LinearMap U U)
  | zro => linearMap_zro
  | + => linearMap_+
  | zro-left => exts \lam _ => U.zro-left
  | +-assoc => exts \lam _ => U.+-assoc
  | +-comm => exts \lam _ => U.+-comm
  | negative => linearMap_negative
  | negative-left => exts \lam _ => U.negative-left
  | ide => LinearMap.id
  | * f g => g LinearMap. f
  | ide-left => idp
  | ide-right => idp
  | *-assoc => idp
  | ldistr => idp
  | rdistr => exts \lam _ => func-+
  \where {
    \func *c-hom {R : CRing} {U : LModule R} : RingHom R (LinearMapRing U) \cowith
      | func (r : R) : LinearMap U U \cowith {
        | func u => r *c u
        | func-+ => *c-ldistr
        | func-*c => inv *c-assoc *> pmap (*c _) R.*-comm *> *c-assoc
      }
      | func-+ => exts \lam _ => *c-rdistr
      | func-ide => exts \lam _ => ide_*c
      | func-* => exts \lam u => pmap (*c u) R.*-comm *> *c-assoc
  }