runner_test.go (16581B)
1 package vm 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "log" 8 "testing" 9 10 "git.defalsify.org/vise.git/cache" 11 "git.defalsify.org/vise.git/render" 12 "git.defalsify.org/vise.git/resource" 13 "git.defalsify.org/vise.git/state" 14 ) 15 16 var dynVal = "three" 17 18 type TestResource struct { 19 resource.MemResource 20 state *state.State 21 RootCode []byte 22 CatchContent string 23 } 24 25 func NewTestResource(st *state.State) TestResource { 26 tr := TestResource{ 27 MemResource: resource.NewMemResource(), 28 state: st, 29 } 30 tr.AddTemplate("foo", "inky pinky blinky clyde") 31 tr.AddTemplate("bar", "inky pinky {{.one}} blinky {{.two}} clyde") 32 tr.AddTemplate("baz", "inky pinky {{.baz}} blinky clyde") 33 tr.AddTemplate("three", "{{.one}} inky pinky {{.three}} blinky clyde {{.two}}") 34 tr.AddTemplate("root", "root") 35 tr.AddTemplate("_catch", tr.CatchContent) 36 tr.AddTemplate("ouf", "ouch") 37 tr.AddTemplate("flagCatch", "flagiee") 38 tr.AddEntryFunc("one", getOne) 39 tr.AddEntryFunc("two", getTwo) 40 tr.AddEntryFunc("dyn", getDyn) 41 tr.AddEntryFunc("arg", tr.getInput) 42 tr.AddEntryFunc("echo", getEcho) 43 tr.AddEntryFunc("setFlagOne", setFlag) 44 tr.AddEntryFunc("set_lang", set_lang) 45 tr.AddEntryFunc("aiee", uhOh) 46 47 var b []byte 48 b = NewLine(nil, HALT, nil, nil, nil) 49 tr.AddBytecode("one", b) 50 51 b = NewLine(nil, MOUT, []string{"0", "repent"}, nil, nil) 52 b = NewLine(b, HALT, nil, nil, nil) 53 tr.AddBytecode("_catch", b) 54 55 b = NewLine(nil, MOUT, []string{"0", "repent"}, nil, nil) 56 b = NewLine(b, HALT, nil, nil, nil) 57 b = NewLine(b, MOVE, []string{"_"}, nil, nil) 58 tr.AddBytecode("flagCatch", b) 59 60 b = NewLine(nil, MOUT, []string{"1", "oo"}, nil, nil) 61 b = NewLine(b, HALT, nil, nil, nil) 62 tr.AddBytecode("ouf", b) 63 64 tr.AddBytecode("root", tr.RootCode) 65 return tr 66 } 67 68 func getOne(ctx context.Context, sym string, input []byte) (resource.Result, error) { 69 return resource.Result{ 70 Content: "one", 71 }, nil 72 } 73 74 func getTwo(ctx context.Context, sym string, input []byte) (resource.Result, error) { 75 return resource.Result{ 76 Content: "two", 77 }, nil 78 } 79 80 func getDyn(ctx context.Context, sym string, input []byte) (resource.Result, error) { 81 return resource.Result{ 82 Content: dynVal, 83 }, nil 84 } 85 86 func getEcho(ctx context.Context, sym string, input []byte) (resource.Result, error) { 87 r := fmt.Sprintf("echo: %s", input) 88 return resource.Result{ 89 Content: r, 90 }, nil 91 } 92 93 94 func uhOh(ctx context.Context, sym string, input []byte) (resource.Result, error) { 95 return resource.Result{}, fmt.Errorf("uh-oh spaghetti'ohs") 96 } 97 98 func setFlag(ctx context.Context, sym string, input []byte) (resource.Result, error) { 99 s := fmt.Sprintf("ping") 100 r := resource.Result{ 101 Content: s, 102 } 103 if len(input) > 0 { 104 r.FlagSet = append(r.FlagSet, uint32(input[0])) 105 } 106 if len(input) > 1 { 107 r.FlagReset = append(r.FlagReset, uint32(input[1])) 108 } 109 log.Printf("setflag %v", r) 110 return r, nil 111 112 } 113 114 func set_lang(ctx context.Context, sym string, input []byte) (resource.Result, error) { 115 return resource.Result{ 116 Content: string(input), 117 FlagSet: []uint32{state.FLAG_LANG}, 118 }, nil 119 } 120 // 121 //type TestStatefulResolver struct { 122 // state *state.State 123 //} 124 125 func (r TestResource) FuncFor(sym string) (resource.EntryFunc, error) { 126 switch sym { 127 case "one": 128 return getOne, nil 129 case "two": 130 return getTwo, nil 131 case "dyn": 132 return getDyn, nil 133 case "arg": 134 return r.getInput, nil 135 case "echo": 136 return getEcho, nil 137 case "setFlagOne": 138 return setFlag, nil 139 case "set_lang": 140 return set_lang, nil 141 case "aiee": 142 return uhOh, nil 143 } 144 return nil, fmt.Errorf("invalid function: '%s'", sym) 145 } 146 147 func(r TestResource) getInput(ctx context.Context, sym string, input []byte) (resource.Result, error) { 148 v, err := r.state.GetInput() 149 return resource.Result{ 150 Content: string(v), 151 }, err 152 } 153 154 func(r TestResource) getCode(sym string) ([]byte, error) { 155 var b []byte 156 switch sym { 157 case "_catch": 158 b = NewLine(b, MOUT, []string{"0", "repent"}, nil, nil) 159 b = NewLine(b, HALT, nil, nil, nil) 160 case "flagCatch": 161 b = NewLine(b, MOUT, []string{"0", "repent"}, nil, nil) 162 b = NewLine(b, HALT, nil, nil, nil) 163 b = NewLine(b, MOVE, []string{"_"}, nil, nil) 164 case "root": 165 b = r.RootCode 166 case "ouf": 167 b = NewLine(b, MOUT, []string{"1", "oo"}, nil, nil) 168 b = NewLine(b, HALT, nil, nil, nil) 169 } 170 171 return b, nil 172 } 173 174 func TestRun(t *testing.T) { 175 st := state.NewState(5) 176 rs := NewTestResource(&st) 177 ca := cache.NewCache() 178 vm := NewVm(&st, &rs, ca, nil) 179 180 b := NewLine(nil, MOVE, []string{"foo"}, nil, nil) 181 b = NewLine(b, HALT, nil, nil, nil) 182 ctx := context.TODO() 183 _, err := vm.Run(ctx, b) 184 if err == nil { 185 t.Fatalf("expected error") 186 } 187 188 b = []byte{0x01, 0x02} 189 _, err = vm.Run(ctx, b) 190 if err == nil { 191 t.Fatalf("no error on invalid opcode") 192 } 193 } 194 195 func TestRunLoadRender(t *testing.T) { 196 st := state.NewState(5) 197 rs := NewTestResource(&st) 198 ca := cache.NewCache() 199 vm := NewVm(&st, &rs, ca, nil) 200 201 st.Down("bar") 202 203 var err error 204 ctx := context.TODO() 205 b := NewLine(nil, LOAD, []string{"one"}, []byte{0x0a}, nil) 206 b = NewLine(b, MAP, []string{"one"}, nil, nil) 207 b = NewLine(b, LOAD, []string{"two"}, []byte{0x0a}, nil) 208 b = NewLine(b, MAP, []string{"two"}, nil, nil) 209 b = NewLine(b, HALT, nil, nil, nil) 210 b, err = vm.Run(ctx, b) 211 if err != nil { 212 t.Fatal(err) 213 } 214 r, err := vm.Render(ctx) 215 if err != nil { 216 t.Fatal(err) 217 } 218 expect := "inky pinky one blinky two clyde" 219 if r != expect { 220 t.Fatalf("Expected\n\t%s\ngot\n\t%s\n", expect, r) 221 } 222 223 b = NewLine(nil, LOAD, []string{"two"}, []byte{0x0a}, nil) 224 b = NewLine(b, MAP, []string{"two"}, nil, nil) 225 b = NewLine(b, HALT, nil, nil, nil) 226 b, err = vm.Run(ctx, b) 227 if err != nil { 228 t.Fatal(err) 229 } 230 b = NewLine(nil, MAP, []string{"one"}, nil, nil) 231 b = NewLine(b, MAP, []string{"two"}, nil, nil) 232 b = NewLine(b, HALT, nil, nil, nil) 233 _, err = vm.Run(ctx, b) 234 if err != nil { 235 t.Fatal(err) 236 } 237 r, err = vm.Render(ctx) 238 if err != nil { 239 t.Fatal(err) 240 } 241 expect = "inky pinky one blinky two clyde" 242 if r != expect { 243 t.Fatalf("Expected %v, got %v", expect, r) 244 } 245 } 246 247 func TestRunMultiple(t *testing.T) { 248 st := state.NewState(5) 249 rs := NewTestResource(&st) 250 ca := cache.NewCache() 251 vm := NewVm(&st, &rs, ca, nil) 252 253 ctx := context.TODO() 254 b := NewLine(nil, MOVE, []string{"test"}, nil, nil) 255 b = NewLine(b, LOAD, []string{"one"}, []byte{0x00}, nil) 256 b = NewLine(b, LOAD, []string{"two"}, []byte{42}, nil) 257 b = NewLine(b, HALT, nil, nil, nil) 258 var err error 259 b, err = vm.Run(ctx, b) 260 if err == nil { 261 t.Fatal(err) 262 } 263 } 264 265 func TestRunReload(t *testing.T) { 266 st := state.NewState(5) 267 rs := NewTestResource(&st) 268 ca := cache.NewCache() 269 szr := render.NewSizer(128) 270 vm := NewVm(&st, &rs, ca, szr) 271 272 ctx := context.TODO() 273 b := NewLine(nil, MOVE, []string{"root"}, nil, nil) 274 b = NewLine(b, LOAD, []string{"dyn"}, nil, []uint8{0}) 275 b = NewLine(b, MAP, []string{"dyn"}, nil, nil) 276 b = NewLine(b, HALT, nil, nil, nil) 277 _, err := vm.Run(ctx, b) 278 if err != nil { 279 t.Fatal(err) 280 } 281 r, err := vm.Render(ctx) 282 if err != nil { 283 t.Fatal(err) 284 } 285 if r != "root" { 286 t.Fatalf("expected result 'root', got %v", r) 287 } 288 dynVal = "baz" 289 b = NewLine(nil, RELOAD, []string{"dyn"}, nil, nil) 290 b = NewLine(b, HALT, nil, nil, nil) 291 _, err = vm.Run(ctx, b) 292 if err != nil { 293 t.Fatal(err) 294 } 295 } 296 297 func TestHalt(t *testing.T) { 298 st := state.NewState(5) 299 rs := NewTestResource(&st) 300 ca := cache.NewCache() 301 vm := NewVm(&st, &rs, ca, nil) 302 303 b := NewLine(nil, MOVE, []string{"root"}, nil, nil) 304 b = NewLine(b, LOAD, []string{"one"}, nil, []uint8{0}) 305 b = NewLine(b, HALT, nil, nil, nil) 306 b = NewLine(b, MOVE, []string{"foo"}, nil, nil) 307 var err error 308 ctx := context.TODO() 309 b, err = vm.Run(ctx, b) 310 if err != nil { 311 t.Error(err) 312 } 313 r, _ := st.Where() 314 if r == "foo" { 315 t.Fatalf("Expected where-symbol not to be 'foo'") 316 } 317 if !bytes.Equal(b[:2], []byte{0x00, MOVE}) { 318 t.Fatalf("Expected MOVE instruction, found '%v'", b) 319 } 320 } 321 322 func TestRunArg(t *testing.T) { 323 st := state.NewState(5) 324 rs := NewTestResource(&st) 325 ca := cache.NewCache() 326 vm := NewVm(&st, &rs, ca, nil) 327 328 input := []byte("bar") 329 _ = st.SetInput(input) 330 331 bi := NewLine(nil, INCMP, []string{"baz", "bar"}, nil, nil) 332 ctx := context.TODO() 333 334 var err error 335 b, err := vm.Run(ctx, bi) 336 if err == nil { 337 t.Fatalf("expected error") 338 } 339 l := len(b) 340 if l != 0 { 341 t.Fatalf("expected empty remainder, got length %v: %v", l, b) 342 } 343 r, _ := st.Where() 344 if r != "baz" { 345 t.Fatalf("expected where-state baz, got %v", r) 346 } 347 } 348 349 func TestRunInputHandler(t *testing.T) { 350 st := state.NewState(5) 351 rs := NewTestResource(&st) 352 ca := cache.NewCache() 353 vm := NewVm(&st, &rs, ca, nil) 354 355 _ = st.SetInput([]byte("baz")) 356 357 bi := NewLine([]byte{}, INCMP, []string{"aiee", "bar"}, nil, nil) 358 bi = NewLine(bi, INCMP, []string{"foo", "baz"}, nil, nil) 359 bi = NewLine(bi, LOAD, []string{"one"}, []byte{0x00}, nil) 360 bi = NewLine(bi, LOAD, []string{"two"}, []byte{0x03}, nil) 361 bi = NewLine(bi, MAP, []string{"one"}, nil, nil) 362 bi = NewLine(bi, MAP, []string{"two"}, nil, nil) 363 bi = NewLine(bi, HALT, nil, nil, nil) 364 365 var err error 366 ctx := context.TODO() 367 _, err = vm.Run(ctx, bi) 368 if err == nil { 369 t.Fatalf("expected error") 370 } 371 r, _ := st.Where() 372 if r != "foo" { 373 t.Fatalf("expected where-sym 'foo', got '%v'", r) 374 } 375 } 376 377 func TestRunArgInvalid(t *testing.T) { 378 st := state.NewState(5) 379 rs := NewTestResource(&st) 380 ca := cache.NewCache() 381 vm := NewVm(&st, &rs, ca, nil) 382 383 _ = st.SetInput([]byte("foo")) 384 385 var err error 386 387 st.Down("root") 388 b := NewLine(nil, INCMP, []string{"baz", "bar"}, nil, nil) 389 390 ctx := context.TODO() 391 b, err = vm.Run(ctx, b) 392 if err != nil { 393 t.Fatal(err) 394 } 395 location, _ := st.Where() 396 if location != "_catch" { 397 t.Fatalf("expected where-state _catch, got %v", location) 398 } 399 400 r, err := vm.Render(ctx) 401 if err != nil { 402 t.Fatal(err) 403 } 404 expect := `invalid input: 'foo' 405 0:repent` 406 if r != expect { 407 t.Fatalf("expected:\n\t%s\ngot:\n\t%s", expect, r) 408 } 409 } 410 411 func TestRunMenu(t *testing.T) { 412 st := state.NewState(5) 413 rs := NewTestResource(&st) 414 ca := cache.NewCache() 415 vm := NewVm(&st, &rs, ca, nil) 416 417 var err error 418 419 ctx := context.TODO() 420 421 rs.AddBytecode("foo", []byte{}) 422 b := NewLine(nil, MOVE, []string{"foo"}, nil, nil) 423 b = NewLine(b, MOUT, []string{"0", "one"}, nil, nil) 424 b = NewLine(b, MOUT, []string{"1", "two"}, nil, nil) 425 b = NewLine(b, HALT, nil, nil, nil) 426 427 b, err = vm.Run(ctx, b) 428 if err != nil { 429 t.Error(err) 430 } 431 l := len(b) 432 if l != 0 { 433 t.Errorf("expected empty remainder, got length %v: %v", l, b) 434 } 435 436 r, err := vm.Render(ctx) 437 if err != nil { 438 t.Fatal(err) 439 } 440 expect := "inky pinky blinky clyde\n0:one\n1:two" 441 if r != expect { 442 t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r) 443 } 444 } 445 446 func TestRunMenuBrowse(t *testing.T) { 447 log.Printf("This test is incomplete, it must check the output of a menu browser once one is implemented. For now it only checks whether it can execute the runner endpoints for the instrucitons.") 448 st := state.NewState(5) 449 rs := NewTestResource(&st) 450 ca := cache.NewCache() 451 vm := NewVm(&st, &rs, ca, nil) 452 453 var err error 454 455 ctx := context.TODO() 456 457 rs.AddBytecode("foo", []byte{}) 458 b := NewLine(nil, MOVE, []string{"foo"}, nil, nil) 459 b = NewLine(b, MOUT, []string{"0", "one"}, nil, nil) 460 b = NewLine(b, MOUT, []string{"1", "two"}, nil, nil) 461 b = NewLine(b, HALT, nil, nil, nil) 462 463 b, err = vm.Run(ctx, b) 464 if err != nil { 465 t.Error(err) 466 } 467 l := len(b) 468 if l != 0 { 469 t.Errorf("expected empty remainder, got length %v: %v", l, b) 470 } 471 472 r, err := vm.Render(ctx) 473 if err != nil { 474 t.Fatal(err) 475 } 476 expect := "inky pinky blinky clyde\n0:one\n1:two" 477 if r != expect { 478 t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r) 479 } 480 } 481 482 func TestRunReturn(t *testing.T) { 483 st := state.NewState(5) 484 rs := NewTestResource(&st) 485 ca := cache.NewCache() 486 vm := NewVm(&st, &rs, ca, nil) 487 488 var err error 489 490 st.Down("root") 491 b := NewLine(nil, INCMP, []string{"bar", "0"}, nil, nil) 492 b = NewLine(b, INCMP, []string{"_", "1"}, nil, nil) 493 494 ctx := context.TODO() 495 496 st.SetInput([]byte("0")) 497 b, err = vm.Run(ctx, b) 498 if err == nil { 499 t.Fatalf("expected error") 500 } 501 location, _ := st.Where() 502 if location != "bar" { 503 t.Fatalf("expected location 'bar', got '%s'", location) 504 } 505 506 st.SetInput([]byte("1")) 507 b, err = vm.Run(ctx, b) 508 if err != nil { 509 t.Fatal(err) 510 } 511 location, _ = st.Where() 512 if location != "root" { 513 t.Fatalf("expected location 'root', got '%s'", location) 514 } 515 } 516 517 518 func TestRunLoadInput(t *testing.T) { 519 st := state.NewState(5) 520 rs := NewTestResource(&st) 521 ca := cache.NewCache() 522 vm := NewVm(&st, &rs, ca, nil) 523 524 var err error 525 526 st.Down("root") 527 st.SetInput([]byte("foobar")) 528 529 b := NewLine(nil, LOAD, []string{"echo"}, []byte{0x00}, nil) 530 b = NewLine(b, HALT, nil, nil, nil) 531 532 ctx := context.TODO() 533 534 b, err = vm.Run(ctx, b) 535 if err != nil { 536 t.Fatal(err) 537 } 538 539 r, err := ca.Get("echo") 540 if err != nil { 541 t.Fatal(err) 542 } 543 if r != "echo: foobar" { 544 t.Fatalf("expected 'echo: foobar', got %s", r) 545 } 546 } 547 548 func TestInputBranch(t *testing.T) { 549 st := state.NewState(5) 550 rs := NewTestResource(&st) 551 ca := cache.NewCache() 552 vm := NewVm(&st, &rs, ca, nil) 553 554 var err error 555 556 st.Down("root") 557 558 b := NewLine(nil, LOAD, []string{"setFlagOne"}, []byte{0x00}, nil) 559 b = NewLine(b, RELOAD, []string{"setFlagOne"}, nil, nil) 560 b = NewLine(b, CATCH, []string{"flagCatch"}, []byte{8}, []uint8{1}) 561 b = NewLine(b, CATCH, []string{"one"}, []byte{9}, []uint8{1}) 562 rs.RootCode = b 563 rs.AddBytecode("root", rs.RootCode) 564 565 ctx := context.TODO() 566 567 st.SetInput([]byte{0x08}) 568 b, err = vm.Run(ctx, b) 569 if err != nil { 570 t.Fatal(err) 571 } 572 location, _ := st.Where() 573 if location != "flagCatch" { 574 t.Fatalf("expected 'flagCatch', got %s", location) 575 } 576 577 st.SetInput([]byte{0x09, 0x08}) 578 b, err = vm.Run(ctx, b) 579 if err != nil { 580 t.Fatal(err) 581 } 582 location, _ = st.Where() 583 if location != "one" { 584 t.Fatalf("expected 'one', got %s", location) 585 } 586 } 587 588 func TestInputIgnore(t *testing.T) { 589 st := state.NewState(5) 590 rs := NewTestResource(&st) 591 ca := cache.NewCache() 592 vm := NewVm(&st, &rs, ca, nil) 593 594 var err error 595 596 st.Down("root") 597 598 b := NewLine(nil, INCMP, []string{"one", "foo"}, nil, nil) 599 b = NewLine(b, INCMP, []string{"two", "bar"}, nil, nil) 600 b = NewLine(b, HALT, nil, nil, nil) 601 602 ctx := context.TODO() 603 604 st.SetInput([]byte("foo")) 605 b, err = vm.Run(ctx, b) 606 if err != nil { 607 t.Fatal(err) 608 } 609 610 location, _ := st.Where() 611 if location != "one" { 612 t.Fatalf("expected 'one', got %s", location) 613 } 614 } 615 616 func TestInputIgnoreWildcard(t *testing.T) { 617 st := state.NewState(5) 618 rs := NewTestResource(&st) 619 ca := cache.NewCache() 620 vm := NewVm(&st, &rs, ca, nil) 621 622 var err error 623 624 st.Down("root") 625 626 b := NewLine(nil, INCMP, []string{"one", "foo"}, nil, nil) 627 b = NewLine(b, INCMP, []string{"two", "*"}, nil, nil) 628 629 ctx := context.TODO() 630 631 st.SetInput([]byte("foo")) 632 b, err = vm.Run(ctx, b) 633 if err != nil { 634 t.Fatal(err) 635 } 636 637 location, _ := st.Where() 638 if location != "one" { 639 t.Fatalf("expected 'one', got %s", location) 640 } 641 } 642 643 func TestCatchCleanMenu(t *testing.T) { 644 st := state.NewState(5) 645 rs := NewTestResource(&st) 646 ca := cache.NewCache() 647 vm := NewVm(&st, &rs, ca, nil) 648 649 var err error 650 651 st.Down("root") 652 653 b := NewLine(nil, MOUT, []string{"1", "one"}, nil, nil) 654 b = NewLine(b, MOUT, []string{"2", "two"}, nil, nil) 655 b = NewLine(b, HALT, nil, nil, nil) 656 b = NewLine(b, INCMP, []string{"foo", "1"}, nil, nil) 657 b = NewLine(b, CATCH, []string{"ouf"}, []byte{0x08}, []uint8{0x00}) 658 659 ctx := context.TODO() 660 661 st.SetInput([]byte("foo")) 662 b, err = vm.Run(ctx, b) 663 if err != nil { 664 t.Fatal(err) 665 } 666 667 st.SetInput([]byte("foo")) 668 b, err = vm.Run(ctx, b) 669 if err != nil { 670 t.Fatal(err) 671 } 672 673 r, err := vm.Render(ctx) 674 if err != nil { 675 t.Fatal(err) 676 } 677 fmt.Printf("Result:\n%s", r) 678 } 679 680 func TestSetLang(t *testing.T) { 681 st := state.NewState(0) 682 rs := NewTestResource(&st) 683 ca := cache.NewCache() 684 vm := NewVm(&st, &rs, ca, nil) 685 686 var err error 687 688 st.Down("root") 689 690 st.SetInput([]byte("no")) 691 b := NewLine(nil, LOAD, []string{"set_lang"}, []byte{0x01, 0x00}, nil) 692 b = NewLine(b, HALT, nil, nil, nil) 693 694 ctx := context.TODO() 695 b, err = vm.Run(ctx, b) 696 if err != nil { 697 t.Fatal(err) 698 } 699 lang := *st.Language 700 if lang.Code != "nor" { 701 t.Fatalf("expected language 'nor',, got %s", lang.Code) 702 } 703 } 704 705 func TestLoadError(t *testing.T) { 706 st := state.NewState(0).WithDebug() 707 rs := NewTestResource(&st) 708 ca := cache.NewCache() 709 vm := NewVm(&st, &rs, ca, nil) 710 711 st.Down("root") 712 st.SetInput([]byte{}) 713 b := NewLine(nil, LOAD, []string{"aiee"}, []byte{0x01, 0x10}, nil) 714 b = NewLine(b, HALT, nil, nil, nil) 715 716 var err error 717 ctx := context.TODO() 718 b, err = vm.Run(ctx, b) 719 if err != nil { 720 t.Fatal(err) 721 } 722 723 r, err := vm.Render(ctx) 724 if err != nil { 725 t.Fatal(err) 726 } 727 expect := `error aiee:0 728 0:repent` 729 if r != expect { 730 t.Fatalf("expected: \n\t%s\ngot:\n\t%s", expect, r) 731 } 732 }