1#[fp_macros::document_module]
51mod inner {
52 use {
53 crate::{
54 brands::RcCoyonedaBrand,
55 classes::{
56 Lift,
57 NaturalTransformation,
58 *,
59 },
60 impl_kind,
61 kinds::*,
62 },
63 fp_macros::*,
64 std::rc::Rc,
65 };
66
67 #[document_type_parameters(
77 "The lifetime of the values.",
78 "The brand of the underlying type constructor.",
79 "The output type of the accumulated mapping function."
80 )]
81 #[document_parameters("The trait object reference.")]
82 trait RcCoyonedaLowerRef<'a, F, A: 'a>: 'a
83 where
84 F: Kind_cdc7cd43dac7585f + 'a, {
85 #[document_signature]
87 #[document_returns("The underlying functor value with accumulated functions applied.")]
89 #[document_examples]
90 fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
101 where
102 F: Functor;
103 }
104
105 struct RcCoyonedaBase<'a, F, A: 'a>
110 where
111 F: Kind_cdc7cd43dac7585f + 'a, {
112 fa: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
113 }
114
115 #[document_type_parameters(
116 "The lifetime of the values.",
117 "The brand of the underlying type constructor.",
118 "The type of the value inside the functor."
119 )]
120 #[document_parameters("The base layer instance.")]
121 impl<'a, F, A: 'a> RcCoyonedaLowerRef<'a, F, A> for RcCoyonedaBase<'a, F, A>
122 where
123 F: Kind_cdc7cd43dac7585f + 'a,
124 Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone,
125 {
126 #[document_signature]
128 #[document_returns("A clone of the underlying functor value.")]
130 #[document_examples]
131 fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
142 where
143 F: Functor, {
144 self.fa.clone()
145 }
146 }
147
148 struct RcCoyonedaMapLayer<'a, F, B: 'a, A: 'a>
153 where
154 F: Kind_cdc7cd43dac7585f + 'a, {
155 inner: Rc<dyn RcCoyonedaLowerRef<'a, F, B> + 'a>,
156 func: Rc<dyn Fn(B) -> A + 'a>,
157 }
158
159 #[document_type_parameters(
160 "The lifetime of the values.",
161 "The brand of the underlying type constructor.",
162 "The input type of this layer's mapping function.",
163 "The output type of this layer's mapping function."
164 )]
165 #[document_parameters("The map layer instance.")]
166 impl<'a, F, B: 'a, A: 'a> RcCoyonedaLowerRef<'a, F, A> for RcCoyonedaMapLayer<'a, F, B, A>
167 where
168 F: Kind_cdc7cd43dac7585f + 'a,
169 {
170 #[document_signature]
172 #[document_returns("The underlying functor value with this layer's function applied.")]
174 #[document_examples]
175 fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
186 where
187 F: Functor, {
188 #[cfg(feature = "stacker")]
189 {
190 stacker::maybe_grow(32 * 1024, 1024 * 1024, || {
191 let lowered = self.inner.lower_ref();
192 let func = self.func.clone();
193 F::map(move |b| (*func)(b), lowered)
194 })
195 }
196 #[cfg(not(feature = "stacker"))]
197 {
198 let lowered = self.inner.lower_ref();
199 let func = self.func.clone();
200 F::map(move |b| (*func)(b), lowered)
201 }
202 }
203 }
204
205 struct RcCoyonedaNewLayer<'a, F, B: 'a, A: 'a>
211 where
212 F: Kind_cdc7cd43dac7585f + 'a, {
213 fb: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
214 func: Rc<dyn Fn(B) -> A + 'a>,
215 }
216
217 #[document_type_parameters(
218 "The lifetime of the values.",
219 "The brand of the underlying type constructor.",
220 "The input type of the stored function.",
221 "The output type of the stored function."
222 )]
223 #[document_parameters("The new layer instance.")]
224 impl<'a, F, B: 'a, A: 'a> RcCoyonedaLowerRef<'a, F, A> for RcCoyonedaNewLayer<'a, F, B, A>
225 where
226 F: Kind_cdc7cd43dac7585f + 'a,
227 Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>): Clone,
228 {
229 #[document_signature]
231 #[document_returns("The underlying functor value with the stored function applied.")]
233 #[document_examples]
234 fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
245 where
246 F: Functor, {
247 let func = self.func.clone();
248 F::map(move |b| (*func)(b), self.fb.clone())
249 }
250 }
251
252 #[document_type_parameters(
263 "The lifetime of the values.",
264 "The brand of the underlying type constructor.",
265 "The current output type."
266 )]
267 pub struct RcCoyoneda<'a, F, A: 'a>(Rc<dyn RcCoyonedaLowerRef<'a, F, A> + 'a>)
268 where
269 F: Kind_cdc7cd43dac7585f + 'a;
270
271 #[document_type_parameters(
272 "The lifetime of the values.",
273 "The brand of the underlying type constructor.",
274 "The current output type."
275 )]
276 #[document_parameters("The `RcCoyoneda` instance to clone.")]
277 impl<'a, F, A: 'a> Clone for RcCoyoneda<'a, F, A>
278 where
279 F: Kind_cdc7cd43dac7585f + 'a,
280 {
281 #[document_signature]
283 #[document_returns("A new `RcCoyoneda` sharing the same inner layers.")]
285 #[document_examples]
286 fn clone(&self) -> Self {
298 RcCoyoneda(self.0.clone())
299 }
300 }
301
302 #[document_type_parameters(
303 "The lifetime of the values.",
304 "The brand of the underlying type constructor.",
305 "The current output type."
306 )]
307 #[document_parameters("The `RcCoyoneda` instance.")]
308 impl<'a, F, A: 'a> RcCoyoneda<'a, F, A>
309 where
310 F: Kind_cdc7cd43dac7585f + 'a,
311 {
312 #[document_signature]
317 #[document_parameters("The functor value to lift.")]
319 #[document_returns("An `RcCoyoneda` wrapping the value.")]
321 #[document_examples]
322 pub fn lift(fa: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)) -> Self
333 where
334 Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone, {
335 RcCoyoneda(Rc::new(RcCoyonedaBase {
336 fa,
337 }))
338 }
339
340 #[document_signature]
346 #[document_returns("The underlying functor value with all accumulated functions applied.")]
348 #[document_examples]
349 pub fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
362 where
363 F: Functor, {
364 self.0.lower_ref()
365 }
366
367 #[document_signature]
376 #[document_returns("A new `RcCoyoneda` with a single base layer.")]
378 #[document_examples]
379 pub fn collapse(&self) -> RcCoyoneda<'a, F, A>
391 where
392 F: Functor,
393 Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone, {
394 RcCoyoneda::lift(self.lower_ref())
395 }
396
397 #[document_signature]
402 #[document_type_parameters(
404 "The brand of the cloneable function to use.",
405 "The type of the monoid."
406 )]
407 #[document_parameters("The function to map each element to a monoid.")]
409 #[document_returns("The combined monoid value.")]
411 #[document_examples]
412 pub fn fold_map<FnBrand: LiftFn + 'a, M>(
426 &self,
427 func: impl Fn(A) -> M + 'a,
428 ) -> M
429 where
430 F: Functor + Foldable,
431 A: Clone,
432 M: Monoid + 'a, {
433 F::fold_map::<FnBrand, A, M>(func, self.lower_ref())
434 }
435
436 #[document_signature]
441 #[document_type_parameters("The new output type after applying the function.")]
443 #[document_parameters("The function to apply.")]
445 #[document_returns("A new `RcCoyoneda` with the function stored for deferred application.")]
447 #[document_examples]
448 pub fn map<B: 'a>(
459 self,
460 f: impl Fn(A) -> B + 'a,
461 ) -> RcCoyoneda<'a, F, B> {
462 RcCoyoneda(Rc::new(RcCoyonedaMapLayer {
463 inner: self.0,
464 func: Rc::new(f),
465 }))
466 }
467
468 #[document_signature]
473 #[document_type_parameters("The input type of the function.")]
475 #[document_parameters("The function to defer.", "The functor value.")]
477 #[document_returns("An `RcCoyoneda` wrapping the value with the deferred function.")]
479 #[document_examples]
480 pub fn new<B: 'a>(
491 f: impl Fn(B) -> A + 'a,
492 fb: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
493 ) -> Self
494 where
495 Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>): Clone, {
496 RcCoyoneda(Rc::new(RcCoyonedaNewLayer {
497 fb,
498 func: Rc::new(f),
499 }))
500 }
501
502 #[document_signature]
509 #[document_type_parameters("The target functor brand.")]
511 #[document_parameters("The natural transformation from `F` to `G`.")]
513 #[document_returns("A new `RcCoyoneda` over the target functor `G`.")]
515 #[document_examples]
516 pub fn hoist<G: Kind_cdc7cd43dac7585f + 'a>(
539 self,
540 nat: impl NaturalTransformation<F, G>,
541 ) -> RcCoyoneda<'a, G, A>
542 where
543 F: Functor,
544 Apply!(<G as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone, {
545 RcCoyoneda::lift(nat.transform(self.lower_ref()))
546 }
547
548 #[document_signature]
552 #[document_parameters("The value to wrap.")]
554 #[document_returns("An `RcCoyoneda` containing the pure value.")]
556 #[document_examples]
557 pub fn pure(a: A) -> Self
568 where
569 F: Pointed,
570 Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone, {
571 RcCoyoneda::lift(F::pure(a))
572 }
573
574 #[document_signature]
580 #[document_type_parameters("The output type of the bound computation.")]
582 #[document_parameters(
584 "The function to apply to the inner value, returning a new `RcCoyoneda`."
585 )]
586 #[document_returns("A new `RcCoyoneda` containing the bound result.")]
588 #[document_examples]
589 pub fn bind<B: 'a>(
601 self,
602 func: impl Fn(A) -> RcCoyoneda<'a, F, B> + 'a,
603 ) -> RcCoyoneda<'a, F, B>
604 where
605 F: Functor + Semimonad,
606 Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>): Clone, {
607 RcCoyoneda::lift(F::bind(self.lower_ref(), move |a| func(a).lower_ref()))
608 }
609
610 #[document_signature]
615 #[document_type_parameters(
617 "The brand of the cloneable function wrapper.",
618 "The type of the function input.",
619 "The type of the function output."
620 )]
621 #[document_parameters(
623 "The `RcCoyoneda` containing the function(s).",
624 "The `RcCoyoneda` containing the value(s)."
625 )]
626 #[document_returns("A new `RcCoyoneda` containing the applied result(s).")]
628 #[document_examples]
629 pub fn apply<FnBrand: LiftFn + 'a, B: Clone + 'a, C: 'a>(
645 ff: RcCoyoneda<'a, F, <FnBrand as CloneFn>::Of<'a, B, C>>,
646 fa: RcCoyoneda<'a, F, B>,
647 ) -> RcCoyoneda<'a, F, C>
648 where
649 F: Functor + Semiapplicative,
650 Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>): Clone, {
651 RcCoyoneda::lift(F::apply::<FnBrand, B, C>(ff.lower_ref(), fa.lower_ref()))
652 }
653
654 #[document_signature]
659 #[document_type_parameters("The type of the second value.", "The type of the result.")]
661 #[document_parameters("The binary function to apply.", "The second `RcCoyoneda` value.")]
663 #[document_returns("An `RcCoyoneda` containing the result.")]
665 #[document_examples]
666 pub fn lift2<B: Clone + 'a, C: 'a>(
679 self,
680 func: impl Fn(A, B) -> C + 'a,
681 fb: RcCoyoneda<'a, F, B>,
682 ) -> RcCoyoneda<'a, F, C>
683 where
684 F: Functor + Lift,
685 A: Clone,
686 Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>): Clone, {
687 RcCoyoneda::lift(F::lift2(func, self.lower_ref(), fb.lower_ref()))
688 }
689 }
690
691 impl_kind! {
694 impl<F: Kind_cdc7cd43dac7585f + 'static> for RcCoyonedaBrand<F> {
695 type Of<'a, A: 'a>: 'a = RcCoyoneda<'a, F, A>;
696 }
697 }
698
699 #[document_type_parameters("The brand of the underlying type constructor.")]
719 impl<F: Kind_cdc7cd43dac7585f + 'static> Functor for RcCoyonedaBrand<F> {
720 #[document_signature]
725 #[document_type_parameters(
727 "The lifetime of the values.",
728 "The type of the current output.",
729 "The type of the new output."
730 )]
731 #[document_parameters("The function to apply.", "The `RcCoyoneda` value.")]
733 #[document_returns("A new `RcCoyoneda` with the function stored for deferred application.")]
735 #[document_examples]
736 fn map<'a, A: 'a, B: 'a>(
749 func: impl Fn(A) -> B + 'a,
750 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
751 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
752 fa.map(func)
753 }
754 }
755
756 #[document_type_parameters("The brand of the underlying foldable functor.")]
759 impl<F: Functor + Foldable + 'static> Foldable for RcCoyonedaBrand<F> {
760 #[document_signature]
764 #[document_type_parameters(
766 "The lifetime of the elements.",
767 "The brand of the cloneable function to use.",
768 "The type of the elements in the structure.",
769 "The type of the monoid."
770 )]
771 #[document_parameters(
773 "The function to map each element to a monoid.",
774 "The `RcCoyoneda` structure to fold."
775 )]
776 #[document_returns("The combined monoid value.")]
778 #[document_examples]
779 fn fold_map<'a, FnBrand, A: 'a + Clone, M>(
795 func: impl Fn(A) -> M + 'a,
796 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
797 ) -> M
798 where
799 M: Monoid + 'a,
800 FnBrand: LiftFn + 'a, {
801 F::fold_map::<FnBrand, A, M>(func, fa.lower_ref())
802 }
803 }
804
805 #[document_type_parameters(
808 "The lifetime of the values.",
809 "The brand of the underlying type constructor.",
810 "The current output type."
811 )]
812 #[document_parameters("The `RcCoyoneda` instance.")]
813 impl<'a, F, A: 'a> core::fmt::Debug for RcCoyoneda<'a, F, A>
814 where
815 F: Kind_cdc7cd43dac7585f + 'a,
816 {
817 #[document_signature]
822 #[document_parameters("The formatter.")]
824 #[document_returns("The formatting result.")]
826 #[document_examples]
827 fn fmt(
838 &self,
839 f: &mut core::fmt::Formatter<'_>,
840 ) -> core::fmt::Result {
841 f.write_str("RcCoyoneda(<opaque>)")
842 }
843 }
844
845 #[document_type_parameters(
848 "The lifetime of the values.",
849 "The brand of the underlying functor.",
850 "The type of the values."
851 )]
852 impl<'a, F, A: 'a> From<RcCoyoneda<'a, F, A>> for crate::types::Coyoneda<'a, F, A>
853 where
854 F: Kind_cdc7cd43dac7585f + Functor + 'a,
855 {
856 #[document_signature]
861 #[document_parameters("The `RcCoyoneda` to convert.")]
863 #[document_returns("A `Coyoneda` containing the lowered value.")]
865 #[document_examples]
866 fn from(rc: RcCoyoneda<'a, F, A>) -> Self {
878 crate::types::Coyoneda::lift(rc.lower_ref())
879 }
880 }
881}
882
883pub use inner::*;
884
885#[cfg(test)]
886mod tests {
887 use crate::{
888 brands::*,
889 functions::*,
890 types::*,
891 };
892
893 #[test]
894 fn lift_lower_ref_identity_option() {
895 let coyo = RcCoyoneda::<OptionBrand, _>::lift(Some(42));
896 assert_eq!(coyo.lower_ref(), Some(42));
897 }
898
899 #[test]
900 fn lift_lower_ref_identity_vec() {
901 let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
902 assert_eq!(coyo.lower_ref(), vec![1, 2, 3]);
903 }
904
905 #[test]
906 fn clone_and_lower_ref() {
907 let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x + 1);
908 let coyo2 = coyo.clone();
909 assert_eq!(coyo.lower_ref(), vec![2, 3, 4]);
910 assert_eq!(coyo2.lower_ref(), vec![2, 3, 4]);
911 }
912
913 #[test]
914 fn chained_maps() {
915 let result = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3])
916 .map(|x| x + 1)
917 .map(|x| x * 2)
918 .map(|x| x.to_string())
919 .lower_ref();
920 assert_eq!(result, vec!["4", "6", "8"]);
921 }
922
923 #[test]
924 fn functor_identity_law() {
925 let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
926 let result =
927 explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(identity, coyo).lower_ref();
928 assert_eq!(result, vec![1, 2, 3]);
929 }
930
931 #[test]
932 fn functor_composition_law() {
933 let f = |x: i32| x + 1;
934 let g = |x: i32| x * 2;
935
936 let coyo1 = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
937 let left = explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(compose(f, g), coyo1)
938 .lower_ref();
939
940 let coyo2 = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
941 let right = explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(
942 f,
943 explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(g, coyo2),
944 )
945 .lower_ref();
946
947 assert_eq!(left, right);
948 }
949
950 #[test]
953 fn fold_map_on_mapped() {
954 let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x * 10);
955 let result = explicit::fold_map::<RcFnBrand, RcCoyonedaBrand<VecBrand>, _, _, _, _>(
956 |x: i32| x.to_string(),
957 coyo,
958 );
959 assert_eq!(result, "102030".to_string());
960 }
961
962 #[test]
963 fn lower_ref_multiple_times() {
964 let coyo = RcCoyoneda::<OptionBrand, _>::lift(Some(5)).map(|x| x + 1);
965 assert_eq!(coyo.lower_ref(), Some(6));
966 assert_eq!(coyo.lower_ref(), Some(6));
967 assert_eq!(coyo.lower_ref(), Some(6));
968 }
969
970 #[test]
971 fn map_on_none_stays_none() {
972 let result = RcCoyoneda::<OptionBrand, _>::lift(None::<i32>)
973 .map(|x| x + 1)
974 .map(|x| x * 2)
975 .lower_ref();
976 assert_eq!(result, None);
977 }
978
979 mod property {
982 use {
983 crate::{
984 brands::*,
985 functions::*,
986 types::*,
987 },
988 quickcheck_macros::quickcheck,
989 };
990
991 #[quickcheck]
992 fn functor_identity_vec(v: Vec<i32>) -> bool {
993 let coyo = RcCoyoneda::<VecBrand, _>::lift(v.clone());
994 explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(identity, coyo).lower_ref() == v
995 }
996
997 #[quickcheck]
998 fn functor_identity_option(x: Option<i32>) -> bool {
999 let coyo = RcCoyoneda::<OptionBrand, _>::lift(x);
1000 explicit::map::<RcCoyonedaBrand<OptionBrand>, _, _, _, _>(identity, coyo).lower_ref()
1001 == x
1002 }
1003
1004 #[quickcheck]
1005 fn functor_composition_vec(v: Vec<i32>) -> bool {
1006 let f = |x: i32| x.wrapping_add(1);
1007 let g = |x: i32| x.wrapping_mul(2);
1008
1009 let left = explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(
1010 compose(f, g),
1011 RcCoyoneda::<VecBrand, _>::lift(v.clone()),
1012 )
1013 .lower_ref();
1014
1015 let right = explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(
1016 f,
1017 explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(
1018 g,
1019 RcCoyoneda::<VecBrand, _>::lift(v),
1020 ),
1021 )
1022 .lower_ref();
1023
1024 left == right
1025 }
1026
1027 #[quickcheck]
1028 fn functor_composition_option(x: Option<i32>) -> bool {
1029 let f = |x: i32| x.wrapping_add(1);
1030 let g = |x: i32| x.wrapping_mul(2);
1031
1032 let left = explicit::map::<RcCoyonedaBrand<OptionBrand>, _, _, _, _>(
1033 compose(f, g),
1034 RcCoyoneda::<OptionBrand, _>::lift(x),
1035 )
1036 .lower_ref();
1037
1038 let right = explicit::map::<RcCoyonedaBrand<OptionBrand>, _, _, _, _>(
1039 f,
1040 explicit::map::<RcCoyonedaBrand<OptionBrand>, _, _, _, _>(
1041 g,
1042 RcCoyoneda::<OptionBrand, _>::lift(x),
1043 ),
1044 )
1045 .lower_ref();
1046
1047 left == right
1048 }
1049
1050 #[quickcheck]
1051 fn foldable_consistency_vec(v: Vec<i32>) -> bool {
1052 let coyo = RcCoyoneda::<VecBrand, _>::lift(v.clone()).map(|x: i32| x.wrapping_add(1));
1053 let via_coyoneda: String =
1054 explicit::fold_map::<RcFnBrand, RcCoyonedaBrand<VecBrand>, _, _, _, _>(
1055 |x: i32| x.to_string(),
1056 coyo,
1057 );
1058 let direct: String = explicit::fold_map::<RcFnBrand, VecBrand, _, _, _, _>(
1059 |x: i32| x.to_string(),
1060 v.iter().map(|x| x.wrapping_add(1)).collect::<Vec<_>>(),
1061 );
1062 via_coyoneda == direct
1063 }
1064
1065 #[quickcheck]
1066 fn collapse_preserves_value(v: Vec<i32>) -> bool {
1067 let coyo = RcCoyoneda::<VecBrand, _>::lift(v)
1068 .map(|x: i32| x.wrapping_add(1))
1069 .map(|x: i32| x.wrapping_mul(2));
1070 let before = coyo.lower_ref();
1071 let after = coyo.collapse().lower_ref();
1072 before == after
1073 }
1074 }
1075}