123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297open!Baseopen!StackmoduleDebug(Stack:S):Swithtype'at='aStack.t=structopenStacktypenonrec'at='atletinvariant=invariantlett_sexp_grammar=t_sexp_grammarletcheck_and_returnt=invariantignoret;t;;letdebugtf=letresult=Result.try_withfininvariantignoret;Result.ok_exnresult;;(* The return-type annotations are to prevent an error where we don't supply all the
arguments to the function, and thus wouldn't be checking the invariant after fully
applying the function. *)letcleart:unit=debugt(fun()->cleart)letcopyt:_t=check_and_return(debugt(fun()->copyt))letcountt~f:int=debugt(fun()->countt~f)[@nontail]letsummt~f=debugt(fun()->summt~f)[@nontail]letcreate():_t=check_and_return(create())letexistst~f:bool=debugt(fun()->existst~f)[@nontail]letfindt~f:_option=debugt(fun()->findt~f)[@nontail]letfind_mapt~f:_option=debugt(fun()->find_mapt~f)[@nontail]letfold(typea)t~init~f:a=debugt(fun()->foldt~init~f)[@nontail]letfor_allt~f:bool=debugt(fun()->for_allt~f)[@nontail]letis_emptyt:bool=debugt(fun()->is_emptyt)letitert~f:unit=debugt(fun()->itert~f)[@nontail]letiter_untilt~f~finish=debugt(fun()->iter_untilt~f~finish)[@nontail]letlengtht:int=debugt(fun()->lengtht)letmemta~equal:bool=debugt(fun()->memta~equal)[@nontail]letof_listl:_t=check_and_return(of_listl)letpopt:_option=debugt(fun()->popt)letpop_exn(typea)t:a=debugt(fun()->pop_exnt)letpushta:unit=debugt(fun()->pushta)letsexp_of_tsexp_of_at:Sexp.t=debugt(fun()->[%sexp_of:at]t)letsingletonx:_t=check_and_return(singletonx)lett_of_sexpa_of_sexpsexp:_t=check_and_return([%of_sexp:at]sexp)letto_arrayt:_array=debugt(fun()->to_arrayt)letto_listt:_list=debugt(fun()->to_listt)lettopt:_option=debugt(fun()->topt)lettop_exn(typea)t:a=debugt(fun()->top_exnt)letuntil_emptytf:unit=debugt(fun()->until_emptytf)[@nontail]letmin_eltt~compare:_option=debugt(fun()->min_eltt~compare)[@nontail]letmax_eltt~compare:_option=debugt(fun()->max_eltt~compare)[@nontail]letfold_resultt~init~f=debugt(fun()->fold_resultt~init~f)[@nontail]letfold_untilt~init~f~finish=debugt(fun()->fold_untilt~init~f~finish)[@nontail];;letfilter_mapt~f=debugt(fun()->filter_mapt~f)[@nontail]letfiltert~f=debugt(fun()->filtert~f)[@nontail]letfilter_inplacet~f=debugt(fun()->filter_inplacet~f)[@nontail]endmoduleTest(Stack:S):Swithtype'at='aStack.t=(* This signature is here to remind us to add a unit test whenever we add something to the
stack interface. *)structopenStacktypenonrec'at='atincludeTest_container.Test_S1(Stack)lett_sexp_grammar=t_sexp_grammarletinvariant=invariantletcreate=createletis_empty=is_emptylettop_exn=top_exnletpop_exn=pop_exnletpop=poplettop=topletsingleton=singletonlet%test_unit_=letempty=create()ininvariantignoreempty;invariant(funb->assertb)(of_list[true]);assert(is_emptyempty);lett=create()inpusht0;assert(not(is_emptyt));assert(Exn.does_raise(fun()->top_exnempty));lett=create()inpusht0;[%test_result:int](top_exnt)~expect:0;assert(Exn.does_raise(fun()->pop_exnempty));lett=create()inpusht0;[%test_result:int](pop_exnt)~expect:0;assert(Option.is_none(popempty));assert(Option.is_some(pop(of_list[0])));assert(Option.is_none(topempty));assert(Option.is_some(top(of_list[0])));assert(Option.is_some(top(singleton0)));assert(Option.is_some(pop(singleton0)));assert(lett=singleton0inignore(pop_exnt:int);Option.is_none(topt));;letmin_elt=min_eltletmax_elt=max_eltlet%test_unit_=letempty=create()in[%test_result:_option](min_elt~compare:Int.compareempty)~expect:None;[%test_result:_option](max_elt~compare:Int.compareempty)~expect:None;[%test_result:int](sum(moduleInt)~f:Fn.idempty)~expect:0;;letpush=pushletcopy=copyletuntil_empty=until_emptylet%test_unit_=lett=lett=create()inpusht0;pusht1;pusht2;tin[%test_result:bool](is_emptyt)~expect:false;[%test_result:int](lengtht)~expect:3;[%test_result:intoption](topt)~expect:(Some2);[%test_result:int](top_exnt)~expect:2;[%test_result:intoption](min_elt~compare:Int.comparet)~expect:(Some0);[%test_result:intoption](max_elt~compare:Int.comparet)~expect:(Some2);[%test_result:int](sum(moduleInt)~f:Fn.idt)~expect:3;lett'=copytin[%test_result:int](pop_exnt')~expect:2;[%test_result:int](pop_exnt')~expect:1;[%test_result:int](pop_exnt')~expect:0;[%test_result:int](lengtht')~expect:0;[%test_result:bool](is_emptyt')~expect:true;lett'=copytin[%test_result:intoption](popt')~expect:(Some2);[%test_result:intoption](popt')~expect:(Some1);[%test_result:intoption](popt')~expect:(Some0);[%test_result:int](lengtht')~expect:0;[%test_result:bool](is_emptyt')~expect:true;(* test that t was not modified by pops applied to copies *)[%test_result:int](lengtht)~expect:3;[%test_result:int](top_exnt)~expect:2;[%test_result:intlist](to_listt)~expect:[2;1;0];[%test_result:intarray](to_arrayt)~expect:[|2;1;0|];[%test_result:int](lengtht)~expect:3;[%test_result:int](top_exnt)~expect:2;lett'=copytinletn=ref0inuntil_emptyt'(funx->n:=!n+x);[%test_result:int]!n~expect:3;[%test_result:bool](is_emptyt')~expect:true;[%test_result:int](lengtht')~expect:0;;let%test_unit_=lett=create()in[%test_result:bool](is_emptyt)~expect:true;[%test_result:int](lengtht)~expect:0;[%test_result:_list](to_listt)~expect:[];[%test_result:_option](popt)~expect:None;pusht13;[%test_result:bool](is_emptyt)~expect:false;[%test_result:int](lengtht)~expect:1;[%test_result:intoption](min_elt~compare:Int.comparet)~expect:(Some13);[%test_result:intoption](max_elt~compare:Int.comparet)~expect:(Some13);[%test_result:int](sum(moduleInt)~f:Fn.idt)~expect:13;[%test_result:int](pop_exnt)~expect:13;[%test_result:bool](is_emptyt)~expect:true;[%test_result:int](lengtht)~expect:0;pusht13;pusht14;[%test_result:bool](is_emptyt)~expect:false;[%test_result:int](lengtht)~expect:2;[%test_result:intlist](to_listt)~expect:[14;13];[%test_result:intoption](min_elt~compare:Int.comparet)~expect:(Some13);[%test_result:intoption](max_elt~compare:Int.comparet)~expect:(Some14);[%test_result:int](sum(moduleInt)~f:Fn.idt)~expect:27;[%test_result:bool](Option.is_some(popt))~expect:true;[%test_result:bool](Option.is_some(popt))~expect:true;;letof_list=of_listlet%test_unit_=forn=0to5doletl=List.initn~f:Fn.idin[%test_result:intlist](to_list(of_listl))~expect:ldone;;letclear=clearlet%test_unit_=forn=0to5dolett=of_list(List.initn~f:Fn.id)incleart;assert(is_emptyt);pusht13;[%test_result:int](lengtht)~expect:1done;;let%test_unit"float test"=lets=create()inpushs1.0;pushs2.0;pushs3.0;;letfilter_map=filter_maplet%test_unit"filter_map"=lets=create()inpushs0;pushs1;pushs2;pushs3;[%test_result:intlist](to_lists)~expect:[3;2;1;0];lets=filter_maps~f:(funi->ifi%2<>0thenSome(i*2)elseNone)in[%test_result:intlist](to_lists)~expect:[6;2];lets=filter_maps~f:(funi->ifi<4thenSome(i*2)elseNone)in[%test_result:intlist](to_lists)~expect:[4];;letfilter=filterlet%test_unit"filter"=lets=create()inpushs0;pushs1;pushs2;pushs3;[%test_result:intlist](to_lists)~expect:[3;2;1;0];lets=filters~f:(funi->i%2<>0)in[%test_result:intlist](to_lists)~expect:[3;1];lets=filters~f:(funi->i<2)in[%test_result:intlist](to_lists)~expect:[1];;letfilter_inplace=filter_inplacelet%test_unit"filter_inplace"=lets=create()inpushs0;pushs1;pushs2;pushs3;[%test_result:intlist](to_lists)~expect:[3;2;1;0];filter_inplaces~f:(funi->i%2<>0);[%test_result:intlist](to_lists)~expect:[3;1];filter_inplaces~f:(funi->i<2);[%test_result:intlist](to_lists)~expect:[1];;let%test_unit"filter_inplace raises after removing"=lets=create()inpushs0;pushs1;pushs2;pushs3;[%test_result:intlist](to_lists)~expect:[3;2;1;0];assert(Exn.does_raise(fun()->filter_inplaces~f:(funi->ifInt.(i=2)thenraise_s[%message"exn"]elsefalse)));[%test_result:intlist](to_lists)~expect:[];;let%test_unit"filter_inplace raises after keeping"=lets=create()inpushs0;pushs1;pushs2;pushs3;[%test_result:intlist](to_lists)~expect:[3;2;1;0];assert(Exn.does_raise(fun()->filter_inplaces~f:(funi->ifInt.(i=2)thenraise_s[%message"exn"]elsetrue)));[%test_result:intlist](to_lists)~expect:[1;0];;end