aboutsummaryrefslogblamecommitdiffstats
path: root/erts/emulator/test/map_SUITE.erl
blob: 1b9758c23ec134f8daab09f72d85f499cad9203c (plain) (tree)
1
2
3
4
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652



                                                   










                                                                           








                                                                    


                                                                           
                                   

                                                 




                                                     
                        

                               
                      
                        
                     
                   
 
                            



                           
                          


                           
                           





                              
                              
                                 
 

                                   
                                
 





                               
               
                            
                                
                               


                  


                                  
                           

                   
       
 
                                                
 






                                                                               
              

          


                                                                       
                                 

                                             




                                                 
                      
                                   



                                                      



                                                       


                                               
                                           
                               
                             
 

                                   
                              
 




                                    
                          
                          
                              
                             

                


                                  
                         

                 













                                                          




                                                                    
                                                          
                                                                
                                                                       
                                                                     

                                                                            


                                                   
                                                         
 
                                                             
 
                                                      


                                                                                    
                                                                                     




                                                                                               




                                                                      

       






































































































































































































































































































































































































































































                                                                                                                
 







                                                          
                                         
                               

                              
                                          
                                          
 


                                                                        










                                                                    
                   



                                             

       







                                      


                                                










                                        
                                   

























































                                                                                                                 

                                                 
                                                      



                                                       
 





                                                           

                                                                    
                                                                                                 








                                                                   
                                                                    

       




































































                                                                                                                 
                                                   

                                                                                                                 

                                                                 








                                                                                                    


                                                                                              
 




                                                  

       
                                              



                                                
                                                                                                                  





                                         



                                                      
















































                                                                                                         
 















                                                                                                            

       
                                              








                                                        








                                                         
                    









                                                                         

       
 










































































                                                                                                            



                                                                      


       




















                                                                                
 




























                                                                                 
 




                                                                                            





































































                                                                                                            




                                                           
 




                                                                        







                                                              



























































                                                                                                            











                                                              






                                                                        


                                          

                                                                 




                           



































                                                                                                




                                                     
                                                                                       

                                                                      
                                                

                                                   
                                                   

                                                    
                                                   

                                                        
                                                   










                                                      
                                                          




                                                                



                                           


                                        


            


                                        











                                 
                                                                                                        


















                                                                         
                                           






                                                         

                                           


                                                                       





























                                                                           


                                                       


                                                                                                              

       
















                                                                                            

                                             


                                     

                                     
                                                       
                                                       

       
                      
                            
                                              

                                                                                 

                                                                                    

























                                                                      
                           



                                                          
                                   



                                                                         
                           


                                                          
                                          
                        
                                                                     




                                                
                                          
                        
                                                                     









                                                

                                       
                

                                                             




























                                                                    



                                                        
 

                                         
                  

                                                               




















                                                         
                       








                                                                            
                      
 


                                                                                               









                                                        
                      

                                            

                      


                                            


                         

      

                                                








                                                  
              
               
                       
                                               
                                              


                                                                                    
                                                            
                                                                              
                            
                                                 

                                           

                                                                    


                                               
                                                                                  















                                                   




















                                                                                               

                                             
                



                                                                    
 











                                                                           
 










                                                                        


                                              
                


                                                  
                                                      
 
                                                               
                                                                           
 











                                                                           

                 

                                       



                                                                                             



                                                                 





                                                                              









                                        
                                      




                                                        

                 



                                                                   


                                              
                        
 

                                                                                    


                                                                              
                                                       

                 



                                                                 


                                             

                                      

       



















                                                                                               

































                                                                            








                                                   
                 







                                                                  


       



                                                                
                                                            
 

                                                 
 
                                              
 

                                                   
 
                                                                              
 

                                                               
 
                                                                                    
 

                                                                               
 
                                                         
 

                                                                                 
 

                                                                                          

                                                                                 
 
                 



                                                                

       






                                                          

                                                

                                                     


                                                                
                               

                                                                            
 
                              

                                                                        
 
                                    

                                                              
 
                                               

                                                
 


                            
 

                                    
 
                                                                                                             

                 



                                                                   

        



















                                                                                                     



                                                                   


       


                                                
                          
                               
 

                                                                                           
 

                                                                              

                                                                        
 







                                                                            
                 



                                                                   



                                             







                               
                                  



                                                                                         
 

                                                       
 
                                           
                               

                                     


                                             

       

                       




                                                                                               
 

                                                             

                                           
                               

                                     


                                                  



                      




                                                                                          
 

                                                         

                                           
                               

                                     


                                              


       




















                                                       



                                                      
                                                                                   



                                                                                
                                                    
                                                       



                                                     

            













                                                                        






                                                                      
 



                                                                    
                           
                                               
                                            






                                                          


                                            
                                                                                     








                                                                                   
                          
                                   

                                                                             





                                                                   
                                                         

                                          










                                                                                     


                                                 
                           





                                                                                     
 


                                                                                    

                  



                                                 



                                                   

                             

                             
                                                      

                                                            
 
                                                  

                                                       
                                                                             

                                                        
                                                                               
                                                                      
 






                                                    
                  



                                                                       

                                                          

       




























                                                                                     
                                                         

                             
                                          

                             
                                    

                              

                      
                              

                      

                              
                                                  








                                                     
                            

                           
                                                     


                                                                 







                                                                           

                             




































                                                                                          




                                                                          

       



































                                                                       























                                              

 

                        
                          

                                                

                                                            




























                                                                    
       




































































                                                                                                 








































                                                                                                                   


                                                      
                                                     
 
                                

                         
                                

                         





























































                                                                        

       


































                                                                            
                                                                         
 

                                                                           

                      
                                                                              
                                  


                                                                              










































                                                                      

                                                                           


                       
                                                                   

               







                                                                                      













                                                                
                                            




                              
 

































                                                          


















































                                                                          



















                                                                
 

                                                                       





























































































                                                                                                   
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2013. All Rights Reserved.
%% 
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%% 
%% %CopyrightEnd%
%%
-module(map_SUITE).
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
	init_per_group/2,end_per_group/2
    ]).

-export([
	t_build_and_match_literals/1, t_build_and_match_literals_large/1,
	t_update_literals/1, t_update_literals_large/1,
        t_match_and_update_literals/1, t_match_and_update_literals_large/1,
	t_update_map_expressions/1,
	t_update_assoc/1, t_update_assoc_large/1,
        t_update_exact/1, t_update_exact_large/1,
	t_guard_bifs/1,
        t_guard_sequence/1, t_guard_sequence_large/1,
        t_guard_update/1, t_guard_update_large/1,
	t_guard_receive/1, t_guard_receive_large/1,
        t_guard_fun/1,
        t_update_deep/1,
	t_list_comprehension/1,
	t_map_sort_literals/1,
	t_map_equal/1,
	t_map_compare/1,
	t_map_size/1,
	t_is_map/1,

	%% Specific Map BIFs
	t_bif_map_get/1,
	t_bif_map_find/1,
	t_bif_map_is_key/1,
	t_bif_map_keys/1,
	t_bif_map_merge/1,
	t_bif_map_new/1,
	t_bif_map_put/1,
	t_bif_map_remove/1,
	t_bif_map_update/1,
	t_bif_map_values/1,
	t_bif_map_to_list/1,
	t_bif_map_from_list/1,

	%% erlang
	t_erlang_hash/1,
	t_map_encode_decode/1,
	t_gc_rare_map_overflow/1,

	%% non specific BIF related
	t_bif_build_and_check/1,
	t_bif_merge_and_check/1,

	%% maps module not bifs
	t_maps_fold/1,
	t_maps_map/1,
	t_maps_size/1,
	t_maps_without/1,

	%% misc
	t_hashmap_balance/1,
        t_erts_internal_order/1,
        t_erts_internal_hash/1,
	t_pdict/1,
	t_ets/1,
	t_dets/1,
	t_tracing/1,

	%% instruction-level tests
	t_has_map_fields/1,
	y_regs/1,
	badmap_17/1
    ]).

-include_lib("stdlib/include/ms_transform.hrl").

-define(CHECK(Cond,Term),
	case (catch (Cond)) of
	    true -> true;
	    _ -> io:format("###### CHECK FAILED ######~nINPUT:  ~p~n", [Term]),
		 exit(Term)
	end).

suite() -> [].

all() -> [
	t_build_and_match_literals, t_build_and_match_literals_large,
	t_update_literals, t_update_literals_large,
        t_match_and_update_literals, t_match_and_update_literals_large,
	t_update_map_expressions,
	t_update_assoc, t_update_assoc_large,
        t_update_exact, t_update_exact_large,
	t_guard_bifs,
        t_guard_sequence, t_guard_sequence_large,
        t_guard_update, t_guard_update_large,
	t_guard_receive, t_guard_receive_large,
        t_guard_fun, t_list_comprehension,
        t_update_deep,
	t_map_equal, t_map_compare,
	t_map_sort_literals,

	%% Specific Map BIFs
	t_bif_map_get,t_bif_map_find,t_bif_map_is_key,
	t_bif_map_keys, t_bif_map_merge, t_bif_map_new,
	t_bif_map_put,
	t_bif_map_remove, t_bif_map_update,
	t_bif_map_values,
	t_bif_map_to_list, t_bif_map_from_list,

	%% erlang
	t_erlang_hash, t_map_encode_decode,
	t_gc_rare_map_overflow,
	t_map_size, t_is_map,

	%% non specific BIF related
	t_bif_build_and_check,
	t_bif_merge_and_check,

	%% maps module
	t_maps_fold, t_maps_map,
	t_maps_size, t_maps_without,


        %% Other functions
	t_hashmap_balance,
        t_erts_internal_order,
        t_erts_internal_hash,
	t_pdict,
	t_ets,
	t_tracing,

	%% instruction-level tests
	t_has_map_fields,
	y_regs,
	badmap_17
    ].

groups() -> [].

init_per_suite(Config) -> Config.
end_per_suite(_Config) -> ok.

init_per_group(_GroupName, Config) -> Config.
end_per_group(_GroupName, Config) -> Config.

%% tests

