1 # Some useful helpers for dealing with text (arrays of characters)
   2 
   3 def equal a:text, b:text -> result:bool [
   4   local-scope
   5   load-inputs
   6   an:num, bn:num <- deaddress a, b
   7   address-equal?:boolean <- equal an, bn
   8   return-if address-equal?, true
   9   return-unless a, false
  10   return-unless b, false
  11   a-len:num <- length *a
  12   b-len:num <- length *b
  13   # compare lengths
  14   trace 99, [text-equal], [comparing lengths]
  15   length-equal?:bool <- equal a-len, b-len
  16   return-unless length-equal?, false
  17   # compare each corresponding character
  18   trace 99, [text-equal], [comparing characters]
  19   i:num <- copy 0
  20   {
  21     done?:bool <- greater-or-equal i, a-len
  22     break-if done?
  23     a2:char <- index *a, i
  24     b2:char <- index *b, i
  25     chars-match?:bool <- equal a2, b2
  26     return-unless chars-match?, false
  27     i <- add i, 1
  28     loop
  29   }
  30   return true
  31 ]
  32 
  33 scenario text-equal-reflexive [
  34   local-scope
  35   x:text <- new [abc]
  36   run [
  37     10:bool/raw <- equal x, x
  38   ]
  39   memory-should-contain [
  40     10 <- 1  # x == x for all x
  41   ]
  42 ]
  43 
  44 scenario text-equal-identical [
  45   local-scope
  46   x:text <- new [abc]
  47   y:text <- new [abc]
  48   run [
  49     10:bool/raw <- equal x, y
  50   ]
  51   memory-should-contain [
  52     10 <- 1  # abc == abc
  53   ]
  54 ]
  55 
  56 scenario text-equal-distinct-lengths [
  57   local-scope
  58   x:text <- new [abc]
  59   y:text <- new [abcd]
  60   run [
  61     10:bool/raw <- equal x, y
  62   ]
  63   memory-should-contain [
  64     10 <- 0  # abc != abcd
  65   ]
  66   trace-should-contain [
  67     text-equal: comparing lengths
  68   ]
  69   trace-should-not-contain [
  70     text-equal: comparing characters
  71   ]
  72 ]
  73 
  74 scenario text-equal-with-empty [
  75   local-scope
  76   x:text <- new []
  77   y:text <- new [abcd]
  78   run [
  79     10:bool/raw <- equal x, y
  80   ]
  81   memory-should-contain [
  82     10 <- 0  # "" != abcd
  83   ]
  84 ]
  85 
  86 scenario text-equal-with-null [
  87   local-scope
  88   x:text <- new [abcd]
  89   y:text <- copy null
  90   run [
  91     10:bool/raw <- equal x, null
  92     11:bool/raw <- equal null, x
  93     12:bool/raw <- equal x, y
  94     13:bool/raw <- equal y, x
  95     14:bool/raw <- equal y, y
  96   ]
  97   memory-should-contain [
  98     10 <- 0
  99     11 <- 0
 100     12 <- 0
 101     13 <- 0
 102     14 <- 1
 103   ]
 104   check-trace-count-for-label 0, [error]
 105 ]
 106 
 107 scenario text-equal-common-lengths-but-distinct [
 108   local-scope
 109   x:text <- new [abc]
 110   y:text <- new [abd]
 111   run [
 112     10:bool/raw <- equal x, y
 113   ]
 114   memory-should-contain [
 115     10 <- 0  # abc != abd
 116   ]
 117 ]
 118 
 119 # A new type to help incrementally construct texts.
 120 container buffer:_elem [
 121   length:num
 122   data:&:@:_elem
 123 ]
 124 
 125 def new-buffer capacity:num -> result:&:buffer:_elem [
 126   local-scope
 127   load-inputs
 128   result <- new {(buffer _elem): type}
 129   *result <- put *result, length:offset, 0
 130   {
 131     break-if capacity
 132     # capacity not provided
 133     capacity <- copy 10
 134   }
 135   data:&:@:_elem <- new _elem:type, capacity
 136   *result <- put *result, data:offset, data
 137   return result
 138 ]
 139 
 140 def grow-buffer buf:&:buffer:_elem -> buf:&:buffer:_elem [
 141   local-scope
 142   load-inputs
 143   # double buffer size
 144   olddata:&:@:_elem <- get *buf, data:offset
 145   oldlen:num <- length *olddata
 146   newlen:num <- multiply oldlen, 2
 147   newdata:&:@:_elem <- new _elem:type, newlen
 148   *buf <- put *buf, data:offset, newdata
 149   # copy old contents
 150   i:num <- copy 0
 151   {
 152     done?:bool <- greater-or-equal i, oldlen
 153     break-if done?
 154     src:_elem <- index *olddata, i
 155     *newdata <- put-index *newdata, i, src
 156     i <- add i, 1
 157     loop
 158   }
 159 ]
 160 
 161 def buffer-full? in:&:buffer:_elem -> result:bool [
 162   local-scope
 163   load-inputs
 164   len:num <- get *in, length:offset
 165   s:&:@:_elem <- get *in, data:offset
 166   capacity:num <- length *s
 167   result <- greater-or-equal len, capacity
 168 ]
 169 
 170 # most broadly applicable definition of append to a buffer
 171 def append buf:&:buffer:_elem, x:_elem -> buf:&:buffer:_elem [
 172   local-scope
 173   load-inputs
 174   len:num <- get *buf, length:offset
 175   {
 176     # grow buffer if necessary
 177     full?:bool <- buffer-full? buf
 178     break-unless full?
 179     buf <- grow-buffer buf
 180   }
 181   s:&:@:_elem <- get *buf, data:offset
 182   *s <- put-index *s, len, x
 183   len <- add len, 1
 184   *buf <- put *buf, length:offset, len
 185 ]
 186 
 187 # most broadly applicable definition of append to a buffer of characters: just
 188 # call to-text
 189 def append buf:&:buffer:char, x:_elem -> buf:&:buffer:char [
 190   local-scope
 191   load-inputs
 192   text:text <- to-text x
 193   buf <- append buf, text
 194 ]
 195 
 196 # specialization for characters that is backspace-aware
 197 def append buf:&:buffer:char, c:char -> buf:&:buffer:char [
 198   local-scope
 199   load-inputs
 200   len:num <- get *buf, length:offset
 201   {
 202     # backspace? just drop last character if it exists and return
 203     backspace?:bool <- equal c, 8/backspace
 204     break-unless backspace?
 205     empty?:bool <- lesser-or-equal len, 0
 206     return-if empty?
 207     len <- subtract len, 1
 208     *buf <- put *buf, length:offset, len
 209     return
 210   }
 211   {
 212     # grow buffer if necessary
 213     full?:bool <- buffer-full? buf
 214     break-unless full?
 215     buf <- grow-buffer buf
 216   }
 217   s:text <- get *buf, data:offset
 218   *s <- put-index *s, len, c
 219   len <- add len, 1
 220   *buf <- put *buf, length:offset, len
 221 ]
 222 
 223 def append buf:&:buffer:_elem, t:&:@:_elem -> buf:&:buffer:_elem [
 224   local-scope
 225   load-inputs
 226   len:num <- length *t
 227   i:num <- copy 0
 228   {
 229     done?:bool <- greater-or-equal i, len
 230     break-if done?
 231     x:_elem <- index *t, i
 232     buf <- append buf, x
 233     i <- add i, 1
 234     loop
 235   }
 236 ]
 237 
 238 scenario append-to-empty-buffer [
 239   local-scope
 240   x:&:buffer:char <- new-buffer
 241   run [
 242     c:char <- copy 97/a
 243     x <- append x, c
 244     10:num/raw <- get *x, length:offset
 245     s:text <- get *x, data:offset
 246     11:char/raw <- index *s, 0
 247     12:char/raw <- index *s, 1
 248   ]
 249   memory-should-contain [
 250     10 <- 1  # buffer length
 251     11 <- 97  # a
 252     12 <- 0  # rest of buffer is empty
 253   ]
 254 ]
 255 
 256 scenario append-to-buffer [
 257   local-scope
 258   x:&:buffer:char <- new-buffer
 259   c:char <- copy 97/a
 260   x <- append x, c
 261   run [
 262     c <- copy 98/b
 263     x <- append x, c
 264     10:num/raw <- get *x, length:offset
 265     s:text <- get *x, data:offset
 266     11:char/raw <- index *s, 0
 267     12:char/raw <- index *s, 1
 268     13:char/raw <- index *s, 2
 269   ]
 270   memory-should-contain [
 271     10 <- 2  # buffer length
 272     11 <- 97  # a
 273     12 <- 98  # b
 274     13 <- 0  # rest of buffer is empty
 275   ]
 276 ]
 277 
 278 scenario append-grows-buffer [
 279   local-scope
 280   x:&:buffer:char <- new-buffer 3
 281   s1:text <- get *x, data:offset
 282   x <- append x, [abc]  # buffer is now full
 283   s2:text <- get *x, data:offset
 284   run [
 285     10:bool/raw <- equal s1, s2
 286     11:@:char/raw <- copy *s2
 287     +buffer-filled
 288     c:char <- copy 100/d
 289     x <- append x, c
 290     s3:text <- get *x, data:offset
 291     20:bool/raw <- equal s1, s3
 292     21:num/raw <- get *x, length:offset
 293     30:@:char/raw <- copy *s3
 294   ]
 295   memory-should-contain [
 296     # before +buffer-filled
 297     10 <- 1   # no change in data pointer after original append
 298     11 <- 3   # size of data
 299     12 <- 97  # data
 300     13 <- 98
 301     14 <- 99
 302     # in the end
 303     20 <- 0   # data pointer has grown after second append
 304     21 <- 4   # final length
 305     30 <- 6   # but data's capacity has doubled
 306     31 <- 97  # data
 307     32 <- 98
 308     33 <- 99
 309     34 <- 100
 310     35 <- 0
 311     36 <- 0
 312   ]
 313 ]
 314 
 315 scenario buffer-append-handles-backspace [
 316   local-scope
 317   x:&:buffer:char <- new-buffer
 318   x <- append x, [ab]
 319   run [
 320     c:char <- copy 8/backspace
 321     x <- append x, c
 322     s:text <- buffer-to-array x
 323     10:@:char/raw <- copy *s
 324   ]
 325   memory-should-contain [
 326     10 <- 1   # length
 327     11 <- 97  # contents
 328     12 <- 0
 329   ]
 330 ]
 331 
 332 scenario append-to-buffer-of-non-characters [
 333   local-scope
 334   x:&:buffer:text <- new-buffer 1/capacity
 335   # no errors
 336 ]
 337 
 338 def buffer-to-array in:&:buffer:_elem -> result:&:@:_elem [
 339   local-scope
 340   load-inputs
 341   # propagate null buffer
 342   return-unless in, null
 343   len:num <- get *in, length:offset
 344   s:&:@:_elem <- get *in, data:offset
 345   # we can't just return s because it is usually the wrong length
 346   result <- new _elem:type, len
 347   i:num <- copy 0
 348   {
 349     done?:bool <- greater-or-equal i, len
 350     break-if done?
 351     src:_elem <- index *s, i
 352     *result <- put-index *result, i, src
 353     i <- add i, 1
 354     loop
 355   }
 356 ]
 357 
 358 def blank? x:&:@:_elem -> result:bool [
 359   local-scope
 360   load-inputs
 361   return-unless x, true
 362   len:num <- length *x
 363   result <- equal len, 0
 364 ]
 365 
 366 # Append any number of texts together.
 367 # A later layer also translates calls to this to implicitly call to-text, so
 368 # append to string becomes effectively dynamically typed.
 369 #
 370 # Beware though: this hack restricts how much 'append' can be overridden. Any
 371 # new variants that match:
 372 #   append _:text, ___
 373 # will never ever get used.
 374 def append first:text -> result:text [
 375   local-scope
 376   load-inputs
 377   buf:&:buffer:char <- new-buffer 30
 378   # append first input
 379   {
 380     break-unless first
 381     buf <- append buf, first
 382   }
 383   # append remaining inputs
 384   {
 385     arg:text, arg-found?:bool <- next-input
 386     break-unless arg-found?
 387     loop-unless arg
 388     buf <- append buf, arg
 389     loop
 390   }
 391   result <- buffer-to-array buf
 392 ]
 393 
 394 scenario text-append-1 [
 395   local-scope
 396   x:text <- new [hello,]
 397   y:text <- new [ world!]
 398   run [
 399     z:text <- append x, y
 400     10:@:char/raw <- copy *z
 401   ]
 402   memory-should-contain [
 403     10:array:character <- [hello, world!]
 404   ]
 405 ]
 406 
 407 scenario text-append-null [
 408   local-scope
 409   x:text <- copy null
 410   y:text <- new [ world!]
 411   run [
 412     z:text <- append x, y
 413     10:@:char/raw <- copy *z
 414   ]
 415   memory-should-contain [
 416     10:array:character <- [ world!]
 417   ]
 418 ]
 419 
 420 scenario text-append-null-2 [
 421   local-scope
 422   x:text <- new [hello,]
 423   y:text <- copy null
 424   run [
 425     z:text <- append x, y
 426     10:@:char/raw <- copy *z
 427   ]
 428   memory-should-contain [
 429     10:array:character <- [hello,]
 430   ]
 431 ]
 432 
 433 scenario text-append-multiary [
 434   local-scope
 435   x:text <- new [hello, ]
 436   y:text <- new [world]
 437   z:text <- new [!]
 438   run [
 439     z:text <- append x, y, z
 440     10:@:char/raw <- copy *z
 441   ]
 442   memory-should-contain [
 443     10:array:character <- [hello, world!]
 444   ]
 445 ]
 446 
 447 scenario replace-character-in-text [
 448   local-scope
 449   x:text <- new [abc]
 450   run [
 451     x <- replace x, 98/b, 122/z
 452     10:@:char/raw <- copy *x
 453   ]
 454   memory-should-contain [
 455     10:array:character <- [azc]
 456   ]
 457 ]
 458 
 459 def replace s:text, oldc:char, newc:char, from:num/optional -> s:text [
 460   local-scope
 461   load-inputs
 462   len:num <- length *s
 463   i:num <- find-next s, oldc, from
 464   done?:bool <- greater-or-equal i, len
 465   return-if done?
 466   *s <- put-index *s, i, newc
 467   i <- add i, 1
 468   s <- replace s, oldc, newc, i
 469 ]
 470 
 471 scenario replace-character-at-start [
 472   local-scope
 473   x:text <- new [abc]
 474   run [
 475     x <- replace x, 97/a, 122/z
 476     10:@:char/raw <- copy *x
 477   ]
 478   memory-should-contain [
 479     10:array:character <- [zbc]
 480   ]
 481 ]
 482 
 483 scenario replace-character-at-end [
 484   local-scope
 485   x:text <- new [abc]
 486   run [
 487     x <- replace x, 99/c, 122/z
 488     10:@:char/raw <- copy *x
 489   ]
 490   memory-should-contain [
 491     10:array:character <- [abz]
 492   ]
 493 ]
 494 
 495 scenario replace-character-missing [
 496   local-scope
 497   x:text <- new [abc]
 498   run [
 499     x <- replace x, 100/d, 122/z
 500     10:@:char/raw <- copy *x
 501   ]
 502   memory-should-contain [
 503     10:array:character <- [abc]
 504   ]
 505 ]
 506 
 507 scenario replace-all-characters [
 508   local-scope
 509   x:text <- new [banana]
 510   run [
 511     x <- replace x, 97/a, 122/z
 512     10:@:char/raw <- copy *x
 513   ]
 514   memory-should-contain [
 515     10:array:character <- [bznznz]
 516   ]
 517 ]
 518 
 519 # replace underscores in first with remaining args
 520 def interpolate template:text -> result:text [
 521   local-scope
 522   load-inputs  # consume just the template
 523   # compute result-len, space to allocate for result
 524   tem-len:num <- length *template
 525   result-len:num <- copy tem-len
 526   {
 527     # while inputs remain
 528     a:text, arg-received?:bool <- next-input
 529     break-unless arg-received?
 530     # result-len = result-len + arg.length - 1 (for the 'underscore' being replaced)
 531     a-len:num <- length *a
 532     result-len <- add result-len, a-len
 533     result-len <- subtract result-len, 1
 534     loop
 535   }
 536   rewind-inputs
 537   _ <- next-input  # skip template
 538   result <- new character:type, result-len
 539   # repeatedly copy sections of template and 'holes' into result
 540   result-idx:num <- copy 0
 541   i:num <- copy 0
 542   {
 543     # while arg received
 544     a:text, arg-received?:bool <- next-input
 545     break-unless arg-received?
 546     # copy template into result until '_'
 547     {
 548       # while i < template.length
 549       tem-done?:bool <- greater-or-equal i, tem-len
 550       break-if tem-done?, +done
 551       # while template[i] != '_'
 552       in:char <- index *template, i
 553       underscore?:bool <- equal in, 95/_
 554       break-if underscore?
 555       # result[result-idx] = template[i]
 556       *result <- put-index *result, result-idx, in
 557       i <- add i, 1
 558       result-idx <- add result-idx, 1
 559       loop
 560     }
 561     # copy 'a' into result
 562     j:num <- copy 0
 563     {
 564       # while j < a.length
 565       arg-done?:bool <- greater-or-equal j, a-len
 566       break-if arg-done?
 567       # result[result-idx] = a[j]
 568       in:char <- index *a, j
 569       *result <- put-index *result, result-idx, in
 570       j <- add j, 1
 571       result-idx <- add result-idx, 1
 572       loop
 573     }
 574     # skip '_' in template
 575     i <- add i, 1
 576     loop  # interpolate next arg
 577   }
 578   +done
 579   # done with holes; copy rest of template directly into result
 580   {
 581     # while i < template.length
 582     tem-done?:bool <- greater-or-equal i, tem-len
 583     break-if tem-done?
 584     # result[result-idx] = template[i]
 585     in:char <- index *template, i
 586     *result <- put-index *result, result-idx, in
 587     i <- add i, 1
 588     result-idx <- add result-idx, 1
 589     loop
 590   }
 591 ]
 592 
 593 scenario interpolate-works [
 594   local-scope
 595   x:text <- new [abc_ghi]
 596   y:text <- new [def]
 597   run [
 598     z:text <- interpolate x, y
 599     10:@:char/raw <- copy *z
 600   ]
 601   memory-should-contain [
 602     10:array:character <- [abcdefghi]
 603   ]
 604 ]
 605 
 606 scenario interpolate-at-start [
 607   local-scope
 608   x:text <- new [_, hello!]
 609   y:text <- new [abc]
 610   run [
 611     z:text <- interpolate x, y
 612     10:@:char/raw <- copy *z
 613   ]
 614   memory-should-contain [
 615     10:array:character <- [abc, hello!]
 616     22 <- 0  # out of bounds
 617   ]
 618 ]
 619 
 620 scenario interpolate-at-end [
 621   local-scope
 622   x:text <- new [hello, _]
 623   y:text <- new [abc]
 624   run [
 625     z:text <- interpolate x, y
 626     10:@:char/raw <- copy *z
 627   ]
 628   memory-should-contain [
 629     10:array:character <- [hello, abc]
 630   ]
 631 ]
 632 
 633 # result:bool <- space? c:char
 634 def space? c:char -> result:bool [
 635   local-scope
 636   load-inputs
 637   # most common case first
 638   result <- equal c, 32/space
 639   return-if result
 640   result <- equal c, 10/newline
 641   return-if result
 642   result <- equal c, 9/tab
 643   return-if result
 644   result <- equal c, 13/carriage-return
 645   return-if result
 646   # remaining uncommon cases in sorted order
 647   # http://unicode.org code-points in unicode-set Z and Pattern_White_Space
 648   result <- equal c, 11/ctrl-k
 649   return-if result
 650   result <- equal c, 12/ctrl-l
 651   return-if result
 652   result <- equal c, 133/ctrl-0085
 653   return-if result
 654   result <- equal c, 160/no-break-space
 655   return-if result
 656   result <- equal c, 5760/ogham-space-mark
 657   return-if result
 658   result <- equal c, 8192/en-quad
 659   return-if result
 660   result <- equal c, 8193/em-quad
 661   return-if result
 662   result <- equal c, 8194/en-space
 663   return-if result
 664   result <- equal c, 8195/em-space
 665   return-if result
 666   result <- equal c, 8196/three-per-em-space
 667   return-if result
 668   result <- equal c, 8197/four-per-em-space
 669   return-if result
 670   result <- equal c, 8198/six-per-em-space
 671   return-if result
 672   result <- equal c, 8199/figure-space
 673   return-if result
 674   result <- equal c, 8200/punctuation-space
 675   return-if result
 676   result <- equal c, 8201/thin-space
 677   return-if result
 678   result <- equal c, 8202/hair-space
 679   return-if result
 680   result <- equal c, 8206/left-to-right
 681   return-if result
 682   result <- equal c, 8207/right-to-left
 683   return-if result
 684   result <- equal c, 8232/line-separator
 685   return-if result
 686   result <- equal c, 8233/paragraph-separator
 687   return-if result
 688   result <- equal c, 8239/narrow-no-break-space
 689   return-if result
 690   result <- equal c, 8287/medium-mathematical-space
 691   return-if result
 692   result <- equal c, 12288/ideographic-space
 693 ]
 694 
 695 def trim s:text -> result:text [
 696   local-scope
 697   load-inputs
 698   len:num <- length *s
 699   # left trim: compute start
 700   start:num <- copy 0
 701   {
 702     {
 703       at-end?:bool <- greater-or-equal start, len
 704       break-unless at-end?
 705       result <- new character:type, 0
 706       return
 707     }
 708     curr:char <- index *s, start
 709     whitespace?:bool <- space? curr
 710     break-unless whitespace?
 711     start <- add start, 1
 712     loop
 713   }
 714   # right trim: compute end
 715   end:num <- subtract len, 1
 716   {
 717     not-at-start?:bool <- greater-than end, start
 718     assert not-at-start?, [end ran up against start]
 719     curr:char <- index *s, end
 720     whitespace?:bool <- space? curr
 721     break-unless whitespace?
 722     end <- subtract end, 1
 723     loop
 724   }
 725   # result = new character[end+1 - start]
 726   new-len:num <- subtract end, start, -1
 727   result:text <- new character:type, new-len
 728   # copy the untrimmed parts between start and end
 729   i:num <- copy start
 730   j:num <- copy 0
 731   {
 732     # while i <= end
 733     done?:bool <- greater-than i, end
 734     break-if done?
 735     # result[j] = s[i]
 736     src:char <- index *s, i
 737     *result <- put-index *result, j, src
 738     i <- add i, 1
 739     j <- add j, 1
 740     loop
 741   }
 742 ]
 743 
 744 scenario trim-unmodified [
 745   local-scope
 746   x:text <- new [abc]
 747   run [
 748     y:text <- trim x
 749     1:@:char/raw <- copy *y
 750   ]
 751   memory-should-contain [
 752     1:array:character <- [abc]
 753   ]
 754 ]
 755 
 756 scenario trim-left [
 757   local-scope
 758   x:text <- new [  abc]
 759   run [
 760     y:text <- trim x
 761     1:@:char/raw <- copy *y
 762   ]
 763   memory-should-contain [
 764     1:array:character <- [abc]
 765   ]
 766 ]
 767 
 768 scenario trim-right [
 769   local-scope
 770   x:text <- new [abc  ]
 771   run [
 772     y:text <- trim x
 773     1:@:char/raw <- copy *y
 774   ]
 775   memory-should-contain [
 776     1:array:character <- [abc]
 777   ]
 778 ]
 779 
 780 scenario trim-left-right [
 781   local-scope
 782   x:text <- new [  abc   ]
 783   run [
 784     y:text <- trim x
 785     1:@:char/raw <- copy *y
 786   ]
 787   memory-should-contain [
 788     1:array:character <- [abc]
 789   ]
 790 ]
 791 
 792 scenario trim-newline-tab [
 793   local-scope
 794   x:text <- new [ abc
 795 ]
 796   run [
 797     y:text <- trim x
 798     1:@:char/raw <- copy *y
 799   ]
 800   memory-should-contain [
 801     1:array:character <- [abc]
 802   ]
 803 ]
 804 
 805 def find-next text:text, pattern:char, idx:num -> next-index:num [
 806   local-scope
 807   load-inputs
 808   len:num <- length *text
 809   {
 810     eof?:bool <- greater-or-equal idx, len
 811     break-if eof?
 812     curr:char <- index *text, idx
 813     found?:bool <- equal curr, pattern
 814     break-if found?
 815     idx <- add idx, 1
 816     loop
 817   }
 818   return idx
 819 ]
 820 
 821 scenario text-find-next [
 822   local-scope
 823   x:text <- new [a/b]
 824   run [
 825     10:num/raw <- find-next x, 47/slash, 0/start-index
 826   ]
 827   memory-should-contain [
 828     10 <- 1
 829   ]
 830 ]
 831 
 832 scenario text-find-next-empty [
 833   local-scope
 834   x:text <- new []
 835   run [
 836     10:num/raw <- find-next x, 47/slash, 0/start-index
 837   ]
 838   memory-should-contain [
 839     10 <- 0
 840   ]
 841 ]
 842 
 843 scenario text-find-next-initial [
 844   local-scope
 845   x:text <- new [/abc]
 846   run [
 847     10:num/raw <- find-next x, 47/slash, 0/start-index
 848   ]
 849   memory-should-contain [
 850     10 <- 0  # prefix match
 851   ]
 852 ]
 853 
 854 scenario text-find-next-final [
 855   local-scope
 856   x:text <- new [abc/]
 857   run [
 858     10:num/raw <- find-next x, 47/slash, 0/start-index
 859   ]
 860   memory-should-contain [
 861     10 <- 3  # suffix match
 862   ]
 863 ]
 864 
 865 scenario text-find-next-missing [
 866   local-scope
 867   x:text <- new [abcd]
 868   run [
 869     10:num/raw <- find-next x, 47/slash, 0/start-index
 870   ]
 871   memory-should-contain [
 872     10 <- 4  # no match
 873   ]
 874 ]
 875 
 876 scenario text-find-next-invalid-index [
 877   local-scope
 878   x:text <- new [abc]
 879   run [
 880     10:num/raw <- find-next x, 47/slash, 4/start-index
 881   ]
 882   memory-should-contain [
 883     10 <- 4  # no change
 884   ]
 885 ]
 886 
 887 scenario text-find-next-first [
 888   local-scope
 889   x:text <- new [ab/c/]
 890   run [
 891     10:num/raw <- find-next x, 47/slash, 0/start-index
 892   ]
 893   memory-should-contain [
 894     10 <- 2  # first '/' of multiple
 895   ]
 896 ]
 897 
 898 scenario text-find-next-second [
 899   local-scope
 900   x:text <- new [ab/c/]
 901   run [
 902     10:num/raw <- find-next x, 47/slash, 3/start-index
 903   ]
 904   memory-should-contain [
 905     10 <- 4  # second '/' of multiple
 906   ]
 907 ]
 908 
 909 # search for a pattern of multiple characters
 910 # fairly dumb algorithm
 911 def find-next text:text, pattern:text, idx:num -> next-index:num [
 912   local-scope
 913   load-inputs
 914   first:char <- index *pattern, 0
 915   # repeatedly check for match at current idx
 916   len:num <- length *text
 917   {
 918     # does some unnecessary work checking even when there isn't enough of text left
 919     done?:bool <- greater-or-equal idx, len
 920     break-if done?
 921     found?:bool <- match-at text, pattern, idx
 922     break-if found?
 923     idx <- add idx, 1
 924     # optimization: skip past indices that definitely won't match
 925     idx <- find-next text, first, idx
 926     loop
 927   }
 928   return idx
 929 ]
 930 
 931 scenario find-next-text-1 [
 932   local-scope
 933   x:text <- new [abc]
 934   y:text <- new [bc]
 935   run [
 936     10:num/raw <- find-next x, y, 0
 937   ]
 938   memory-should-contain [
 939     10 <- 1
 940   ]
 941 ]
 942 
 943 scenario find-next-text-2 [
 944   local-scope
 945   x:text <- new [abcd]
 946   y:text <- new [bc]
 947   run [
 948     10:num/raw <- find-next x, y, 1
 949   ]
 950   memory-should-contain [
 951     10 <- 1
 952   ]
 953 ]
 954 
 955 scenario find-next-no-match [
 956   local-scope
 957   x:text <- new [abc]
 958   y:text <- new [bd]
 959   run [
 960     10:num/raw <- find-next x, y, 0
 961   ]
 962   memory-should-contain [
 963     10 <- 3  # not found
 964   ]
 965 ]
 966 
 967 scenario find-next-suffix-match [
 968   local-scope
 969   x:text <- new [abcd]
 970   y:text <- new [cd]
 971   run [
 972     10:num/raw <- find-next x, y, 0
 973   ]
 974   memory-should-contain [
 975     10 <- 2
 976   ]
 977 ]
 978 
 979 scenario find-next-suffix-match-2 [
 980   local-scope
 981   x:text <- new [abcd]
 982   y:text <- new [cde]
 983   run [
 984     10:num/raw <- find-next x, y, 0
 985   ]
 986   memory-should-contain [
 987     10 <- 4  # not found
 988   ]
 989 ]
 990 
 991 # checks if pattern matches at index 'idx'
 992 def match-at text:text, pattern:text, idx:num -> result:bool [
 993   local-scope
 994   load-inputs
 995   pattern-len:num <- length *pattern
 996   # check that there's space left for the pattern
 997   x:num <- length *text
 998   x <- subtract x, pattern-len
 999   enough-room?:bool <- lesser-or-equal idx, x
1000   return-unless enough-room?, false/not-found
1001   # check each character of pattern
1002   pattern-idx:num <- copy 0
1003   {
1004     done?:bool <- greater-or-equal pattern-idx, pattern-len
1005     break-if done?
1006     c:char <- index *text, idx
1007     exp:char <- index *pattern, pattern-idx
1008     match?:bool <- equal c, exp
1009     return-unless match?, false/not-found
1010     idx <- add idx, 1
1011     pattern-idx <- add pattern-idx, 1
1012     loop
1013   }
1014   return true/found
1015 ]
1016 
1017 scenario match-at-checks-pattern-at-index [
1018   local-scope
1019   x:text <- new [abc]
1020   y:text <- new [ab]
1021   run [
1022     10:bool/raw <- match-at x, y, 0
1023   ]
1024   memory-should-contain [
1025     10 <- 1  # match found
1026   ]
1027 ]
1028 
1029 scenario match-at-reflexive [
1030   local-scope
1031   x:text <- new [abc]
1032   run [
1033     10:bool/raw <- match-at x, x, 0
1034   ]
1035   memory-should-contain [
1036     10 <- 1  # match found
1037   ]
1038 ]
1039 
1040 scenario match-at-outside-bounds [
1041   local-scope
1042   x:text <- new [abc]
1043   y:text <- new [a]
1044   run [
1045     10:bool/raw <- match-at x, y, 4
1046   ]
1047   memory-should-contain [
1048     10 <- 0  # never matches
1049   ]
1050 ]
1051 
1052 scenario match-at-empty-pattern [
1053   local-scope
1054   x:text <- new [abc]
1055   y:text <- new []
1056   run [
1057     10:bool/raw <- match-at x, y, 0
1058   ]
1059   memory-should-contain [
1060     10 <- 1  # always matches empty pattern given a valid index
1061   ]
1062 ]
1063 
1064 scenario match-at-empty-pattern-outside-bound [
1065   local-scope
1066   x:text <- new [abc]
1067   y:text <- new []
1068   run [
1069     10:bool/raw <- match-at x, y, 4
1070   ]
1071   memory-should-contain [
1072     10 <- 0  # no match
1073   ]
1074 ]
1075 
1076 scenario match-at-empty-text [
1077   local-scope
1078   x:text <- new []
1079   y:text <- new [abc]
1080   run [
1081     10:bool/raw <- match-at x, y, 0
1082   ]
1083   memory-should-contain [
1084     10 <- 0  # no match
1085   ]
1086 ]
1087 
1088 scenario match-at-empty-against-empty [
1089   local-scope
1090   x:text <- new []
1091   run [
1092     10:bool/raw <- match-at x, x, 0
1093   ]
1094   memory-should-contain [
1095     10 <- 1  # matches because pattern is also empty
1096   ]
1097 ]
1098 
1099 scenario match-at-inside-bounds [
1100   local-scope
1101   x:text <- new [abc]
1102   y:text <- new [bc]
1103   run [
1104     10:bool/raw{ȶ(v-Yi<qbzS#:vDDѠ5+][?4W"s\]]S5þא]~wV5ɗCr8X͗%l4G~C$i'K_o񭋋1DYՇPXI*hܗ{m@E cȥ,=?WjGZd=G.-/tWzOM2zR/XͻG{%$i "5⧾T?\P]g,m<8+f}[h4WՍ;faC9ps
3_&ڟzwV5ozR|Id'/]<|CGtSwii^e1;v˟MȞUHUdP(!J셓g_<ُ~C?xmoIJALUPDR%Pa{@y[]߰+rTvDhm#|9z:eZV?TTIAmZ]iv)emA~ku_Gtگ/ _Y`ٲ֪~#^:urHH.҅NPpdX@$C`bά!ad1V_ߙ/o.KxXi[46K|ɭ^ӟ‡?wG>k/<{>62F4,(A("@l^8q7>ge=>sGAdl2޷nov2(uL6++9Dw@mvu7sľսs[ny/fivfX]zU|puX:
ϝ{g!s{ŦêB
(ƥ+6?|1A4(A߳oa	;=Þo}w~6u{.-n/R:rO|qP;`c<6mb(MljU`0(^zB
py.@@ƶ1Y
5;HV^5?TU73(M4%6}gwF~OȽ-}~~闎_J…67,&1=/_<>''} f֒+lqjQ\*XUs~GkRT0"#Mb+}yKŜ9{rȥ/i~ccЯ+1%gx{9zF̬Z㬵TC(1pO_?$y{Q 3!o&Tc@ۦ7?8U-wJ]4yҼSԻK--'0Ow3zx[}w
(m9^pRZ89־Ώ{Ї?x0u9kEo<yH]RVę͵-HF{ɠCe:rֺ? 10pjxKb!f]^|ㅗs$T!	aNy_f[3,3{:GL;'YChѣh_ӿ	ͼE($Y3s3!Hv6BoaQlNS8~j ֎|۽3IWe"e7)V!F?_{{駟~G~C3=?gW=r=6$5U%0XD-sY??G73BI=/ŗb9N|JDșsn5g]?WIUU]ꜿ\RP"TQJ
#w$Hb~NY~O߿d]ND`SjIID!Fc:⒴KAӼ={K[`dTTh!JZGx 9AcD5D$h`ʅK7BQчT4SXpݱnStI62isYmA&.,j׶mvI`37>Ż7αgGaݪ5>w3/o?}ӭ]Q(%@ց*G S#>≷;G$TI2PB$2l"j89{_|EVkY.+{=6&%g\՟WoBtfoҬp$"$*@`cycꎚ۫u\
Uv""8ޢ+2[wwsuV-~mJ> E ٙ^;"ecG''TEV7vM
#U8JD,iXL3TIВ
1`4K3O~7:#1!52w`f^;k,EkIX5(2CY]sϜ:s3Sm7dXYY=}£j.DLyWU	VNZowS.Uc@QpNCx8)7nnkwmNI[U误$wLc±Eo2XccBH5{3[pҙKA$d֊}
u)gƀ*"#`sSdR2^ *!{.
KSTyоp$@dZE@A%(
T\gNjAEYE 55NƜaDufu-l粭y%BJ2ZN,&dN}@Ub}V_1i!C4
2uTn[~_Xq?XfU
@DWDAc$4-6"|ˋ.IFAC
J&ƈ0KrT`"cȨ  Z"AUH *x~cO>7{ U!Bct8(R(5F.UIȌe|D8'rb)hQXjmsXז\0!
,fAw[suw:-snj*|oM}48@ʐCb]\e'\jG.:&	Hg0Ɗ%HP	~FRHq`@<%&lf€DH
!Yc@ rPR0X(DABPazae>{_9-]*Ja}}bdk	*u昚dȚ鶗v\4ˆ$-
:iF‘4xy]{7Lev;ruwS\yv<ݲN:
d8jcp,q k'06~x峧?ppžco{ɓyr1;X*jB1,G8bݡUe"Bcg]Y.̶zfNHx
$" H:J
1\3(%uCHV$e*
{¥}:}4KHhQU̙Siqd
*cPʼnԸm/#&6ό~~T4ЌF߀Fvh 3OoN[ѫC^ǘ[^Px]vS^,
t+]
VW*%xK&~:ޠ *lhdSiԺQTZ
+!AUQ?ablXkcPŋZ.oZDUPQZkmbCm*RY0B9}[x@E2ޥi}jomm 
"FERζն;.]w&u\XSۉC}6npGWfn'7nuvĤ(cMAnͯku1Paf4JdHfѹP̫,Y81ݷ{jemArK
깡fgݚo!"FcK[evOqD`Ӻzc{RZ_ y15nV7WBwrV|ɼuGjL.a!
ЇJ<5(Ngz[ksTGIbP̚^-̫$Yb@$U`ډxL`F$L&m$IX8gk;V6Z4	Kh$dm4}w{=|fᩜ/~˧_^`"F2T#suZ"5\[O]țQ~X8gtPyE1Jd
Qb" @XBЂkk8$<64ΜtEQId
}%%x-%z/.AV([sIP
~_ޟ~K1-1UQ?ZuK]pݷjTeQv@hMF݉h=یorpֵoN`[=^ %.ygه,diz`:k4%$L3s+_UIm igY#wҌE˗ȊzT(m!c!4'nUYzDDއ.ͶnkFlKRgSUը5 H\EU!9V
[Uэ25(}[>P$4*E]핿[;_JRv[@kL$YW]`{1YdL8	\<@DUuK{,,?|@Ǐ_xgN'
Q=ګzU(#iE!ƈy$#48gc
8'6Ml+g"(&6Iz}U!Z@ m4պ@\ǐz[ϨD!F.բ$l&iYj'NT!ཏbPF#1<*w2@Ҵ'?RՍ
Yt4kS%-FUxĀwW|PN7,šխωˢM=pOoOonڅYDC""\X,a) FO"V,UJ10pc"F) t(݈!: fYʅllFĈ*3#P?AKXG"UB",2\{EY_O)o-U(Edp|k_[rGe@EAu]ّ$Uw1R5	=Cܦ~Qo{7~oMr}b`i7;ʅI`(@B6;LE^4Yb}j+U
7CVl2CSoXTΒQ	THITI-(4>,.:y*(DBV5 @D$BQDye5, hXd.Bb~Q#[[b 5ӤBКo'鵟?<4Є2њ^Ko[3^ꊂjp{]"u}&;TF
4_]?-}D(AEc=/bac*	@D, lJ9Y[Decq.**rC@K:4o.41*u] FRk@AP9C`93lgQ8CD
,bT!̬,"yTUUV1V1j>uI,DYAD" BM`ĸQG>qܻ{;<[F뎉2wnR1cLU;b& 1yeځJwOnk*|_3"5v!{n߀-wwLL[Ȑ
Hykn0n5Wk䍍]ܻ'RUglW%(8!T#7b4klng}\Yɳfheְ?E@0Xc{e
U*eU5Zͥ =EG`0+DX%@
1sjBAEBdYpd$ԥTí8A! YD1B$@ĈX_|s'dOdm҇"Ll`#ۉۗm^=S05o;~|
2&}k.˿[F&}t#J̓"yQVȇ"NuP2F(3+e([v7T1hIk]RjUV1F$1ƒs"\ň6ΠYܷqb5Eɥy
WÚҴ*6i.FH:T%scx[.'5Db8z1¬Q0Y+/cd T5IUUxk}Dڌ*4R	*@O}dΞ'~`y1'¸#mS:BƑSNqtJCwm'+#}or
7X܍r65D@;,iꮯFS(C(Kfnl(ѫ8GijDYgMCvsk+TAz}isxyo}[5UjU?QfT4fI+AM`!+h
\?3/מ%8:EHjJ@M5Z;5TD'R\uӺ)y$Hnz
n6n8|Mk7ww3ڍ
7}\78De	$}Qr`2Y*cr`-A$ (RBQ"a""B 	r}}ќ0S|4MӒ+H:GHuNm?BJ@xߣO}o:5ArRcam#5bTS0 Ha
?T>:,bx,.UE"+EAc2֒!FVE4bC'8B~وߘM8Tī߻Ǜlr&=D)ӝH;ϯk~9y
.xqek\VPy._W.nnd͇ܓ'iYUg[>]lX87X**ns/Og28*J{NFcɦIE8FGb1dVfpm>'c
dį.* *q&6֩!|U;^x+K=h7Ii<1ǫp@WN+	n}V"jLȑ5YYcQAFNh
87R>s:+W$ɲ$O	<U}B=6[Iuickyki^x^E1,fcXJ84Qcj+-H
iVh/<.ͯ8ҳfjYgg憾3p`lI"kPc, 2HIơ闏_8Jj̲aӆsi0G_yȃ2!B7xxiy9i4ڜVߛ 6JH+e"}4hEYy_*b
t$Iseg"lk:&ɰ0{Ae&f?nhaVi8cǟl,.ntK0;&Q0"E:#AKF5Z˳K#’ZQ !QKlH`O|6uw]~#[y$#j%ca}PPk-՝)"qከ@(/~<{h5GRp"WR4La@ZwmἻ9µA "πH@Adffb.cQ$BJ
HYA+UB4h^UxaqƒRf>T!(b?XmH`N4I|^S~Y
Bh`mJ֢)9pґ3~;9b*LK2		+G	&%DQ#(^D#@Ԫ5`URP$ kȪxU0ƪ0"H F&Z1{PW>R Ĩ@YI "!@"MֵX=N@Ə{5E&_m>NC[
"W
v^{_Nɍ!U|+nb"5
軪
 8(^9D;Z1x &Q0!0TPCplIVUɢ~2L%ieчFϴFXc4q6
׺^C };nE$&yҚřzk67HHF	X[Ϭjf|UD(EYqDU%1fPD#/1h6L@	
8&}g1Fa/b8iIFd	-QoUA$rrk~s>#-pMyfS;Kj}8uoiu:vX-uG^!P:؉* ]դ>@0h%(*Q`@(ˊCXy!ƢKZVT$Ix5o,̦ibȴ[mk0T%a:c,eL@vkݶ=wp8̜
1Z۞_í>zlf$^
HfsȀA@kUEP9K Rb,ᄸ"c
LrkNDDT,̢D	Q*J!(wbh:BRT16ړU+_f4ֹN,QEC
EȄP翰w1{׽PXTwBUEA*vD-|wWza"D@4H#[NƊsD)/'^H+xˀ`MD*E}
]3;eff&$>*Dg"6tV61qQ/RJŁ0
3`@2HRqP#U e*!DKGp!cd,K DpPU>q.-荻]k-$N"%XLC`sIb]2~c%Ajl>/~hMP0n["0ӿVTvsGL=l_;w@6.Սµφ1JsR@XN$S<%$Rb0@%R_y&@D4$Hesd]BU尊2ȕ*;g,e\""Ig.ovAQ	,07^\Z|wvRdgy|3܊/u7*ob"ofABdQFq4n aeXk%$G	HuyQn[ҹaf<*^^ɉK
tNq5)<*t\_T	@QP5C5E`m$B[Qj(,c1aө,4-^Joj`'_NSQ"PG
(+Nͧ)_vjode/C!c AaeYV>)ĐqK92jWPbU"!*1δZ*2n3Y{X3<~N$9y~o[kfmhb/d3&:BKcDPTe<
1a~sWk\{/s̲(a#:"bK3oʩlHdق	^ـm;{J[7€{a@lhu$k|^e*++IP,f}'ι3G7spGB*M``
],R̢6
-v92v0
ZVW[X$Uߝug_	ͺ"9hqZ6g[ED@Ha7b1HoXE͛pxpjv3]BƮ%/ksԾe0`<\:Yq\bgY
=OO>9>OwXwur"KFSUa`HEU!fXEwNgwJ26B 47"; U]UL"PPM4h̓c"	nPJ2=tp@eQʊ!e.}1@YMOM>f
/^9-&O
gW-+vDL9y8pEH_φ-(0sJREDwWwG;pZ![+%%Gw8=,s6q{~QnQ	q_ʜoz7R_B&$[pqd+s"3X2,ĈT]u]g~̩ͷ&gd
wWR
x:NnݵM]b
s?RUz=8w=imzG/|pۍݻwtr_ōt>?pvr$ɏ"[CD]3MS#WCYSVЀ1 Ẃq;!"h.v9p06#8EbdڌqM33n~I|T&|=X!TD@PzTH$Jfw?jG7vml{sstiȪ -WzNUq8gwhWd1p@-S"9xH	m'f˓ϋX旺wqMtƍe-]JL_+"XAY3(lS1lŷ"j a&$DFM
#U`fK4dzuߧ,fD(ӭNjM#3c<<:l*nYg=Y.K@NUMSuݶzqxއi3h7䇟֯m{&!Z	v,fF^j8]U;"`Ef`)3Qpt"*Œhj~¡ϖg|n(jHzyH},)T5T˒M܈(T`ɒpt~ݓS}_Yj;ھO\uCܫ|!T"%6(RqV82^)
Nga4ص&}JMG'QDsp1B?$RnHÐ7Ŭ&vf's/"e|3uYij$9u9	K)AZMDW^0 "!Vcssڱ7X	ݽH{WvDZX]=}s:0Mg!6\(8S5[B="j1|@<< !X37RE
.ÿ>5!]do?r.ܙ<߉î/	7W벞7,Uڶqsvws3|0~x;9Yi%pPZ9CNrCkc_oO/͐}bUUu6&9蓃`,ܬk R
1rLTUU4!Hf!||>9oV509H86ZɁƉ@pGk	cyЮ D!O=2A xsx`~bPwxli7ۭ;
UfUC
Eguц,Y#"Xx$
c5#883RࡓO>}%и-Gs+;gG\nөNo:uuoh.#o픞/S|/	064%3@WvZ"R1ՔD67"4çgڶmQXL~0dsssD9nvI}`Tp6@"Rv6@ 4 HLr{7YwnB
"Xib(U@TAgg?{{^ӵK[pm?rb6.z9?8F_?pj-qRxaMz'{8'P\_>]}-Nq߷-||߿\rEiy6DG֯g>]+)/6`a+7cG^귢57_}ܞbEIEn3]{eǻMZ7%V֪`ypeU')9~{36_>"u|ί+}q׎;&t2/;
jG)s|,+_uTiq[{vli勚Oi
lk)
ӧY1y.@^~aXoow_K?[ă^|k=᷏؛gF<7;+;wltŷP1G:]
e_P+Kk]\S:D,.)	}?VkE)}'cO>g9.=??Y'vwλՒf:6_xc~=]MT
gan]=7vy_xשmJ|1|xY6qYo|HrC~[c??]u2Gy@˄yφ|cC]ܷi㮉ZV=`/e4
~?X׾1'1Uwx)|gtjt޶]:oO+oEٌ#GN-,8ZƮ_ԐэձH&{_Zy>eK?Ui>QUGU;w{pFKe>:l{]G>?&2ϛ_Xďu6/|UG$rƵ񛤺(ݏvݱ?|GK}>uv{ֈ+{=~g)[nm|k=)לMbO=ki\cx.]Dy̳VV?6v׭0c{';;p?1T&=Ʈ6Nݵ`pڅ}Za)Y~=M:!ptnjS~G'jݹׁz|{S'zxt^ܦ_3*Os;ۏ͒yOݿNG/w5~u%7Jm!q訋<\'_=|ŜF϶-_y^xo/gh|ӵk_u:x)}o%ÖW~.nZ1pי.nC7цUԞ`."}rgvn`CӚ?Rbډf÷6.}ضvN%VQ#:y64cܚӭzZV_+w?WjJ 2
j[f,Եt93pyym=7f-Zvw
ѫ'L=TFfGtFk|̕b;/][o/Vk!ceX>嗋~g_իQ:t=mͥw>8OU-"
ݹޥ/ag//+~y>e:hQGn`{_;V=p`s/̉M2)kC?9ԥ{u^VaݕsᆧW7f+s;D4drItq=f9Ou϶9zSCt—6~}ȹ-G弫{i_ˏ;=Թe,|
>?;kefY[1{Kw=ص|OK~uۖ]ibgKʶ_Ng?mS;Y˒կ%g™[^_4muk̓S(Nxhe7wL{n6p_4g/GgͶ1ыv:G7ir{aG܇~co߹kuyܤst3fq7;/-֎u{KmevG?}f&/蟾@%a901)q+|w6,]O6w_{XlӮޞ6g,rohKq]]s;յ,/=WwaڿWy㤺sW>τ}9㜴s6qdnn*F|xƛ}yUVlݺu⅃38%Gէ[iW̆}{_fX~-}{vvCu&ӡgf:}}?إTҷLr~ug.oQ2}H§^/=/vvjYδҺL[zα󝗺:;qZ}۱.97#PLLϝo9|'gl=̝}z7>;4b׬=bFeam\6`GOD3)7ugji?=׫4ȈgpyjI798iϬ,O:?n'O|EQߗTa۔9jŪwI>s^#
}mm{/.]r_w1ԇv-߽fmzc;1pACҚ!aiy/Òn;uU_=ӓqS?oo={h̚T+Y;c'@fģ>Ѡf6^Z'f\aO{NAO辎ō?]4ͭ=8uISM{S+"
w5益ziQ+9aɴY<XԶonR+vԿOqroxw5 ej6r/m3#ފG-Z~=s+Ck5\0{FvVŴ92c-[6ySy-s[4.٩WG:%:YoW*,lRDݺ'.k,>|3ORj	hm[}[_ެ[Q7xϳy-֍xl?9+[o~/vg7ޓ~Šu"~~{lRQ
r/lЋK'o|9la=n'gi;N̲_כ>gI˗~JI~:O{~uvDn՝c@Rf΃?p`GH>->Vܺ77.:{R=?8t2kGǬK}.^j m͙f[Faߺ,[>we-*^]i_&ꯪWx~8qž|3[[پ}*s{ҥ&NMw۹tmZO/*Ml۴mO*pAENڤ{w{{T7_9{Ksu5Υ
r,vO+e-[;enOz\zSgzN=U>5omspuw\3~nC[c{ݴto:_^uiko,lYo	Ň;[Ÿ,.vw42;z^K;Јw㑩|%m?$
:$a쇾f6}>뗲3TciMꇥ7iq
yqeM>9ѧ^U;z	ݹŏT&ۻāc>q15;-?6=|߁G{'O}}W}\W{C֟Q:lݽF{:~{󧿟sϋdIZL ԗZ>$A_~>9/~K}dͶsܤ3.zew双ݟvpx>/3/)]_GgOԮ6$wIze'	7||1ei{?oݦuܾzII]>*}b_}:Nx)yܒ[ywk]6'ȺOWk{޾淫kA:>DÌ/mOoM͚+wĜ=q-nٴ>W|ݙ6e&1wNת56o}IVP/5#CG;N0ifzNڸI)a/vXUb؜6S'T5ޡ?.7C;R.rW<߽sƍQo/h㏷%|	y܃Z
x;ec7-aS^Gotg^>oy閟{?;\r5O^~Wñ>WeWOF&ooOZ=h]'6;'L^'<žK_}\Fwv̞mJo<ʓ/V<ȡ_[&MῘQl\?6.:'G}~=䉿}輅}'7\7p元Mϼ>pVwc⮾Wj]qc̳YS}jcV{./]:_߽tqu>8-mm\#7?r7ƞ1Sf\\1ӊv|^ye6Yqz幻ۭ[Q?֢nwh£~>koJ-?'yn#ǐM=}N1YuC׌ϻkmZ/usY5tw~sl/+Ɗa	"l1	%.	]$F٣u;J.H5"%w^GIMMذ_'Jx^o= YܼJIXJ*0Qq1aK'>#KHoT{tz^R.\uu2Ȉ6
-2RXp31NβH)p6gXsV.Ȳ(y.mLe|"g""*12$`T
ED3SP8xdEF`t;%nfD>T}"S	@$v[pn//ƊO("EX%"`kA":!bH1+$D:%r!AHWVfvF>Haݜ8V%	
 Otv8gfдGUHeT
@(YNQ)(ϋȑ|NuXyN$C}ZzRP$
KTFU9pHAw!`2I]KX{F	 گ]IJ
Q?<$?!G8
`"U#
B{ӨYHbS*3&88sޤ!]OUbCun`sv
(.i\]^ČS«Bc
U ^cԫ
aJ5kGÚB02Gd]i	yx3#𕰾9͡	~Bd,8MaGi5ytC/R3%d
†zMrz|_NX|eQT_bq%31.!	_	$=]N*vO@pB]"'ә..E	Nہ< h8	/p{؃+&-->0!3UM;}X>LJ2s{Y32+T
_CLP/}X%V+V7jժWXI"CDĎ!Plb:tzSE, 4YHZA{xyXg
}^3Ɋ5*?!'
X<F91X$ð˲;E4bD]DX@)mH%`e!n `I됐X2\qT1@	"'樂IY)^ďHXPIdV"4`˴V'1"L밤d
!w+[V6TDHB(1\uXXf#fB{1IzM$i0BF#hE}âK(U(Рg+X-A")!!ثNl0EjMPM2vG}sh	__ўp?HA:1g$ʌmCn"Ѝ1yTˍ~9(hq_.e‚
r3[Ra
vdo`ޟWl
I"!aQ0ceJbNuɺ.zw7|z2S3e^S		q5ěo
8jyU<صgfv9m*+)q`]q`[8,e`=Y	u
Ĭ
9ؐeyZ|fSe#6[Eql?.$EbNן$kgQ%a<\%vzɲmLhKG[-Q&3iR-2cˉUU`2Zl/$6nj*?:&.1HG\׎I^4a)]і8KZ4+-󽓮*9(hjZ0;os"X=	^g	\0,$zUhݷI.VDSC=prژ>
1X9a9La0 JU^L!*7#\Du VE^Xvf49m]1oA31"S#"lep
5?݊DЧK5>0/0/􊻕`*,‡D@#pL)yrD|A*d"#8NAJn3A"R-@xDed@=fp8fJ[{$ ԛ	!NC0Uvܢ+Y6px)%:`z!:+In,#&b.,5>͉{2
IEϴX`Qqm"k6IǷp8ys^#fMDZ6@ۼ:(<T6-6CUvb7}u[hvb'>AIA
9y~^YrGJn/==7+#tC1Cy"!T`Rdc|gs&|^h(i,D$jw?Ifl0|%%VE:l)0%ظP&k`2s0t;ꡦ&SrӭF@$ȋuڍJt;(04K.PI	8;3Y83Sx!d)+)Fj^5&;\`zBv§_P3/qJߟE8dF[lTff0#sT
c"cyKP_\yk꬐h> e)WK]grB"|~>tTF)Oԛhw*fFWw@su(D86޴0^t$\M
>KybS{m)K'_/Kѱpғͳ"*>|.֋jt#({u#4RdORq'AypޏQ0E6M!	nŖ%pb
MNcENaE2sTBLW%1jSNI+OQ\lJdSu4y=.4*8+7+F?8n&Wc'<;?g=4v&&ajB dHxOD<[=Co>9*&h5X VNLb"VVE=!R,_
dЁO*%%<+4sR\?ADQdAAq;=`Ik4S
>с3kl.	ԂWMe$zAl2>1UT|tDt8w)bR o@7 }qJf[O7'Rl]L@E'?	,ǃe`nC3<Xt+BweRLW"L~(.ƕu20sdU?	~86aOE% W.q-T8ރ=*X	|-'5Ú+0zfg"-sprM_ChPsF9, 	CW",8/`+vFy?-Ázqk6; QREFew}T,}D(p.B|
>""d6;|p𯙩x/6{zhUg&N:JҜFNmP٥2bRԊmL9#ku,!Vhlx(p>SqB2al^`b1HVx1%lkj?f!hӵVÆgf2:꫎ZvRy'yQeL݁=ujh?j4G^z2{`=P8/
_g]l*8N
wpHY	[!<&"xJ3BM!8&{NU*oa&#JHx.6.DnaNpW&N}D(rU>՜Z=6''Z'CnV"I64
by0hhQL&r=;]F >	LL#~{@$,%EMNnrrZTX1*+A%WEC|͂MZ';I@='{+#=ԓQه_h@'4S&␋ 4a+a9 ^C\.qnS		LA\aL%ԏ`s!̔,D"`B"|>/;15\&!H!E;j+pnh6SzpӉ++Uݐj&6Z
7IaI<֢bC'.zcWplXʶcߊYYC´*ReY@P9ݥ~}xA[%W”|%H	Pb5AŲ`LYhA}	sgȪ§}YJDQTz0BYV;,KlPLI![n(j).%!`Ӆ/ PDq=a	1FOH,8Q[,=kH*Xҋ#xC񷑹)fE"/
)K~)Lf	?GkTAv(n9mkx9)
CF>O)~!QѬB(@<ؤIThXBe"AxiO f^nRɑD-yл(kzkw Lfy[RzQ9^xg=- VBF! C+
IsWfDD:
2{HBC-=|4m$PZ7
hIz""X9?dy
	Pf {jSϥTǪ%%8L=MeZHPI7HS+ĖȥΏ*DAGIR-N{BR,ԉhƶ-27aWz[&B?@(
tJ;kb}|O
hLg\N 01YMD#eZ8.$-Dz	L/ea+}ȣH^($	
Rºhr2JBͰvu,ˏ$we#?v2xFvXN2zVhVBhEfjܵdқl:sωD
z
x!㍘--"GN2RwV*㱥@S033ʇ&9aA$y
c]x`ŏW&&1v֠(DEN̊^)!N_)؃WGb2r[hC.j&9)JZDKnbϛM22rOQ3`ϑ3{h\Cz!y8+r4$>9ddHa2"hޙ
.Di
/.=ϯ2yH330z2~f^6iG6eMt(T5CPd݉2J4jg&CI49jW+QNE@!Ղ2Dz*g+W$D@V"2(C	VhaUCDLzeF~9H1n&1%P(`_;
OkAÏ(sNɫ3>O&tݮjywz;j Ќy/ߥV
{Chp2%ɉ&eVT`4|~;iUX*7Q4f菰Eܷ&K
d$pV	SIa>&,Iyt1veR	6x8(TNɕ)q>j4;Uc=bF*mRAD$ nt
O4dZ}7LΙ"#	)J)r%rFDdaǓh9#& #FΈ
Ȉ32x9#! #AHH3dt3dt&I@$'H5OjniD[^6܍*OՈ
ͯݮ'05v݁Q۵m,ll`a:
K }VД)QFu:N
;kA
рiXC_h|T)W\.}TQy'KՂ&Ѩb&!5  7BӯFyj+5"Z jC(YW}BIy$wA%IG\J`ln;+샻nO:CbQL׎4ɇ N6NqBkkFr"nЈt8C
9H73 Ʃh1^%uꖯ:;mʑUA
$W8xaK1kSqvR~o6*VoQDlxNLTV}Nr&
%F`"e8z^|^U!
fs-EGxpHM$筩zٮ]`MzEXe`5S#]+&Qf@:00>AQ/B>&YHj
X.Z3|:JYf_C92ډ^uB"dؼTUKg$|o,6N3U͢f9Yt,VEPeٵYy"u
{*J1˷0hQRLB,\Io53F΍I4;-w-Q^*@d+h]o<>7]b:z$ÉwH.vF Ł+]7J 3܍iQ͜Ԃ`'ifJ3QPh&lHBdwJ)BN(W3\.	8|[&;	uGrB‘p$Ksыpʩ\Zz6GO$ו=k3Lz
p~ƉOILQ6C_Su|7')+	a袓ٵ?^R iAVZ6%AKA#[1(K|2eP=D\vInx“Q&U]?5*>M<`[QRB+NjY\@V0D)U>)!BG*w}«\{LAcx2h
`A:î:U&%-cI*KRaX%7||.!P8A#!3>X7ụO4w$ 2+>mFgERgiS^3j үWw{?TgpLZ}^7zx^ 2;HQRrfQ}8 IbK8C@Y9yz&"SO0jOsM
Daˮ\ltTP8\.<bEU#XJh&
\#@	Dp\+[P񳨕[smJ|ߚ)HX*-z-\D;X	i4	Z2aZC/8b׆UGf$Fb(Ce)$_7}Iq;5CiE(63̌.19H|tdH`1CcH$/qb/Dh/?ĸn7t|Ζ8] 6-L/EpQD@
lPV._'	])CcZH