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