diff --git a/tests/BcTest.php b/tests/BcTest.php index e80bad8..5e84124 100644 --- a/tests/BcTest.php +++ b/tests/BcTest.php @@ -1 +1,510 @@ -PD9waHAKCmRlY2xhcmUoc3RyaWN0X3R5cGVzPTEpOwoKbmFtZXNwYWNlIFRlelxVdGlsc1xUZXN0c1xOdW07Cgp1c2UgUEhQVW5pdFxGcmFtZXdvcmtcVGVzdENhc2U7CnVzZSBUZXpcVXRpbHNcTnVtXEJjOwoKZmluYWwgY2xhc3MgQmNUZXN0IGV4dGVuZHMgVGVzdENhc2UKewogICAgLy8g4pSA4pSAIGFkZCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdEFkZERlZmF1bHRTY2FsZSgpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnMzAuODAwMDAwMDAnLCBCYzo6YWRkKCcxMC41JywgJzIwLjMnKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RBZGRDdXN0b21TY2FsZSgpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnMzAuODAnLCBCYzo6YWRkKCcxMC41JywgJzIwLjMnLCAyKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RBZGROZWdhdGl2ZVZhbHVlcygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnLTUuMDAwMDAwMDAnLCBCYzo6YWRkKCctMTAnLCAnNScpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdEFkZFplcm8oKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzQyLjAwMDAwMDAwJywgQmM6OmFkZCgnNDInLCAnMCcpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdEFkZEludElucHV0cygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnMy4wMDAwMDAwMCcsIEJjOjphZGQoMSwgMikpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0QWRkRmxvYXRJbnB1dHMoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzMuNTAwMDAwMDAnLCBCYzo6YWRkKDEuMCwgMi41KSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RBZGRNaXhlZElucHV0cygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnMy41MDAwMDAwMCcsIEJjOjphZGQoMSwgJzIuNScpKTsKICAgIH0KCiAgICAvLyDilIDilIAgc3ViIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0U3ViRGVmYXVsdFNjYWxlKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCc5LjIwMDAwMDAwJywgQmM6OnN1YignMjAuMycsICcxMS4xJykpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0U3ViUmVzdWx0TmVnYXRpdmUoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJy01LjAwMDAwMDAwJywgQmM6OnN1YignNScsICcxMCcpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdFN1YlNhbWVWYWx1ZXMoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzAuMDAwMDAwMDAnLCBCYzo6c3ViKCcxMDAnLCAnMTAwJykpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0U3ViSW50SW5wdXRzKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCc3LjAwMDAwMDAwJywgQmM6OnN1YigxMCwgMykpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0U3ViRmxvYXRJbnB1dHMoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzcuNTAwMDAwMDAnLCBCYzo6c3ViKDEwLjAsIDIuNSkpOwogICAgfQoKICAgIC8vIOKUgOKUgCBtdWwg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RNdWxEZWZhdWx0U2NhbGUoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzIwLjAwMDAwMDAwJywgQmM6Om11bCgnNCcsICc1JykpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0TXVsRGVjaW1hbFByZWNpc2lvbigpOiB2b2lkCiAgICB7CiAgICAgICAgLy8gMC4xIMOXIDAuMSA9IDAuMDEgZXhhY3RseSBhdCBzY2FsZSA4CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnMC4wMTAwMDAwMCcsIEJjOjptdWwoJzAuMScsICcwLjEnKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RNdWxCeVplcm8oKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzAuMDAwMDAwMDAnLCBCYzo6bXVsKCcxMjM0NS42NzgnLCAnMCcpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdE11bE5lZ2F0aXZlKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCctNi4wMDAwMDAwMCcsIEJjOjptdWwoJy0yJywgJzMnKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RNdWxJbnRJbnB1dHMoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzIwLjAwMDAwMDAwJywgQmM6Om11bCg0LCA1KSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RNdWxGbG9hdElucHV0cygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnMC4yNTAwMDAwMCcsIEJjOjptdWwoMC41LCAwLjUpKTsKICAgIH0KCiAgICAvLyDilIDilIAgZGl2IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0RGl2RGVmYXVsdFNjYWxlKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCcwLjMzMzMzMzMzJywgQmM6OmRpdignMScsICczJykpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0RGl2RXhhY3RSZXN1bHQoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzIuNTAwMDAwMDAnLCBCYzo6ZGl2KCc1JywgJzInKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3REaXZDdXN0b21TY2FsZSgpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnMC4zMycsIEJjOjpkaXYoJzEnLCAnMycsIDIpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdERpdkJ5WmVyb1Rocm93cygpOiB2b2lkCiAgICB7CiAgICAgICAgJHRoaXMtPmV4cGVjdEV4Y2VwdGlvbihcRGl2aXNpb25CeVplcm9FcnJvcjo6Y2xhc3MpOwogICAgICAgIEJjOjpkaXYoJzEnLCAnMCcpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0RGl2QnlaZXJvRGVjaW1hbFRocm93cygpOiB2b2lkCiAgICB7CiAgICAgICAgJHRoaXMtPmV4cGVjdEV4Y2VwdGlvbihcRGl2aXNpb25CeVplcm9FcnJvcjo6Y2xhc3MpOwogICAgICAgIEJjOjpkaXYoJzEnLCAnMC4wMDAwMDAwMCcpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0RGl2SW50SW5wdXRzKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCcyLjUwMDAwMDAwJywgQmM6OmRpdig1LCAyKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3REaXZGbG9hdElucHV0cygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnMi4wMDAwMDAwMCcsIEJjOjpkaXYoNS4wLCAyLjUpKTsKICAgIH0KCiAgICAvLyDilIDilIAgc2NhbGUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RTY2FsZVBhZHNTaG9ydERlY2ltYWxzKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCcxLjUwMDAwMDAwJywgQmM6OnNjYWxlKCcxLjUnKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RTY2FsZVRydW5jYXRlc0xvbmdEZWNpbWFscygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnMS4xMjM0NTY3OCcsIEJjOjpzY2FsZSgnMS4xMjM0NTY3ODknKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RTY2FsZUludGVnZXJJbnB1dCgpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnNDIuMDAwMDAwMDAnLCBCYzo6c2NhbGUoJzQyJykpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0U2NhbGVDdXN0b21TY2FsZSgpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnMy4xNCcsIEJjOjpzY2FsZSgnMy4xNDE1OScsIDIpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdFNjYWxlSW50SW5wdXQoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzQyLjAwMDAwMDAwJywgQmM6OnNjYWxlKDQyKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RTY2FsZUZsb2F0SW5wdXQoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzEuNTAwMDAwMDAnLCBCYzo6c2NhbGUoMS41KSk7CiAgICB9CgogICAgLy8g4pSA4pSAIGFicyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdEFic05lZ2F0aXZlVmFsdWUoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzUuMDAwMDAwMDAnLCBCYzo6YWJzKCctNScpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdEFic1Bvc2l0aXZlVmFsdWVVbmNoYW5nZWQoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzMuMTQwMDAwMDAnLCBCYzo6YWJzKCczLjE0JykpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0QWJzWmVybygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnMC4wMDAwMDAwMCcsIEJjOjphYnMoJzAnKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RBYnNJbnRJbnB1dCgpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnNS4wMDAwMDAwMCcsIEJjOjphYnMoLTUpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdEFic0Zsb2F0SW5wdXQoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzMuMTQwMDAwMDAnLCBCYzo6YWJzKC0zLjE0KSk7CiAgICB9CgogICAgLy8g4pSA4pSAIG1heCAvIG1pbiDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdE1heFJldHVybnNMYXJnZXIoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzEwLjAwMDAwMDAwJywgQmM6Om1heCgnMTAnLCAnNScpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdE1heFdoZW5FcXVhbCgpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnNS4wMDAwMDAwMCcsIEJjOjptYXgoJzUnLCAnNScpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdE1pblJldHVybnNTbWFsbGVyKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCczLjAwMDAwMDAwJywgQmM6Om1pbignMTAnLCAnMycpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdE1pbk5lZ2F0aXZlVmFsdWVzKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCctMTAuMDAwMDAwMDAnLCBCYzo6bWluKCctNScsICctMTAnKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RNYXhJbnRJbnB1dHMoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzEwLjAwMDAwMDAwJywgQmM6Om1heCgxMCwgNSkpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0TWF4RmxvYXRJbnB1dHMoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzIuNTAwMDAwMDAnLCBCYzo6bWF4KDIuNSwgMS41KSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RNaW5JbnRJbnB1dHMoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzMuMDAwMDAwMDAnLCBCYzo6bWluKDEwLCAzKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RNaW5GbG9hdElucHV0cygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnMS41MDAwMDAwMCcsIEJjOjptaW4oMi41LCAxLjUpKTsKICAgIH0KCiAgICAvLyDilIDilIAgY29tcGFyZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdENvbXBhcmVHcmVhdGVyKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKDEsIEJjOjpjb21wYXJlKCcxMCcsICc1JykpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0Q29tcGFyZUxlc3MoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoLTEsIEJjOjpjb21wYXJlKCc1JywgJzEwJykpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0Q29tcGFyZUVxdWFsKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKDAsIEJjOjpjb21wYXJlKCc1LjAwJywgJzUuMDAwJykpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0Q29tcGFyZUludElucHV0cygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgxLCBCYzo6Y29tcGFyZSgxMCwgNSkpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0Q29tcGFyZUZsb2F0SW5wdXRzKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKC0xLCBCYzo6Y29tcGFyZSgxLjUsIDIuNSkpOwogICAgfQoKICAgIC8vIOKUgOKUgCBpc1plcm8g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RJc1plcm9UcnVlRm9yWmVybygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0VHJ1ZShCYzo6aXNaZXJvKCcwJykpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0SXNaZXJvVHJ1ZUZvckZvcm1hdHRlZFplcm8oKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFRydWUoQmM6OmlzWmVybygnMC4wMDAwMDAwMCcpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdElzWmVyb0ZhbHNlRm9yTm9uWmVybygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0RmFsc2UoQmM6OmlzWmVybygnMC4wMDAwMDAwMScpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdElzWmVyb0ludElucHV0KCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRUcnVlKEJjOjppc1plcm8oMCkpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0SXNaZXJvRmxvYXRJbnB1dCgpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0RmFsc2UoQmM6OmlzWmVybygwLjAwMDAwMDAxKSk7CiAgICB9CgogICAgLy8g4pSA4pSAIGd0IC8gbHQg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RHdFRydWVXaGVuR3JlYXRlcigpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0VHJ1ZShCYzo6Z3QoJzEwJywgJzUnKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RHdEZhbHNlV2hlbkVxdWFsKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRGYWxzZShCYzo6Z3QoJzUnLCAnNScpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdEx0VHJ1ZVdoZW5MZXNzKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRUcnVlKEJjOjpsdCgnMycsICc3JykpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0THRGYWxzZVdoZW5HcmVhdGVyKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRGYWxzZShCYzo6bHQoJzcnLCAnMycpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdEd0SW50SW5wdXRzKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRUcnVlKEJjOjpndCgxMCwgNSkpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0THRGbG9hdElucHV0cygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0VHJ1ZShCYzo6bHQoMS41LCAyLjUpKTsKICAgIH0KCiAgICAvLyDilIDilIAgZ3RlIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0R3RlVHJ1ZVdoZW5HcmVhdGVyKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRUcnVlKEJjOjpndGUoJzEwJywgJzUnKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RHdGVUcnVlV2hlbkVxdWFsKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRUcnVlKEJjOjpndGUoJzUnLCAnNScpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdEd0ZUZhbHNlV2hlbkxlc3MoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydEZhbHNlKEJjOjpndGUoJzMnLCAnNycpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdEd0ZUludElucHV0cygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0VHJ1ZShCYzo6Z3RlKDEwLCA1KSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RHdGVGbG9hdElucHV0cygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0VHJ1ZShCYzo6Z3RlKDUuMCwgNS4wKSk7CiAgICB9CgogICAgLy8g4pSA4pSAIGx0ZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdEx0ZVRydWVXaGVuTGVzcygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0VHJ1ZShCYzo6bHRlKCczJywgJzcnKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RMdGVUcnVlV2hlbkVxdWFsKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRUcnVlKEJjOjpsdGUoJzUnLCAnNScpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdEx0ZUZhbHNlV2hlbkdyZWF0ZXIoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydEZhbHNlKEJjOjpsdGUoJzEwJywgJzUnKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RMdGVJbnRJbnB1dHMoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFRydWUoQmM6Omx0ZSgzLCA3KSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RMdGVGbG9hdElucHV0cygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0VHJ1ZShCYzo6bHRlKDUuMCwgNS4wKSk7CiAgICB9CgogICAgLy8g4pSA4pSAIGVqIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0RXFUcnVlV2hlbkVxdWFsKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRUcnVlKEJjOjplcSgnNS4wMCcsICc1LjAwMCcpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdEVxRmFsc2VXaGVuRGlmZmVyZW50KCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRGYWxzZShCYzo6ZXEoJzUnLCAnNS4wMDAwMDAwMScpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdEVxUmVzcGVjdHNTY2FsZSgpOiB2b2lkCiAgICB7CiAgICAgICAgLy8gQXQgc2NhbGUgMiwgNS4wMDEgYW5kIDUuMDA5IGJvdGggdHJ1bmNhdGUgdG8gNS4wMCBhbmQgYXJlIGVxdWFsCiAgICAgICAgc2VsZjo6YXNzZXJ0VHJ1ZShCYzo6ZXEoJzUuMDAxJywgJzUuMDA5JywgMikpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0RXFJbnRJbnB1dHMoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFRydWUoQmM6OmVxKDUsIDUpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdEVxRmxvYXRJbnB1dHMoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFRydWUoQmM6OmVxKDUuMCwgNS4wKSk7CiAgICB9CgogICAgLy8g4pSA4pSAIGlzUG9zaXRpdmUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RJc1Bvc2l0aXZlVHJ1ZUZvclBvc2l0aXZlVmFsdWUoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFRydWUoQmM6OmlzUG9zaXRpdmUoJzAuMDAwMDAwMDEnKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RJc1Bvc2l0aXZlRmFsc2VGb3JaZXJvKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRGYWxzZShCYzo6aXNQb3NpdGl2ZSgnMCcpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdElzUG9zaXRpdmVGYWxzZUZvck5lZ2F0aXZlKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRGYWxzZShCYzo6aXNQb3NpdGl2ZSgnLTEnKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RJc1Bvc2l0aXZlSW50SW5wdXQoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFRydWUoQmM6OmlzUG9zaXRpdmUoMSkpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0SXNQb3NpdGl2ZUZsb2F0SW5wdXQoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFRydWUoQmM6OmlzUG9zaXRpdmUoMC4xKSk7CiAgICB9CgogICAgLy8g4pSA4pSAIGlzTmVnYXRpdmUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RJc05lZ2F0aXZlVHJ1ZUZvck5lZ2F0aXZlVmFsdWUoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFRydWUoQmM6OmlzTmVnYXRpdmUoJy0wLjAwMDAwMDAxJykpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0SXNOZ2F0aXZlRmFsc2VGb3JaZXJvKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRGYWxzZShCYzo6aXNOZWdhdGl2ZSgnMCcpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdElzTmVnYXRpdmVGYWxzZUZvclBvc2l0aXZlKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRGYWxzZShCYzo6aXNOZWdhdGl2ZSgnMScpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdElzTmVnYXRpdmVJbnRJbnB1dCgpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0VHJ1ZShCYzo6aXNOZWdhdGl2ZSgtMSkpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0SXNOZWdhdGl2ZUZsb2F0SW5wdXQoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFRydWUoQmM6OmlzTmVnYXRpdmUoLTAuMSkpOwogICAgfQoKICAgIC8vIOKUgOKUgCBuZWdhdGUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3ROZWdhdGVQb3NpdGl2ZVZhbHVlKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCctNS4wMDAwMDAwMCcsIEJjOjpuZWdhdGUoJzUnKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3ROZWdhdGVOZWdhdGl2ZVZhbHVlKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCc1LjAwMDAwMDAwJywgQmM6Om5lZ2F0ZSgnLTUnKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3ROZWdhdGVaZXJvUmVtYWluc1plcm8oKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzAuMDAwMDAwMDAnLCBCYzo6bmVnYXRlKCcwJykpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0TmVnYXRlQ3VzdG9tU2NhbGUoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJy0zLjE0JywgQmM6Om5lZ2F0ZSgnMy4xNCcsIDIpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdE5lZ2F0ZUludElucHV0KCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCctNS4wMDAwMDAwMCcsIEJjOjpuZWdhdGUoNSkpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0TmVnYXRlRmxvYXRJbnB1dCgpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnLTMuMTQwMDAwMDAnLCBCYzo6bmVnYXRlKDMuMTQpKTsKICAgIH0KCiAgICAvLyDilIDilIAgc3VtIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0U3VtRW1wdHlBcnJheVJldHVybnNaZXJvKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCcwLjAwMDAwMDAwJywgQmM6OnN1bShbXSkpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0U3VtU2luZ2xlRWxlbWVudCgpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnNS4wMDAwMDAwMCcsIEJjOjpzdW0oWyc1J10pKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdFN1bU11bHRpcGxlRWxlbWVudHMoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzYuMDAwMDAwMDAnLCBCYzo6c3VtKFsnMScsICcyJywgJzMnXSkpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0U3VtV2l0aE5lZ2F0aXZlVmFsdWVzKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCctMi4wMDAwMDAwMCcsIEJjOjpzdW0oWyctMScsICcyJywgJy0zJ10pKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdFN1bUN1c3RvbVNjYWxlKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCczLjMwJywgQmM6OnN1bShbJzEuMScsICcyLjInXSwgMikpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0U3VtVGhyb3dzT25Ob25OdW1lcmljRWxlbWVudCgpOiB2b2lkCiAgICB7CiAgICAgICAgJHRoaXMtPmV4cGVjdEV4Y2VwdGlvbihcSW52YWxpZEFyZ3VtZW50RXhjZXB0aW9uOjpjbGFzcyk7CiAgICAgICAgLyoqIEBwaHBzdGFuLWlnbm9yZS1uZXh0LWxpbmUgKi8KICAgICAgICBCYzo6c3VtKFsnMScsICdub3QtYS1udW1iZXInLCAnMyddKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdFN1bUludEVsZW1lbnRzKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCc2LjAwMDAwMDAwJywgQmM6OnN1bShbMSwgMiwgM10pKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdFN1bUZsb2F0RWxlbWVudHMoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzMuMzAwMDAwMDAnLCBCYzo6c3VtKFsxLjEsIDIuMl0pKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdFN1bU1peGVkRWxlbWVudHMoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzYuNTAwMDAwMDAnLCBCYzo6c3VtKFsxLCAyLjUsICczJ10pKTsKICAgIH0KCiAgICAvLyDilIDilIAgcHJvZHVjdCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdFByb2R1Y3RFbXB0eUFycmF5UmV0dXJuc09uZSgpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnMS4wMDAwMDAwMCcsIEJjOjpwcm9kdWN0KFtdKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RQcm9kdWN0U2luZ2xlRWxlbWVudCgpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnNy4wMDAwMDAwMCcsIEJjOjpwcm9kdWN0KFsnNyddKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RQcm9kdWN0TXVsdGlwbGVFbGVtZW50cygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnMjQuMDAwMDAwMDAnLCBCYzo6cHJvZHVjdChbJzInLCAnMycsICc0J10pKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdFByb2R1Y3RXaXRoRGVjaW1hbFZhbHVlcygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnMC4yNTAwMDAwMCcsIEJjOjpwcm9kdWN0KFsnMC41JywgJzAuNSddKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RQcm9kdWN0V2l0aE5lZ2F0aXZlVmFsdWUoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJy02LjAwMDAwMDAwJywgQmM6OnByb2R1Y3QoWyctMicsICczJ10pKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdFByb2R1Y3RJbnRFbGVtZW50cygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnMjQuMDAwMDAwMDAnLCBCYzo6cHJvZHVjdChbMiwgMywgNF0pKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdFByb2R1Y3RGbG9hdEVsZW1lbnRzKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCcwLjI1MDAwMDAwJywgQmM6OnByb2R1Y3QoWzAuNSwgMC41XSkpOwogICAgfQoKICAgIC8vIOKUgOKUgCBzdWJBbGwg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RTdWJBbGxFbXB0eUFycmF5UmV0dXJuc1plcm8oKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzAuMDAwMDAwMDAnLCBCYzo6c3ViQWxsKFtdKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RTdWJBbGxTaW5nbGVFbGVtZW50KCk6IHZvaWQKICAgIHsKICAgICAgICAvLyBTaW5nbGUgZWxlbWVudCBpcyByZXR1cm5lZCBhcy1pcyAobm8gcmVkdWNlIHN0ZXAgYXBwbGllcykKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCc1JywgQmM6OnN1YkFsbChbJzUnXSkpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0U3ViQWxsTXVsdGlwbGVFbGVtZW50cygpOiB2b2lkCiAgICB7CiAgICAgICAgLy8gMTAgLSAzIC0gMiA9IDUKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCc1LjAwMDAwMDAwJywgQmM6OnN1YkFsbChbJzEwJywgJzMnLCAnMiddKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RTdWJBbGxSZXN1bHRDYW5CZU5lZ2F0aXZlKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCctNS4wMDAwMDAwMCcsIEJjOjpzdWJBbGwoWyc1JywgJzEwJ10pKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdFN1YkFsbEludEVsZW1lbnRzKCk6IHZvaWQKICAgIHsKICAgICAgICAvLyAxMCAtIDMgLSAyID0gNQogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzUuMDAwMDAwMDAnLCBCYzo6c3ViQWxsKFsxMCwgMywgMl0pKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdFN1YkFsbEZsb2F0RWxlbWVudHMoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzcuNTAwMDAwMDAnLCBCYzo6c3ViQWxsKFsxMC4wLCAyLjVdKSk7CiAgICB9CgogICAgLy8g4pSA4pSAIGRpdkFsbCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdERpdkFsbEVtcHR5QXJyYXlSZXR1cm5zT25lKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCcxLjAwMDAwMDAwJywgQmM6OmRpdkFsbChbXSkpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0RGl2QWxsU2luZ2xlRWxlbWVudCgpOiB2b2lkCiAgICB7CiAgICAgICAgLy8gU2luZ2xlIGVsZW1lbnQgaXMgcmV0dXJuZWQgYXMtaXMgKG5vIHJlZHVjZSBzdGVwIGFwcGxpZXMpCiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnOCcsIEJjOjpkaXZBbGwoWyc4J10pKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdERpdkFsbE11bHRpcGxlRWxlbWVudHMoKTogdm9pZAogICAgewogICAgICAgIC8vIDEwMCAvIDUgLyA0ID0gNQogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzUuMDAwMDAwMDAnLCBCYzo6ZGl2QWxsKFsnMTAwJywgJzUnLCAnNCddKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3REaXZBbGxUaHJvd3NPbkRpdmlzaW9uQnlaZXJvKCk6IHZvaWQKICAgIHsKICAgICAgICAkdGhpcy0+ZXhwZWN0RXhjZXB0aW9uKFxEaXZpc2lvbkJ5WmVyb0Vycm9yOjpjbGFzcyk7CiAgICAgICAgQmM6OmRpdkFsbChbJzEwJywgJzAnXSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3REaXZBbGxJbnRFbGVtZW50cygpOiB2b2lkCiAgICB7CiAgICAgICAgLy8gMTAwIC8gNSAvIDQgPSA1CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnNS4wMDAwMDAwMCcsIEJjOjpkaXZBbGwoWzEwMCwgNSwgNF0pKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdERpdkFsbEZsb2F0RWxlbWVudHMoKTogdm9pZAogICAgewogICAgICAgIHNlbGY6OmFzc2VydFNhbWUoJzIuMDAwMDAwMDAnLCBCYzo6ZGl2QWxsKFs1LjAsIDIuNV0pKTsKICAgIH0KCiAgICAvLyDilIDilIAgcGVyY2VudCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdFBlcmNlbnRCYXNpY0NhbGN1bGF0aW9uKCk6IHZvaWQKICAgIHsKICAgICAgICAvLyAxNSUgb2YgMjAwID0gMzAKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCczMC4wMDAwMDAwMCcsIEJjOjpwZXJjZW50KCcyMDAnLCAnMTUnKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RQZXJjZW50RmlmdHlQZXJjZW50KCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCc1MC4wMDAwMDAwMCcsIEJjOjpwZXJjZW50KCcxMDAnLCAnNTAnKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RQZXJjZW50RnJhY3Rpb25hbFJlc3VsdCgpOiB2b2lkCiAgICB7CiAgICAgICAgLy8gMzMuMzMlIG9mIDEwMCA9IDMzLjMzMDAwMDAwCiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnMzMuMzMwMDAwMDAnLCBCYzo6cGVyY2VudCgnMTAwJywgJzMzLjMzJykpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0UGVyY2VudFplcm9QZXJjZW50KCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCcwLjAwMDAwMDAwJywgQmM6OnBlcmNlbnQoJzEwMCcsICcwJykpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0UGVyY2VudEN1c3RvbVNjYWxlKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCczMC4wMCcsIEJjOjpwZXJjZW50KCcyMDAnLCAnMTUnLCAyKSk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RQZXJjZW50SW50SW5wdXRzKCk6IHZvaWQKICAgIHsKICAgICAgICBzZWxmOjphc3NlcnRTYW1lKCczMC4wMDAwMDAwMCcsIEJjOjpwZXJjZW50KDIwMCwgMTUpKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdFBlcmNlbnRGbG9hdElucHV0cygpOiB2b2lkCiAgICB7CiAgICAgICAgc2VsZjo6YXNzZXJ0U2FtZSgnMzAuMDAwMDAwMDAnLCBCYzo6cGVyY2VudCgyMDAuMCwgMTUuMCkpOwogICAgfQoKICAgIC8vIOKUgOKUgCBHdWFyZCB2YWxpZGF0aW9uIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0QWRkVGhyb3dzT25Ob25OdW1lcmljSW5wdXQoKTogdm9pZAogICAgewogICAgICAgICR0aGlzLT5leHBlY3RFeGNlcHRpb24oXEludmFsaWRBcmd1bWVudEV4Y2VwdGlvbjo6Y2xhc3MpOwogICAgICAgIC8qKiBAcGhwc3Rhbi1pZ25vcmUtbmV4dC1saW5lICovCiAgICAgICAgQmM6OmFkZCgnbm90LWEtbnVtYmVyJywgJzEnKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdFN1YlRocm93c09uTm9uTnVtZXJpY0lucHV0KCk6IHZvaWQKICAgIHsKICAgICAgICAkdGhpcy0+ZXhwZWN0RXhjZXB0aW9uKFxJbnZhbGlkQXJndW1lbnRFeGNlcHRpb246OmNsYXNzKTsKICAgICAgICAvKiogQHBocHN0YW4taWdub3JlLW5leHQtbGluZSAqLwogICAgICAgIEJjOjpzdWIoJzUnLCAnYWJjJyk7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIHRlc3RNdWxUaHJvd3NPbk5vbk51bWVyaWNJbnB1dCgpOiB2b2lkCiAgICB7CiAgICAgICAgJHRoaXMtPmV4cGVjdEV4Y2VwdGlvbihcSW52YWxpZEFyZ3VtZW50RXhjZXB0aW9uOjpjbGFzcyk7CiAgICAgICAgLyoqIEBwaHBzdGFuLWlnbm9yZS1uZXh0LWxpbmUgKi8KICAgICAgICBCYzo6bXVsKCd4JywgJzInKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdERpdlRocm93c09uTm9uTnVtZXJpY0lucHV0KCk6IHZvaWQKICAgIHsKICAgICAgICAkdGhpcy0+ZXhwZWN0RXhjZXB0aW9uKFxJbnZhbGlkQXJndW1lbnRFeGNlcHRpb246OmNsYXNzKTsKICAgICAgICAvKiogQHBocHN0YW4taWdub3JlLW5leHQtbGluZSAqLwogICAgICAgIEJjOjpkaXYoJzEwJywgJ3knKTsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gdGVzdEd0VGhyb3dzT25Ob25OdW1lcmljSW5wdXQoKTogdm9pZAogICAgewogICAgICAgICR0aGlzLT5leHBlY3RFeGNlcHRpb24oXEludmFsaWRBcmd1bWVudEV4Y2VwdGlvbjo6Y2xhc3MpOwogICAgICAgIC8qKiBAcGhwc3Rhbi1pZ25vcmUtbmV4dC1saW5lICovCiAgICAgICAgQmM6Omd0KCdiYWQnLCAnMScpOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiB0ZXN0THRUaHJvd3NPbk5vbk51bWVyaWNJbnB1dCgpOiB2b2lkCiAgICB7CiAgICAgICAgJHRoaXMtPmV4cGVjdEV4Y2VwdGlvbihcSW52YWxpZEFyZ3VtZW50RXhjZXB0aW9uOjpjbGFzcyk7CiAgICAgICAgLyoqIEBwaHBzdGFuLWlnbm9yZS1uZXh0LWxpbmUgKi8KICAgICAgICBCYzo6bHQoJzEnLCAnYmFkJyk7CiAgICB9Cn0K \ No newline at end of file +expectException(\DivisionByZeroError::class); + Bc::div('1', '0'); + } + + public function testDivByZeroDecimalThrows(): void + { + $this->expectException(\DivisionByZeroError::class); + Bc::div('1', '0.00000000'); + } + + // ── scale ───────────────────────────────────────────────────────────────── + + public function testScalePadsShortDecimals(): void + { + self::assertSame('1.50000000', Bc::scale('1.5')); + } + + public function testScaleTruncatesLongDecimals(): void + { + self::assertSame('1.12345678', Bc::scale('1.123456789')); + } + + public function testScaleIntegerInput(): void + { + self::assertSame('42.00000000', Bc::scale('42')); + } + + public function testScaleCustomScale(): void + { + self::assertSame('3.14', Bc::scale('3.14159', 2)); + } + + // ── abs ─────────────────────────────────────────────────────────────────── + + public function testAbsNegativeValue(): void + { + self::assertSame('5.00000000', Bc::abs('-5')); + } + + public function testAbsPositiveValueUnchanged(): void + { + self::assertSame('3.14000000', Bc::abs('3.14')); + } + + public function testAbsZero(): void + { + self::assertSame('0.00000000', Bc::abs('0')); + } + + // ── max / min ───────────────────────────────────────────────────────────── + + public function testMaxReturnsLarger(): void + { + self::assertSame('10.00000000', Bc::max('10', '5')); + } + + public function testMaxWhenEqual(): void + { + self::assertSame('5.00000000', Bc::max('5', '5')); + } + + public function testMinReturnsSmaller(): void + { + self::assertSame('3.00000000', Bc::min('10', '3')); + } + + public function testMinNegativeValues(): void + { + self::assertSame('-10.00000000', Bc::min('-5', '-10')); + } + + // ── compare ─────────────────────────────────────────────────────────────── + + public function testCompareGreater(): void + { + self::assertSame(1, Bc::compare('10', '5')); + } + + public function testCompareLess(): void + { + self::assertSame(-1, Bc::compare('5', '10')); + } + + public function testCompareEqual(): void + { + self::assertSame(0, Bc::compare('5.00', '5.000')); + } + + // ── isZero ──────────────────────────────────────────────────────────────── + + public function testIsZeroTrueForZero(): void + { + self::assertTrue(Bc::isZero('0')); + } + + public function testIsZeroTrueForFormattedZero(): void + { + self::assertTrue(Bc::isZero('0.00000000')); + } + + public function testIsZeroFalseForNonZero(): void + { + self::assertFalse(Bc::isZero('0.00000001')); + } + + // ── gt / lt ─────────────────────────────────────────────────────────────── + + public function testGtTrueWhenGreater(): void + { + self::assertTrue(Bc::gt('10', '5')); + } + + public function testGtFalseWhenEqual(): void + { + self::assertFalse(Bc::gt('5', '5')); + } + + public function testLtTrueWhenLess(): void + { + self::assertTrue(Bc::lt('3', '7')); + } + + public function testLtFalseWhenGreater(): void + { + self::assertFalse(Bc::lt('7', '3')); + } + + // ── gte ─────────────────────────────────────────────────────────────────── + + public function testGteTrueWhenGreater(): void + { + self::assertTrue(Bc::gte('10', '5')); + } + + public function testGteTrueWhenEqual(): void + { + self::assertTrue(Bc::gte('5', '5')); + } + + public function testGteFalseWhenLess(): void + { + self::assertFalse(Bc::gte('3', '7')); + } + + // ── lte ─────────────────────────────────────────────────────────────────── + + public function testLteTrueWhenLess(): void + { + self::assertTrue(Bc::lte('3', '7')); + } + + public function testLteTrueWhenEqual(): void + { + self::assertTrue(Bc::lte('5', '5')); + } + + public function testLteFalseWhenGreater(): void + { + self::assertFalse(Bc::lte('10', '5')); + } + + // ── eq ──────────────────────────────────────────────────────────────────── + + public function testEqTrueWhenEqual(): void + { + self::assertTrue(Bc::eq('5.00', '5.000')); + } + + public function testEqFalseWhenDifferent(): void + { + self::assertFalse(Bc::eq('5', '5.00000001')); + } + + public function testEqRespectsScale(): void + { + // At scale 2, 5.001 and 5.009 both truncate to 5.00 and are equal + self::assertTrue(Bc::eq('5.001', '5.009', 2)); + } + + // ── isPositive ──────────────────────────────────────────────────────────── + + public function testIsPositiveTrueForPositiveValue(): void + { + self::assertTrue(Bc::isPositive('0.00000001')); + } + + public function testIsPositiveFalseForZero(): void + { + self::assertFalse(Bc::isPositive('0')); + } + + public function testIsPositiveFalseForNegative(): void + { + self::assertFalse(Bc::isPositive('-1')); + } + + // ── isNegative ──────────────────────────────────────────────────────────── + + public function testIsNegativeTrueForNegativeValue(): void + { + self::assertTrue(Bc::isNegative('-0.00000001')); + } + + public function testIsNegativeFalseForZero(): void + { + self::assertFalse(Bc::isNegative('0')); + } + + public function testIsNegativeFalseForPositive(): void + { + self::assertFalse(Bc::isNegative('1')); + } + + // ── negate ──────────────────────────────────────────────────────────────── + + public function testNegatePositiveValue(): void + { + self::assertSame('-5.00000000', Bc::negate('5')); + } + + public function testNegateNegativeValue(): void + { + self::assertSame('5.00000000', Bc::negate('-5')); + } + + public function testNegateZeroRemainsZero(): void + { + self::assertSame('0.00000000', Bc::negate('0')); + } + + public function testNegateCustomScale(): void + { + self::assertSame('-3.14', Bc::negate('3.14', 2)); + } + + // ── sum ─────────────────────────────────────────────────────────────────── + + public function testSumEmptyArrayReturnsZero(): void + { + self::assertSame('0.00000000', Bc::sum([])); + } + + public function testSumSingleElement(): void + { + self::assertSame('5.00000000', Bc::sum(['5'])); + } + + public function testSumMultipleElements(): void + { + self::assertSame('6.00000000', Bc::sum(['1', '2', '3'])); + } + + public function testSumWithNegativeValues(): void + { + self::assertSame('-2.00000000', Bc::sum(['-1', '2', '-3'])); + } + + public function testSumCustomScale(): void + { + self::assertSame('3.30', Bc::sum(['1.1', '2.2'], 2)); + } + + public function testSumThrowsOnNonNumericElement(): void + { + $this->expectException(\InvalidArgumentException::class); + /** @phpstan-ignore-next-line */ + Bc::sum(['1', 'not-a-number', '3']); + } + + // ── product ─────────────────────────────────────────────────────────────── + + public function testProductEmptyArrayReturnsOne(): void + { + self::assertSame('1.00000000', Bc::product([])); + } + + public function testProductSingleElement(): void + { + self::assertSame('7.00000000', Bc::product(['7'])); + } + + public function testProductMultipleElements(): void + { + self::assertSame('24.00000000', Bc::product(['2', '3', '4'])); + } + + public function testProductWithDecimalValues(): void + { + self::assertSame('0.25000000', Bc::product(['0.5', '0.5'])); + } + + public function testProductWithNegativeValue(): void + { + self::assertSame('-6.00000000', Bc::product(['-2', '3'])); + } + + // ── subAll ──────────────────────────────────────────────────────────────── + + public function testSubAllEmptyArrayReturnsZero(): void + { + self::assertSame('0.00000000', Bc::subAll([])); + } + + public function testSubAllSingleElement(): void + { + // Single element is returned as-is (no reduce step applies) + self::assertSame('5', Bc::subAll(['5'])); + } + + public function testSubAllMultipleElements(): void + { + // 10 - 3 - 2 = 5 + self::assertSame('5.00000000', Bc::subAll(['10', '3', '2'])); + } + + public function testSubAllResultCanBeNegative(): void + { + self::assertSame('-5.00000000', Bc::subAll(['5', '10'])); + } + + // ── divAll ──────────────────────────────────────────────────────────────── + + public function testDivAllEmptyArrayReturnsOne(): void + { + self::assertSame('1.00000000', Bc::divAll([])); + } + + public function testDivAllSingleElement(): void + { + // Single element is returned as-is (no reduce step applies) + self::assertSame('8', Bc::divAll(['8'])); + } + + public function testDivAllMultipleElements(): void + { + // 100 / 5 / 4 = 5 + self::assertSame('5.00000000', Bc::divAll(['100', '5', '4'])); + } + + public function testDivAllThrowsOnDivisionByZero(): void + { + $this->expectException(\DivisionByZeroError::class); + Bc::divAll(['10', '0']); + } + + // ── percent ─────────────────────────────────────────────────────────────── + + public function testPercentBasicCalculation(): void + { + // 15% of 200 = 30 + self::assertSame('30.00000000', Bc::percent('200', '15')); + } + + public function testPercentFiftyPercent(): void + { + self::assertSame('50.00000000', Bc::percent('100', '50')); + } + + public function testPercentFractionalResult(): void + { + // 33.33% of 100 = 33.33000000 + self::assertSame('33.33000000', Bc::percent('100', '33.33')); + } + + public function testPercentZeroPercent(): void + { + self::assertSame('0.00000000', Bc::percent('100', '0')); + } + + public function testPercentCustomScale(): void + { + self::assertSame('30.00', Bc::percent('200', '15', 2)); + } + + // ── Guard validation ────────────────────────────────────────────────────── + + public function testAddThrowsOnNonNumericInput(): void + { + $this->expectException(\InvalidArgumentException::class); + /** @phpstan-ignore-next-line */ + Bc::add('not-a-number', '1'); + } + + public function testSubThrowsOnNonNumericInput(): void + { + $this->expectException(\InvalidArgumentException::class); + /** @phpstan-ignore-next-line */ + Bc::sub('5', 'abc'); + } + + public function testMulThrowsOnNonNumericInput(): void + { + $this->expectException(\InvalidArgumentException::class); + /** @phpstan-ignore-next-line */ + Bc::mul('x', '2'); + } + + public function testDivThrowsOnNonNumericInput(): void + { + $this->expectException(\InvalidArgumentException::class); + /** @phpstan-ignore-next-line */ + Bc::div('10', 'y'); + } + + public function testGtThrowsOnNonNumericInput(): void + { + $this->expectException(\InvalidArgumentException::class); + /** @phpstan-ignore-next-line */ + Bc::gt('bad', '1'); + } + + public function testLtThrowsOnNonNumericInput(): void + { + $this->expectException(\InvalidArgumentException::class); + /** @phpstan-ignore-next-line */ + Bc::lt('1', 'bad'); + } +}