t_build_and_match_literals(Config) when is_list(Config) ->
    #{} = id(#{}),
    #{1:=a} = id(#{1=>a}),
    #{1:=a,2:=b} = id(#{1=>a,2=>b}),
    #{1:=a,2:=b,3:="c"} = id(#{1=>a,2=>b,3=>"c"}),
    #{1:=a,2:=b,3:="c","4":="d"} = id(#{1=>a,2=>b,3=>"c","4"=>"d"}),
    #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>} =
	id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>}),
    #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f"} =
	id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f"}),
    #{1:=a,2:=b,3:="c","4":="d",<<"5">>:=<<"e">>,{"6",7}:="f",8:=g} =
	id(#{1=>a,2=>b,3=>"c","4"=>"d",<<"5">>=><<"e">>,{"6",7}=>"f",8=>g}),

    #{[]:=a,42.0:=b,x:={x,y},[a,b]:=list} =
         id(#{[]=>a,42.0=>b,x=>{x,y},[a,b]=>list}),

    #{<<"hi all">> := 1} = id(#{<<"hi",32,"all">> => 1}),

    #{a:=X,a:=X=3,b:=4} = id(#{a=>3,b=>4}), % weird but ok =)

    #{ a:=#{ b:=#{c := third, b:=second}}, b:=first} =
	id(#{ b=>first, a=>#{ b=>#{c => third, b=> second}}}),

    M = #{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first},
    M = #{ map_1:=#{ map_2:=#{value_3 := third}, value_2:= second}, value_1:=first} =
	 id(#{ map_1=>#{ map_2=>#{value_3 => third}, value_2=> second}, value_1=>first}),

    %% error case
    %V = 32,
    %{'EXIT',{{badmatch,_},_}} = (catch (#{<<"hi all">> => 1} = id(#{<<"hi",V,"all">> => 1}))),
    {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3,x:=2} = id(#{x=>3}))),
    {'EXIT',{{badmatch,_},_}} = (catch (#{x:=2} = id(#{x=>3}))),
    {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id({a,b,c}))),
    {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{y=>3}))),
    {'EXIT',{{badmatch,_},_}} = (catch (#{x:=3} = id(#{x=>"three"}))),
    ok.

t_build_and_match_literals_large(Config) when is_list(Config) ->
    % normal non-repeating
    M0 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
               11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
               12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
               13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
               14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

               15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
               16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
               17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
               18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
               19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" }),

    #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M0,
    #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M0,
    #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M0,
    #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M0,
    #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M0,

    #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M0,
    #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M0,
    #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M0,
    #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M0,
    #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M0,

    60 = map_size(M0),
    60 = maps:size(M0),

    % with repeating
    M1 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
               11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
               12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
               13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
               14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

               10=>na0,20=>nb0,30=>"nc0","40"=>"nd0",<<"50">>=>"ne0",{["00"]}=>"n10",
               11=>na1,21=>nb1,31=>"nc1","41"=>"nd1",<<"51">>=>"ne1",{["01"]}=>"n11",
               12=>na2,22=>nb2,32=>"nc2","42"=>"nd2",<<"52">>=>"ne2",{["02"]}=>"n12",

               15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
               16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
               17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",

               13=>na3,23=>nb3,33=>"nc3","43"=>"nd3",<<"53">>=>"ne3",{["03"]}=>"n13",
               14=>na4,24=>nb4,34=>"nc4","44"=>"nd4",<<"54">>=>"ne4",{["04"]}=>"n14",

               18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
               19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" }),

    #{10:=na0,20:=nb0,30:="nc0","40":="nd0",<<"50">>:="ne0",{["00"]}:="n10"} = M1,
    #{11:=na1,21:=nb1,31:="nc1","41":="nd1",<<"51">>:="ne1",{["01"]}:="n11"} = M1,
    #{12:=na2,22:=nb2,32:="nc2","42":="nd2",<<"52">>:="ne2",{["02"]}:="n12"} = M1,
    #{13:=na3,23:=nb3,33:="nc3","43":="nd3",<<"53">>:="ne3",{["03"]}:="n13"} = M1,
    #{14:=na4,24:=nb4,34:="nc4","44":="nd4",<<"54">>:="ne4",{["04"]}:="n14"} = M1,

    #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M1,
    #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M1,
    #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M1,
    #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M1,
    #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M1,

    60 = map_size(M1),
    60 = maps:size(M1),

    % with floats

    M2 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
               11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
               12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
               13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
               14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

               15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
               16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
               17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
               18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
               19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19",

               10.0=>fa0,20.0=>fb0,30.0=>"fc0",
               11.0=>fa1,21.0=>fb1,31.0=>"fc1",
               12.0=>fa2,22.0=>fb2,32.0=>"fc2",
               13.0=>fa3,23.0=>fb3,33.0=>"fc3",
               14.0=>fa4,24.0=>fb4,34.0=>"fc4",

               15.0=>fa5,25.0=>fb5,35.0=>"fc5",
               16.0=>fa6,26.0=>fb6,36.0=>"fc6",
               17.0=>fa7,27.0=>fb7,37.0=>"fc7",
               18.0=>fa8,28.0=>fb8,38.0=>"fc8",
               19.0=>fa9,29.0=>fb9,39.0=>"fc9"}),

    #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M2,
    #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M2,
    #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M2,
    #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M2,
    #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M2,

    #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M2,
    #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M2,
    #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M2,
    #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M2,
    #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M2,

    #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M2,
    #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M2,
    #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M2,
    #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M2,
    #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M2,

    #{15.0:=fa5,25.0:=fb5,35.0:="fc5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M2,
    #{16.0:=fa6,26.0:=fb6,36.0:="fc6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M2,
    #{17.0:=fa7,27.0:=fb7,37.0:="fc7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M2,
    #{18.0:=fa8,28.0:=fb8,38.0:="fc8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M2,
    #{19.0:=fa9,29.0:=fb9,39.0:="fc9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M2,

    90 = map_size(M2),
    90 = maps:size(M2),

    % with bignums
    M3 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
               11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
               12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
               13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
               14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

               15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
               16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
               17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
               18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
               19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19",

               10.0=>fa0,20.0=>fb0,30.0=>"fc0",
               11.0=>fa1,21.0=>fb1,31.0=>"fc1",
               12.0=>fa2,22.0=>fb2,32.0=>"fc2",
               13.0=>fa3,23.0=>fb3,33.0=>"fc3",
               14.0=>fa4,24.0=>fb4,34.0=>"fc4",

               15.0=>fa5,25.0=>fb5,35.0=>"fc5",
               16.0=>fa6,26.0=>fb6,36.0=>"fc6",
               17.0=>fa7,27.0=>fb7,37.0=>"fc7",
               18.0=>fa8,28.0=>fb8,38.0=>"fc8",
               19.0=>fa9,29.0=>fb9,39.0=>"fc9",

               36893488147419103232=>big1,  73786976294838206464=>big2,
               147573952589676412928=>big3, 18446744073709551616=>big4,
               4294967296=>big5,            8589934592=>big6,
               4294967295=>big7,            67108863=>big8
             }),

    #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M3,
    #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M3,
    #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M3,
    #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M3,
    #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M3,

    #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3,
    #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3,
    #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3,
    #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3,
    #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3,

    #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M3,
    #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M3,
    #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M3,
    #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M3,
    #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M3,

    #{15.0:=fa5,25.0:=fb5,35.0:="fc5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3,
    #{16.0:=fa6,26.0:=fb6,36.0:="fc6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3,
    #{17.0:=fa7,27.0:=fb7,37.0:="fc7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3,
    #{18.0:=fa8,28.0:=fb8,38.0:="fc8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3,
    #{19.0:=fa9,29.0:=fb9,39.0:="fc9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3,

    #{36893488147419103232:=big1,67108863:=big8,"45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3,
    #{147573952589676412928:=big3,8589934592:=big6,"46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3,
    #{4294967296:=big5,18446744073709551616:=big4,"47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3,
    #{4294967295:=big7,73786976294838206464:=big2,"48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3,

    98 = map_size(M3),
    98 = maps:size(M3),

    %% with maps

    M4 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
               11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
               12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
               13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
               14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

               15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
               16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
               17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
               18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
               19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19",

               10.0=>fa0,20.0=>fb0,30.0=>"fc0",
               11.0=>fa1,21.0=>fb1,31.0=>"fc1",
               12.0=>fa2,22.0=>fb2,32.0=>"fc2",
               13.0=>fa3,23.0=>fb3,33.0=>"fc3",
               14.0=>fa4,24.0=>fb4,34.0=>"fc4",

               15.0=>fa5,25.0=>fb5,35.0=>"fc5",
               16.0=>fa6,26.0=>fb6,36.0=>"fc6",
               17.0=>fa7,27.0=>fb7,37.0=>"fc7",
               18.0=>fa8,28.0=>fb8,38.0=>"fc8",
               19.0=>fa9,29.0=>fb9,39.0=>"fc9",

               #{ one => small, map => key } => "small map key 1",
               #{ second => small, map => key } => "small map key 2",
               #{ third => small, map => key } => "small map key 3",

               #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
                  11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
                  12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
                  13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
                  14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

                  15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
                  16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
                  17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
                  18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
                  19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1",

               #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
                  11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
                  12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
                  13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
                  14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

                  15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
                  k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
                  17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
                  18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
                  19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }),

    #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M4,
    #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M4,
    #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M4,
    #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M4,
    #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M4,

    #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M4,
    #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M4,
    #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M4,
    #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M4,
    #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M4,

    #{ #{ one => small, map => key }    := "small map key 1",
       #{ second => small, map => key } := "small map key 2",
       #{ third => small, map => key }  := "small map key 3" } = M4,

    #{ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
          11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
          12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
          13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
          14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

          15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
          16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
          17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
          18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
          19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 1",

       #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
          11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
          12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
          13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
          14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

          15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
          k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
          17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
          18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
          19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 2" } = M4,


    #{ 15:=V1,25:=b5,35:=V2,"45":="d5",<<"55">>:=V3,{["05"]}:="15",
       #{ one => small, map => key }    := "small map key 1",
       #{ second => small, map => key } := V4,
       #{ third => small, map => key }  := "small map key 3",
       #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
          11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
          12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
          13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
          14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

          15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
          16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
          17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
          18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
          19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := V5 } = M4,

    a5   = V1,
    "c5" = V2,
    "e5" = V3,
    "small map key 2" = V4,
    "large map key 1" = V5,

    95 = map_size(M4),
    95 = maps:size(M4),

    % call for value

    M5 = id(#{ 10=>id(a0),20=>b0,30=>id("c0"),"40"=>"d0",<<"50">>=>id("e0"),{["00"]}=>"10",
               11=>id(a1),21=>b1,31=>id("c1"),"41"=>"d1",<<"51">>=>id("e1"),{["01"]}=>"11",
               12=>id(a2),22=>b2,32=>id("c2"),"42"=>"d2",<<"52">>=>id("e2"),{["02"]}=>"12",
               13=>id(a3),23=>b3,33=>id("c3"),"43"=>"d3",<<"53">>=>id("e3"),{["03"]}=>"13",
               14=>id(a4),24=>b4,34=>id("c4"),"44"=>"d4",<<"54">>=>id("e4"),{["04"]}=>"14",

               15=>id(a5),25=>b5,35=>id("c5"),"45"=>"d5",<<"55">>=>id("e5"),{["05"]}=>"15",
               16=>id(a6),26=>b6,36=>id("c6"),"46"=>"d6",<<"56">>=>id("e6"),{["06"]}=>"16",
               17=>id(a7),27=>b7,37=>id("c7"),"47"=>"d7",<<"57">>=>id("e7"),{["07"]}=>"17",
               18=>id(a8),28=>b8,38=>id("c8"),"48"=>"d8",<<"58">>=>id("e8"),{["08"]}=>"18",
               19=>id(a9),29=>b9,39=>id("c9"),"49"=>"d9",<<"59">>=>id("e9"),{["09"]}=>"19",

               10.0=>fa0,20.0=>id(fb0),30.0=>id("fc0"),
               11.0=>fa1,21.0=>id(fb1),31.0=>id("fc1"),
               12.0=>fa2,22.0=>id(fb2),32.0=>id("fc2"),
               13.0=>fa3,23.0=>id(fb3),33.0=>id("fc3"),
               14.0=>fa4,24.0=>id(fb4),34.0=>id("fc4"),

               15.0=>fa5,25.0=>id(fb5),35.0=>id("fc5"),
               16.0=>fa6,26.0=>id(fb6),36.0=>id("fc6"),
               17.0=>fa7,27.0=>id(fb7),37.0=>id("fc7"),
               18.0=>fa8,28.0=>id(fb8),38.0=>id("fc8"),
               19.0=>fa9,29.0=>id(fb9),39.0=>id("fc9"),

               #{ one => small, map => key } => id("small map key 1"),
               #{ second => small, map => key } => "small map key 2",
               #{ third => small, map => key } => "small map key 3",

               #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
                  11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
                  12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
                  13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
                  14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

                  15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
                  16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
                  17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
                  18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
                  19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1",

               #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
                  11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
                  12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
                  13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
                  14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

                  15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
                  k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
                  17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
                  18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
                  19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => id("large map key 2") }),

    #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M5,
    #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M5,
    #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M5,
    #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M5,
    #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M5,

    #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M5,
    #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M5,
    #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M5,
    #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M5,
    #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M5,

    #{ #{ one => small, map => key }    := "small map key 1",
       #{ second => small, map => key } := "small map key 2",
       #{ third => small, map => key }  := "small map key 3" } = M5,

    #{ #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
          11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
          12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
          13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
          14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

          15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
          16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
          17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
          18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
          19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 1",

       #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
          11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
          12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
          13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
          14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

          15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
          k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
          17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
          18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
          19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 2" } = M5,

    95 = map_size(M5),
    95 = maps:size(M5),

    %% remember

    #{10:=a0,20:=b0,30:="c0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M0,
    #{11:=a1,21:=b1,31:="c1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M0,
    #{12:=a2,22:=b2,32:="c2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M0,
    #{13:=a3,23:=b3,33:="c3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M0,
    #{14:=a4,24:=b4,34:="c4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M0,

    #{10:=na0,20:=nb0,30:="nc0","40":="nd0",<<"50">>:="ne0",{["00"]}:="n10"} = M1,
    #{11:=na1,21:=nb1,31:="nc1","41":="nd1",<<"51">>:="ne1",{["01"]}:="n11"} = M1,
    #{12:=na2,22:=nb2,32:="nc2","42":="nd2",<<"52">>:="ne2",{["02"]}:="n12"} = M1,
    #{13:=na3,23:=nb3,33:="nc3","43":="nd3",<<"53">>:="ne3",{["03"]}:="n13"} = M1,
    #{14:=na4,24:=nb4,34:="nc4","44":="nd4",<<"54">>:="ne4",{["04"]}:="n14"} = M1,

    #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M1,
    #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M1,
    #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M1,
    #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M1,
    #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M1,

    #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M2,
    #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M2,
    #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M2,
    #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M2,
    #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M2,

    #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M2,
    #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M2,
    #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M2,
    #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M2,
    #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M2,

    #{15:=a5,25:=b5,35:="c5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3,
    #{16:=a6,26:=b6,36:="c6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3,
    #{17:=a7,27:=b7,37:="c7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3,
    #{18:=a8,28:=b8,38:="c8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3,
    #{19:=a9,29:=b9,39:="c9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3,

    #{10.0:=fa0,20.0:=fb0,30.0:="fc0","40":="d0",<<"50">>:="e0",{["00"]}:="10"} = M3,
    #{11.0:=fa1,21.0:=fb1,31.0:="fc1","41":="d1",<<"51">>:="e1",{["01"]}:="11"} = M3,
    #{12.0:=fa2,22.0:=fb2,32.0:="fc2","42":="d2",<<"52">>:="e2",{["02"]}:="12"} = M3,
    #{13.0:=fa3,23.0:=fb3,33.0:="fc3","43":="d3",<<"53">>:="e3",{["03"]}:="13"} = M3,
    #{14.0:=fa4,24.0:=fb4,34.0:="fc4","44":="d4",<<"54">>:="e4",{["04"]}:="14"} = M3,

    #{15.0:=fa5,25.0:=fb5,35.0:="fc5","45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3,
    #{16.0:=fa6,26.0:=fb6,36.0:="fc6","46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3,
    #{17.0:=fa7,27.0:=fb7,37.0:="fc7","47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3,
    #{18.0:=fa8,28.0:=fb8,38.0:="fc8","48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3,
    #{19.0:=fa9,29.0:=fb9,39.0:="fc9","49":="d9",<<"59">>:="e9",{["09"]}:="19"} = M3,

    #{36893488147419103232:=big1,67108863:=big8,"45":="d5",<<"55">>:="e5",{["05"]}:="15"} = M3,
    #{147573952589676412928:=big3,8589934592:=big6,"46":="d6",<<"56">>:="e6",{["06"]}:="16"} = M3,
    #{4294967296:=big5,18446744073709551616:=big4,"47":="d7",<<"57">>:="e7",{["07"]}:="17"} = M3,
    #{4294967295:=big7,73786976294838206464:=big2,"48":="d8",<<"58">>:="e8",{["08"]}:="18"} = M3,

    ok.


