123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444openCoremoduleUnderlying=structtypet=|StringofString.t|BigstringofBigstring.t|Charofcharletlength=function|Stringstr->String.lengthstr|Bigstringstr->Bigstring.lengthstr|Char_->1;;letblit_bytes~src=matchsrcwith|Stringsrc->fun?(src_pos=0)?src_len:(len=String.lengthsrc)~dst?(dst_pos=0)()->Bytes.From_string.blit~src~src_pos~len~dst~dst_pos|Bigstringsrc->Bigstring.To_bytes.blito~src|Charc->fun?(src_pos=0)?(src_len=1)~dst?(dst_pos=0)()->(matchsrc_pos,src_lenwith|0,1->Bytes.setdstdst_posc|(0|1),0->()|_,_->invalid_arg"index out of bounds");;letblit_bigstring~src=matchsrcwith|Stringsrc->Bigstring.From_string.blito~src|Bigstringsrc->Bigstring.blito~src|Charc->fun?(src_pos=0)?(src_len=1)~dst?(dst_pos=0)()->(matchsrc_pos,src_lenwith|0,1->dst.{dst_pos}<-c|(0|1),0->()|_,_->invalid_arg"index out of bounds");;letoutput_channel~channel=function|Stringstr->Out_channel.output_stringchannelstr|Bigstringbstr->Bigstring_unix.really_outputchannelbstr|Charc->Out_channel.output_charchannelc;;letoutput_unix~writer=function|Stringstr->Async.Writer.writewriterstr|Bigstringbstr->Async.Writer.write_bigstringwriterbstr|Charc->Async.Writer.write_charwriterc;;letoutput_bigbuffer~bigbuffer=function|Strings->Bigbuffer.add_stringbigbuffers|Bigstringbstr->Bigbuffer.add_bigstringbigbufferbstr|Charc->Bigbuffer.add_charbigbufferc;;letis_substr_string?(pos=0)?lent~string=letlen=Option.valuelen~default:(lengtht-pos)iniflen<>Substring.lengthstring||pos<0||pos+len>lengthtthenfalseelse(letget=matchtwith|Stringstr->String.getstr|Bigstringbstr->Bigstring.getbstr|Charc->constcinletrecloopi=ifi>=lenthentrueelseChar.equal(get(i+pos))(Substring.getstringi)&&loop(i+1)inloop0);;letis_substr_prefix?(pos=0)?lent~prefix=letlen=Option.valuelen~default:(lengtht-pos)iniflen<Substring.lengthprefixthenfalseelseis_substr_string~pos~len:(Substring.lengthprefix)t~string:prefix;;letis_substr_suffix?(pos=0)?lent~suffix=letlen=Option.valuelen~default:(lengtht-pos)iniflen<Substring.lengthsuffixthenfalseelseis_substr_string~pos:(pos+len-Substring.lengthsuffix)~len:(Substring.lengthsuffix)t~string:suffix;;letis_substr_substring?(pos=0)?lent~substring=letlen=Option.valuelen~default:(lengtht-pos)iniflen<Substring.lengthsubstringthenfalseelse(letrecloopi=ifi+Substring.lengthsubstring>lenthenfalseelseis_substr_string~pos:(pos+i)~len:(Substring.lengthsubstring)t~string:substring||loop(i+1)inloop0);;endtypet=|Listof(int*tlist)|LeafofUnderlying.tletempty=List(0,[])letof_strings=ifString.is_emptysthenemptyelseLeaf(Underlying.Strings)letof_bigstringbs=if0=Bigstring.lengthbsthenemptyelseLeaf(Underlying.Bigstringbs);;letof_charc=Leaf(Underlying.Charc)letnl=of_char'\n'letlength=function|List(len,_)->len|Leafunderlying->Underlying.lengthunderlying;;letis_emptyt=lengtht=0(**
The plus operation is not associative over individual representations,
but is associative over the quotient space with the equivalence
relationship
x ~ y == (to_string x) = (to_string y)
*)letplusab=matcha,bwith|b,List(0,_)->b|List(0,_),b->b|List(len,_),b->List(len+lengthb,[a;b])|Leafa',List(len,l)->List(Underlying.lengtha'+len,a::l)|Leafx,Leafy->List(Underlying.(lengthx+lengthy),[a;b]);;letconcat?(sep=empty)ts=(* Fold right is more efficient than fold_left, as it will create a
flat List node *)matchtswith|[]->empty|t::ts->plust(List.fold_rightts~f:(funtts->plussep(plustts))~init:empty);;letconcat_underlying~of_underlying?sepstrs=letsep=Option.mapsep~f:of_underlyinginletts=List.mapstrs~f:of_underlyinginconcat?septs;;letconcat_string=concat_underlying~of_underlying:of_string(*
let __UNUSED_VALUE__concat_bigstring =
concat_underlying ~of_underlying:of_bigstring;;
*)typeblitter=src:Underlying.t->?src_pos:int->?src_len:int->?dst_pos:int->unit->unitletblit~(dst_blit:blitter)t=letrecblitdst_post=matchtwith|Leafsrc->dst_blit~src~dst_pos()|List(len,srcs)->letlen'=List.fold_leftsrcs~init:dst_pos~f:(fundst_post->blitdst_post;dst_pos+lengtht)inassert(len'-dst_pos=len)inblit0t;;letto_stringt=letdst=Bytes.create(lengtht)inblit~dst_blit:(Underlying.blit_bytes~dst)t;Bytes.unsafe_to_string~no_mutation_while_string_reachable:dst;;letto_bigstringt=letdst=Bigstring.create(lengtht)inblit~dst_blit:(Underlying.blit_bigstring~dst)t;dst;;letoutput~dst_outputt=letrecoutputt=matchtwith|Leafunderlying->dst_outputunderlying|List(_,ts)->List.iter~f:outputtsinoutputt;;letoutput_channeltchannel=output~dst_output:(Underlying.output_channel~channel)tletoutput_unixtwriter=output~dst_output:(Underlying.output_unix~writer)tletoutput_bigbuffertbigbuffer=output~dst_output:(Underlying.output_bigbuffer~bigbuffer)t;;letrecfoldt~init~f=matchtwith|List(_,list)->List.foldlist~init~f:(funinitt->foldt~init~f)|Leafunderlying->finitunderlying;;letitert~f=foldt~init:()~f:(fun()->f)letrecis_substr_stringt~string=ifis_emptyt&&Substring.is_emptystringthentrueelseiflengtht<>Substring.lengthstringthenfalseelse(matchtwith|Leafu->Underlying.is_substr_stringu~string|List(len,ts)->assert(len=Substring.lengthstring);List.fold_untilts~init:0~f:(funpost->ifis_substr_stringt~string:(Substring.substring~pos~len:(lengtht))thenContinue_or_stop.Continue(pos+lengtht)elseContinue_or_stop.Stopfalse)~finish:(Fn.consttrue));;letsubstring_splitsubstring~pos=Substring.subsubstring~len:pos,Substring.subsubstring~pos;;letrecis_substr_suffixt~suffix=ifSubstring.lengthsuffix=0thentrueelseiflengtht<Substring.lengthsuffixthenfalseelse(matchtwith|Leafu->Underlying.is_substr_suffixu~suffix|List(_,[])->Substring.is_emptysuffix|List(_,[t])->is_substr_suffixt~suffix|List(len,hd::tl)->lettl=List(len-lengthhd,tl)iniflengthtl>=Substring.lengthsuffixthenis_substr_suffixtl~suffixelse(lethd_part,tl_part=substring_splitsuffix~pos:(Substring.lengthsuffix-lengthtl)inis_substr_suffixhd~suffix:hd_part&&is_substr_stringtl~string:tl_part));;letrecis_substr_prefixt~prefix=ifSubstring.is_emptyprefixthentrueelseiflengtht<Substring.lengthprefixthenfalseelse(matchtwith|Leafu->Underlying.is_substr_prefixu~prefix|List(_,[])->Substring.is_emptyprefix|List(len,hd::tl)->iflengthhd>=Substring.lengthprefixthenis_substr_prefixhd~prefixelse(lettl=List(len-lengthhd,tl)inlethd_part,tl_part=substring_splitprefix~pos:(lengthhd)inis_substr_stringhd~string:hd_part&&is_substr_prefixtl~prefix:tl_part));;letrecis_substr_substringt~substring=ifSubstring.is_emptysubstringthentrueelseiflengtht<Substring.lengthsubstringthenfalseelse(matchtwith|Leafu->Underlying.is_substr_substringu~substring|List(_,[])->Substring.is_emptysubstring|List(_,[t])->is_substr_substringt~substring|List(len,hd::tl)->lettl=List(len-lengthhd,tl)inletrecsuffix_looppos=ifpos<=0thenis_substr_substringtl~substringelse(lethd_part,tl_part=substring_splitsubstring~posin(is_substr_suffixhd~suffix:hd_part&&is_substr_prefixtl~prefix:tl_part)||suffix_loop(pos-1))inis_substr_substringhd~substring||suffix_loop(Substring.lengthsubstring-1));;letis_stringt~string=is_substr_stringt~string:(Substring.create(Bytes.unsafe_of_string_promise_no_mutationstring));;letis_prefixt~prefix=is_substr_prefixt~prefix:(Substring.create(Bytes.unsafe_of_string_promise_no_mutationprefix));;letis_suffixt~suffix=is_substr_suffixt~suffix:(Substring.create(Bytes.unsafe_of_string_promise_no_mutationsuffix));;letis_substringt~substring=is_substr_substringt~substring:(Substring.create(Bytes.unsafe_of_string_promise_no_mutationsubstring));;let%test_module_=(modulestructlethaystack=concat~sep:(of_char' ')[of_string"hello";of_bigstring(Bigstring.of_string"big");of_string"world"];;let%expect_test"is_string"=printf"%b"(is_stringhaystack~string:"hello big world");[%expect{| true |}];printf"%b"(is_stringhaystack~string:"hello");[%expect{| false |}];printf"%b"(is_stringhaystack~string:"o big");[%expect{| false |}];;let%expect_test"is_prefix"=printf"%b"(is_prefixhaystack~prefix:"");[%expect{| true |}];printf"%b"(is_prefixhaystack~prefix:"h");[%expect{| true |}];printf"%b"(is_prefixhaystack~prefix:"hello");[%expect{| true |}];printf"%b"(is_prefixhaystack~prefix:"hello ");[%expect{| true |}];printf"%b"(is_prefixhaystack~prefix:"hello b");[%expect{| true |}];printf"%b"(is_prefixhaystack~prefix:"hello big");[%expect{| true |}];printf"%b"(is_prefixhaystack~prefix:"hello big world");[%expect{| true |}];printf"%b"(is_prefixhaystack~prefix:"hello big round");[%expect{| false |}];printf"%b"(is_prefixhaystack~prefix:"hello big world!");[%expect{| false |}];printf"%b"(is_prefixhaystack~prefix:"b");[%expect{| false |}];printf"%b"(is_prefixhaystack~prefix:"world");[%expect{| false |}];printf"%b"(is_prefixhaystack~prefix:"d");[%expect{| false |}];;let%expect_test"is_suffix"=printf"%b"(is_suffixhaystack~suffix:"");[%expect{| true |}];printf"%b"(is_suffixhaystack~suffix:"d");[%expect{| true |}];printf"%b"(is_suffixhaystack~suffix:"world");[%expect{| true |}];printf"%b"(is_suffixhaystack~suffix:" world");[%expect{| true |}];printf"%b"(is_suffixhaystack~suffix:"g world");[%expect{| true |}];printf"%b"(is_suffixhaystack~suffix:"big world");[%expect{| true |}];printf"%b"(is_suffixhaystack~suffix:"hello big world");[%expect{| true |}];printf"%b"(is_suffixhaystack~suffix:"round world");[%expect{| false |}];printf"%b"(is_suffixhaystack~suffix:"hello big world!");[%expect{| false |}];printf"%b"(is_suffixhaystack~suffix:"hello");[%expect{| false |}];;let%expect_test"is_substring"=printf"%b"(is_substringhaystack~substring:"");[%expect{| true |}];printf"%b"(is_substringhaystack~substring:"w");[%expect{| true |}];printf"%b"(is_substringhaystack~substring:"d");[%expect{| true |}];printf"%b"(is_substringhaystack~substring:"big");[%expect{| true |}];printf"%b"(is_substringhaystack~substring:"ell");[%expect{| true |}];printf"%b"(is_substringhaystack~substring:"ell");[%expect{| true |}];printf"%b"(is_substringhaystack~substring:"o b");[%expect{| true |}];printf"%b"(is_substringhaystack~substring:"o big w");[%expect{| true |}];printf"%b"(is_substringhaystack~substring:"hello big world");[%expect{| true |}];printf"%b"(is_substringhaystack~substring:"hello big world!");[%expect{| false |}];printf"%b"(is_substringhaystack~substring:"big world");[%expect{| true |}];printf"%b"(is_substringhaystack~substring:"hello big");[%expect{| true |}];printf"%b"(is_substringhaystack~substring:"hello big ");[%expect{| true |}];printf"%b"(is_substringhaystack~substring:"hello big round");[%expect{| false |}];printf"%b"(is_substringhaystack~substring:"round");[%expect{| false |}];printf"%b"(is_substringhaystack~substring:"big round");[%expect{| false |}];;end);;