1use crate::{
4 Apply,
5 classes::{clonable_fn::ClonableFn, monoid::Monoid, semigroup::Semigroup},
6 functions::identity,
7};
8use std::{
9 fmt::{self, Debug, Formatter},
10 hash::Hash,
11};
12
13pub struct Endofunction<'a, CFB: ClonableFn, A>(
24 pub Apply!(brand: CFB, kind: ClonableFn, lifetimes: ('a), types: (A, A)),
25);
26
27impl<'a, CFB: ClonableFn, A> Endofunction<'a, CFB, A> {
28 pub fn new(f: Apply!(brand: CFB, kind: ClonableFn, lifetimes: ('a), types: (A, A))) -> Self {
42 Self(f)
43 }
44}
45
46impl<'a, CFB: ClonableFn, A> Clone for Endofunction<'a, CFB, A> {
47 fn clone(&self) -> Self {
48 Self::new(self.0.clone())
49 }
50}
51
52impl<'a, CFB: ClonableFn, A> Debug for Endofunction<'a, CFB, A>
53where
54 Apply!(brand: CFB, kind: ClonableFn, lifetimes: ('a), types: (A, A)): Debug,
55{
56 fn fmt(
57 &self,
58 fmt: &mut Formatter<'_>,
59 ) -> fmt::Result {
60 fmt.debug_tuple("Endofunction").field(&self.0).finish()
61 }
62}
63
64impl<'a, CFB: ClonableFn, A> Eq for Endofunction<'a, CFB, A> where
65 Apply!(brand: CFB, kind: ClonableFn, lifetimes: ('a), types: (A, A)): Eq
66{
67}
68
69impl<'a, CFB: ClonableFn, A> Hash for Endofunction<'a, CFB, A>
70where
71 Apply!(brand: CFB, kind: ClonableFn, lifetimes: ('a), types: (A, A)): Hash,
72{
73 fn hash<H: std::hash::Hasher>(
74 &self,
75 state: &mut H,
76 ) {
77 self.0.hash(state);
78 }
79}
80
81impl<'a, CFB: ClonableFn, A> Ord for Endofunction<'a, CFB, A>
82where
83 Apply!(brand: CFB, kind: ClonableFn, lifetimes: ('a), types: (A, A)): Ord,
84{
85 fn cmp(
86 &self,
87 other: &Self,
88 ) -> std::cmp::Ordering {
89 self.0.cmp(&other.0)
90 }
91}
92
93impl<'a, CFB: ClonableFn, A> PartialEq for Endofunction<'a, CFB, A>
94where
95 Apply!(brand: CFB, kind: ClonableFn, lifetimes: ('a), types: (A, A)): PartialEq,
96{
97 fn eq(
98 &self,
99 other: &Self,
100 ) -> bool {
101 self.0 == other.0
102 }
103}
104
105impl<'a, CFB: ClonableFn, A> PartialOrd for Endofunction<'a, CFB, A>
106where
107 Apply!(brand: CFB, kind: ClonableFn, lifetimes: ('a), types: (A, A)): PartialOrd,
108{
109 fn partial_cmp(
110 &self,
111 other: &Self,
112 ) -> Option<std::cmp::Ordering> {
113 self.0.partial_cmp(&other.0)
114 }
115}
116
117impl<'a, CFB: 'a + ClonableFn, A: 'a> Semigroup for Endofunction<'a, CFB, A> {
118 fn append(
147 a: Self,
148 b: Self,
149 ) -> Self {
150 let f = a.0;
151 let g = b.0;
152 Self::new(<CFB as ClonableFn>::new(move |x| f(g(x))))
154 }
155}
156
157impl<'a, CFB: 'a + ClonableFn, A: 'a> Monoid for Endofunction<'a, CFB, A> {
158 fn empty() -> Self {
179 Self::new(<CFB as ClonableFn>::new(identity))
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186 use crate::{
187 brands::RcFnBrand,
188 classes::{clonable_fn::ClonableFn, monoid::empty, semigroup::append},
189 };
190 use quickcheck_macros::quickcheck;
191
192 #[quickcheck]
196 fn semigroup_associativity(val: i32) -> bool {
197 let f = Endofunction::<RcFnBrand, _>::new(<RcFnBrand as ClonableFn>::new(|x: i32| {
198 x.wrapping_add(1)
199 }));
200 let g = Endofunction::<RcFnBrand, _>::new(<RcFnBrand as ClonableFn>::new(|x: i32| {
201 x.wrapping_mul(2)
202 }));
203 let h = Endofunction::<RcFnBrand, _>::new(<RcFnBrand as ClonableFn>::new(|x: i32| {
204 x.wrapping_sub(3)
205 }));
206
207 let lhs = append(f.clone(), append(g.clone(), h.clone()));
208 let rhs = append(append(f, g), h);
209
210 lhs.0(val) == rhs.0(val)
211 }
212
213 #[quickcheck]
217 fn monoid_left_identity(val: i32) -> bool {
218 let f = Endofunction::<RcFnBrand, _>::new(<RcFnBrand as ClonableFn>::new(|x: i32| {
219 x.wrapping_add(1)
220 }));
221 let id = empty::<Endofunction<RcFnBrand, i32>>();
222
223 let res = append(id, f.clone());
224 res.0(val) == f.0(val)
225 }
226
227 #[quickcheck]
229 fn monoid_right_identity(val: i32) -> bool {
230 let f = Endofunction::<RcFnBrand, _>::new(<RcFnBrand as ClonableFn>::new(|x: i32| {
231 x.wrapping_add(1)
232 }));
233 let id = empty::<Endofunction<RcFnBrand, i32>>();
234
235 let res = append(f.clone(), id);
236 res.0(val) == f.0(val)
237 }
238}