t_map_size(Config) when is_list(Config) ->
    0 = map_size(id(#{})),
    1 = map_size(id(#{a=>1})),
    1 = map_size(id(#{a=>"wat"})),
    2 = map_size(id(#{a=>1, b=>2})),
    3 = map_size(id(#{a=>1, b=>2, b=>"3","33"=><<"n">>})),

    true = map_is_size(#{a=>1}, 1),
    true = map_is_size(#{a=>1, a=>2}, 1),
    M = #{ "a" => 1, "b" => 2},
    true  = map_is_size(M, 2),
    false = map_is_size(M, 3),
    true  = map_is_size(M#{ "a" => 2}, 2),
    false = map_is_size(M#{ "c" => 2}, 2),

    Ks = [build_key(fun(K) -> <<1,K:32,1>> end,I)||I<-lists:seq(1,100)],
    ok = build_and_check_size(Ks,0,#{}),

    %% try deep collisions
    %% statistically we get another subtree at 50k -> 100k elements
    %% Try to be nice and don't use too much memory in the testcase,

    N  = 500000,
    Is = lists:seq(1,N),
    N  = map_size(maps:from_list([{I,I}||I<-Is])),
    N  = map_size(maps:from_list([{<<I:32>>,I}||I<-Is])),
    N  = map_size(maps:from_list([{integer_to_list(I),I}||I<-Is])),
    N  = map_size(maps:from_list([{float(I),I}||I<-Is])),

    %% Error cases.
    do_badmap(fun(T) ->
                    {'EXIT',{{badmap,T},_}} =
                      (catch map_size(T))
              end),
    ok.

build_and_check_size([K|Ks],N,M0) ->
    N = map_size(M0),
    M1 = M0#{ K => K },
    build_and_check_size(Ks,N + 1,M1);
build_and_check_size([],N,M) ->
    N = map_size(M),
    ok.

map_is_size(M,N) when map_size(M) =:= N -> true;
map_is_size(_,_) -> false.

t_is_map(Config) when is_list(Config) ->
    true = is_map(#{}),
    true = is_map(#{a=>1}),
    false = is_map({a,b}),
    false = is_map(x),
    if is_map(#{}) -> ok end,
    if is_map(#{b=>1}) -> ok end,
    if not is_map([1,2,3]) -> ok end,
    if not is_map(x) -> ok end,
    ok.

% test map updates without matching
t_update_literals_large(Config) when is_list(Config) ->
    Map = id(#{ 10=>id(a0),20=>b0,30=>id("c0"),"40"=>"d0",<<"50">>=>id("e0"),{["00"]}=>"10",
                11=>id(a1),21=>b1,31=>id("c1"),"41"=>"d1",<<"51">>=>id("e1"),{["01"]}=>"11",
                12=>id(a2),22=>b2,32=>id("c2"),"42"=>"d2",<<"52">>=>id("e2"),{["02"]}=>"12",
                13=>id(a3),23=>b3,33=>id("c3"),"43"=>"d3",<<"53">>=>id("e3"),{["03"]}=>"13",
                14=>id(a4),24=>b4,34=>id("c4"),"44"=>"d4",<<"54">>=>id("e4"),{["04"]}=>"14",

                15=>id(a5),25=>b5,35=>id("c5"),"45"=>"d5",<<"55">>=>id("e5"),{["05"]}=>"15",
                16=>id(a6),26=>b6,36=>id("c6"),"46"=>"d6",<<"56">>=>id("e6"),{["06"]}=>"16",
                17=>id(a7),27=>b7,37=>id("c7"),"47"=>"d7",<<"57">>=>id("e7"),{["07"]}=>"17",
                18=>id(a8),28=>b8,38=>id("c8"),"48"=>"d8",<<"58">>=>id("e8"),{["08"]}=>"18",
                19=>id(a9),29=>b9,39=>id("c9"),"49"=>"d9",<<"59">>=>id("e9"),{["09"]}=>"19",

                10.0=>fa0,20.0=>id(fb0),30.0=>id("fc0"),
                11.0=>fa1,21.0=>id(fb1),31.0=>id("fc1"),
                12.0=>fa2,22.0=>id(fb2),32.0=>id("fc2"),
                13.0=>fa3,23.0=>id(fb3),33.0=>id("fc3"),
                14.0=>fa4,24.0=>id(fb4),34.0=>id("fc4"),

                15.0=>fa5,25.0=>id(fb5),35.0=>id("fc5"),
                16.0=>fa6,26.0=>id(fb6),36.0=>id("fc6"),
                17.0=>fa7,27.0=>id(fb7),37.0=>id("fc7"),
                18.0=>fa8,28.0=>id(fb8),38.0=>id("fc8"),
                19.0=>fa9,29.0=>id(fb9),39.0=>id("fc9"),

                #{ one => small, map => key } => id("small map key 1"),
                #{ second => small, map => key } => "small map key 2",
                #{ third => small, map => key } => "small map key 3",

                #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
                   11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
                   12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
                   13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
                   14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

                   15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
                   16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
                   17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
                   18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
                   19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1",

                #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
                   11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
                   12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
                   13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
                   14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

                   15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
                   k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
                   17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
                   18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
                   19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => id("large map key 2") }),

    #{x:="d",q:="4"} = loop_update_literals_x_q(Map, [
		{"a","1"},{"b","2"},{"c","3"},{"d","4"}
	]),
    ok.

t_update_literals(Config) when is_list(Config) ->
    Map = #{x=>1,y=>2,z=>3,q=>4},
    #{x:="d",q:="4"} = loop_update_literals_x_q(Map, [
		{"a","1"},{"b","2"},{"c","3"},{"d","4"}
	]),
    ok.


loop_update_literals_x_q(Map, []) -> Map;
loop_update_literals_x_q(Map, [{X,Q}|Vs]) ->
    loop_update_literals_x_q(Map#{q=>Q,x=>X},Vs).

% test map updates with matching
t_match_and_update_literals(Config) when is_list(Config) ->
    Map = #{ x=>0,y=>"untouched",z=>"also untouched",q=>1,
             #{ "one" => small, map => key } => "small map key 1" },
    #{x:=16,q:=21,y:="untouched",z:="also untouched"} = loop_match_and_update_literals_x_q(Map, [
	    {1,2},{3,4},{5,6},{7,8}
	]),
    M0 = id(#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
	    4 => number, 18446744073709551629 => wat}),
    M1 = id(#{}),
    M2 = M1#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
	4 => number, 18446744073709551629 => wat},
    M0 = M2,

    #{ 4 := another_number, int := 3 } = M2#{ 4 => another_number },
    ok.

t_match_and_update_literals_large(Config) when is_list(Config) ->
    Map = id(#{ 10=>id(a0),20=>b0,30=>id("c0"),"40"=>"d0",<<"50">>=>id("e0"),{["00"]}=>"10",
                11=>id(a1),21=>b1,31=>id("c1"),"41"=>"d1",<<"51">>=>id("e1"),{["01"]}=>"11",
                12=>id(a2),22=>b2,32=>id("c2"),"42"=>"d2",<<"52">>=>id("e2"),{["02"]}=>"12",
                13=>id(a3),23=>b3,33=>id("c3"),"43"=>"d3",<<"53">>=>id("e3"),{["03"]}=>"13",
                14=>id(a4),24=>b4,34=>id("c4"),"44"=>"d4",<<"54">>=>id("e4"),{["04"]}=>"14",

                15=>id(a5),25=>b5,35=>id("c5"),"45"=>"d5",<<"55">>=>id("e5"),{["05"]}=>"15",
                16=>id(a6),26=>b6,36=>id("c6"),"46"=>"d6",<<"56">>=>id("e6"),{["06"]}=>"16",
                17=>id(a7),27=>b7,37=>id("c7"),"47"=>"d7",<<"57">>=>id("e7"),{["07"]}=>"17",
                18=>id(a8),28=>b8,38=>id("c8"),"48"=>"d8",<<"58">>=>id("e8"),{["08"]}=>"18",
                19=>id(a9),29=>b9,39=>id("c9"),"49"=>"d9",<<"59">>=>id("e9"),{["09"]}=>"19",

                10.0=>fa0,20.0=>id(fb0),30.0=>id("fc0"),
                11.0=>fa1,21.0=>id(fb1),31.0=>id("fc1"),
                12.0=>fa2,22.0=>id(fb2),32.0=>id("fc2"),
                13.0=>fa3,23.0=>id(fb3),33.0=>id("fc3"),
                14.0=>fa4,24.0=>id(fb4),34.0=>id("fc4"),

                15.0=>fa5,25.0=>id(fb5),35.0=>id("fc5"),
                16.0=>fa6,26.0=>id(fb6),36.0=>id("fc6"),
                17.0=>fa7,27.0=>id(fb7),37.0=>id("fc7"),
                18.0=>fa8,28.0=>id(fb8),38.0=>id("fc8"),
                19.0=>fa9,29.0=>id(fb9),39.0=>id("fc9"),

                x=>0,y=>"untouched",z=>"also untouched",q=>1,

                #{ "one" => small, map => key } => id("small map key 1"),
                #{ second => small, map => key } => "small map key 2",
                #{ third => small, map => key } => "small map key 3",

                #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
                   11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
                   12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
                   13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
                   14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

                   15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
                   16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
                   17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
                   18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
                   19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1",

                #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
                   11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
                   12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
                   13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
                   14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

                   15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
                   k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
                   17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
                   18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
                   19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => id("large map key 2") }),

    #{x:=16,q:=21,y:="untouched",z:="also untouched"} = loop_match_and_update_literals_x_q(Map, [
	    {1,2},{3,4},{5,6},{7,8}
	]),
    M0 = id(Map#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
                  4 => number, 18446744073709551629 => wat}),
    M1 = id(Map#{}),
    M2 = M1#{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
              4 => number, 18446744073709551629 => wat},
    M0 = M2,

    #{ 4 := another_number, int := 3 } = M2#{ 4 => another_number },
    ok.


loop_match_and_update_literals_x_q(Map, []) -> Map;
loop_match_and_update_literals_x_q(#{ q:=Q0, x:=X0,
                                     #{ "one" => small, map => key } := "small map key 1" } = Map, [{X,Q}|Vs]) ->
    loop_match_and_update_literals_x_q(Map#{q=>Q0+Q,x=>X0+X},Vs).


t_update_map_expressions(Config) when is_list(Config) ->
    M = maps:new(),
    #{ a := 1 } = M#{a => 1},

    #{ b := 2 } = (maps:new())#{ b => 2 },

    #{ a :=42, b:=42, c:=42 } = (maps:from_list([{a,1},{b,2},{c,3}]))#{ a := 42, b := 42, c := 42 },
    #{ "a" :=1, "b":=42, "c":=42 } = (maps:from_list([{"a",1},{"b",2}]))#{ "b" := 42, "c" => 42 },
    Ks = lists:seq($a,$z),
    #{ "aa" := {$a,$a}, "ac":=41, "dc":=42 } =
        (maps:from_list([{[K1,K2],{K1,K2}}|| K1 <- Ks, K2 <- Ks]))#{ "ac" := 41, "dc" => 42 },

    %% Error cases.
    do_badmap(fun(T) ->
		      {'EXIT',{{badmap,T},_}} =
			  (catch (T)#{a:=42,b=>2})
	      end),
    ok.

t_update_assoc(Config) when is_list(Config) ->
    M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}),

    M1 = M0#{1=>42,2=>100,4=>[a,b,c]},
    #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1,
    #{1:=42,2:=b,4:=d,5:=e,2.0:=100,3.0:=c,4.0:=[a,b,c]} = M0#{1.0=>float,1:=42,2.0=>wrong,2.0=>100,4.0=>[a,b,c]},

    M2 = M0#{3.0=>new},
    #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2,
    M2 = M0#{3.0:=wrong,3.0=>new},

    %% Errors cases.
    do_badmap(fun(T) ->
		      {'EXIT',{{badmap,T},_}} =
			  (catch T#{nonexisting=>val})
	      end),
    ok.


t_update_assoc_large(Config) when is_list(Config) ->
    M0 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
               11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
               12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
               13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
               14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

               15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
               16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
               17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
               18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
               19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19",

               10.0=>fa0,20.0=>fb0,30.0=>"fc0",
               11.0=>fa1,21.0=>fb1,31.0=>"fc1",
               12.0=>fa2,22.0=>fb2,32.0=>"fc2",
               13.0=>fa3,23.0=>fb3,33.0=>"fc3",
               14.0=>fa4,24.0=>fb4,34.0=>"fc4",

               15.0=>fa5,25.0=>fb5,35.0=>"fc5",
               16.0=>fa6,26.0=>fb6,36.0=>"fc6",
               17.0=>fa7,27.0=>fb7,37.0=>"fc7",
               18.0=>fa8,28.0=>fb8,38.0=>"fc8",
               19.0=>fa9,29.0=>fb9,39.0=>"fc9",

               #{ one => small, map => key } => "small map key 1",
               #{ second => small, map => key } => "small map key 2",
               #{ third => small, map => key } => "small map key 3",

               #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
                  11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
                  12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
                  13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
                  14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

                  15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
                  16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
                  17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
                  18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
                  19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1",

               #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
                  11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
                  12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
                  13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
                  14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

                  15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
                  k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
                  17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
                  18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
                  19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }),


    M1 = M0#{1=>42,2=>100,4=>[a,b,c]},
    #{1:=42,2:=100,10.0:=fa0,4:=[a,b,c],25:=b5} = M1,
    #{ 10:=43, 24:=b4, 15:=a5, 35:="c5", 2.0:=100, 13.0:=fa3, 4.0:=[a,b,c]} =
        M0#{1.0=>float,10:=43,2.0=>wrong,2.0=>100,4.0=>[a,b,c]},

    M2 = M0#{13.0=>new},
    #{10:=a0,20:=b0,13.0:=new,"40":="d0",<<"50">>:="e0"} = M2,
    M2 = M0#{13.0:=wrong,13.0=>new},

    ok.

t_update_exact(Config) when is_list(Config) ->
    M0 = id(#{1=>a,2=>b,3.0=>c,4=>d,5=>e}),

    M1 = M0#{1:=42,2:=100,4:=[a,b,c]},
    #{1:=42,2:=100,3.0:=c,4:=[a,b,c],5:=e} = M1,
    M1 = M0#{1:=wrong,1=>42,2=>wrong,2:=100,4:=[a,b,c]},

    M2 = M0#{3.0:=new},
    #{1:=a,2:=b,3.0:=new,4:=d,5:=e} = M2,
    M2 = M0#{3.0=>wrong,3.0:=new},
    true = M2 =/= M0#{3=>right,3.0:=new},
    #{ 3 := right, 3.0 := new } = M0#{3=>right,3.0:=new},

    M3 = id(#{ 1 => val}),
    #{1 := update2,1.0 := new_val4} = M3#{
	1.0 => new_val1, 1 := update, 1=> update3,
	1 := update2, 1.0 := new_val2, 1.0 => new_val3,
	1.0 => new_val4 },

    %% Errors cases.
    do_badmap(fun(T) ->
		      {'EXIT',{{badmap,T},_}} =
			  (catch T#{nonexisting=>val})
	      end),
    Empty = id(#{}),
    {'EXIT',{{badkey,nonexisting},_}} = (catch Empty#{nonexisting:=val}),
    {'EXIT',{{badkey,nonexisting},_}} = (catch M0#{nonexisting:=val}),
    {'EXIT',{{badkey,1.0},_}} = (catch M0#{1.0:=v,1.0=>v2}),
    {'EXIT',{{badkey,42},_}} = (catch M0#{42.0:=v,42:=v2}),
    {'EXIT',{{badkey,42.0},_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}),

    ok.

t_update_exact_large(Config) when is_list(Config) ->
    M0 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
               11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
               12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
               13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
               14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

               15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
               16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
               17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
               18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
               19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19",

               10.0=>fa0,20.0=>fb0,30.0=>"fc0",
               11.0=>fa1,21.0=>fb1,31.0=>"fc1",
               12.0=>fa2,22.0=>fb2,32.0=>"fc2",
               13.0=>fa3,23.0=>fb3,33.0=>"fc3",
               14.0=>fa4,24.0=>fb4,34.0=>"fc4",

               15.0=>fa5,25.0=>fb5,35.0=>"fc5",
               16.0=>fa6,26.0=>fb6,36.0=>"fc6",
               17.0=>fa7,27.0=>fb7,37.0=>"fc7",
               18.0=>fa8,28.0=>fb8,38.0=>"fc8",
               19.0=>fa9,29.0=>fb9,39.0=>"fc9",

               #{ one => small, map => key } => "small map key 1",
               #{ second => small, map => key } => "small map key 2",
               #{ third => small, map => key } => "small map key 3",

               #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
                  11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
                  12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
                  13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
                  14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

                  15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
                  16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
                  17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
                  18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
                  19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1",

               #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
                  11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
                  12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
                  13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
                  14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

                  15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
                  k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
                  17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
                  18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
                  19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }),


    M1 = M0#{10:=42,<<"55">>:=100,10.0:=[a,b,c]},
    #{ 10:=42,<<"55">>:=100,{["05"]}:="15",10.0:=[a,b,c],
       #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
          11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
          12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
          13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
          14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

          15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
          16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
          17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
          18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
          19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } := "large map key 1" } = M1,

    M1 = M0#{10:=wrong,10=>42,<<"55">>=>wrong,<<"55">>:=100,10.0:=[a,b,c]},

    M2 = M0#{13.0:=new},
    #{10:=a0,20:=b0,13.0:=new} = M2,
    M2 = M0#{13.0=>wrong,13.0:=new},

    %% Errors cases.
    {'EXIT',{{badkey,nonexisting},_}} = (catch M0#{nonexisting:=val}),
    {'EXIT',{{badkey,1.0},_}} = (catch M0#{1.0:=v,1.0=>v2}),
    {'EXIT',{{badkey,42},_}} = (catch M0#{42.0:=v,42:=v2}),
    {'EXIT',{{badkey,42.0},_}} = (catch M0#{42=>v1,42.0:=v2,42:=v3}),

    ok.

t_update_deep(Config) when is_list(Config) ->
    N = 250000,
    M0 = maps:from_list([{integer_to_list(I),a}||I<-lists:seq(1,N)]),
    #{ "1" := a, "10" := a, "100" := a, "1000" := a, "10000" := a } = M0,

    M1 = M0#{ "1" := b, "10" := b, "100" := b, "1000" := b, "10000" := b },
    #{ "1" := a, "10" := a, "100" := a, "1000" := a, "10000" := a } = M0,
    #{ "1" := b, "10" := b, "100" := b, "1000" := b, "10000" := b } = M1,

    M2 = M0#{ "1" => c, "10" => c, "100" => c, "1000" => c, "10000" => c },
    #{ "1" := a, "10" := a, "100" := a, "1000" := a, "10000" := a } = M0,
    #{ "1" := b, "10" := b, "100" := b, "1000" := b, "10000" := b } = M1,
    #{ "1" := c, "10" := c, "100" := c, "1000" := c, "10000" := c } = M2,

    M3 = M2#{ "n1" => d, "n10" => d, "n100" => d, "n1000" => d, "n10000" => d },
    #{  "1" := a,  "10" := a,  "100" := a,  "1000" := a,  "10000" := a } = M0,
    #{  "1" := b,  "10" := b,  "100" := b,  "1000" := b,  "10000" := b } = M1,
    #{  "1" := c,  "10" := c,  "100" := c,  "1000" := c,  "10000" := c } = M2,
    #{  "1" := c,  "10" := c,  "100" := c,  "1000" := c,  "10000" := c } = M3,
    #{ "n1" := d, "n10" := d, "n100" := d, "n1000" := d, "n10000" := d } = M3,
    ok.

t_guard_bifs(Config) when is_list(Config) ->
    true   = map_guard_head(#{a=>1}),
    false  = map_guard_head([]),
    true   = map_guard_body(#{a=>1}),
    false  = map_guard_body({}),
    true   = map_guard_pattern(#{a=>1, <<"hi">> => "hi" }),
    false  = map_guard_pattern("list"),
    ok.

map_guard_head(M) when is_map(M) -> true;
map_guard_head(_) -> false.

map_guard_body(M) -> is_map(M).

map_guard_pattern(#{}) -> true;
map_guard_pattern(_)   -> false.

t_guard_sequence(Config) when is_list(Config) ->
	{1, "a"} = map_guard_sequence_1(#{seq=>1,val=>id("a")}),
	{2, "b"} = map_guard_sequence_1(#{seq=>2,val=>id("b")}),
	{3, "c"} = map_guard_sequence_1(#{seq=>3,val=>id("c")}),
	{4, "d"} = map_guard_sequence_1(#{seq=>4,val=>id("d")}),
	{5, "e"} = map_guard_sequence_1(#{seq=>5,val=>id("e")}),

	{1,M1}       = map_guard_sequence_2(M1 = id(#{a=>3})),
	{2,M2}       = map_guard_sequence_2(M2 = id(#{a=>4, b=>4})),
	{3,gg,M3}    = map_guard_sequence_2(M3 = id(#{a=>gg, b=>4})),
	{4,sc,sc,M4} = map_guard_sequence_2(M4 = id(#{a=>sc, b=>3, c=>sc2})),
	{5,kk,kk,M5} = map_guard_sequence_2(M5 = id(#{a=>kk, b=>other, c=>sc2})),

	%% error case
	{'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(#{seq=>6,val=>id("e")})),
	{'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(#{b=>5})),
	ok.

t_guard_sequence_large(Config) when is_list(Config) ->
    M0 = id(#{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00",03]}=>"10",
               11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01",03]}=>"11",
               12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02",03]}=>"12",
               13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03",03]}=>"13",
               14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04",03]}=>"14",

               15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05",03]}=>"15",
               16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06",03]}=>"16",
               17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07",03]}=>"17",
               18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08",03]}=>"18",
               19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09",03]}=>"19",

               10.0=>fa0,20.0=>fb0,30.0=>"fc0",
               11.0=>fa1,21.0=>fb1,31.0=>"fc1",
               12.0=>fa2,22.0=>fb2,32.0=>"fc2",
               13.0=>fa3,23.0=>fb3,33.0=>"fc3",
               14.0=>fa4,24.0=>fb4,34.0=>"fc4",

               15.0=>fa5,25.0=>fb5,35.0=>"fc5",
               16.0=>fa6,26.0=>fb6,36.0=>"fc6",
               17.0=>fa7,27.0=>fb7,37.0=>"fc7",
               18.0=>fa8,28.0=>fb8,38.0=>"fc8",
               19.0=>fa9,29.0=>fb9,39.0=>"fc9",

               #{ one => small, map => key } => "small map key 1",
               #{ second => small, map => key } => "small map key 2",
               #{ third => small, map => key } => "small map key 3",

               #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
                  11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
                  12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
                  13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
                  14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

                  15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
                  16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
                  17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
                  18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
                  19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1",

               #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
                  11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
                  12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
                  13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
                  14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

                  15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
                  k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
                  17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
                  18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
                  19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }),

	{1, "a"} = map_guard_sequence_1(M0#{seq=>1,val=>id("a")}),
	{2, "b"} = map_guard_sequence_1(M0#{seq=>2,val=>id("b")}),
	{3, "c"} = map_guard_sequence_1(M0#{seq=>3,val=>id("c")}),
	{4, "d"} = map_guard_sequence_1(M0#{seq=>4,val=>id("d")}),
	{5, "e"} = map_guard_sequence_1(M0#{seq=>5,val=>id("e")}),

	{1,M1}       = map_guard_sequence_2(M1 = id(M0#{a=>3})),
	{2,M2}       = map_guard_sequence_2(M2 = id(M0#{a=>4, b=>4})),
	{3,gg,M3}    = map_guard_sequence_2(M3 = id(M0#{a=>gg, b=>4})),
	{4,sc,sc,M4} = map_guard_sequence_2(M4 = id(M0#{a=>sc, b=>3, c=>sc2})),
	{5,kk,kk,M5} = map_guard_sequence_2(M5 = id(M0#{a=>kk, b=>other, c=>sc2})),

	{'EXIT',{function_clause,_}} = (catch map_guard_sequence_1(M0#{seq=>6,val=>id("e")})),
	{'EXIT',{function_clause,_}} = (catch map_guard_sequence_2(M0#{b=>5})),
        ok.


map_guard_sequence_1(#{seq:=1=Seq, val:=Val}) -> {Seq,Val};
map_guard_sequence_1(#{seq:=2=Seq, val:=Val}) -> {Seq,Val};
map_guard_sequence_1(#{seq:=3=Seq, val:=Val}) -> {Seq,Val};
map_guard_sequence_1(#{seq:=4=Seq, val:=Val}) -> {Seq,Val};
map_guard_sequence_1(#{seq:=5=Seq, val:=Val}) -> {Seq,Val}.

map_guard_sequence_2(#{ a:=3 }=M) -> {1, M};
map_guard_sequence_2(#{ a:=4 }=M) -> {2, M};
map_guard_sequence_2(#{ a:=X, a:=X, b:=4 }=M) -> {3,X,M};
map_guard_sequence_2(#{ a:=X, a:=Y, b:=3 }=M) when X =:= Y -> {4,X,Y,M};
map_guard_sequence_2(#{ a:=X, a:=Y }=M) when X =:= Y -> {5,X,Y,M}.


t_guard_update(Config) when is_list(Config) ->
    error  = map_guard_update(#{},#{}),
    first  = map_guard_update(#{}, #{x=>first}),
    second = map_guard_update(#{y=>old}, #{x=>second,y=>old}),
    ok.

t_guard_update_large(Config) when is_list(Config) ->
    M0 = id(#{ 70=>a0,80=>b0,90=>"c0","40"=>"d0",<<"50">>=>"e0",{["00",03]}=>"10",
               71=>a1,81=>b1,91=>"c1","41"=>"d1",<<"51">>=>"e1",{["01",03]}=>"11",
               72=>a2,82=>b2,92=>"c2","42"=>"d2",<<"52">>=>"e2",{["02",03]}=>"12",
               73=>a3,83=>b3,93=>"c3","43"=>"d3",<<"53">>=>"e3",{["03",03]}=>"13",
               74=>a4,84=>b4,94=>"c4","44"=>"d4",<<"54">>=>"e4",{["04",03]}=>"14",

               75=>a5,85=>b5,95=>"c5","45"=>"d5",<<"55">>=>"e5",{["05",03]}=>"15",
               76=>a6,86=>b6,96=>"c6","46"=>"d6",<<"56">>=>"e6",{["06",03]}=>"16",
               77=>a7,87=>b7,97=>"c7","47"=>"d7",<<"57">>=>"e7",{["07",03]}=>"17",
               78=>a8,88=>b8,98=>"c8","48"=>"d8",<<"58">>=>"e8",{["08",03]}=>"18",
               79=>a9,89=>b9,99=>"c9","49"=>"d9",<<"59">>=>"e9",{["09",03]}=>"19",

               70.0=>fa0,80.0=>fb0,90.0=>"fc0",
               71.0=>fa1,81.0=>fb1,91.0=>"fc1",
               72.0=>fa2,82.0=>fb2,92.0=>"fc2",
               73.0=>fa3,83.0=>fb3,93.0=>"fc3",
               74.0=>fa4,84.0=>fb4,94.0=>"fc4",

               75.0=>fa5,85.0=>fb5,95.0=>"fc5",
               76.0=>fa6,86.0=>fb6,96.0=>"fc6",
               77.0=>fa7,87.0=>fb7,97.0=>"fc7",
               78.0=>fa8,88.0=>fb8,98.0=>"fc8",
               79.0=>fa9,89.0=>fb9,99.0=>"fc9",

               #{ one => small, map => key } => "small map key 1",
               #{ second => small, map => key } => "small map key 2",
               #{ third => small, map => key } => "small map key 3",

               #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
                  11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
                  12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
                  13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
                  14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

                  15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
                  16=>a6,26=>b6,36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
                  17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
                  18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
                  19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 1",

               #{ 10=>a0,20=>b0,30=>"c0","40"=>"d0",<<"50">>=>"e0",{["00"]}=>"10",
                  11=>a1,21=>b1,31=>"c1","41"=>"d1",<<"51">>=>"e1",{["01"]}=>"11",
                  12=>a2,22=>b2,32=>"c2","42"=>"d2",<<"52">>=>"e2",{["02"]}=>"12",
                  13=>a3,23=>b3,33=>"c3","43"=>"d3",<<"53">>=>"e3",{["03"]}=>"13",
                  14=>a4,24=>b4,34=>"c4","44"=>"d4",<<"54">>=>"e4",{["04"]}=>"14",

                  15=>a5,25=>b5,35=>"c5","45"=>"d5",<<"55">>=>"e5",{["05"]}=>"15",
                  k16=>a6,k26=>b6,k36=>"c6","46"=>"d6",<<"56">>=>"e6",{["06"]}=>"16",
                  17=>a7,27=>b7,37=>"c7","47"=>"d7",<<"57">>=>"e7",{["07"]}=>"17",
                  18=>a8,28=>b8,38=>"c8","48"=>"d8",<<"58">>=>"e8",{["08"]}=>"18",
                  19=>a9,29=>b9,39=>"c9","49"=>"d9",<<"59">>=>"e9",{["09"]}=>"19" } => "large map key 2" }),


    error  = map_guard_update(M0#{},M0#{}),
    first  = map_guard_update(M0#{},M0#{x=>first}),
    second = map_guard_update(M0#{y=>old}, M0#{x=>second,y=>old}),
    ok.


map_guard_update(M1, M2) when M1#{x=>first}  =:= M2 -> first;
map_guard_update(M1, M2) when M1#{x=>second} =:= M2 -> second;
map_guard_update(_, _) -> error.

t_guard_receive(Config) when is_list(Config) ->
    M0  = #{ id => 0 },
    Pid = spawn_link(fun() -> guard_receive_loop() end),
    Big = 36893488147419103229,
    B1  = <<"some text">>,
    B2  = <<"was appended">>,
    B3  = <<B1/binary, B2/binary>>,

    #{id:=1, res:=Big} = M1 = call(Pid, M0#{op=>sub,in=>{1 bsl 65, 3}}),
    #{id:=2, res:=26}  = M2 = call(Pid, M1#{op=>idiv,in=>{53,2}}),
    #{id:=3, res:=832} = M3 = call(Pid, M2#{op=>imul,in=>{26,32}}),
    #{id:=4, res:=4}   = M4 = call(Pid, M3#{op=>add,in=>{1,3}}),
    #{id:=5, res:=Big} = M5 = call(Pid, M4#{op=>sub,in=>{1 bsl 65, 3}}),
    #{id:=6, res:=B3}  = M6 = call(Pid, M5#{op=>"append",in=>{B1,B2}}),
    #{id:=7, res:=4}   = _  = call(Pid, M6#{op=>add,in=>{1,3}}),


    %% update old maps and check id update
    #{id:=2, res:=B3} = call(Pid, M1#{op=>"append",in=>{B1,B2}}),
    #{id:=5, res:=99} = call(Pid, M4#{op=>add,in=>{33, 66}}),

    %% cleanup
    done = call(Pid, done),
    ok.

-define(t_guard_receive_large_procs, 1500).

t_guard_receive_large(Config) when is_list(Config) ->
    M = lists:foldl(fun(_,#{procs := Ps } = M) ->
                            M#{ procs := Ps#{ spawn_link(fun() -> grecv_loop() end) => 0 }}
                    end, #{procs => #{}, done => 0}, lists:seq(1,?t_guard_receive_large_procs)),
    lists:foreach(fun(Pid) ->
                          Pid ! {self(), hello}
                  end, maps:keys(maps:get(procs,M))),
    ok = guard_receive_large_loop(M),
    ok.

guard_receive_large_loop(#{done := ?t_guard_receive_large_procs}) ->
    ok;
guard_receive_large_loop(M) ->
    receive
        #{pid := Pid, msg := hello} ->
            case M of
                #{done := Count, procs := #{Pid := 150}} ->
                    Pid ! {self(), done},
                    guard_receive_large_loop(M#{done := Count + 1});
                #{procs := #{Pid := Count} = Ps} ->
                    Pid ! {self(), hello},
                    guard_receive_large_loop(M#{procs := Ps#{Pid := Count + 1}})
            end
    end.

grecv_loop() ->
    receive
        {_, done} ->
            ok;
        {Pid, hello} ->
            Pid ! #{pid=>self(), msg=>hello},
            grecv_loop()
    end.

call(Pid, M) ->
    Pid ! {self(), M}, receive {Pid, Res} -> Res end.

guard_receive_loop() ->
    receive
	{Pid, #{ id:=Id, op:="append", in:={X,Y}}=M} when is_binary(X), is_binary(Y) ->
	    Pid ! {self(), M#{ id=>Id+1, res=><<X/binary,Y/binary>>}},
	    guard_receive_loop();
	{Pid, #{ id:=Id, op:=add, in:={X,Y}}} ->
	    Pid ! {self(), #{ id=>Id+1, res=>X+Y}},
	    guard_receive_loop();
	{Pid, #{ id:=Id, op:=sub,  in:={X,Y}}=M} ->
	    Pid ! {self(), M#{ id=>Id+1, res=>X-Y}},
	    guard_receive_loop();
	{Pid, #{ id:=Id, op:=idiv, in:={X,Y}}=M} ->
	    Pid ! {self(), M#{ id=>Id+1, res=>X div Y}},
	    guard_receive_loop();
	{Pid, #{ id:=Id, op:=imul, in:={X,Y}}=M} ->
	    Pid ! {self(), M#{ id=>Id+1, res=>X * Y}},
	    guard_receive_loop();
	{Pid, done} ->
	    Pid ! {self(), done};
	{Pid, Other} ->
	    Pid ! {error, Other},
	    guard_receive_loop()
    end.


t_list_comprehension(Config) when is_list(Config) ->
    [#{k:=1},#{k:=2},#{k:=3}] = [#{k=>I} || I <- [1,2,3]],

    Ks = lists:seq($a,$z),
    Ms = [#{[K1,K2]=>{K1,K2}} || K1 <- Ks, K2 <- Ks],
    [#{"aa" := {$a,$a}},#{"ab":={$a,$b}}|_] = Ms,
    [#{"zz" := {$z,$z}},#{"zy":={$z,$y}}|_] = lists:reverse(Ms),
    ok.

t_guard_fun(Config) when is_list(Config) ->
    F1 = fun
	    (#{s:=v,v:=V})     -> {v,V};
	    (#{s:=t,v:={V,V}}) -> {t,V};
	    (#{s:=l,v:=[V,V]}) -> {l,V}
    end,
    
    F2 = fun
	    (#{s:=T,v:={V,V}}) -> {T,V};
	    (#{s:=T,v:=[V,V]}) -> {T,V};
	    (#{s:=T,v:=V})     -> {T,V}
    end,
    V = <<"hi">>,

    {v,V} = F1(#{s=>v,v=>V}),
    {t,V} = F1(#{s=>t,v=>{V,V}}),
    {l,V} = F1(#{s=>l,v=>[V,V]}),

    {v,V} = F2(#{s=>v,v=>V}),
    {t,V} = F2(#{s=>t,v=>{V,V}}),
    {l,V} = F2(#{s=>l,v=>[V,V]}),

    %% error case
    {'EXIT', {function_clause,[{?MODULE,_,[#{s:=none,v:=none}],_}|_]}} = (catch F1(#{s=>none,v=>none})),
    ok.


t_map_sort_literals(Config) when is_list(Config) ->
    % test relation

    %% size order
    true  = #{ a => 1, b => 2} < id(#{ a => 1, b => 1, c => 1}),
    true  = #{ b => 1, a => 1} < id(#{ c => 1, a => 1, b => 1}),
    false = #{ c => 1, b => 1, a => 1} < id(#{ c => 1, a => 1}),

    %% key order
    true  = #{ a => 1 } < id(#{ b => 1}),
    false = #{ b => 1 } < id(#{ a => 1}),
    true  = #{ a => 1, b => 1, c => 1 } < id(#{ b => 1, c => 1, d => 1}),
    true  = #{ b => 1, c => 1, d => 1 } > id(#{ a => 1, b => 1, c => 1}),
    true  = #{ c => 1, b => 1, a => 1 } < id(#{ b => 1, c => 1, d => 1}),
    true  = #{ "a" => 1 } < id(#{ <<"a">> => 1}),
    false = #{ <<"a">> => 1 } < id(#{ "a" => 1}),
    true  = #{ 1 => 1 } < id(#{ 1.0 => 1}),
    false = #{ 1.0 => 1 } < id(#{ 1 => 1}),

    %% value order
    true  = #{ a => 1 } < id(#{ a => 2}),
    false = #{ a => 2 } < id(#{ a => 1}),
    false = #{ a => 2, b => 1 } < id(#{ a => 1, b => 3}),
    true  = #{ a => 1, b => 1 } < id(#{ a => 1, b => 3}),
    false = #{ a => 1 } < id(#{ a => 1.0}),
    false = #{ a => 1.0 } < id(#{ a => 1}),

    true  = #{ "a" => "hi", b => 134 } == id(#{ b => 134,"a" => "hi"}),

    %% large maps

    M = maps:from_list([{I,I}||I <- lists:seq(1,500)]),

    %% size order
    true  = M#{ a => 1, b => 2} < id(M#{ a => 1, b => 1, c => 1}),
    true  = M#{ b => 1, a => 1} < id(M#{ c => 1, a => 1, b => 1}),
    false = M#{ c => 1, b => 1, a => 1} < id(M#{ c => 1, a => 1}),

    %% key order
    true  = M#{ a => 1 } < id(M#{ b => 1}),
    false = M#{ b => 1 } < id(M#{ a => 1}),
    true  = M#{ a => 1, b => 1, c => 1 } < id(M#{ b => 1, c => 1, d => 1}),
    true  = M#{ b => 1, c => 1, d => 1 } > id(M#{ a => 1, b => 1, c => 1}),
    true  = M#{ c => 1, b => 1, a => 1 } < id(M#{ b => 1, c => 1, d => 1}),
    true  = M#{ "a" => 1 } < id(M#{ <<"a">> => 1}),
    false = M#{ <<"a">> => 1 } < id(#{ "a" => 1}),
    true  = M#{ 1 => 1 } < id(maps:remove(1,M#{ 1.0 => 1})),
    false = M#{ 1.0 => 1 } < id(M#{ 1 => 1}),

    %% value order
    true  = M#{ a => 1 } < id(M#{ a => 2}),
    false = M#{ a => 2 } < id(M#{ a => 1}),
    false = M#{ a => 2, b => 1 } < id(M#{ a => 1, b => 3}),
    true  = M#{ a => 1, b => 1 } < id(M#{ a => 1, b => 3}),
    false = M#{ a => 1 } < id(M#{ a => 1.0}),
    false = M#{ a => 1.0 } < id(M#{ a => 1}),

    true  = M#{ "a" => "hi", b => 134 } == id(M#{ b => 134,"a" => "hi"}),

    %% lists:sort

    SortVs = [#{"a"=>1},#{a=>2},#{1=>3},#{<<"a">>=>4}],
    [#{1:=ok},#{a:=ok},#{"a":=ok},#{<<"a">>:=ok}] = lists:sort([#{"a"=>ok},#{a=>ok},#{1=>ok},#{<<"a">>=>ok}]),
    [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(SortVs),
    [#{1:=3},#{a:=2},#{"a":=1},#{<<"a">>:=4}] = lists:sort(lists:reverse(SortVs)),
    ok.

t_map_equal(Config) when is_list(Config) ->
    true  = id(#{}) =:= id(#{}),
    false = id(#{}) =:= id(#{a=>1}),
    false = id(#{a=>1}) =:= id(#{}),
    true  = id(#{ "a" => "hi", b => 134 }) =:= id(#{ b => 134,"a" => "hi"}),

    false = id(#{ a => 1 }) =:= id(#{ a => 2}),
    false = id(#{ a => 2 }) =:= id(#{ a => 1}),
    false = id(#{ a => 2, b => 1 }) =:= id(#{ a => 1, b => 3}),
    false = id(#{ a => 1, b => 1 }) =:= id(#{ a => 1, b => 3}),

    true = id(#{ a => 1 }) =:= id(#{ a => 1}),
    true = id(#{ "a" => 2 }) =:= id(#{ "a" => 2}),
    true = id(#{ "a" => 2, b => 3 }) =:= id(#{ "a" => 2, b => 3}),
    true = id(#{ a => 1, b => 3, c => <<"wat">> }) =:= id(#{ a => 1, b => 3, c=><<"wat">>}),
    ok.


t_map_compare(Config) when is_list(Config) ->
    Seed = {erlang:monotonic_time(),
	    erlang:time_offset(),
	    erlang:unique_integer()},
    io:format("seed = ~p\n", [Seed]),
    random:seed(Seed),
    repeat(100, fun(_) -> float_int_compare() end, []),
    repeat(100, fun(_) -> recursive_compare() end, []),
    ok.

float_int_compare() ->
    Terms = numeric_keys(3),
    %%io:format("Keys to use: ~p\n", [Terms]),
    Pairs = lists:map(fun(K) -> list_to_tuple([{K,V} || V <- Terms]) end, Terms),
    lists:foreach(fun(Size) ->
			  MapGen = fun() -> map_gen(list_to_tuple(Pairs), Size) end,
			  repeat(100, fun do_compare/1, [MapGen, MapGen])
		  end,
		  lists:seq(1,length(Terms))),
    ok.

numeric_keys(N) ->
    lists:foldl(fun(_,Acc) ->
			Int = random:uniform(N*4) - N*2,
			Float = float(Int),
			[Int, Float, Float * 0.99, Float * 1.01 | Acc]
		end,
		[],
		lists:seq(1,N)).


repeat(0, _, _) ->
    ok;
repeat(N, Fun, Arg) ->
    Fun(Arg),
    repeat(N-1, Fun, Arg).

copy_term(T) ->
    Papa = self(),
    P = spawn_link(fun() -> receive Msg -> Papa ! Msg end end),
    P ! T,
    receive R -> R end.

do_compare([Gen1, Gen2]) ->
    M1 = Gen1(),
    M2 = Gen2(),
    %%io:format("Maps to compare: ~p AND ~p\n", [M1, M2]),
    C = (M1 < M2),
    Erlang = maps_lessthan(M1, M2),
    C = Erlang,
    ?CHECK(M1==M1, M1),

    %% Change one key from int to float (or vice versa) and check compare
    ML1 = maps:to_list(M1),
    {K1,V1} = lists:nth(random:uniform(length(ML1)), ML1),
    case K1 of
	I when is_integer(I) ->
	    case maps:find(float(I),M1) of
		error ->
		    M1f = maps:remove(I, maps:put(float(I), V1, M1)),
		    ?CHECK(M1f > M1, [M1f, M1]);
		_ -> ok
	    end;

	F when is_float(F), round(F) == F ->
	    case maps:find(round(F),M1) of
		error ->
		    M1i = maps:remove(F, maps:put(round(F), V1, M1)),
		    ?CHECK(M1i < M1, [M1i, M1]);
		_ -> ok
	    end;

	_ -> ok   % skip floats with decimals
    end,

    ?CHECK(M2 == M2, [M2]).


maps_lessthan(M1, M2) ->
  case {maps:size(M1),maps:size(M2)} of
      {_S,_S} ->
	  {K1,V1} = lists:unzip(term_sort(maps:to_list(M1))),
	  {K2,V2} = lists:unzip(term_sort(maps:to_list(M2))),

	  case erts_internal:cmp_term(K1,K2) of
	      -1 -> true;
	      0 -> (V1 < V2);
	      1 -> false
	  end;

      {S1, S2} ->
	  S1 < S2
  end.

term_sort(L) ->
    lists:sort(fun(A,B) -> erts_internal:cmp_term(A,B) =< 0 end,
	       L).


cmp(T1, T2, Exact) when is_tuple(T1) and is_tuple(T2) ->
    case {size(T1),size(T2)} of
	{_S,_S} -> cmp(tuple_to_list(T1), tuple_to_list(T2), Exact);
	{S1,S2} when S1 < S2 -> -1;
	{S1,S2} when S1 > S2 -> 1
    end;

cmp([H1|T1], [H2|T2], Exact) ->
    case cmp(H1,H2, Exact) of
	0 -> cmp(T1,T2, Exact);
	C -> C
    end;

cmp(M1, M2, Exact) when is_map(M1) andalso is_map(M2) ->
    cmp_maps(M1,M2,Exact);
cmp(M1, M2, Exact) ->
    cmp_others(M1, M2, Exact).

cmp_maps(M1, M2, Exact) ->
    case {maps:size(M1),maps:size(M2)} of
	{_S,_S} ->
	    {K1,V1} = lists:unzip(term_sort(maps:to_list(M1))),
	    {K2,V2} = lists:unzip(term_sort(maps:to_list(M2))),

	    case cmp(K1, K2, true) of
		0 -> cmp(V1, V2, Exact);
		C -> C
	    end;

	{S1,S2} when S1 < S2 -> -1;
	{S1,S2} when S1 > S2 -> 1
    end.

cmp_others(I, F, true) when is_integer(I), is_float(F) ->
    -1;
cmp_others(F, I, true) when is_float(F), is_integer(I) ->
    1;
cmp_others(T1, T2, _) ->
    case {T1<T2, T1==T2} of
	{true,false} -> -1;
	{false,true} -> 0;
	{false,false} -> 1
    end.

map_gen(Pairs, Size) ->
    {_,L} = lists:foldl(fun(_, {Keys, Acc}) ->
				KI = random:uniform(size(Keys)),
				K = element(KI,Keys),
				KV = element(random:uniform(size(K)), K),
				{erlang:delete_element(KI,Keys), [KV | Acc]}
			end,
			{Pairs, []},
			lists:seq(1,Size)),

    maps:from_list(L).


recursive_compare() ->
    Leafs = {atom, 17, 16.9, 17.1, [], self(), spawn(fun() -> ok end), make_ref(), make_ref()},
    {A, B} = term_gen_recursive(Leafs, 0, 0),
    %%io:format("Recursive term A = ~p\n", [A]),
    %%io:format("Recursive term B = ~p\n", [B]),

    ?CHECK({true,false} =:=  case do_cmp(A, B, false) of
				 -1 -> {A<B, A>=B};
				 0 -> {A==B, A/=B};
				 1 -> {A>B, A=<B}
			     end,
	   {A,B}),
    A2 = copy_term(A),
    ?CHECK(A == A2, {A,A2}),
    ?CHECK(0 =:= cmp(A, A2, false), {A,A2}),

    B2 = copy_term(B),
    ?CHECK(B == B2, {B,B2}),
    ?CHECK(0 =:= cmp(B, B2, false), {B,B2}),
    ok.

do_cmp(A, B, Exact) ->
    C = cmp(A, B, Exact),
    C.

%% Generate two terms {A,B} that may only differ
%% at float vs integer types.
term_gen_recursive(Leafs, Flags, Depth) ->
    MaxDepth = 10,
    Rnd = case {Flags, Depth} of
	      {_, MaxDepth} -> % Only leafs
		  random:uniform(size(Leafs)) + 3;
	      {0, 0} ->        % Only containers
		  random:uniform(3);
	      {0,_} ->         % Anything
		  random:uniform(size(Leafs)+3)
	  end,
    case Rnd of
	1 -> % Make map
	    Size = random:uniform(size(Leafs)),
	    lists:foldl(fun(_, {Acc1,Acc2}) ->
				{K1,K2} = term_gen_recursive(Leafs, Flags,
							     Depth+1),
				{V1,V2} = term_gen_recursive(Leafs, Flags, Depth+1),
				%%ok = check_keys(K1,K2, 0),
				{maps:put(K1,V1, Acc1), maps:put(K2,V2, Acc2)}
			end,
			{maps:new(), maps:new()},
			lists:seq(1,Size));
	2 -> % Make cons
	    {Car1,Car2} = term_gen_recursive(Leafs, Flags, Depth+1),
	    {Cdr1,Cdr2} = term_gen_recursive(Leafs, Flags, Depth+1),
	    {[Car1 | Cdr1], [Car2 | Cdr2]};
	3 -> % Make tuple
	    Size = random:uniform(size(Leafs)),
	    L = lists:map(fun(_) -> term_gen_recursive(Leafs, Flags, Depth+1) end,
			  lists:seq(1,Size)),
	    {L1, L2} = lists:unzip(L),
	    {list_to_tuple(L1), list_to_tuple(L2)};

	N -> % Make leaf
	    case element(N-3, Leafs) of
		I when is_integer(I) ->
		    case random:uniform(4) of
			1 -> {I, float(I)};
			2 -> {float(I), I};
			_ -> {I,I}
		    end;
		T -> {T,T}
	    end
    end.

check_keys(K1, K2, _) when K1 =:= K2 ->
    case erlang:phash3(K1) =:= erlang:phash3(K2) of
	true -> ok;
	false ->
	    io:format("Same keys with different hash values !!!\nK1 = ~p\nK2 = ~p\n", [K1,K2]),
	    error
    end;
check_keys(K1, K2, 0) ->
    case {erlang:phash3(K1), erlang:phash3(K2)} of
	{H,H} -> check_keys(K1, K2, 1);
	{_,_} -> ok
    end;
check_keys(K1, K2, L) when L < 10 ->
    case {erlang:phash3([L|K1]), erlang:phash3([L|K2])} of
	{H,H} -> check_keys(K1, K2, L+1);
	{_,_} -> ok
    end;
check_keys(K1, K2, L) ->
    io:format("Same hash value at level ~p !!!\nK1 = ~p\nK2 = ~p\n", [L,K1,K2]),
    error.

%% BIFs
t_bif_map_get(Config) when is_list(Config) ->
    %% small map
    1    = maps:get(a, #{ a=> 1}),
    2    = maps:get(b, #{ a=> 1, b => 2}),
    "hi" = maps:get("hello", #{ a=>1, "hello" => "hi"}),
    "tuple hi" = maps:get({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}),

    M0    = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }),
    "v4" = maps:get(<<"k2">>, M0#{<<"k2">> => "v4"}),

    %% large map
    M1   = maps:from_list([{I,I}||I<-lists:seq(1,100)] ++
			  [{a,1},{b,2},{"hello","hi"},{{1,1.0},"tuple hi"},
			   {k1,"v1"},{<<"k2">>,"v3"}]),
    1    = maps:get(a, M1),
    2    = maps:get(b, M1),
    "hi" = maps:get("hello", M1),
    "tuple hi" = maps:get({1,1.0}, M1),
    "v3" = maps:get(<<"k2">>, M1),

    %% error cases
    do_badmap(fun(T) ->
		      {'EXIT',{{badmap,T},[{maps,get,_,_}|_]}} =
			  (catch maps:get(a, T))
	      end),

    {'EXIT',{{badkey,{1,1}},[{maps,get,_,_}|_]}} =
	(catch maps:get({1,1}, #{{1,1.0} => "tuple"})),
    {'EXIT',{{badkey,a},[{maps,get,_,_}|_]}} = (catch maps:get(a, #{})),
    {'EXIT',{{badkey,a},[{maps,get,_,_}|_]}} =
	(catch maps:get(a, #{b=>1, c=>2})),
    ok.

t_bif_map_find(Config) when is_list(Config) ->
    %% small map
    {ok, 1}     = maps:find(a, #{ a=> 1}),
    {ok, 2}     = maps:find(b, #{ a=> 1, b => 2}),
    {ok, "int"} = maps:find(1, #{ 1   => "int"}),
    {ok, "float"} = maps:find(1.0, #{ 1.0=> "float"}),

    {ok, "hi"} = maps:find("hello", #{ a=>1, "hello" => "hi"}),
    {ok, "tuple hi"} = maps:find({1,1.0}, #{ a=>a, {1,1.0} => "tuple hi"}),

    M0 = id(#{ k1=>"v1", <<"k2">> => <<"v3">> }),
    {ok, "v4"} = maps:find(<<"k2">>, M0#{ <<"k2">> => "v4" }),

    %% large map
    M1   = maps:from_list([{I,I}||I<-lists:seq(1,100)] ++
			  [{a,1},{b,2},{"hello","hi"},{{1,1.0},"tuple hi"},
			   {k1,"v1"},{<<"k2">>,"v3"}]),
    {ok, 1}    = maps:find(a, M1),
    {ok, 2}    = maps:find(b, M1),
    {ok, "hi"} = maps:find("hello", M1),
    {ok, "tuple hi"} = maps:find({1,1.0}, M1),
    {ok, "v3"} = maps:find(<<"k2">>, M1),

    %% error case
    error = maps:find(a,#{}),
    error = maps:find(a,#{b=>1, c=>2}),
    error = maps:find(1.0, #{ 1 => "int"}),
    error = maps:find(1, #{ 1.0  => "float"}),
    error = maps:find({1.0,1}, #{ a=>a, {1,1.0} => "tuple hi"}), % reverse types in tuple key

    do_badmap(fun(T) ->
		      {'EXIT',{{badmap,T},[{maps,find,_,_}|_]}} =
			  (catch maps:find(a, T))
	      end),
    ok.


t_bif_map_is_key(Config) when is_list(Config) ->
    M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},

    true  = maps:is_key("hi", M1),
    true  = maps:is_key(int, M1),
    true  = maps:is_key(<<"key">>, M1),
    true  = maps:is_key(4, M1),

    false = maps:is_key(5, M1),
    false = maps:is_key(<<"key2">>, M1),
    false = maps:is_key("h", M1),
    false = maps:is_key("hello", M1),
    false = maps:is_key(atom, M1),
    false = maps:is_key(any, id(#{})),

    false = maps:is_key("hi", maps:remove("hi", M1)),
    true  = maps:is_key("hi", M1),
    true  = maps:is_key(1, maps:put(1, "number", M1)),
    false = maps:is_key(1.0, maps:put(1, "number", M1)),

    %% error case
    do_badmap(fun(T) ->
		      {'EXIT',{{badmap,T},[{maps,is_key,_,_}|_]}} =
			  (catch maps:is_key(a, T))
	      end),
    ok.

t_bif_map_keys(Config) when is_list(Config) ->
    [] = maps:keys(#{}),

    [1,2,3,4,5] = lists:sort(maps:keys(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e})),
    [1,2,3,4,5] = lists:sort(maps:keys(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c})),

    % values in key order: [4,int,"hi",<<"key">>]
    M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
    [4,int,"hi",<<"key">>] = lists:sort(maps:keys(M1)),

    %% error case
    do_badmap(fun(T) ->
		      {'EXIT',{{badmap,T},[{maps,keys,_,_}|_]}} =
			  (catch maps:keys(T))
	      end),
    ok.

t_bif_map_new(Config) when is_list(Config) ->
    #{} = maps:new(),
    0   = erlang:map_size(maps:new()),
    ok.

t_bif_map_merge(Config) when is_list(Config) ->
    0   = erlang:map_size(maps:merge(#{},#{})),

    M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
	4 => number, 18446744073709551629 => wat},

    #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
	4 := number, 18446744073709551629 := wat} = maps:merge(#{}, M0),

    #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
	4 := number, 18446744073709551629 := wat} = maps:merge(M0, #{}),

    M1 = #{ "hi" => "hello again", float => 3.3, {1,2} => "tuple", 4 => integer },

    #{4 := number, 18446744073709551629 := wat, float := 3.3, int := 3,
	{1,2} := "tuple", "hi" := "hello", <<"key">> := <<"value">>} = maps:merge(M1,M0),

    #{4 := integer, 18446744073709551629 := wat, float := 3.3, int := 3,
	{1,2} := "tuple", "hi" := "hello again", <<"key">> := <<"value">>} = maps:merge(M0,M1),

    %% try deep collisions
    N  = 150000,
    Is = lists:seq(1,N),
    M2 = maps:from_list([{I,I}||I<-Is]),
    150000 = maps:size(M2),
    M3 = maps:from_list([{<<I:32>>,I}||I<-Is]),
    150000 = maps:size(M3),
    M4 = maps:merge(M2,M3),
    300000 = maps:size(M4),
    M5 = maps:from_list([{integer_to_list(I),I}||I<-Is]),
    150000 = maps:size(M5),
    M6 = maps:merge(M4,M5),
    450000 = maps:size(M6),
    M7 = maps:from_list([{float(I),I}||I<-Is]),
    150000 = maps:size(M7),
    M8 = maps:merge(M7,M6),
    600000 = maps:size(M8),

    #{      1 := 1,           "1" := 1,           <<1:32>> := 1      } = M8,
    #{     10 := 10,         "10" := 10,         <<10:32>> := 10     } = M8,
    #{    100 := 100,       "100" := 100,       <<100:32>> := 100    } = M8,
    #{   1000 := 1000,     "1000" := 1000,     <<1000:32>> := 1000   } = M8,
    #{  10000 := 10000,   "10000" := 10000,   <<10000:32>> := 10000  } = M8,
    #{ 100000 := 100000, "100000" := 100000, <<100000:32>> := 100000 } = M8,

    %% overlapping
    M8 = maps:merge(M2,M8),
    M8 = maps:merge(M3,M8),
    M8 = maps:merge(M4,M8),
    M8 = maps:merge(M5,M8),
    M8 = maps:merge(M6,M8),
    M8 = maps:merge(M7,M8),
    M8 = maps:merge(M8,M8),

    %% maps:merge/2 and mixed

    Ks1 = [764492191,2361333849], %% deep collision
    Ks2 = lists:seq(1,33),
    M9  = maps:from_list([{K,K}||K <- Ks1]),
    M10 = maps:from_list([{K,K}||K <- Ks2]),
    M11 = maps:merge(M9,M10),
    ok = check_keys_exist(Ks1 ++ Ks2, M11),

    %% error case
    do_badmap(fun(T) ->
		      {'EXIT',{{badmap,T},[{maps,merge,_,_}|_]}} =
			  (catch maps:merge(#{}, T)),
		      {'EXIT',{{badmap,T},[{maps,merge,_,_}|_]}} =
			  (catch maps:merge(T, #{})),
		      {'EXIT',{{badmap,T},[{maps,merge,_,_}|_]}} =
			  (catch maps:merge(T, T))
	      end),
    ok.


t_bif_map_put(Config) when is_list(Config) ->
    M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
	4 => number, 18446744073709551629 => wat},

    M1 = #{ "hi" := "hello"} = maps:put("hi", "hello", #{}),

    true = is_members(["hi"],maps:keys(M1)),
    true = is_members(["hello"],maps:values(M1)),

    M2 = #{ int := 3 } = maps:put(int, 3, M1),

    true = is_members([int,"hi"],maps:keys(M2)),
    true = is_members([3,"hello"],maps:values(M2)),

    M3 = #{ <<"key">> := <<"value">> } = maps:put(<<"key">>, <<"value">>, M2),

    true = is_members([int,"hi",<<"key">>],maps:keys(M3)),
    true = is_members([3,"hello",<<"value">>],maps:values(M3)),

    M4 = #{ 18446744073709551629 := wat } = maps:put(18446744073709551629, wat, M3),

    true = is_members([18446744073709551629,int,"hi",<<"key">>],maps:keys(M4)),
    true = is_members([wat,3,"hello",<<"value">>],maps:values(M4)),

    M0 = #{ 4 := number } = M5 = maps:put(4, number, M4),

    true = is_members([4,18446744073709551629,int,"hi",<<"key">>],maps:keys(M5)),
    true = is_members([number,wat,3,"hello",<<"value">>],maps:values(M5)),

    M6 = #{ <<"key">> := <<"other value">> } = maps:put(<<"key">>, <<"other value">>, M5),

    true = is_members([4,18446744073709551629,int,"hi",<<"key">>],maps:keys(M6)),
    true = is_members([number,wat,3,"hello",<<"other value">>],maps:values(M6)),

    %% error case
    do_badmap(fun(T) ->
		      {'EXIT',{{badmap,T},[{maps,put,_,_}|_]}} =
			  (catch maps:put(1, a, T))
	      end),
    ok.

is_members(Ks,Ls) when length(Ks) =/= length(Ls) -> false;
is_members(Ks,Ls) -> is_members_do(Ks,Ls).

is_members_do([],[]) -> true;
is_members_do([],_) -> false;
is_members_do([K|Ks],Ls) ->
    is_members_do(Ks, lists:delete(K,Ls)).

t_bif_map_remove(Config) when is_list(Config) ->
    0  = erlang:map_size(maps:remove(some_key, #{})),

    M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
	4 => number, 18446744073709551629 => wat},

    M1 = maps:remove("hi", M0),
    true = is_members([4,18446744073709551629,int,<<"key">>],maps:keys(M1)),
    true = is_members([number,wat,3,<<"value">>],maps:values(M1)),

    M2 = maps:remove(int, M1),
    true = is_members([4,18446744073709551629,<<"key">>],maps:keys(M2)),
    true = is_members([number,wat,<<"value">>],maps:values(M2)),

    M3 = maps:remove(<<"key">>, M2),
    true = is_members([4,18446744073709551629],maps:keys(M3)),
    true = is_members([number,wat],maps:values(M3)),

    M4 = maps:remove(18446744073709551629, M3),
    true = is_members([4],maps:keys(M4)),
    true = is_members([number],maps:values(M4)),

    M5 = maps:remove(4, M4),
    [] = maps:keys(M5),
    [] = maps:values(M5),

    M0 = maps:remove(5,M0),
    M0 = maps:remove("hi there",M0),

    #{ "hi" := "hello", int := 3, 4 := number} = maps:remove(18446744073709551629,maps:remove(<<"key">>,M0)),

    %% error case
    do_badmap(fun(T) ->
		      {'EXIT',{{badmap,T},[{maps,remove,_,_}|_]}} =
	(catch maps:remove(a, T))
	      end),
     ok.

t_bif_map_update(Config) when is_list(Config) ->
    M0 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>,
	4 => number, 18446744073709551629 => wat},

    #{ "hi" := "hello again", int := 3, <<"key">> := <<"value">>,
	4 := number, 18446744073709551629 := wat} = maps:update("hi", "hello again", M0),

    #{ "hi" := "hello", int := 1337, <<"key">> := <<"value">>,
	4 := number, 18446744073709551629 := wat} = maps:update(int, 1337, M0),

    #{ "hi" := "hello", int := 3, <<"key">> := <<"new value">>,
	4 := number, 18446744073709551629 := wat} = maps:update(<<"key">>, <<"new value">>, M0),

    #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
	4 := integer, 18446744073709551629 := wat} = maps:update(4, integer, M0),

    #{ "hi" := "hello", int := 3, <<"key">> := <<"value">>,
	4 := number, 18446744073709551629 := wazzup} = maps:update(18446744073709551629, wazzup, M0),

    %% error case
    do_badmap(fun(T) ->
		      {'EXIT',{{badmap,T},[{maps,update,_,_}|_]}} =
			  (catch maps:update(1, none, T))
	      end),
    ok.



t_bif_map_values(Config) when is_list(Config) ->

    [] = maps:values(#{}),
    [1] = maps:values(#{a=>1}),

    true = is_members([a,b,c,d,e],maps:values(#{ 1 => a, 2 => b, 3 => c, 4 => d, 5 => e})),
    true = is_members([a,b,c,d,e],maps:values(#{ 4 => d, 5 => e, 1 => a, 2 => b, 3 => c})),

    M1 = #{ "hi" => "hello", int => 3, <<"key">> => <<"value">>, 4 => number},
    M2 = M1#{ "hi" => "hello2", <<"key">> => <<"value2">> },
    true = is_members([number,3,"hello2",<<"value2">>],maps:values(M2)),
    true = is_members([number,3,"hello",<<"value">>],maps:values(M1)),

    Vs = lists:seq(1000,20000),
    M3 = maps:from_list([{K,K}||K<-Vs]),
    M4 = maps:merge(M1,M3),
    M5 = maps:merge(M2,M3),
    true = is_members(Vs,maps:values(M3)),
    true = is_members([number,3,"hello",<<"value">>]++Vs,maps:values(M4)),
    true = is_members([number,3,"hello2",<<"value2">>]++Vs,maps:values(M5)),

    %% error case
    do_badmap(fun(T) ->
		      {'EXIT',{{badmap,T},[{maps,values,_,_}|_]}} =
			  (catch maps:values(T))
	      end),
    ok.

t_erlang_hash(Config) when is_list(Config) ->

    ok = t_bif_erlang_phash2(),
    ok = t_bif_erlang_phash(),
    ok = t_bif_erlang_hash(),

    ok.

t_bif_erlang_phash2() ->

    39679005 = erlang:phash2(#{}),
    33667975 = erlang:phash2(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 }), % 78942764
    95332690 = erlang:phash2(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} }), % 37338230
    108954384 = erlang:phash2(#{ 1 => a }), % 14363616
    59617982 = erlang:phash2(#{ a => 1 }), % 51612236

    42770201 = erlang:phash2(#{{} => <<>>}), % 37468437
    71687700 = erlang:phash2(#{<<>> => {}}), % 44049159

    M0 = #{ a => 1, "key" => <<"value">> },
    M1 = maps:remove("key",M0),
    M2 = M1#{ "key" => <<"value">> },

    70249457 = erlang:phash2(M0), % 118679416
    59617982 = erlang:phash2(M1), % 51612236
    70249457 = erlang:phash2(M2), % 118679416
    ok.

t_bif_erlang_phash() ->
    Sz = 1 bsl 32,
    1113425985 = erlang:phash(#{},Sz), % 268440612
    1510068139 = erlang:phash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz), % 1196461908
    3182345590 = erlang:phash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz), % 3944426064
    2927531828 = erlang:phash(#{ 1 => a },Sz), % 1394238263
    1670235874 = erlang:phash(#{ a => 1 },Sz), % 4066388227

    3935089469 = erlang:phash(#{{} => <<>>},Sz), % 1578050717
    71692856   = erlang:phash(#{<<>> => {}},Sz), % 1578050717

    M0 = #{ a => 1, "key" => <<"value">> },
    M1 = maps:remove("key",M0),
    M2 = M1#{ "key" => <<"value">> },

    2620391445 = erlang:phash(M0,Sz), % 3590546636
    1670235874 = erlang:phash(M1,Sz), % 4066388227
    2620391445 = erlang:phash(M2,Sz), % 3590546636
    ok.

t_bif_erlang_hash() ->
    Sz = 1 bsl 27 - 1,
    39684169 = erlang:hash(#{},Sz),  % 5158
    33673142 = erlang:hash(#{ a => 1, "a" => 2, <<"a">> => 3, {a,b} => 4 },Sz), % 71555838
    95337869 = erlang:hash(#{ 1 => a, 2 => "a", 3 => <<"a">>, 4 => {a,b} },Sz), % 5497225
    108959561 = erlang:hash(#{ 1 => a },Sz), % 126071654
    59623150 = erlang:hash(#{ a => 1 },Sz), % 126426236

    42775386 = erlang:hash(#{{} => <<>>},Sz), % 101655720
    71692856 = erlang:hash(#{<<>> => {}},Sz), % 101655720

    M0 = #{ a => 1, "key" => <<"value">> },
    M1 = maps:remove("key",M0),
    M2 = M1#{ "key" => <<"value">> },

    70254632 = erlang:hash(M0,Sz), % 38260486
    59623150 = erlang:hash(M1,Sz), % 126426236
    70254632 = erlang:hash(M2,Sz), % 38260486
    ok.


t_map_encode_decode(Config) when is_list(Config) ->
    <<131,116,0,0,0,0>> = erlang:term_to_binary(#{}),
    Pairs = [
	{a,b},{"key","values"},{<<"key">>,<<"value">>},
	{1,b},{[atom,1],{<<"wat">>,1,2,3}},
	{aa,"values"},
	{1 bsl 64 + (1 bsl 50 - 1), sc1},
	{99, sc2},
	{1 bsl 65 + (1 bsl 51 - 1), sc3},
	{88, sc4},
	{1 bsl 66 + (1 bsl 52 - 1), sc5},
	{77, sc6},
	{1 bsl 67 + (1 bsl 53 - 1), sc3},
	{75, sc6}, {-10,sc8},
	{<<>>, sc9}, {3.14158, sc10},
	{[3.14158], sc11}, {more_atoms, sc12},
	{{more_tuples}, sc13}, {self(), sc14},
	{{},{}},{[],[]}
    ],
    ok = map_encode_decode_and_match(Pairs,[],#{}),

    %% check sorting

    %% literally #{ b=>2, a=>1 } in the internal order
    #{ a:=1, b:=2 } =
	erlang:binary_to_term(<<131,116,0,0,0,2,100,0,1,98,97,2,100,0,1,97,97,1>>),


    %% literally #{ "hi" => "value", a=>33, b=>55 } in the internal order
    #{ a:=33, b:=55, "hi" := "value"} = erlang:binary_to_term(<<131,116,0,0,0,3,
	107,0,2,104,105,            % "hi" :: list()
	107,0,5,118,97,108,117,101, % "value" :: list()
	100,0,1,97,                 % a :: atom()
	97,33,                      % 33 :: integer()
	100,0,1,98,                 % b :: atom()
	97,55                       % 55 :: integer()
	>>),

    %% Maps of different sizes
    lists:foldl(fun(Key, M0) ->
			M1 = M0#{Key => Key},
			case Key rem 17 of
			    0 ->
				M1 = binary_to_term(term_to_binary(M1));
			    _ ->
				ok
			end,
			M1
		end,
		#{},
		lists:seq(1,10000)),

    %% many maps in same binary
    MapList = lists:foldl(fun(K, [M|_]=Acc) -> [M#{K => K} | Acc] end,
			  [#{}],
			  lists:seq(1,100)),
    MapList = binary_to_term(term_to_binary(MapList)),
    MapListR = lists:reverse(MapList),
    MapListR = binary_to_term(term_to_binary(MapListR)),

    %% error cases
    %% template: <<131,116,0,0,0,2,100,0,1,97,100,0,1,98,97,1,97,1>>
    %% which is: #{ a=>1, b=>1 }

    %% uniqueness violation
    %% literally #{ a=>1, "hi"=>"value", a=>2 }
    {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch
	erlang:binary_to_term(<<131,116,0,0,0,3,
			       100,0,1,97,
			       97,1,
			       107,0,2,104,105,
			       107,0,5,118,97,108,117,101,
			       100,0,1,97,
			       97,2>>)),

    %% bad size (too large)
    {'EXIT',{badarg,[{_,_,_,_}|_]}} = (catch
	erlang:binary_to_term(<<131,116,0,0,0,12,100,0,1,97,97,1,100,0,1,98,97,1>>)),

    %% bad size (too small) .. should fail just truncate it .. weird.
    %% possibly change external format so truncated will be #{a:=1}
    #{ a:=b } =
	erlang:binary_to_term(<<131,116,0,0,0,1,100,0,1,97,100,0,1,98,97,1,97,1>>),

    ok.

map_encode_decode_and_match([{K,V}|Pairs], EncodedPairs, M0) ->
    M1 = maps:put(K,V,M0),
    B0 = erlang:term_to_binary(M1),
    Ls = [{erlang:term_to_binary(K), erlang:term_to_binary(V)}|EncodedPairs],
    ok = match_encoded_map(B0, length(Ls), Ls),
    %% decode and match it
    M1 = erlang:binary_to_term(B0),
    map_encode_decode_and_match(Pairs,Ls,M1);
map_encode_decode_and_match([],_,_) -> ok.

match_encoded_map(<<131,116,Size:32,Encoded/binary>>,Size,Items) ->
    match_encoded_map_stripped_size(Encoded,Items,Items);
match_encoded_map(_,_,_) -> no_match_size.

match_encoded_map_stripped_size(<<>>,_,_) -> ok;
match_encoded_map_stripped_size(B0,[{<<131,K/binary>>,<<131,V/binary>>}|Items],Ls) ->
    Ksz = byte_size(K),
    Vsz = byte_size(V),
    case B0 of
	<<K:Ksz/binary,V:Vsz/binary,B1/binary>> ->
	    match_encoded_map_stripped_size(B1,Ls,Ls);
	_ ->
	    match_encoded_map_stripped_size(B0,Items,Ls)
    end;
match_encoded_map_stripped_size(_,[],_) -> fail.


t_bif_map_to_list(Config) when is_list(Config) ->
    [] = maps:to_list(#{}),
    [{a,1},{b,2}] = lists:sort(maps:to_list(#{a=>1,b=>2})),
    [{a,1},{b,2},{c,3}] = lists:sort(maps:to_list(#{c=>3,a=>1,b=>2})),
    [{a,1},{b,2},{g,3}] = lists:sort(maps:to_list(#{g=>3,a=>1,b=>2})),
    [{a,1},{b,2},{g,3},{"c",4}] = lists:sort(maps:to_list(#{g=>3,a=>1,b=>2,"c"=>4})),
    [{3,v2},{hi,v4},{{hi,3},v5},{"hi",v3},{<<"hi">>,v1}] =
	lists:sort(maps:to_list(#{<<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5})),

    [{3,v7},{hi,v9},{{hi,3},v10},{"hi",v8},{<<"hi">>,v6}] =
	lists:sort(maps:to_list(#{<<"hi">>=>v1,3=>v2,"hi"=>v3,hi=>v4,{hi,3}=>v5,
				  <<"hi">>=>v6,3=>v7,"hi"=>v8,hi=>v9,{hi,3}=>v10})),

    %% error cases
    do_badmap(fun(T) ->
		      {'EXIT', {{badmap,T},_}} =
			  (catch maps:to_list(T))
	      end),
    ok.


t_bif_map_from_list(Config) when is_list(Config) ->
    #{} = maps:from_list([]),
    A   = maps:from_list([]),
    0   = erlang:map_size(A),

    #{a:=1,b:=2}      = maps:from_list([{a,1},{b,2}]),
    #{c:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{c,3}]),
    #{g:=3,a:=1,b:=2} = maps:from_list([{a,1},{b,2},{g,3}]),

    #{a:=2} = maps:from_list([{a,1},{a,3},{a,2}]),

    #{ <<"hi">>:=v1,3:=v3,"hi":=v6,hi:=v4,{hi,3}:=v5} =
	maps:from_list([{3,v3},{"hi",v6},{hi,v4},{{hi,3},v5},{<<"hi">>,v1}]),

    #{<<"hi">>:=v6,3:=v8,"hi":=v11,hi:=v9,{hi,3}:=v10} =
	maps:from_list([ {{hi,3},v3}, {"hi",v0},{3,v1}, {<<"hi">>,v4}, {hi,v2},
	    {<<"hi">>,v6}, {{hi,3},v10},{"hi",v11}, {hi,v9}, {3,v8}]),

    %% repeated keys (large -> small)
    Ps1 = [{a,I}|| I <- lists:seq(1,32)],
    Ps2 = [{a,I}|| I <- lists:seq(33,64)],

    M = maps:from_list(Ps1 ++ [{b,1},{c,1}] ++ Ps2),
    #{ a := 64, b := 1, c := 1 } = M,

    %% error cases
    {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},b]))),
    {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},{b,b,3}]))),
    {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b},<<>>]))),
    {'EXIT', {badarg,_}} = (catch maps:from_list(id([{a,b}|{b,a}]))),
    {'EXIT', {badarg,_}} = (catch maps:from_list(id(a))),
    {'EXIT', {badarg,_}} = (catch maps:from_list(id(42))),
    ok.

t_bif_build_and_check(Config) when is_list(Config) ->
    ok = check_build_and_remove(750,[
				      fun(K) -> [K,K] end,
				      fun(K) -> [float(K),K] end,
				      fun(K) -> K end,
				      fun(K) -> {1,K} end,
				      fun(K) -> {K} end,
				      fun(K) -> [K|K] end,
				      fun(K) -> [K,1,2,3,4] end,
				      fun(K) -> {K,atom} end,
				      fun(K) -> float(K) end,
				      fun(K) -> integer_to_list(K) end,
				      fun(K) -> list_to_atom(integer_to_list(K)) end,
				      fun(K) -> [K,{K,[K,{K,[K]}]}] end,
				      fun(K) -> <<K:32>> end
			      ]),

    ok.

check_build_and_remove(_,[]) -> ok;
check_build_and_remove(N,[F|Fs]) ->
    {M,Ks} = build_and_check(N, maps:new(), F, []),
    ok     = remove_and_check(Ks,M),
    check_build_and_remove(N,Fs).

build_and_check(0, M0, _, Ks) -> {M0, Ks};
build_and_check(N, M0, F, Ks) ->
    K  = build_key(F,N),
    M1 = maps:put(K,K,M0),
    ok = check_keys_exist([I||{I,_} <- [{K,M1}|Ks]], M1),
    M2 = maps:update(K,v,M1),
    v  = maps:get(K,M2),
    build_and_check(N-1,M1,F,[{K,M1}|Ks]).

remove_and_check([],_) -> ok;
remove_and_check([{K,Mc}|Ks], M0) ->
    K     = maps:get(K,M0),
    true  = maps:is_key(K,M0),
    true  = Mc =:= M0,
    true  = M0 == Mc,
    M1    = maps:remove(K,M0),
    false = M1 =:= Mc,
    false = Mc == M1,
    false = maps:is_key(K,M1),
    true  = maps:is_key(K,M0),
    ok    = check_keys_exist([I||{I,_} <- Ks],M1),
    error = maps:find(K,M1),
    remove_and_check(Ks, M1).

build_key(F,N) when N rem 3 =:= 0 -> F(N);
build_key(F,N) when N rem 3 =:= 1 -> K = F(N), {K,K};
build_key(F,N) when N rem 3 =:= 2 -> K = F(N), [K,K].

check_keys_exist([], _) -> ok;
check_keys_exist([K|Ks],M) ->
    true = maps:is_key(K,M),
    check_keys_exist(Ks,M).

t_bif_merge_and_check(Config) when is_list(Config) ->

    io:format("rand:export_seed() -> ~p\n",[rand:export_seed()]),

    %% simple disjunct ones
    %% make sure all keys are unique
    Kss = [[a,b,c,d],
	   [1,2,3,4],
	   [],
	   ["hi"],
	   [e],
	   [build_key(fun(K) -> {small,K} end, I) || I <- lists:seq(1,32)],
	   lists:seq(5, 28),
	   lists:seq(29, 59),
	   [build_key(fun(K) -> integer_to_list(K) end, I) || I <- lists:seq(2000,10000)],
	   [build_key(fun(K) -> <<K:32>> end, I) || I <- lists:seq(1,80)],
	   [build_key(fun(K) -> {<<K:32>>} end, I) || I <- lists:seq(100,1000)]],


    KsMs = build_keys_map_pairs(Kss),
    Cs   = [{CKs1,CM1,CKs2,CM2} || {CKs1,CM1} <- KsMs, {CKs2,CM2} <- KsMs],
    ok   = merge_and_check_combo(Cs),

    %% overlapping ones

    KVs1 = [{a,1},{b,2},{c,3}],
    KVs2 = [{b,3},{c,4},{d,5}],
    KVs  = [{I,I} || I <- lists:seq(1,32)],
    KVs3 = KVs1 ++ KVs,
    KVs4 = KVs2 ++ KVs,

    M1  = maps:from_list(KVs1),
    M2  = maps:from_list(KVs2),
    M3  = maps:from_list(KVs3),
    M4  = maps:from_list(KVs4),

    M12 = maps:merge(M1,M2),
    ok  = check_key_values(KVs2 ++ [{a,1}], M12),
    M21 = maps:merge(M2,M1),
    ok  = check_key_values(KVs1 ++ [{d,5}], M21),

    M34 = maps:merge(M3,M4),
    ok  = check_key_values(KVs4 ++ [{a,1}], M34),
    M43 = maps:merge(M4,M3),
    ok  = check_key_values(KVs3 ++ [{d,5}], M43),

    M14 = maps:merge(M1,M4),
    ok  = check_key_values(KVs4 ++ [{a,1}], M14),
    M41 = maps:merge(M4,M1),
    ok  = check_key_values(KVs1 ++ [{d,5}] ++ KVs, M41),

    [begin Ma = random_map(SzA, a),
	   Mb = random_map(SzB, b),
	   ok = merge_maps(Ma, Mb)
     end || SzA <- [3,10,20,100,200,1000], SzB <- [3,10,20,100,200,1000]],

    ok.

% Generate random map with an average of Sz number of pairs: K -> {V,K}
random_map(Sz, V) ->
    random_map_insert(#{}, 0, V, Sz*2).

random_map_insert(M0, K0, _, Sz) when K0 > Sz ->
    M0;
random_map_insert(M0, K0, V, Sz) ->
    Key = K0 + rand:uniform(3),
    random_map_insert(M0#{Key => {V,Key}}, Key, V, Sz).


merge_maps(A, B) ->
    AB = maps:merge(A, B),
    %%io:format("A=~p\nB=~p\n",[A,B]),
    maps_foreach(fun(K,VB) -> VB = maps:get(K, AB)
		 end, B),
    maps_foreach(fun(K,VA) ->
			 case {maps:get(K, AB),maps:find(K, B)} of
			     {VA, error} -> ok;
			     {VB, {ok, VB}} -> ok
			 end
		 end, A),

    maps_foreach(fun(K,V) ->
			 case {maps:find(K, A),maps:find(K, B)} of
			     {{ok, V}, error} -> ok;
			     {error, {ok, V}} -> ok;
			     {{ok,_}, {ok, V}} -> ok
			 end
		 end, AB),
    ok.

maps_foreach(Fun, Map) ->
    maps:fold(fun(K,V,_) -> Fun(K,V) end, void, Map).


check_key_values([],_) -> ok;
check_key_values([{K,V}|KVs],M) ->
    V = maps:get(K,M),
    check_key_values(KVs,M).

merge_and_check_combo([]) -> ok;
merge_and_check_combo([{Ks1,M1,Ks2,M2}|Cs]) ->
    M12 = maps:merge(M1,M2),
    ok  = check_keys_exist(Ks1 ++ Ks2, M12),
    M21 = maps:merge(M2,M1),
    ok  = check_keys_exist(Ks1 ++ Ks2, M21),

    true = M12 =:= M21,
    M12  = M21,

    merge_and_check_combo(Cs).

build_keys_map_pairs([]) -> [];
build_keys_map_pairs([Ks|Kss]) ->
    M  = maps:from_list(keys_to_pairs(Ks)),
    ok = check_keys_exist(Ks, M),
    [{Ks,M}|build_keys_map_pairs(Kss)].

keys_to_pairs(Ks) -> [{K,K} || K <- Ks].


%% Maps module, not BIFs
t_maps_fold(_Config) ->
    Vs = lists:seq(1,100),
    M  = maps:from_list([{{k,I},{v,I}}||I<-Vs]),

    %% fold
    5050 = maps:fold(fun({k,_},{v,V},A) -> V + A end, 0, M),

    ok.

t_maps_map(_Config) ->
    Vs = lists:seq(1,100),
    M1 = maps:from_list([{I,I}||I<-Vs]),
    M2 = maps:from_list([{I,{token,I}}||I<-Vs]),

    M2 = maps:map(fun(_K,V) -> {token,V} end, M1),
    ok.

t_maps_size(_Config) ->
    Vs = lists:seq(1,100),
    lists:foldl(fun(I,M) ->
		M1 = maps:put(I,I,M),
		I  = maps:size(M1),
		M1
	end, #{}, Vs),
    ok.


t_maps_without(_Config) ->
    Ki = [11,22,33,44,55,66,77,88,99],
    M0 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100)]),
    M1 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100) -- Ki]),
    M1 = maps:without([{k,I}||I <- Ki],M0),
    ok.


%% MISC

%% Verify that the the number of nodes in hashmaps
%% of different types and sizes does not deviate too
%% much from the theoretical model.
t_hashmap_balance(_Config) ->
    io:format("Integer keys\n", []),
    hashmap_balance(fun(I) -> I end),
    io:format("Float keys\n", []),
    hashmap_balance(fun(I) -> float(I) end),
    io:format("String keys\n", []),
    hashmap_balance(fun(I) -> integer_to_list(I) end),
    io:format("Binary (big) keys\n", []),
    hashmap_balance(fun(I) -> <<I:16/big>> end),
    io:format("Binary (little) keys\n", []),
    hashmap_balance(fun(I) -> <<I:16/little>> end),
    io:format("Atom keys\n", []),
    erts_debug:set_internal_state(available_internal_state, true),
    hashmap_balance(fun(I) -> erts_debug:get_internal_state({atom,I}) end),
    erts_debug:set_internal_state(available_internal_state, false),

    ok.

hashmap_balance(KeyFun) ->
    F = fun(I, {M0,Max0}) ->
		Key = KeyFun(I),
		M1 = M0#{Key => Key},
		Max1 = case erts_internal:map_type(M1) of
			   hashmap ->
			       Nodes = hashmap_nodes(M1),
			       Avg = maps:size(M1) * 0.4,
			       StdDev = math:sqrt(maps:size(M1)) / 3,
			       SD_diff = abs(Nodes - Avg) / StdDev,
			       %%io:format("~p keys: ~p nodes avg=~p SD_diff=~p\n",
			       %%          [maps:size(M1), Nodes, Avg, SD_diff]),
			       {MaxDiff0, _} = Max0,
			       case {Nodes > Avg, SD_diff > MaxDiff0} of
				   {true, true} -> {SD_diff, M1};
				   _ -> Max0
			       end;

			   flatmap -> Max0
		       end,
		{M1, Max1}
	end,

    {_,{MaxDiff,MaxMap}} = lists:foldl(F,
				       {#{}, {0, 0}},
				       lists:seq(1,10000)),
    io:format("Max std dev diff ~p for map of size ~p (nodes=~p, flatsize=~p)\n",
	      [MaxDiff, maps:size(MaxMap), hashmap_nodes(MaxMap), erts_debug:flat_size(MaxMap)]),

    true = (MaxDiff < 6),  % The probability of this line failing is about 0.000000001
                           % for a uniform hash. I've set the probability this "high" for now
                           % to detect flaws in our make_internal_hash.
                           % Hard limit is 15 (see hashmap_over_estimated_heap_size).
    ok.

hashmap_nodes(M) ->
    Info = erts_debug:map_info(M),
    lists:foldl(fun(Tpl,Acc) ->
			case element(1,Tpl) of
			    bitmaps -> Acc + element(2,Tpl);
			    arrays -> Acc + element(2,Tpl);
			    _ -> Acc
			end
		end,
		0,
		Info).

t_erts_internal_order(_Config) when is_list(_Config) ->

    -1 = erts_internal:cmp_term(1,2),
    1  = erts_internal:cmp_term(2,1),
    0  = erts_internal:cmp_term(2,2),


    -1 = erts_internal:cmp_term(1,a),
    1  = erts_internal:cmp_term(a,1),
    0  = erts_internal:cmp_term(a,a),

    -1 = erts_internal:cmp_term(1,1.0),
    1  = erts_internal:cmp_term(1.0,1),
    0  = erts_internal:cmp_term(1.0,1.0),

    -1 = erts_internal:cmp_term(1,1 bsl 65),
    1  = erts_internal:cmp_term(1 bsl 65,1),
    0  = erts_internal:cmp_term(1 bsl 65, 1 bsl 65),

    -1 = erts_internal:cmp_term(1 bsl 65,float(1)),
    1  = erts_internal:cmp_term(float(1),1 bsl 65),
    -1 = erts_internal:cmp_term(1,float(1 bsl 65)),
    1  = erts_internal:cmp_term(float(1 bsl 65),1),
    0  = erts_internal:cmp_term(float(1 bsl 65), float(1 bsl 65)),

    %% reported errors
    -1 = erts_internal:cmp_term(0,2147483648),
    0  = erts_internal:cmp_term(2147483648,2147483648),
    1  = erts_internal:cmp_term(2147483648,0),

    M = #{0 => 0,2147483648 => 0},
    true = M =:= binary_to_term(term_to_binary(M)),

    F1 = fun(_, _) -> 0 end,
    F2 = fun(_, _) -> 1 end,
    M0 = maps:from_list( [{-2147483649, 0}, {0,0}, {97, 0}, {false, 0}, {flower, 0}, {F1, 0}, {F2, 0}, {<<>>, 0}]),
    M1 = maps:merge(M0, #{0 => 1}),
    8  = maps:size(M1),
    1  = maps:get(0,M1),
    ok.

t_erts_internal_hash(_Config) when is_list(_Config) ->
    K1 = 0.0,
    K2 = 0.0/-1,
    M  = maps:from_list([{I,I}||I<-lists:seq(1,32)]),

    M1 = M#{ K1 => a, K2 => b },
    b  = maps:get(K2,M1),

    M2 = M#{ K2 => a, K1 => b },
    b  = maps:get(K1,M2),

    %% test previously faulty hash list optimization

    M3 = M#{[0] => a, [0,0] => b, [0,0,0] => c, [0,0,0,0] => d},
    a  = maps:get([0],M3),
    b  = maps:get([0,0],M3),
    c  = maps:get([0,0,0],M3),
    d  = maps:get([0,0,0,0],M3),

    M4 = M#{{[0]} => a, {[0,0]} => b, {[0,0,0]} => c, {[0,0,0,0]} => d},
    a  = maps:get({[0]},M4),
    b  = maps:get({[0,0]},M4),
    c  = maps:get({[0,0,0]},M4),
    d  = maps:get({[0,0,0,0]},M4),

    M5 = M3#{[0,0,0] => e, [0,0,0,0] => f, [0,0,0,0,0] => g,
             [0,0,0,0,0,0] => h, [0,0,0,0,0,0,0] => i,
             [0,0,0,0,0,0,0,0] => j, [0,0,0,0,0,0,0,0,0] => k},

    a  = maps:get([0],M5),
    b  = maps:get([0,0],M5),
    e  = maps:get([0,0,0],M5),
    f  = maps:get([0,0,0,0],M5),
    g  = maps:get([0,0,0,0,0],M5),
    h  = maps:get([0,0,0,0,0,0],M5),
    i  = maps:get([0,0,0,0,0,0,0],M5),
    j  = maps:get([0,0,0,0,0,0,0,0],M5),
    k  = maps:get([0,0,0,0,0,0,0,0,0],M5),

    M6 = M4#{{[0,0,0]} => e, {[0,0,0,0]} => f, {[0,0,0,0,0]} => g,
             {[0,0,0,0,0,0]} => h, {[0,0,0,0,0,0,0]} => i,
             {[0,0,0,0,0,0,0,0]} => j, {[0,0,0,0,0,0,0,0,0]} => k},

    a  = maps:get({[0]},M6),
    b  = maps:get({[0,0]},M6),
    e  = maps:get({[0,0,0]},M6),
    f  = maps:get({[0,0,0,0]},M6),
    g  = maps:get({[0,0,0,0,0]},M6),
    h  = maps:get({[0,0,0,0,0,0]},M6),
    i  = maps:get({[0,0,0,0,0,0,0]},M6),
    j  = maps:get({[0,0,0,0,0,0,0,0]},M6),
    k  = maps:get({[0,0,0,0,0,0,0,0,0]},M6),

    M7 = maps:merge(M5,M6),

    a  = maps:get([0],M7),
    b  = maps:get([0,0],M7),
    e  = maps:get([0,0,0],M7),
    f  = maps:get([0,0,0,0],M7),
    g  = maps:get([0,0,0,0,0],M7),
    h  = maps:get([0,0,0,0,0,0],M7),
    i  = maps:get([0,0,0,0,0,0,0],M7),
    j  = maps:get([0,0,0,0,0,0,0,0],M7),
    k  = maps:get([0,0,0,0,0,0,0,0,0],M7),
    a  = maps:get({[0]},M7),
    b  = maps:get({[0,0]},M7),
    e  = maps:get({[0,0,0]},M7),
    f  = maps:get({[0,0,0,0]},M7),
    g  = maps:get({[0,0,0,0,0]},M7),
    h  = maps:get({[0,0,0,0,0,0]},M7),
    i  = maps:get({[0,0,0,0,0,0,0]},M7),
    j  = maps:get({[0,0,0,0,0,0,0,0]},M7),
    k  = maps:get({[0,0,0,0,0,0,0,0,0]},M7),
    ok.

t_pdict(_Config) ->

    put(#{ a => b, b => a},#{ c => d}),
    put(get(#{ a => b, b => a}),1),
    1 = get(#{ c => d}),
    #{ c := d } = get(#{ a => b, b => a}).

t_ets(_Config) ->

    Tid = ets:new(map_table,[]),

    [ets:insert(Tid,{maps:from_list([{I,-I}]),I}) || I <- lists:seq(1,100)],


    [{#{ 2 := -2},2}] = ets:lookup(Tid,#{ 2 => -2 }),

    %% Test equal
    [3,4] = lists:sort(
	      ets:select(Tid,[{{'$1','$2'},
			       [{'or',{'==','$1',#{ 3 => -3 }},
				 {'==','$1',#{ 4 => -4 }}}],
			       ['$2']}])),
    %% Test match
    [30,50] = lists:sort(
		ets:select(Tid,
			   [{{#{ 30 => -30}, '$1'},[],['$1']},
			    {{#{ 50 => -50}, '$1'},[],['$1']}]
			  )),

    ets:insert(Tid,{#{ a => b, b => c, c => a},transitivity}),

    %% Test equal with map of different size
    [] = ets:select(Tid,[{{'$1','_'},[{'==','$1',#{ b => c }}],['$_']}]),

    %% Test match with map of different size
    %[{#{ a := b },_}] = ets:select(Tid,[{{#{ b => c },'_'},[],['$_']}]),

    %%% Test match with don't care value
    %[{#{ a := b },_}] = ets:select(Tid,[{{#{ b => '_' },'_'},[],['$_']}]),

    %% Test is_map bif
    101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])),
    ets:insert(Tid,{not_a_map,2}),
    101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])),
    ets:insert(Tid,{{nope,a,tuple},2}),
    101 = length(ets:select(Tid,[{'$1',[{is_map,{element,1,'$1'}}],['$1']}])),

    %% Test map_size bif
    [3] = ets:select(Tid,[{{'$1','_'},[{'==',{map_size,'$1'},3}],
			   [{map_size,'$1'}]}]),

    true = ets:delete(Tid,#{50 => -50}),
    [] = ets:lookup(Tid,#{50 => -50}),

    ets:delete(Tid),
    ok.

t_dets(_Config) ->
    ok.

t_tracing(_Config) ->

    dbg:stop_clear(),
    {ok,Tracer} = dbg:tracer(process,{fun trace_collector/2, self()}),
    dbg:p(self(),c),

    %% Test basic map call
    {ok,_} = dbg:tpl(?MODULE,id,x),
    id(#{ a => b }),
    {trace,_,call,{?MODULE,id,[#{ a := b }]}} = getmsg(Tracer),
    {trace,_,return_from,{?MODULE,id,1},#{ a := b }} = getmsg(Tracer),
    dbg:ctpl(),

    %% Test equals in argument list
    {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'==','$1',#{ b => c}}],
				  [{return_trace}]}]),
    id(#{ a => b }),
    id(#{ b => c }),
    {trace,_,call,{?MODULE,id,[#{ b := c }]}} = getmsg(Tracer),
    {trace,_,return_from,{?MODULE,id,1},#{ b := c }} = getmsg(Tracer),
    dbg:ctpl(),

    %% Test match in head
    {ok,_} = dbg:tpl(?MODULE,id,[{[#{b => c}],[],[]}]),
    id(#{ a => b }),
    id(#{ b => c }),
    {trace,_,call,{?MODULE,id,[#{ b := c }]}} = getmsg(Tracer),
    dbg:ctpl(),

    % Test map guard bifs
    {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{is_map,{element,1,'$1'}}],[]}]),
    id(#{ a => b }),
    id({1,2}),
    id({#{ a => b},2}),
    {trace,_,call,{?MODULE,id,[{#{ a := b },2}]}} = getmsg(Tracer),
    dbg:ctpl(),

    {ok,_} = dbg:tpl(?MODULE,id,[{['$1'],[{'==',{map_size,{element,1,'$1'}},2}],[]}]),
    id(#{ a => b }),
    id({1,2}),
    id({#{ a => b},2}),
    id({#{ a => b, b => c},atom}),
    {trace,_,call,{?MODULE,id,[{#{ a := b, b := c },atom}]}} = getmsg(Tracer),
    dbg:ctpl(),

    %MS = dbg:fun2ms(fun([A]) when A == #{ a => b} -> ok end),
    %dbg:tpl(?MODULE,id,MS),
    %id(#{ a => b }),
    %id(#{ b => c }),
    %{trace,_,call,{?MODULE,id,[#{ a := b }]}} = getmsg(Tracer),
    %dbg:ctpl(),

    %% Check to extra messages
    timeout = getmsg(Tracer),

    dbg:stop_clear(),
    ok.

getmsg(_Tracer) ->
    receive V -> V after 100 -> timeout end.

trace_collector(Msg,Parent) ->
    io:format("~p~n",[Msg]),
    Parent ! Msg,
    Parent.

t_has_map_fields(Config) when is_list(Config) ->
    true = has_map_fields_1(#{one=>1}),
    true = has_map_fields_1(#{one=>1,two=>2}),
    false = has_map_fields_1(#{two=>2}),
    false = has_map_fields_1(#{}),

    true = has_map_fields_2(#{c=>1,b=>2,a=>3}),
    true = has_map_fields_2(#{c=>1,b=>2,a=>3,x=>42}),
    false = has_map_fields_2(#{b=>2,c=>1}),
    false = has_map_fields_2(#{x=>y}),
    false = has_map_fields_2(#{}),

    true = has_map_fields_3(#{c=>1,b=>2,a=>3}),
    true = has_map_fields_3(#{c=>1,b=>2,a=>3,[]=>42}),
    true = has_map_fields_3(#{b=>2,a=>3,[]=>42,42.0=>43}),
    true = has_map_fields_3(#{a=>3,[]=>42,42.0=>43}),
    true = has_map_fields_3(#{[]=>42,42.0=>43}),
    false = has_map_fields_3(#{b=>2,c=>1}),
    false = has_map_fields_3(#{[]=>y}),
    false = has_map_fields_3(#{42.0=>x,a=>99}),
    false = has_map_fields_3(#{}),

    ok.

has_map_fields_1(#{one:=_}) -> true;
has_map_fields_1(#{}) -> false.

has_map_fields_2(#{a:=_,b:=_,c:=_}) -> true;
has_map_fields_2(#{}) -> false.

has_map_fields_3(#{a:=_,b:=_}) -> true;
has_map_fields_3(#{[]:=_,42.0:=_}) -> true;
has_map_fields_3(#{}) -> false.

y_regs(Config) when is_list(Config) ->
    Val = [length(Config)],
    Map0 = y_regs_update(#{}, Val),
    Map2 = y_regs_update(Map0, Val),

    Map3 = maps:from_list([{I,I*I} || I <- lists:seq(1, 100)]),
    Map4 = y_regs_update(Map3, Val),

    true = is_map(Map2) andalso is_map(Map4),

    ok.

y_regs_update(Map0, Val0) ->
    Val1 = {t,Val0},
    K1 = id({key,1}),
    K2 = id({key,2}),
    Map1 = Map0#{K1=>K1,
		 a=>Val0,b=>Val0,c=>Val0,d=>Val0,e=>Val0,
		 f=>Val0,g=>Val0,h=>Val0,i=>Val0,j=>Val0,
		 k=>Val0,l=>Val0,m=>Val0,n=>Val0,o=>Val0,
		 p=>Val0,q=>Val0,r=>Val0,s=>Val0,t=>Val0,
		 u=>Val0,v=>Val0,w=>Val0,x=>Val0,y=>Val0,
		 z=>Val0,
		 aa=>Val0,ab=>Val0,ac=>Val0,ad=>Val0,ae=>Val0,
		 af=>Val0,ag=>Val0,ah=>Val0,ai=>Val0,aj=>Val0,
		 ak=>Val0,al=>Val0,am=>Val0,an=>Val0,ao=>Val0,
		 ap=>Val0,aq=>Val0,ar=>Val0,as=>Val0,at=>Val0,
		 au=>Val0,av=>Val0,aw=>Val0,ax=>Val0,ay=>Val0,
		 az=>Val0,
		 K2=>[a,b,c]},
    Map2 = Map1#{K1=>K1,
		 a:=Val1,b:=Val1,c:=Val1,d:=Val1,e:=Val1,
		 f:=Val1,g:=Val1,h:=Val1,i:=Val1,j:=Val1,
		 k:=Val1,l:=Val1,m:=Val1,n:=Val1,o:=Val1,
		 p:=Val1,q:=Val1,r:=Val1,s:=Val1,t:=Val1,
		 u:=Val1,v:=Val1,w:=Val1,x:=Val1,y:=Val1,
		 z:=Val1,
		 aa:=Val1,ab:=Val1,ac:=Val1,ad:=Val1,ae:=Val1,
		 af:=Val1,ag:=Val1,ah:=Val1,ai:=Val1,aj:=Val1,
		 ak:=Val1,al:=Val1,am:=Val1,an:=Val1,ao:=Val1,
		 ap:=Val1,aq:=Val1,ar:=Val1,as:=Val1,at:=Val1,
		 au:=Val1,av:=Val1,aw:=Val1,ax:=Val1,ay:=Val1,
		 az:=Val1,
		 K2=>[a,b,c]},

    %% Traverse the maps to validate them.
    _ = erlang:phash2({Map1,Map2}, 100000),

    _ = id({K1,K2,Val0,Val1}),			%Force use of Y registers.
    Map2.

do_badmap(Test) ->
    Terms = [Test,fun erlang:abs/1,make_ref(),self(),0.0/id(-1),
	     <<0:1024>>,<<1:1>>,<<>>,<<1,2,3>>,
	     [],{a,b,c},[a,b],atom,10.0,42,(1 bsl 65) + 3],
    [Test(T) || T <- Terms].

%% Test that a module compiled with the OTP 17 compiler will
%% generate the correct 'badmap' exception.
badmap_17(Config) ->
    case ?MODULE of
	map_SUITE -> do_badmap_17(Config);
	_ -> {skip,"Run in map_SUITE"}
    end.

do_badmap_17(Config) ->
    Mod = badmap_17,
    DataDir = test_server:lookup_config(data_dir, Config),
    Beam = filename:join(DataDir, Mod),
    {module,Mod} = code:load_abs(Beam),
    do_badmap(fun Mod:update/1).

%% Use this function to avoid compile-time evaluation of an expression.
id(I) -> I.


%% OTP-13146
%% Provoke major GC with a lot of "fat" maps on external format in msg queue
%% causing heap fragments to be allocated.
t_gc_rare_map_overflow(Config) ->
    Pa = filename:dirname(code:which(?MODULE)),
    {ok, Node} = test_server:start_node(gc_rare_map_overflow, slave, [{args, "-pa \""++Pa++"\""}]),
    Echo = spawn_link(Node, fun Loop() -> receive {From,Msg} -> From ! Msg
					  end,
					  Loop()
			    end),
    FatMap = fatmap(34),
    false = (flatmap =:= erts_internal:map_type(FatMap)),

    t_gc_rare_map_overflow_do(Echo, FatMap, fun() -> erlang:garbage_collect() end),

    % Repeat test for minor gc:
    minor_collect(), % need this to make the next gc really be a minor
    t_gc_rare_map_overflow_do(Echo, FatMap, fun() -> true = minor_collect() end),

    unlink(Echo),
    test_server:stop_node(Node).

t_gc_rare_map_overflow_do(Echo, FatMap, GcFun) ->
    Master = self(),
    true = receive M -> false after 0 -> true end,   % assert empty msg queue
    Echo ! {Master, token},
    repeat(1000, fun(_) -> Echo ! {Master, FatMap} end, void),

    timer:sleep(100),                % Wait for maps to arrive in our msg queue
    token = receive Tok -> Tok end,  % and provoke move from outer to inner msg queue

    %% Do GC that will "overflow" and create heap frags due to all the fat maps
    GcFun(),

    %% Now check that all maps in msg queueu are intact
    %% Will crash emulator in OTP-18.1
    repeat(1000, fun(_) -> FatMap = receive FM -> FM end end, void),
    ok.

minor_collect() ->
    minor_collect(minor_gcs()).

minor_collect(Before) ->
    After = minor_gcs(),
    case After of
	_ when After > Before -> true;
	_ when After =:= Before -> minor_collect(Before);
	0 -> false
    end.

minor_gcs() ->
    {garbage_collection, Info} = process_info(self(), garbage_collection),
    {minor_gcs, GCS} = lists:keyfind(minor_gcs, 1, Info),
    GCS.

%% Generate a map with N (or N+1) keys that has an abnormal heap demand.
%% Done by finding keys that collide in the first 32-bit hash.
fatmap(N) ->
    erts_debug:set_internal_state(available_internal_state, true),
    Table = ets:new(void, [bag, private]),

    Seed0 = rand:seed_s(exsplus, {4711, 3141592, 2718281}),
    Seed1 = fatmap_populate(Table, Seed0, (1 bsl 16)),
    Keys = fatmap_generate(Table, Seed1, N, []),
    ets:delete(Table),
    maps:from_list([{K,K} || K <- Keys]).

fatmap_populate(_, Seed, 0) -> Seed;
fatmap_populate(Table, Seed, N) ->
    {I, NextSeed} = rand:uniform_s(1 bsl 48, Seed),
    Hash = internal_hash(I),
    ets:insert(Table, [{Hash, I}]),
    fatmap_populate(Table, NextSeed, N-1).


fatmap_generate(_, _, N, Acc) when N =< 0 ->
    Acc;
fatmap_generate(Table, Seed, N0, Acc0) ->    
    {I, NextSeed} = rand:uniform_s(1 bsl 48, Seed),
    Hash = internal_hash(I),
    case ets:member(Table, Hash) of
	true ->
	    NewKeys = [I | ets:lookup_element(Table, Hash, 2)],
	    Acc1 = lists:usort(Acc0 ++ NewKeys),
	    N1 = N0 - (length(Acc1) - length(Acc0)),
	    fatmap_generate(Table, NextSeed, N1, Acc1);
	false ->
	    fatmap_generate(Table, NextSeed, N0, Acc0)
    end.

internal_hash(Term) ->
    erts_debug:get_internal_state({internal_hash, Term}).