)]}'
{"version": 3, "sources": ["/web/static/lib/Chart/Chart.js", "/web/static/lib/chartjs-adapter-luxon/chartjs-adapter-luxon.js"], "mappings": "AAAA;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACzkdA;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA", "sourcesContent": ["/*!\n * Chart.js v4.4.1\n * https://www.chartjs.org\n * (c) 2024 Chart.js Contributors\n * Released under the MIT License\n */\n(function (global, factory) {\n    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n        typeof define === 'function' && define.amd ? define(factory) :\n            (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Chart = factory());\n})(this, (function () { 'use strict';\n\n    var plugins = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        get Colors () { return plugin_colors; },\n        get Decimation () { return plugin_decimation; },\n        get Filler () { return index; },\n        get Legend () { return plugin_legend; },\n        get SubTitle () { return plugin_subtitle; },\n        get Title () { return plugin_title; },\n        get Tooltip () { return plugin_tooltip; }\n    });\n\n    /**\n     * @namespace Chart.helpers\n     */ /**\n     * An empty function that can be used, for example, for optional callback.\n     */ function noop() {\n        /* noop */ }\n    /**\n     * Returns a unique id, sequentially generated from a global variable.\n     */ const uid = (()=>{\n        let id = 0;\n        return ()=>id++;\n    })();\n    /**\n     * Returns true if `value` is neither null nor undefined, else returns false.\n     * @param value - The value to test.\n     * @since 2.7.0\n     */ function isNullOrUndef(value) {\n        return value === null || typeof value === 'undefined';\n    }\n    /**\n     * Returns true if `value` is an array (including typed arrays), else returns false.\n     * @param value - The value to test.\n     * @function\n     */ function isArray(value) {\n        if (Array.isArray && Array.isArray(value)) {\n            return true;\n        }\n        const type = Object.prototype.toString.call(value);\n        if (type.slice(0, 7) === '[object' && type.slice(-6) === 'Array]') {\n            return true;\n        }\n        return false;\n    }\n    /**\n     * Returns true if `value` is an object (excluding null), else returns false.\n     * @param value - The value to test.\n     * @since 2.7.0\n     */ function isObject(value) {\n        return value !== null && Object.prototype.toString.call(value) === '[object Object]';\n    }\n    /**\n     * Returns true if `value` is a finite number, else returns false\n     * @param value  - The value to test.\n     */ function isNumberFinite(value) {\n        return (typeof value === 'number' || value instanceof Number) && isFinite(+value);\n    }\n    /**\n     * Returns `value` if finite, else returns `defaultValue`.\n     * @param value - The value to return if defined.\n     * @param defaultValue - The value to return if `value` is not finite.\n     */ function finiteOrDefault(value, defaultValue) {\n        return isNumberFinite(value) ? value : defaultValue;\n    }\n    /**\n     * Returns `value` if defined, else returns `defaultValue`.\n     * @param value - The value to return if defined.\n     * @param defaultValue - The value to return if `value` is undefined.\n     */ function valueOrDefault(value, defaultValue) {\n        return typeof value === 'undefined' ? defaultValue : value;\n    }\n    const toPercentage = (value, dimension)=>typeof value === 'string' && value.endsWith('%') ? parseFloat(value) / 100 : +value / dimension;\n    const toDimension = (value, dimension)=>typeof value === 'string' && value.endsWith('%') ? parseFloat(value) / 100 * dimension : +value;\n    /**\n     * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the\n     * value returned by `fn`. If `fn` is not a function, this method returns undefined.\n     * @param fn - The function to call.\n     * @param args - The arguments with which `fn` should be called.\n     * @param [thisArg] - The value of `this` provided for the call to `fn`.\n     */ function callback(fn, args, thisArg) {\n        if (fn && typeof fn.call === 'function') {\n            return fn.apply(thisArg, args);\n        }\n    }\n    function each(loopable, fn, thisArg, reverse) {\n        let i, len, keys;\n        if (isArray(loopable)) {\n            len = loopable.length;\n            if (reverse) {\n                for(i = len - 1; i >= 0; i--){\n                    fn.call(thisArg, loopable[i], i);\n                }\n            } else {\n                for(i = 0; i < len; i++){\n                    fn.call(thisArg, loopable[i], i);\n                }\n            }\n        } else if (isObject(loopable)) {\n            keys = Object.keys(loopable);\n            len = keys.length;\n            for(i = 0; i < len; i++){\n                fn.call(thisArg, loopable[keys[i]], keys[i]);\n            }\n        }\n    }\n    /**\n     * Returns true if the `a0` and `a1` arrays have the same content, else returns false.\n     * @param a0 - The array to compare\n     * @param a1 - The array to compare\n     * @private\n     */ function _elementsEqual(a0, a1) {\n        let i, ilen, v0, v1;\n        if (!a0 || !a1 || a0.length !== a1.length) {\n            return false;\n        }\n        for(i = 0, ilen = a0.length; i < ilen; ++i){\n            v0 = a0[i];\n            v1 = a1[i];\n            if (v0.datasetIndex !== v1.datasetIndex || v0.index !== v1.index) {\n                return false;\n            }\n        }\n        return true;\n    }\n    /**\n     * Returns a deep copy of `source` without keeping references on objects and arrays.\n     * @param source - The value to clone.\n     */ function clone$1(source) {\n        if (isArray(source)) {\n            return source.map(clone$1);\n        }\n        if (isObject(source)) {\n            const target = Object.create(null);\n            const keys = Object.keys(source);\n            const klen = keys.length;\n            let k = 0;\n            for(; k < klen; ++k){\n                target[keys[k]] = clone$1(source[keys[k]]);\n            }\n            return target;\n        }\n        return source;\n    }\n    function isValidKey(key) {\n        return [\n            '__proto__',\n            'prototype',\n            'constructor'\n        ].indexOf(key) === -1;\n    }\n    /**\n     * The default merger when Chart.helpers.merge is called without merger option.\n     * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback.\n     * @private\n     */ function _merger(key, target, source, options) {\n        if (!isValidKey(key)) {\n            return;\n        }\n        const tval = target[key];\n        const sval = source[key];\n        if (isObject(tval) && isObject(sval)) {\n            // eslint-disable-next-line @typescript-eslint/no-use-before-define\n            merge(tval, sval, options);\n        } else {\n            target[key] = clone$1(sval);\n        }\n    }\n    function merge(target, source, options) {\n        const sources = isArray(source) ? source : [\n            source\n        ];\n        const ilen = sources.length;\n        if (!isObject(target)) {\n            return target;\n        }\n        options = options || {};\n        const merger = options.merger || _merger;\n        let current;\n        for(let i = 0; i < ilen; ++i){\n            current = sources[i];\n            if (!isObject(current)) {\n                continue;\n            }\n            const keys = Object.keys(current);\n            for(let k = 0, klen = keys.length; k < klen; ++k){\n                merger(keys[k], target, current, options);\n            }\n        }\n        return target;\n    }\n    function mergeIf(target, source) {\n        // eslint-disable-next-line @typescript-eslint/no-use-before-define\n        return merge(target, source, {\n            merger: _mergerIf\n        });\n    }\n    /**\n     * Merges source[key] in target[key] only if target[key] is undefined.\n     * @private\n     */ function _mergerIf(key, target, source) {\n        if (!isValidKey(key)) {\n            return;\n        }\n        const tval = target[key];\n        const sval = source[key];\n        if (isObject(tval) && isObject(sval)) {\n            mergeIf(tval, sval);\n        } else if (!Object.prototype.hasOwnProperty.call(target, key)) {\n            target[key] = clone$1(sval);\n        }\n    }\n    /**\n     * @private\n     */ function _deprecated(scope, value, previous, current) {\n        if (value !== undefined) {\n            console.warn(scope + ': \"' + previous + '\" is deprecated. Please use \"' + current + '\" instead');\n        }\n    }\n    // resolveObjectKey resolver cache\n    const keyResolvers = {\n        // Chart.helpers.core resolveObjectKey should resolve empty key to root object\n        '': (v)=>v,\n        // default resolvers\n        x: (o)=>o.x,\n        y: (o)=>o.y\n    };\n    /**\n     * @private\n     */ function _splitKey(key) {\n        const parts = key.split('.');\n        const keys = [];\n        let tmp = '';\n        for (const part of parts){\n            tmp += part;\n            if (tmp.endsWith('\\\\')) {\n                tmp = tmp.slice(0, -1) + '.';\n            } else {\n                keys.push(tmp);\n                tmp = '';\n            }\n        }\n        return keys;\n    }\n    function _getKeyResolver(key) {\n        const keys = _splitKey(key);\n        return (obj)=>{\n            for (const k of keys){\n                if (k === '') {\n                    break;\n                }\n                obj = obj && obj[k];\n            }\n            return obj;\n        };\n    }\n    function resolveObjectKey(obj, key) {\n        const resolver = keyResolvers[key] || (keyResolvers[key] = _getKeyResolver(key));\n        return resolver(obj);\n    }\n    /**\n     * @private\n     */ function _capitalize(str) {\n        return str.charAt(0).toUpperCase() + str.slice(1);\n    }\n    const defined = (value)=>typeof value !== 'undefined';\n    const isFunction = (value)=>typeof value === 'function';\n    // Adapted from https://stackoverflow.com/questions/31128855/comparing-ecma6-sets-for-equality#31129384\n    const setsEqual = (a, b)=>{\n        if (a.size !== b.size) {\n            return false;\n        }\n        for (const item of a){\n            if (!b.has(item)) {\n                return false;\n            }\n        }\n        return true;\n    };\n    /**\n     * @param e - The event\n     * @private\n     */ function _isClickEvent(e) {\n        return e.type === 'mouseup' || e.type === 'click' || e.type === 'contextmenu';\n    }\n\n    /**\n     * @alias Chart.helpers.math\n     * @namespace\n     */ const PI = Math.PI;\n    const TAU = 2 * PI;\n    const PITAU = TAU + PI;\n    const INFINITY = Number.POSITIVE_INFINITY;\n    const RAD_PER_DEG = PI / 180;\n    const HALF_PI = PI / 2;\n    const QUARTER_PI = PI / 4;\n    const TWO_THIRDS_PI = PI * 2 / 3;\n    const log10 = Math.log10;\n    const sign = Math.sign;\n    function almostEquals(x, y, epsilon) {\n        return Math.abs(x - y) < epsilon;\n    }\n    /**\n     * Implementation of the nice number algorithm used in determining where axis labels will go\n     */ function niceNum(range) {\n        const roundedRange = Math.round(range);\n        range = almostEquals(range, roundedRange, range / 1000) ? roundedRange : range;\n        const niceRange = Math.pow(10, Math.floor(log10(range)));\n        const fraction = range / niceRange;\n        const niceFraction = fraction <= 1 ? 1 : fraction <= 2 ? 2 : fraction <= 5 ? 5 : 10;\n        return niceFraction * niceRange;\n    }\n    /**\n     * Returns an array of factors sorted from 1 to sqrt(value)\n     * @private\n     */ function _factorize(value) {\n        const result = [];\n        const sqrt = Math.sqrt(value);\n        let i;\n        for(i = 1; i < sqrt; i++){\n            if (value % i === 0) {\n                result.push(i);\n                result.push(value / i);\n            }\n        }\n        if (sqrt === (sqrt | 0)) {\n            result.push(sqrt);\n        }\n        result.sort((a, b)=>a - b).pop();\n        return result;\n    }\n    function isNumber(n) {\n        return !isNaN(parseFloat(n)) && isFinite(n);\n    }\n    function almostWhole(x, epsilon) {\n        const rounded = Math.round(x);\n        return rounded - epsilon <= x && rounded + epsilon >= x;\n    }\n    /**\n     * @private\n     */ function _setMinAndMaxByKey(array, target, property) {\n        let i, ilen, value;\n        for(i = 0, ilen = array.length; i < ilen; i++){\n            value = array[i][property];\n            if (!isNaN(value)) {\n                target.min = Math.min(target.min, value);\n                target.max = Math.max(target.max, value);\n            }\n        }\n    }\n    function toRadians(degrees) {\n        return degrees * (PI / 180);\n    }\n    function toDegrees(radians) {\n        return radians * (180 / PI);\n    }\n    /**\n     * Returns the number of decimal places\n     * i.e. the number of digits after the decimal point, of the value of this Number.\n     * @param x - A number.\n     * @returns The number of decimal places.\n     * @private\n     */ function _decimalPlaces(x) {\n        if (!isNumberFinite(x)) {\n            return;\n        }\n        let e = 1;\n        let p = 0;\n        while(Math.round(x * e) / e !== x){\n            e *= 10;\n            p++;\n        }\n        return p;\n    }\n    // Gets the angle from vertical upright to the point about a centre.\n    function getAngleFromPoint(centrePoint, anglePoint) {\n        const distanceFromXCenter = anglePoint.x - centrePoint.x;\n        const distanceFromYCenter = anglePoint.y - centrePoint.y;\n        const radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);\n        let angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);\n        if (angle < -0.5 * PI) {\n            angle += TAU; // make sure the returned angle is in the range of (-PI/2, 3PI/2]\n        }\n        return {\n            angle,\n            distance: radialDistanceFromCenter\n        };\n    }\n    function distanceBetweenPoints(pt1, pt2) {\n        return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));\n    }\n    /**\n     * Shortest distance between angles, in either direction.\n     * @private\n     */ function _angleDiff(a, b) {\n        return (a - b + PITAU) % TAU - PI;\n    }\n    /**\n     * Normalize angle to be between 0 and 2*PI\n     * @private\n     */ function _normalizeAngle(a) {\n        return (a % TAU + TAU) % TAU;\n    }\n    /**\n     * @private\n     */ function _angleBetween(angle, start, end, sameAngleIsFullCircle) {\n        const a = _normalizeAngle(angle);\n        const s = _normalizeAngle(start);\n        const e = _normalizeAngle(end);\n        const angleToStart = _normalizeAngle(s - a);\n        const angleToEnd = _normalizeAngle(e - a);\n        const startToAngle = _normalizeAngle(a - s);\n        const endToAngle = _normalizeAngle(a - e);\n        return a === s || a === e || sameAngleIsFullCircle && s === e || angleToStart > angleToEnd && startToAngle < endToAngle;\n    }\n    /**\n     * Limit `value` between `min` and `max`\n     * @param value\n     * @param min\n     * @param max\n     * @private\n     */ function _limitValue(value, min, max) {\n        return Math.max(min, Math.min(max, value));\n    }\n    /**\n     * @param {number} value\n     * @private\n     */ function _int16Range(value) {\n        return _limitValue(value, -32768, 32767);\n    }\n    /**\n     * @param value\n     * @param start\n     * @param end\n     * @param [epsilon]\n     * @private\n     */ function _isBetween(value, start, end, epsilon = 1e-6) {\n        return value >= Math.min(start, end) - epsilon && value <= Math.max(start, end) + epsilon;\n    }\n\n    function _lookup(table, value, cmp) {\n        cmp = cmp || ((index)=>table[index] < value);\n        let hi = table.length - 1;\n        let lo = 0;\n        let mid;\n        while(hi - lo > 1){\n            mid = lo + hi >> 1;\n            if (cmp(mid)) {\n                lo = mid;\n            } else {\n                hi = mid;\n            }\n        }\n        return {\n            lo,\n            hi\n        };\n    }\n    /**\n     * Binary search\n     * @param table - the table search. must be sorted!\n     * @param key - property name for the value in each entry\n     * @param value - value to find\n     * @param last - lookup last index\n     * @private\n     */ const _lookupByKey = (table, key, value, last)=>_lookup(table, value, last ? (index)=>{\n        const ti = table[index][key];\n        return ti < value || ti === value && table[index + 1][key] === value;\n    } : (index)=>table[index][key] < value);\n    /**\n     * Reverse binary search\n     * @param table - the table search. must be sorted!\n     * @param key - property name for the value in each entry\n     * @param value - value to find\n     * @private\n     */ const _rlookupByKey = (table, key, value)=>_lookup(table, value, (index)=>table[index][key] >= value);\n    /**\n     * Return subset of `values` between `min` and `max` inclusive.\n     * Values are assumed to be in sorted order.\n     * @param values - sorted array of values\n     * @param min - min value\n     * @param max - max value\n     */ function _filterBetween(values, min, max) {\n        let start = 0;\n        let end = values.length;\n        while(start < end && values[start] < min){\n            start++;\n        }\n        while(end > start && values[end - 1] > max){\n            end--;\n        }\n        return start > 0 || end < values.length ? values.slice(start, end) : values;\n    }\n    const arrayEvents = [\n        'push',\n        'pop',\n        'shift',\n        'splice',\n        'unshift'\n    ];\n    function listenArrayEvents(array, listener) {\n        if (array._chartjs) {\n            array._chartjs.listeners.push(listener);\n            return;\n        }\n        Object.defineProperty(array, '_chartjs', {\n            configurable: true,\n            enumerable: false,\n            value: {\n                listeners: [\n                    listener\n                ]\n            }\n        });\n        arrayEvents.forEach((key)=>{\n            const method = '_onData' + _capitalize(key);\n            const base = array[key];\n            Object.defineProperty(array, key, {\n                configurable: true,\n                enumerable: false,\n                value (...args) {\n                    const res = base.apply(this, args);\n                    array._chartjs.listeners.forEach((object)=>{\n                        if (typeof object[method] === 'function') {\n                            object[method](...args);\n                        }\n                    });\n                    return res;\n                }\n            });\n        });\n    }\n    function unlistenArrayEvents(array, listener) {\n        const stub = array._chartjs;\n        if (!stub) {\n            return;\n        }\n        const listeners = stub.listeners;\n        const index = listeners.indexOf(listener);\n        if (index !== -1) {\n            listeners.splice(index, 1);\n        }\n        if (listeners.length > 0) {\n            return;\n        }\n        arrayEvents.forEach((key)=>{\n            delete array[key];\n        });\n        delete array._chartjs;\n    }\n    /**\n     * @param items\n     */ function _arrayUnique(items) {\n        const set = new Set(items);\n        if (set.size === items.length) {\n            return items;\n        }\n        return Array.from(set);\n    }\n\n    function fontString(pixelSize, fontStyle, fontFamily) {\n        return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;\n    }\n    /**\n     * Request animation polyfill\n     */ const requestAnimFrame = function() {\n        if (typeof window === 'undefined') {\n            return function(callback) {\n                return callback();\n            };\n        }\n        return window.requestAnimationFrame;\n    }();\n    /**\n     * Throttles calling `fn` once per animation frame\n     * Latest arguments are used on the actual call\n     */ function throttled(fn, thisArg) {\n        let argsToUse = [];\n        let ticking = false;\n        return function(...args) {\n            // Save the args for use later\n            argsToUse = args;\n            if (!ticking) {\n                ticking = true;\n                requestAnimFrame.call(window, ()=>{\n                    ticking = false;\n                    fn.apply(thisArg, argsToUse);\n                });\n            }\n        };\n    }\n    /**\n     * Debounces calling `fn` for `delay` ms\n     */ function debounce(fn, delay) {\n        let timeout;\n        return function(...args) {\n            if (delay) {\n                clearTimeout(timeout);\n                timeout = setTimeout(fn, delay, args);\n            } else {\n                fn.apply(this, args);\n            }\n            return delay;\n        };\n    }\n    /**\n     * Converts 'start' to 'left', 'end' to 'right' and others to 'center'\n     * @private\n     */ const _toLeftRightCenter = (align)=>align === 'start' ? 'left' : align === 'end' ? 'right' : 'center';\n    /**\n     * Returns `start`, `end` or `(start + end) / 2` depending on `align`. Defaults to `center`\n     * @private\n     */ const _alignStartEnd = (align, start, end)=>align === 'start' ? start : align === 'end' ? end : (start + end) / 2;\n    /**\n     * Returns `left`, `right` or `(left + right) / 2` depending on `align`. Defaults to `left`\n     * @private\n     */ const _textX = (align, left, right, rtl)=>{\n        const check = rtl ? 'left' : 'right';\n        return align === check ? right : align === 'center' ? (left + right) / 2 : left;\n    };\n    /**\n     * Return start and count of visible points.\n     * @private\n     */ function _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) {\n        const pointCount = points.length;\n        let start = 0;\n        let count = pointCount;\n        if (meta._sorted) {\n            const { iScale , _parsed  } = meta;\n            const axis = iScale.axis;\n            const { min , max , minDefined , maxDefined  } = iScale.getUserBounds();\n            if (minDefined) {\n                start = _limitValue(Math.min(// @ts-expect-error Need to type _parsed\n                    _lookupByKey(_parsed, axis, min).lo, // @ts-expect-error Need to fix types on _lookupByKey\n                    animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo), 0, pointCount - 1);\n            }\n            if (maxDefined) {\n                count = _limitValue(Math.max(// @ts-expect-error Need to type _parsed\n                    _lookupByKey(_parsed, iScale.axis, max, true).hi + 1, // @ts-expect-error Need to fix types on _lookupByKey\n                    animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1), start, pointCount) - start;\n            } else {\n                count = pointCount - start;\n            }\n        }\n        return {\n            start,\n            count\n        };\n    }\n    /**\n     * Checks if the scale ranges have changed.\n     * @param {object} meta - dataset meta.\n     * @returns {boolean}\n     * @private\n     */ function _scaleRangesChanged(meta) {\n        const { xScale , yScale , _scaleRanges  } = meta;\n        const newRanges = {\n            xmin: xScale.min,\n            xmax: xScale.max,\n            ymin: yScale.min,\n            ymax: yScale.max\n        };\n        if (!_scaleRanges) {\n            meta._scaleRanges = newRanges;\n            return true;\n        }\n        const changed = _scaleRanges.xmin !== xScale.min || _scaleRanges.xmax !== xScale.max || _scaleRanges.ymin !== yScale.min || _scaleRanges.ymax !== yScale.max;\n        Object.assign(_scaleRanges, newRanges);\n        return changed;\n    }\n\n    class Animator {\n        constructor(){\n            this._request = null;\n            this._charts = new Map();\n            this._running = false;\n            this._lastDate = undefined;\n        }\n        _notify(chart, anims, date, type) {\n            const callbacks = anims.listeners[type];\n            const numSteps = anims.duration;\n            callbacks.forEach((fn)=>fn({\n                chart,\n                initial: anims.initial,\n                numSteps,\n                currentStep: Math.min(date - anims.start, numSteps)\n            }));\n        }\n        _refresh() {\n            if (this._request) {\n                return;\n            }\n            this._running = true;\n            this._request = requestAnimFrame.call(window, ()=>{\n                this._update();\n                this._request = null;\n                if (this._running) {\n                    this._refresh();\n                }\n            });\n        }\n        _update(date = Date.now()) {\n            let remaining = 0;\n            this._charts.forEach((anims, chart)=>{\n                if (!anims.running || !anims.items.length) {\n                    return;\n                }\n                const items = anims.items;\n                let i = items.length - 1;\n                let draw = false;\n                let item;\n                for(; i >= 0; --i){\n                    item = items[i];\n                    if (item._active) {\n                        if (item._total > anims.duration) {\n                            anims.duration = item._total;\n                        }\n                        item.tick(date);\n                        draw = true;\n                    } else {\n                        items[i] = items[items.length - 1];\n                        items.pop();\n                    }\n                }\n                if (draw) {\n                    chart.draw();\n                    this._notify(chart, anims, date, 'progress');\n                }\n                if (!items.length) {\n                    anims.running = false;\n                    this._notify(chart, anims, date, 'complete');\n                    anims.initial = false;\n                }\n                remaining += items.length;\n            });\n            this._lastDate = date;\n            if (remaining === 0) {\n                this._running = false;\n            }\n        }\n        _getAnims(chart) {\n            const charts = this._charts;\n            let anims = charts.get(chart);\n            if (!anims) {\n                anims = {\n                    running: false,\n                    initial: true,\n                    items: [],\n                    listeners: {\n                        complete: [],\n                        progress: []\n                    }\n                };\n                charts.set(chart, anims);\n            }\n            return anims;\n        }\n        listen(chart, event, cb) {\n            this._getAnims(chart).listeners[event].push(cb);\n        }\n        add(chart, items) {\n            if (!items || !items.length) {\n                return;\n            }\n            this._getAnims(chart).items.push(...items);\n        }\n        has(chart) {\n            return this._getAnims(chart).items.length > 0;\n        }\n        start(chart) {\n            const anims = this._charts.get(chart);\n            if (!anims) {\n                return;\n            }\n            anims.running = true;\n            anims.start = Date.now();\n            anims.duration = anims.items.reduce((acc, cur)=>Math.max(acc, cur._duration), 0);\n            this._refresh();\n        }\n        running(chart) {\n            if (!this._running) {\n                return false;\n            }\n            const anims = this._charts.get(chart);\n            if (!anims || !anims.running || !anims.items.length) {\n                return false;\n            }\n            return true;\n        }\n        stop(chart) {\n            const anims = this._charts.get(chart);\n            if (!anims || !anims.items.length) {\n                return;\n            }\n            const items = anims.items;\n            let i = items.length - 1;\n            for(; i >= 0; --i){\n                items[i].cancel();\n            }\n            anims.items = [];\n            this._notify(chart, anims, Date.now(), 'complete');\n        }\n        remove(chart) {\n            return this._charts.delete(chart);\n        }\n    }\n    var animator = /* #__PURE__ */ new Animator();\n\n    /*!\n   * @kurkle/color v0.3.2\n   * https://github.com/kurkle/color#readme\n   * (c) 2023 Jukka Kurkela\n   * Released under the MIT License\n   */\n    function round(v) {\n        return v + 0.5 | 0;\n    }\n    const lim = (v, l, h) => Math.max(Math.min(v, h), l);\n    function p2b(v) {\n        return lim(round(v * 2.55), 0, 255);\n    }\n    function n2b(v) {\n        return lim(round(v * 255), 0, 255);\n    }\n    function b2n(v) {\n        return lim(round(v / 2.55) / 100, 0, 1);\n    }\n    function n2p(v) {\n        return lim(round(v * 100), 0, 100);\n    }\n    const map$1 = {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, A: 10, B: 11, C: 12, D: 13, E: 14, F: 15, a: 10, b: 11, c: 12, d: 13, e: 14, f: 15};\n    const hex = [...'0123456789ABCDEF'];\n    const h1 = b => hex[b & 0xF];\n    const h2 = b => hex[(b & 0xF0) >> 4] + hex[b & 0xF];\n    const eq = b => ((b & 0xF0) >> 4) === (b & 0xF);\n    const isShort = v => eq(v.r) && eq(v.g) && eq(v.b) && eq(v.a);\n    function hexParse(str) {\n        var len = str.length;\n        var ret;\n        if (str[0] === '#') {\n            if (len === 4 || len === 5) {\n                ret = {\n                    r: 255 & map$1[str[1]] * 17,\n                    g: 255 & map$1[str[2]] * 17,\n                    b: 255 & map$1[str[3]] * 17,\n                    a: len === 5 ? map$1[str[4]] * 17 : 255\n                };\n            } else if (len === 7 || len === 9) {\n                ret = {\n                    r: map$1[str[1]] << 4 | map$1[str[2]],\n                    g: map$1[str[3]] << 4 | map$1[str[4]],\n                    b: map$1[str[5]] << 4 | map$1[str[6]],\n                    a: len === 9 ? (map$1[str[7]] << 4 | map$1[str[8]]) : 255\n                };\n            }\n        }\n        return ret;\n    }\n    const alpha = (a, f) => a < 255 ? f(a) : '';\n    function hexString(v) {\n        var f = isShort(v) ? h1 : h2;\n        return v\n            ? '#' + f(v.r) + f(v.g) + f(v.b) + alpha(v.a, f)\n            : undefined;\n    }\n    const HUE_RE = /^(hsla?|hwb|hsv)\\(\\s*([-+.e\\d]+)(?:deg)?[\\s,]+([-+.e\\d]+)%[\\s,]+([-+.e\\d]+)%(?:[\\s,]+([-+.e\\d]+)(%)?)?\\s*\\)$/;\n    function hsl2rgbn(h, s, l) {\n        const a = s * Math.min(l, 1 - l);\n        const f = (n, k = (n + h / 30) % 12) => l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);\n        return [f(0), f(8), f(4)];\n    }\n    function hsv2rgbn(h, s, v) {\n        const f = (n, k = (n + h / 60) % 6) => v - v * s * Math.max(Math.min(k, 4 - k, 1), 0);\n        return [f(5), f(3), f(1)];\n    }\n    function hwb2rgbn(h, w, b) {\n        const rgb = hsl2rgbn(h, 1, 0.5);\n        let i;\n        if (w + b > 1) {\n            i = 1 / (w + b);\n            w *= i;\n            b *= i;\n        }\n        for (i = 0; i < 3; i++) {\n            rgb[i] *= 1 - w - b;\n            rgb[i] += w;\n        }\n        return rgb;\n    }\n    function hueValue(r, g, b, d, max) {\n        if (r === max) {\n            return ((g - b) / d) + (g < b ? 6 : 0);\n        }\n        if (g === max) {\n            return (b - r) / d + 2;\n        }\n        return (r - g) / d + 4;\n    }\n    function rgb2hsl(v) {\n        const range = 255;\n        const r = v.r / range;\n        const g = v.g / range;\n        const b = v.b / range;\n        const max = Math.max(r, g, b);\n        const min = Math.min(r, g, b);\n        const l = (max + min) / 2;\n        let h, s, d;\n        if (max !== min) {\n            d = max - min;\n            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n            h = hueValue(r, g, b, d, max);\n            h = h * 60 + 0.5;\n        }\n        return [h | 0, s || 0, l];\n    }\n    function calln(f, a, b, c) {\n        return (\n            Array.isArray(a)\n                ? f(a[0], a[1], a[2])\n                : f(a, b, c)\n        ).map(n2b);\n    }\n    function hsl2rgb(h, s, l) {\n        return calln(hsl2rgbn, h, s, l);\n    }\n    function hwb2rgb(h, w, b) {\n        return calln(hwb2rgbn, h, w, b);\n    }\n    function hsv2rgb(h, s, v) {\n        return calln(hsv2rgbn, h, s, v);\n    }\n    function hue(h) {\n        return (h % 360 + 360) % 360;\n    }\n    function hueParse(str) {\n        const m = HUE_RE.exec(str);\n        let a = 255;\n        let v;\n        if (!m) {\n            return;\n        }\n        if (m[5] !== v) {\n            a = m[6] ? p2b(+m[5]) : n2b(+m[5]);\n        }\n        const h = hue(+m[2]);\n        const p1 = +m[3] / 100;\n        const p2 = +m[4] / 100;\n        if (m[1] === 'hwb') {\n            v = hwb2rgb(h, p1, p2);\n        } else if (m[1] === 'hsv') {\n            v = hsv2rgb(h, p1, p2);\n        } else {\n            v = hsl2rgb(h, p1, p2);\n        }\n        return {\n            r: v[0],\n            g: v[1],\n            b: v[2],\n            a: a\n        };\n    }\n    function rotate(v, deg) {\n        var h = rgb2hsl(v);\n        h[0] = hue(h[0] + deg);\n        h = hsl2rgb(h);\n        v.r = h[0];\n        v.g = h[1];\n        v.b = h[2];\n    }\n    function hslString(v) {\n        if (!v) {\n            return;\n        }\n        const a = rgb2hsl(v);\n        const h = a[0];\n        const s = n2p(a[1]);\n        const l = n2p(a[2]);\n        return v.a < 255\n            ? `hsla(${h}, ${s}%, ${l}%, ${b2n(v.a)})`\n            : `hsl(${h}, ${s}%, ${l}%)`;\n    }\n    const map$2 = {\n        x: 'dark',\n        Z: 'light',\n        Y: 're',\n        X: 'blu',\n        W: 'gr',\n        V: 'medium',\n        U: 'slate',\n        A: 'ee',\n        T: 'ol',\n        S: 'or',\n        B: 'ra',\n        C: 'lateg',\n        D: 'ights',\n        R: 'in',\n        Q: 'turquois',\n        E: 'hi',\n        P: 'ro',\n        O: 'al',\n        N: 'le',\n        M: 'de',\n        L: 'yello',\n        F: 'en',\n        K: 'ch',\n        G: 'arks',\n        H: 'ea',\n        I: 'ightg',\n        J: 'wh'\n    };\n    const names$1 = {\n        OiceXe: 'f0f8ff',\n        antiquewEte: 'faebd7',\n        aqua: 'ffff',\n        aquamarRe: '7fffd4',\n        azuY: 'f0ffff',\n        beige: 'f5f5dc',\n        bisque: 'ffe4c4',\n        black: '0',\n        blanKedOmond: 'ffebcd',\n        Xe: 'ff',\n        XeviTet: '8a2be2',\n        bPwn: 'a52a2a',\n        burlywood: 'deb887',\n        caMtXe: '5f9ea0',\n        KartYuse: '7fff00',\n        KocTate: 'd2691e',\n        cSO: 'ff7f50',\n        cSnflowerXe: '6495ed',\n        cSnsilk: 'fff8dc',\n        crimson: 'dc143c',\n        cyan: 'ffff',\n        xXe: '8b',\n        xcyan: '8b8b',\n        xgTMnPd: 'b8860b',\n        xWay: 'a9a9a9',\n        xgYF: '6400',\n        xgYy: 'a9a9a9',\n        xkhaki: 'bdb76b',\n        xmagFta: '8b008b',\n        xTivegYF: '556b2f',\n        xSange: 'ff8c00',\n        xScEd: '9932cc',\n        xYd: '8b0000',\n        xsOmon: 'e9967a',\n        xsHgYF: '8fbc8f',\n        xUXe: '483d8b',\n        xUWay: '2f4f4f',\n        xUgYy: '2f4f4f',\n        xQe: 'ced1',\n        xviTet: '9400d3',\n        dAppRk: 'ff1493',\n        dApskyXe: 'bfff',\n        dimWay: '696969',\n        dimgYy: '696969',\n        dodgerXe: '1e90ff',\n        fiYbrick: 'b22222',\n        flSOwEte: 'fffaf0',\n        foYstWAn: '228b22',\n        fuKsia: 'ff00ff',\n        gaRsbSo: 'dcdcdc',\n        ghostwEte: 'f8f8ff',\n        gTd: 'ffd700',\n        gTMnPd: 'daa520',\n        Way: '808080',\n        gYF: '8000',\n        gYFLw: 'adff2f',\n        gYy: '808080',\n        honeyMw: 'f0fff0',\n        hotpRk: 'ff69b4',\n        RdianYd: 'cd5c5c',\n        Rdigo: '4b0082',\n        ivSy: 'fffff0',\n        khaki: 'f0e68c',\n        lavFMr: 'e6e6fa',\n        lavFMrXsh: 'fff0f5',\n        lawngYF: '7cfc00',\n        NmoncEffon: 'fffacd',\n        ZXe: 'add8e6',\n        ZcSO: 'f08080',\n        Zcyan: 'e0ffff',\n        ZgTMnPdLw: 'fafad2',\n        ZWay: 'd3d3d3',\n        ZgYF: '90ee90',\n        ZgYy: 'd3d3d3',\n        ZpRk: 'ffb6c1',\n        ZsOmon: 'ffa07a',\n        ZsHgYF: '20b2aa',\n        ZskyXe: '87cefa',\n        ZUWay: '778899',\n        ZUgYy: '778899',\n        ZstAlXe: 'b0c4de',\n        ZLw: 'ffffe0',\n        lime: 'ff00',\n        limegYF: '32cd32',\n        lRF: 'faf0e6',\n        magFta: 'ff00ff',\n        maPon: '800000',\n        VaquamarRe: '66cdaa',\n        VXe: 'cd',\n        VScEd: 'ba55d3',\n        VpurpN: '9370db',\n        VsHgYF: '3cb371',\n        VUXe: '7b68ee',\n        VsprRggYF: 'fa9a',\n        VQe: '48d1cc',\n        VviTetYd: 'c71585',\n        midnightXe: '191970',\n        mRtcYam: 'f5fffa',\n        mistyPse: 'ffe4e1',\n        moccasR: 'ffe4b5',\n        navajowEte: 'ffdead',\n        navy: '80',\n        Tdlace: 'fdf5e6',\n        Tive: '808000',\n        TivedBb: '6b8e23',\n        Sange: 'ffa500',\n        SangeYd: 'ff4500',\n        ScEd: 'da70d6',\n        pOegTMnPd: 'eee8aa',\n        pOegYF: '98fb98',\n        pOeQe: 'afeeee',\n        pOeviTetYd: 'db7093',\n        papayawEp: 'ffefd5',\n        pHKpuff: 'ffdab9',\n        peru: 'cd853f',\n        pRk: 'ffc0cb',\n        plum: 'dda0dd',\n        powMrXe: 'b0e0e6',\n        purpN: '800080',\n        YbeccapurpN: '663399',\n        Yd: 'ff0000',\n        Psybrown: 'bc8f8f',\n        PyOXe: '4169e1',\n        saddNbPwn: '8b4513',\n        sOmon: 'fa8072',\n        sandybPwn: 'f4a460',\n        sHgYF: '2e8b57',\n        sHshell: 'fff5ee',\n        siFna: 'a0522d',\n        silver: 'c0c0c0',\n        skyXe: '87ceeb',\n        UXe: '6a5acd',\n        UWay: '708090',\n        UgYy: '708090',\n        snow: 'fffafa',\n        sprRggYF: 'ff7f',\n        stAlXe: '4682b4',\n        tan: 'd2b48c',\n        teO: '8080',\n        tEstN: 'd8bfd8',\n        tomato: 'ff6347',\n        Qe: '40e0d0',\n        viTet: 'ee82ee',\n        JHt: 'f5deb3',\n        wEte: 'ffffff',\n        wEtesmoke: 'f5f5f5',\n        Lw: 'ffff00',\n        LwgYF: '9acd32'\n    };\n    function unpack() {\n        const unpacked = {};\n        const keys = Object.keys(names$1);\n        const tkeys = Object.keys(map$2);\n        let i, j, k, ok, nk;\n        for (i = 0; i < keys.length; i++) {\n            ok = nk = keys[i];\n            for (j = 0; j < tkeys.length; j++) {\n                k = tkeys[j];\n                nk = nk.replace(k, map$2[k]);\n            }\n            k = parseInt(names$1[ok], 16);\n            unpacked[nk] = [k >> 16 & 0xFF, k >> 8 & 0xFF, k & 0xFF];\n        }\n        return unpacked;\n    }\n    let names;\n    function nameParse(str) {\n        if (!names) {\n            names = unpack();\n            names.transparent = [0, 0, 0, 0];\n        }\n        const a = names[str.toLowerCase()];\n        return a && {\n            r: a[0],\n            g: a[1],\n            b: a[2],\n            a: a.length === 4 ? a[3] : 255\n        };\n    }\n    const RGB_RE = /^rgba?\\(\\s*([-+.\\d]+)(%)?[\\s,]+([-+.e\\d]+)(%)?[\\s,]+([-+.e\\d]+)(%)?(?:[\\s,/]+([-+.e\\d]+)(%)?)?\\s*\\)$/;\n    function rgbParse(str) {\n        const m = RGB_RE.exec(str);\n        let a = 255;\n        let r, g, b;\n        if (!m) {\n            return;\n        }\n        if (m[7] !== r) {\n            const v = +m[7];\n            a = m[8] ? p2b(v) : lim(v * 255, 0, 255);\n        }\n        r = +m[1];\n        g = +m[3];\n        b = +m[5];\n        r = 255 & (m[2] ? p2b(r) : lim(r, 0, 255));\n        g = 255 & (m[4] ? p2b(g) : lim(g, 0, 255));\n        b = 255 & (m[6] ? p2b(b) : lim(b, 0, 255));\n        return {\n            r: r,\n            g: g,\n            b: b,\n            a: a\n        };\n    }\n    function rgbString(v) {\n        return v && (\n            v.a < 255\n                ? `rgba(${v.r}, ${v.g}, ${v.b}, ${b2n(v.a)})`\n                : `rgb(${v.r}, ${v.g}, ${v.b})`\n        );\n    }\n    const to = v => v <= 0.0031308 ? v * 12.92 : Math.pow(v, 1.0 / 2.4) * 1.055 - 0.055;\n    const from = v => v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);\n    function interpolate$1(rgb1, rgb2, t) {\n        const r = from(b2n(rgb1.r));\n        const g = from(b2n(rgb1.g));\n        const b = from(b2n(rgb1.b));\n        return {\n            r: n2b(to(r + t * (from(b2n(rgb2.r)) - r))),\n            g: n2b(to(g + t * (from(b2n(rgb2.g)) - g))),\n            b: n2b(to(b + t * (from(b2n(rgb2.b)) - b))),\n            a: rgb1.a + t * (rgb2.a - rgb1.a)\n        };\n    }\n    function modHSL(v, i, ratio) {\n        if (v) {\n            let tmp = rgb2hsl(v);\n            tmp[i] = Math.max(0, Math.min(tmp[i] + tmp[i] * ratio, i === 0 ? 360 : 1));\n            tmp = hsl2rgb(tmp);\n            v.r = tmp[0];\n            v.g = tmp[1];\n            v.b = tmp[2];\n        }\n    }\n    function clone(v, proto) {\n        return v ? Object.assign(proto || {}, v) : v;\n    }\n    function fromObject(input) {\n        var v = {r: 0, g: 0, b: 0, a: 255};\n        if (Array.isArray(input)) {\n            if (input.length >= 3) {\n                v = {r: input[0], g: input[1], b: input[2], a: 255};\n                if (input.length > 3) {\n                    v.a = n2b(input[3]);\n                }\n            }\n        } else {\n            v = clone(input, {r: 0, g: 0, b: 0, a: 1});\n            v.a = n2b(v.a);\n        }\n        return v;\n    }\n    function functionParse(str) {\n        if (str.charAt(0) === 'r') {\n            return rgbParse(str);\n        }\n        return hueParse(str);\n    }\n    class Color {\n        constructor(input) {\n            if (input instanceof Color) {\n                return input;\n            }\n            const type = typeof input;\n            let v;\n            if (type === 'object') {\n                v = fromObject(input);\n            } else if (type === 'string') {\n                v = hexParse(input) || nameParse(input) || functionParse(input);\n            }\n            this._rgb = v;\n            this._valid = !!v;\n        }\n        get valid() {\n            return this._valid;\n        }\n        get rgb() {\n            var v = clone(this._rgb);\n            if (v) {\n                v.a = b2n(v.a);\n            }\n            return v;\n        }\n        set rgb(obj) {\n            this._rgb = fromObject(obj);\n        }\n        rgbString() {\n            return this._valid ? rgbString(this._rgb) : undefined;\n        }\n        hexString() {\n            return this._valid ? hexString(this._rgb) : undefined;\n        }\n        hslString() {\n            return this._valid ? hslString(this._rgb) : undefined;\n        }\n        mix(color, weight) {\n            if (color) {\n                const c1 = this.rgb;\n                const c2 = color.rgb;\n                let w2;\n                const p = weight === w2 ? 0.5 : weight;\n                const w = 2 * p - 1;\n                const a = c1.a - c2.a;\n                const w1 = ((w * a === -1 ? w : (w + a) / (1 + w * a)) + 1) / 2.0;\n                w2 = 1 - w1;\n                c1.r = 0xFF & w1 * c1.r + w2 * c2.r + 0.5;\n                c1.g = 0xFF & w1 * c1.g + w2 * c2.g + 0.5;\n                c1.b = 0xFF & w1 * c1.b + w2 * c2.b + 0.5;\n                c1.a = p * c1.a + (1 - p) * c2.a;\n                this.rgb = c1;\n            }\n            return this;\n        }\n        interpolate(color, t) {\n            if (color) {\n                this._rgb = interpolate$1(this._rgb, color._rgb, t);\n            }\n            return this;\n        }\n        clone() {\n            return new Color(this.rgb);\n        }\n        alpha(a) {\n            this._rgb.a = n2b(a);\n            return this;\n        }\n        clearer(ratio) {\n            const rgb = this._rgb;\n            rgb.a *= 1 - ratio;\n            return this;\n        }\n        greyscale() {\n            const rgb = this._rgb;\n            const val = round(rgb.r * 0.3 + rgb.g * 0.59 + rgb.b * 0.11);\n            rgb.r = rgb.g = rgb.b = val;\n            return this;\n        }\n        opaquer(ratio) {\n            const rgb = this._rgb;\n            rgb.a *= 1 + ratio;\n            return this;\n        }\n        negate() {\n            const v = this._rgb;\n            v.r = 255 - v.r;\n            v.g = 255 - v.g;\n            v.b = 255 - v.b;\n            return this;\n        }\n        lighten(ratio) {\n            modHSL(this._rgb, 2, ratio);\n            return this;\n        }\n        darken(ratio) {\n            modHSL(this._rgb, 2, -ratio);\n            return this;\n        }\n        saturate(ratio) {\n            modHSL(this._rgb, 1, ratio);\n            return this;\n        }\n        desaturate(ratio) {\n            modHSL(this._rgb, 1, -ratio);\n            return this;\n        }\n        rotate(deg) {\n            rotate(this._rgb, deg);\n            return this;\n        }\n    }\n\n    function isPatternOrGradient(value) {\n        if (value && typeof value === 'object') {\n            const type = value.toString();\n            return type === '[object CanvasPattern]' || type === '[object CanvasGradient]';\n        }\n        return false;\n    }\n    function color(value) {\n        return isPatternOrGradient(value) ? value : new Color(value);\n    }\n    function getHoverColor(value) {\n        return isPatternOrGradient(value) ? value : new Color(value).saturate(0.5).darken(0.1).hexString();\n    }\n\n    const numbers = [\n        'x',\n        'y',\n        'borderWidth',\n        'radius',\n        'tension'\n    ];\n    const colors = [\n        'color',\n        'borderColor',\n        'backgroundColor'\n    ];\n    function applyAnimationsDefaults(defaults) {\n        defaults.set('animation', {\n            delay: undefined,\n            duration: 1000,\n            easing: 'easeOutQuart',\n            fn: undefined,\n            from: undefined,\n            loop: undefined,\n            to: undefined,\n            type: undefined\n        });\n        defaults.describe('animation', {\n            _fallback: false,\n            _indexable: false,\n            _scriptable: (name)=>name !== 'onProgress' && name !== 'onComplete' && name !== 'fn'\n        });\n        defaults.set('animations', {\n            colors: {\n                type: 'color',\n                properties: colors\n            },\n            numbers: {\n                type: 'number',\n                properties: numbers\n            }\n        });\n        defaults.describe('animations', {\n            _fallback: 'animation'\n        });\n        defaults.set('transitions', {\n            active: {\n                animation: {\n                    duration: 400\n                }\n            },\n            resize: {\n                animation: {\n                    duration: 0\n                }\n            },\n            show: {\n                animations: {\n                    colors: {\n                        from: 'transparent'\n                    },\n                    visible: {\n                        type: 'boolean',\n                        duration: 0\n                    }\n                }\n            },\n            hide: {\n                animations: {\n                    colors: {\n                        to: 'transparent'\n                    },\n                    visible: {\n                        type: 'boolean',\n                        easing: 'linear',\n                        fn: (v)=>v | 0\n                    }\n                }\n            }\n        });\n    }\n\n    function applyLayoutsDefaults(defaults) {\n        defaults.set('layout', {\n            autoPadding: true,\n            padding: {\n                top: 0,\n                right: 0,\n                bottom: 0,\n                left: 0\n            }\n        });\n    }\n\n    const intlCache = new Map();\n    function getNumberFormat(locale, options) {\n        options = options || {};\n        const cacheKey = locale + JSON.stringify(options);\n        let formatter = intlCache.get(cacheKey);\n        if (!formatter) {\n            formatter = new Intl.NumberFormat(locale, options);\n            intlCache.set(cacheKey, formatter);\n        }\n        return formatter;\n    }\n    function formatNumber(num, locale, options) {\n        return getNumberFormat(locale, options).format(num);\n    }\n\n    const formatters = {\n        values (value) {\n            return isArray(value) ?  value : '' + value;\n        },\n        numeric (tickValue, index, ticks) {\n            if (tickValue === 0) {\n                return '0';\n            }\n            const locale = this.chart.options.locale;\n            let notation;\n            let delta = tickValue;\n            if (ticks.length > 1) {\n                const maxTick = Math.max(Math.abs(ticks[0].value), Math.abs(ticks[ticks.length - 1].value));\n                if (maxTick < 1e-4 || maxTick > 1e+15) {\n                    notation = 'scientific';\n                }\n                delta = calculateDelta(tickValue, ticks);\n            }\n            const logDelta = log10(Math.abs(delta));\n            const numDecimal = isNaN(logDelta) ? 1 : Math.max(Math.min(-1 * Math.floor(logDelta), 20), 0);\n            const options = {\n                notation,\n                minimumFractionDigits: numDecimal,\n                maximumFractionDigits: numDecimal\n            };\n            Object.assign(options, this.options.ticks.format);\n            return formatNumber(tickValue, locale, options);\n        },\n        logarithmic (tickValue, index, ticks) {\n            if (tickValue === 0) {\n                return '0';\n            }\n            const remain = ticks[index].significand || tickValue / Math.pow(10, Math.floor(log10(tickValue)));\n            if ([\n                1,\n                2,\n                3,\n                5,\n                10,\n                15\n            ].includes(remain) || index > 0.8 * ticks.length) {\n                return formatters.numeric.call(this, tickValue, index, ticks);\n            }\n            return '';\n        }\n    };\n    function calculateDelta(tickValue, ticks) {\n        let delta = ticks.length > 3 ? ticks[2].value - ticks[1].value : ticks[1].value - ticks[0].value;\n        if (Math.abs(delta) >= 1 && tickValue !== Math.floor(tickValue)) {\n            delta = tickValue - Math.floor(tickValue);\n        }\n        return delta;\n    }\n    var Ticks = {\n        formatters\n    };\n\n    function applyScaleDefaults(defaults) {\n        defaults.set('scale', {\n            display: true,\n            offset: false,\n            reverse: false,\n            beginAtZero: false,\n            bounds: 'ticks',\n            clip: true,\n            grace: 0,\n            grid: {\n                display: true,\n                lineWidth: 1,\n                drawOnChartArea: true,\n                drawTicks: true,\n                tickLength: 8,\n                tickWidth: (_ctx, options)=>options.lineWidth,\n                tickColor: (_ctx, options)=>options.color,\n                offset: false\n            },\n            border: {\n                display: true,\n                dash: [],\n                dashOffset: 0.0,\n                width: 1\n            },\n            title: {\n                display: false,\n                text: '',\n                padding: {\n                    top: 4,\n                    bottom: 4\n                }\n            },\n            ticks: {\n                minRotation: 0,\n                maxRotation: 50,\n                mirror: false,\n                textStrokeWidth: 0,\n                textStrokeColor: '',\n                padding: 3,\n                display: true,\n                autoSkip: true,\n                autoSkipPadding: 3,\n                labelOffset: 0,\n                callback: Ticks.formatters.values,\n                minor: {},\n                major: {},\n                align: 'center',\n                crossAlign: 'near',\n                showLabelBackdrop: false,\n                backdropColor: 'rgba(255, 255, 255, 0.75)',\n                backdropPadding: 2\n            }\n        });\n        defaults.route('scale.ticks', 'color', '', 'color');\n        defaults.route('scale.grid', 'color', '', 'borderColor');\n        defaults.route('scale.border', 'color', '', 'borderColor');\n        defaults.route('scale.title', 'color', '', 'color');\n        defaults.describe('scale', {\n            _fallback: false,\n            _scriptable: (name)=>!name.startsWith('before') && !name.startsWith('after') && name !== 'callback' && name !== 'parser',\n            _indexable: (name)=>name !== 'borderDash' && name !== 'tickBorderDash' && name !== 'dash'\n        });\n        defaults.describe('scales', {\n            _fallback: 'scale'\n        });\n        defaults.describe('scale.ticks', {\n            _scriptable: (name)=>name !== 'backdropPadding' && name !== 'callback',\n            _indexable: (name)=>name !== 'backdropPadding'\n        });\n    }\n\n    const overrides = Object.create(null);\n    const descriptors = Object.create(null);\n    function getScope$1(node, key) {\n        if (!key) {\n            return node;\n        }\n        const keys = key.split('.');\n        for(let i = 0, n = keys.length; i < n; ++i){\n            const k = keys[i];\n            node = node[k] || (node[k] = Object.create(null));\n        }\n        return node;\n    }\n    function set(root, scope, values) {\n        if (typeof scope === 'string') {\n            return merge(getScope$1(root, scope), values);\n        }\n        return merge(getScope$1(root, ''), scope);\n    }\n    class Defaults {\n        constructor(_descriptors, _appliers){\n            this.animation = undefined;\n            this.backgroundColor = 'rgba(0,0,0,0.1)';\n            this.borderColor = 'rgba(0,0,0,0.1)';\n            this.color = '#666';\n            this.datasets = {};\n            this.devicePixelRatio = (context)=>context.chart.platform.getDevicePixelRatio();\n            this.elements = {};\n            this.events = [\n                'mousemove',\n                'mouseout',\n                'click',\n                'touchstart',\n                'touchmove'\n            ];\n            this.font = {\n                family: \"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif\",\n                size: 12,\n                style: 'normal',\n                lineHeight: 1.2,\n                weight: null\n            };\n            this.hover = {};\n            this.hoverBackgroundColor = (ctx, options)=>getHoverColor(options.backgroundColor);\n            this.hoverBorderColor = (ctx, options)=>getHoverColor(options.borderColor);\n            this.hoverColor = (ctx, options)=>getHoverColor(options.color);\n            this.indexAxis = 'x';\n            this.interaction = {\n                mode: 'nearest',\n                intersect: true,\n                includeInvisible: false\n            };\n            this.maintainAspectRatio = true;\n            this.onHover = null;\n            this.onClick = null;\n            this.parsing = true;\n            this.plugins = {};\n            this.responsive = true;\n            this.scale = undefined;\n            this.scales = {};\n            this.showLine = true;\n            this.drawActiveElementsOnTop = true;\n            this.describe(_descriptors);\n            this.apply(_appliers);\n        }\n        set(scope, values) {\n            return set(this, scope, values);\n        }\n        get(scope) {\n            return getScope$1(this, scope);\n        }\n        describe(scope, values) {\n            return set(descriptors, scope, values);\n        }\n        override(scope, values) {\n            return set(overrides, scope, values);\n        }\n        route(scope, name, targetScope, targetName) {\n            const scopeObject = getScope$1(this, scope);\n            const targetScopeObject = getScope$1(this, targetScope);\n            const privateName = '_' + name;\n            Object.defineProperties(scopeObject, {\n                [privateName]: {\n                    value: scopeObject[name],\n                    writable: true\n                },\n                [name]: {\n                    enumerable: true,\n                    get () {\n                        const local = this[privateName];\n                        const target = targetScopeObject[targetName];\n                        if (isObject(local)) {\n                            return Object.assign({}, target, local);\n                        }\n                        return valueOrDefault(local, target);\n                    },\n                    set (value) {\n                        this[privateName] = value;\n                    }\n                }\n            });\n        }\n        apply(appliers) {\n            appliers.forEach((apply)=>apply(this));\n        }\n    }\n    var defaults = /* #__PURE__ */ new Defaults({\n        _scriptable: (name)=>!name.startsWith('on'),\n        _indexable: (name)=>name !== 'events',\n        hover: {\n            _fallback: 'interaction'\n        },\n        interaction: {\n            _scriptable: false,\n            _indexable: false\n        }\n    }, [\n        applyAnimationsDefaults,\n        applyLayoutsDefaults,\n        applyScaleDefaults\n    ]);\n\n    /**\n     * Note: typedefs are auto-exported, so use a made-up `dom` namespace where\n     * necessary to avoid duplicates with `export * from './helpers`; see\n     * https://github.com/microsoft/TypeScript/issues/46011\n     * @typedef { import('../core/core.controller.js').default } dom.Chart\n     * @typedef { import('../../types').ChartEvent } ChartEvent\n     */ /**\n     * @private\n     */ function _isDomSupported() {\n        return typeof window !== 'undefined' && typeof document !== 'undefined';\n    }\n    /**\n     * @private\n     */ function _getParentNode(domNode) {\n        let parent = domNode.parentNode;\n        if (parent && parent.toString() === '[object ShadowRoot]') {\n            parent = parent.host;\n        }\n        return parent;\n    }\n    /**\n     * convert max-width/max-height values that may be percentages into a number\n     * @private\n     */ function parseMaxStyle(styleValue, node, parentProperty) {\n        let valueInPixels;\n        if (typeof styleValue === 'string') {\n            valueInPixels = parseInt(styleValue, 10);\n            if (styleValue.indexOf('%') !== -1) {\n                // percentage * size in dimension\n                valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];\n            }\n        } else {\n            valueInPixels = styleValue;\n        }\n        return valueInPixels;\n    }\n    const getComputedStyle = (element)=>element.ownerDocument.defaultView.getComputedStyle(element, null);\n    function getStyle(el, property) {\n        return getComputedStyle(el).getPropertyValue(property);\n    }\n    const positions = [\n        'top',\n        'right',\n        'bottom',\n        'left'\n    ];\n    function getPositionedStyle(styles, style, suffix) {\n        const result = {};\n        suffix = suffix ? '-' + suffix : '';\n        for(let i = 0; i < 4; i++){\n            const pos = positions[i];\n            result[pos] = parseFloat(styles[style + '-' + pos + suffix]) || 0;\n        }\n        result.width = result.left + result.right;\n        result.height = result.top + result.bottom;\n        return result;\n    }\n    const useOffsetPos = (x, y, target)=>(x > 0 || y > 0) && (!target || !target.shadowRoot);\n    /**\n     * @param e\n     * @param canvas\n     * @returns Canvas position\n     */ function getCanvasPosition(e, canvas) {\n        const touches = e.touches;\n        const source = touches && touches.length ? touches[0] : e;\n        const { offsetX , offsetY  } = source;\n        let box = false;\n        let x, y;\n        if (useOffsetPos(offsetX, offsetY, e.target)) {\n            x = offsetX;\n            y = offsetY;\n        } else {\n            const rect = canvas.getBoundingClientRect();\n            x = source.clientX - rect.left;\n            y = source.clientY - rect.top;\n            box = true;\n        }\n        return {\n            x,\n            y,\n            box\n        };\n    }\n    /**\n     * Gets an event's x, y coordinates, relative to the chart area\n     * @param event\n     * @param chart\n     * @returns x and y coordinates of the event\n     */ function getRelativePosition(event, chart) {\n        if ('native' in event) {\n            return event;\n        }\n        const { canvas , currentDevicePixelRatio  } = chart;\n        const style = getComputedStyle(canvas);\n        const borderBox = style.boxSizing === 'border-box';\n        const paddings = getPositionedStyle(style, 'padding');\n        const borders = getPositionedStyle(style, 'border', 'width');\n        const { x , y , box  } = getCanvasPosition(event, canvas);\n        const xOffset = paddings.left + (box && borders.left);\n        const yOffset = paddings.top + (box && borders.top);\n        let { width , height  } = chart;\n        if (borderBox) {\n            width -= paddings.width + borders.width;\n            height -= paddings.height + borders.height;\n        }\n        return {\n            x: Math.round((x - xOffset) / width * canvas.width / currentDevicePixelRatio),\n            y: Math.round((y - yOffset) / height * canvas.height / currentDevicePixelRatio)\n        };\n    }\n    function getContainerSize(canvas, width, height) {\n        let maxWidth, maxHeight;\n        if (width === undefined || height === undefined) {\n            const container = _getParentNode(canvas);\n            if (!container) {\n                width = canvas.clientWidth;\n                height = canvas.clientHeight;\n            } else {\n                const rect = container.getBoundingClientRect(); // this is the border box of the container\n                const containerStyle = getComputedStyle(container);\n                const containerBorder = getPositionedStyle(containerStyle, 'border', 'width');\n                const containerPadding = getPositionedStyle(containerStyle, 'padding');\n                width = rect.width - containerPadding.width - containerBorder.width;\n                height = rect.height - containerPadding.height - containerBorder.height;\n                maxWidth = parseMaxStyle(containerStyle.maxWidth, container, 'clientWidth');\n                maxHeight = parseMaxStyle(containerStyle.maxHeight, container, 'clientHeight');\n            }\n        }\n        return {\n            width,\n            height,\n            maxWidth: maxWidth || INFINITY,\n            maxHeight: maxHeight || INFINITY\n        };\n    }\n    const round1 = (v)=>Math.round(v * 10) / 10;\n    // eslint-disable-next-line complexity\n    function getMaximumSize(canvas, bbWidth, bbHeight, aspectRatio) {\n        const style = getComputedStyle(canvas);\n        const margins = getPositionedStyle(style, 'margin');\n        const maxWidth = parseMaxStyle(style.maxWidth, canvas, 'clientWidth') || INFINITY;\n        const maxHeight = parseMaxStyle(style.maxHeight, canvas, 'clientHeight') || INFINITY;\n        const containerSize = getContainerSize(canvas, bbWidth, bbHeight);\n        let { width , height  } = containerSize;\n        if (style.boxSizing === 'content-box') {\n            const borders = getPositionedStyle(style, 'border', 'width');\n            const paddings = getPositionedStyle(style, 'padding');\n            width -= paddings.width + borders.width;\n            height -= paddings.height + borders.height;\n        }\n        width = Math.max(0, width - margins.width);\n        height = Math.max(0, aspectRatio ? width / aspectRatio : height - margins.height);\n        width = round1(Math.min(width, maxWidth, containerSize.maxWidth));\n        height = round1(Math.min(height, maxHeight, containerSize.maxHeight));\n        if (width && !height) {\n            // https://github.com/chartjs/Chart.js/issues/4659\n            // If the canvas has width, but no height, default to aspectRatio of 2 (canvas default)\n            height = round1(width / 2);\n        }\n        const maintainHeight = bbWidth !== undefined || bbHeight !== undefined;\n        if (maintainHeight && aspectRatio && containerSize.height && height > containerSize.height) {\n            height = containerSize.height;\n            width = round1(Math.floor(height * aspectRatio));\n        }\n        return {\n            width,\n            height\n        };\n    }\n    /**\n     * @param chart\n     * @param forceRatio\n     * @param forceStyle\n     * @returns True if the canvas context size or transformation has changed.\n     */ function retinaScale(chart, forceRatio, forceStyle) {\n        const pixelRatio = forceRatio || 1;\n        const deviceHeight = Math.floor(chart.height * pixelRatio);\n        const deviceWidth = Math.floor(chart.width * pixelRatio);\n        chart.height = Math.floor(chart.height);\n        chart.width = Math.floor(chart.width);\n        const canvas = chart.canvas;\n        // If no style has been set on the canvas, the render size is used as display size,\n        // making the chart visually bigger, so let's enforce it to the \"correct\" values.\n        // See https://github.com/chartjs/Chart.js/issues/3575\n        if (canvas.style && (forceStyle || !canvas.style.height && !canvas.style.width)) {\n            canvas.style.height = `${chart.height}px`;\n            canvas.style.width = `${chart.width}px`;\n        }\n        if (chart.currentDevicePixelRatio !== pixelRatio || canvas.height !== deviceHeight || canvas.width !== deviceWidth) {\n            chart.currentDevicePixelRatio = pixelRatio;\n            canvas.height = deviceHeight;\n            canvas.width = deviceWidth;\n            chart.ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);\n            return true;\n        }\n        return false;\n    }\n    /**\n     * Detects support for options object argument in addEventListener.\n     * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support\n     * @private\n     */ const supportsEventListenerOptions = function() {\n        let passiveSupported = false;\n        try {\n            const options = {\n                get passive () {\n                    passiveSupported = true;\n                    return false;\n                }\n            };\n            if (_isDomSupported()) {\n                window.addEventListener('test', null, options);\n                window.removeEventListener('test', null, options);\n            }\n        } catch (e) {\n            // continue regardless of error\n        }\n        return passiveSupported;\n    }();\n    /**\n     * The \"used\" size is the final value of a dimension property after all calculations have\n     * been performed. This method uses the computed style of `element` but returns undefined\n     * if the computed style is not expressed in pixels. That can happen in some cases where\n     * `element` has a size relative to its parent and this last one is not yet displayed,\n     * for example because of `display: none` on a parent node.\n     * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value\n     * @returns Size in pixels or undefined if unknown.\n     */ function readUsedSize(element, property) {\n        const value = getStyle(element, property);\n        const matches = value && value.match(/^(\\d+)(\\.\\d+)?px$/);\n        return matches ? +matches[1] : undefined;\n    }\n\n    /**\n     * Converts the given font object into a CSS font string.\n     * @param font - A font object.\n     * @return The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font\n     * @private\n     */ function toFontString(font) {\n        if (!font || isNullOrUndef(font.size) || isNullOrUndef(font.family)) {\n            return null;\n        }\n        return (font.style ? font.style + ' ' : '') + (font.weight ? font.weight + ' ' : '') + font.size + 'px ' + font.family;\n    }\n    /**\n     * @private\n     */ function _measureText(ctx, data, gc, longest, string) {\n        let textWidth = data[string];\n        if (!textWidth) {\n            textWidth = data[string] = ctx.measureText(string).width;\n            gc.push(string);\n        }\n        if (textWidth > longest) {\n            longest = textWidth;\n        }\n        return longest;\n    }\n    /**\n     * @private\n     */ // eslint-disable-next-line complexity\n    function _longestText(ctx, font, arrayOfThings, cache) {\n        cache = cache || {};\n        let data = cache.data = cache.data || {};\n        let gc = cache.garbageCollect = cache.garbageCollect || [];\n        if (cache.font !== font) {\n            data = cache.data = {};\n            gc = cache.garbageCollect = [];\n            cache.font = font;\n        }\n        ctx.save();\n        ctx.font = font;\n        let longest = 0;\n        const ilen = arrayOfThings.length;\n        let i, j, jlen, thing, nestedThing;\n        for(i = 0; i < ilen; i++){\n            thing = arrayOfThings[i];\n            // Undefined strings and arrays should not be measured\n            if (thing !== undefined && thing !== null && !isArray(thing)) {\n                longest = _measureText(ctx, data, gc, longest, thing);\n            } else if (isArray(thing)) {\n                // if it is an array lets measure each element\n                // to do maybe simplify this function a bit so we can do this more recursively?\n                for(j = 0, jlen = thing.length; j < jlen; j++){\n                    nestedThing = thing[j];\n                    // Undefined strings and arrays should not be measured\n                    if (nestedThing !== undefined && nestedThing !== null && !isArray(nestedThing)) {\n                        longest = _measureText(ctx, data, gc, longest, nestedThing);\n                    }\n                }\n            }\n        }\n        ctx.restore();\n        const gcLen = gc.length / 2;\n        if (gcLen > arrayOfThings.length) {\n            for(i = 0; i < gcLen; i++){\n                delete data[gc[i]];\n            }\n            gc.splice(0, gcLen);\n        }\n        return longest;\n    }\n    /**\n     * Returns the aligned pixel value to avoid anti-aliasing blur\n     * @param chart - The chart instance.\n     * @param pixel - A pixel value.\n     * @param width - The width of the element.\n     * @returns The aligned pixel value.\n     * @private\n     */ function _alignPixel(chart, pixel, width) {\n        const devicePixelRatio = chart.currentDevicePixelRatio;\n        const halfWidth = width !== 0 ? Math.max(width / 2, 0.5) : 0;\n        return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth;\n    }\n    /**\n     * Clears the entire canvas.\n     */ function clearCanvas(canvas, ctx) {\n        ctx = ctx || canvas.getContext('2d');\n        ctx.save();\n        // canvas.width and canvas.height do not consider the canvas transform,\n        // while clearRect does\n        ctx.resetTransform();\n        ctx.clearRect(0, 0, canvas.width, canvas.height);\n        ctx.restore();\n    }\n    function drawPoint(ctx, options, x, y) {\n        // eslint-disable-next-line @typescript-eslint/no-use-before-define\n        drawPointLegend(ctx, options, x, y, null);\n    }\n    // eslint-disable-next-line complexity\n    function drawPointLegend(ctx, options, x, y, w) {\n        let type, xOffset, yOffset, size, cornerRadius, width, xOffsetW, yOffsetW;\n        const style = options.pointStyle;\n        const rotation = options.rotation;\n        const radius = options.radius;\n        let rad = (rotation || 0) * RAD_PER_DEG;\n        if (style && typeof style === 'object') {\n            type = style.toString();\n            if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {\n                ctx.save();\n                ctx.translate(x, y);\n                ctx.rotate(rad);\n                ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height);\n                ctx.restore();\n                return;\n            }\n        }\n        if (isNaN(radius) || radius <= 0) {\n            return;\n        }\n        ctx.beginPath();\n        switch(style){\n            // Default includes circle\n            default:\n                if (w) {\n                    ctx.ellipse(x, y, w / 2, radius, 0, 0, TAU);\n                } else {\n                    ctx.arc(x, y, radius, 0, TAU);\n                }\n                ctx.closePath();\n                break;\n            case 'triangle':\n                width = w ? w / 2 : radius;\n                ctx.moveTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);\n                rad += TWO_THIRDS_PI;\n                ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);\n                rad += TWO_THIRDS_PI;\n                ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);\n                ctx.closePath();\n                break;\n            case 'rectRounded':\n                // NOTE: the rounded rect implementation changed to use `arc` instead of\n                // `quadraticCurveTo` since it generates better results when rect is\n                // almost a circle. 0.516 (instead of 0.5) produces results with visually\n                // closer proportion to the previous impl and it is inscribed in the\n                // circle with `radius`. For more details, see the following PRs:\n                // https://github.com/chartjs/Chart.js/issues/5597\n                // https://github.com/chartjs/Chart.js/issues/5858\n                cornerRadius = radius * 0.516;\n                size = radius - cornerRadius;\n                xOffset = Math.cos(rad + QUARTER_PI) * size;\n                xOffsetW = Math.cos(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);\n                yOffset = Math.sin(rad + QUARTER_PI) * size;\n                yOffsetW = Math.sin(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);\n                ctx.arc(x - xOffsetW, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);\n                ctx.arc(x + yOffsetW, y - xOffset, cornerRadius, rad - HALF_PI, rad);\n                ctx.arc(x + xOffsetW, y + yOffset, cornerRadius, rad, rad + HALF_PI);\n                ctx.arc(x - yOffsetW, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);\n                ctx.closePath();\n                break;\n            case 'rect':\n                if (!rotation) {\n                    size = Math.SQRT1_2 * radius;\n                    width = w ? w / 2 : size;\n                    ctx.rect(x - width, y - size, 2 * width, 2 * size);\n                    break;\n                }\n                rad += QUARTER_PI;\n            /* falls through */ case 'rectRot':\n                xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n                xOffset = Math.cos(rad) * radius;\n                yOffset = Math.sin(rad) * radius;\n                yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n                ctx.moveTo(x - xOffsetW, y - yOffset);\n                ctx.lineTo(x + yOffsetW, y - xOffset);\n                ctx.lineTo(x + xOffsetW, y + yOffset);\n                ctx.lineTo(x - yOffsetW, y + xOffset);\n                ctx.closePath();\n                break;\n            case 'crossRot':\n                rad += QUARTER_PI;\n            /* falls through */ case 'cross':\n                xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n                xOffset = Math.cos(rad) * radius;\n                yOffset = Math.sin(rad) * radius;\n                yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n                ctx.moveTo(x - xOffsetW, y - yOffset);\n                ctx.lineTo(x + xOffsetW, y + yOffset);\n                ctx.moveTo(x + yOffsetW, y - xOffset);\n                ctx.lineTo(x - yOffsetW, y + xOffset);\n                break;\n            case 'star':\n                xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n                xOffset = Math.cos(rad) * radius;\n                yOffset = Math.sin(rad) * radius;\n                yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n                ctx.moveTo(x - xOffsetW, y - yOffset);\n                ctx.lineTo(x + xOffsetW, y + yOffset);\n                ctx.moveTo(x + yOffsetW, y - xOffset);\n                ctx.lineTo(x - yOffsetW, y + xOffset);\n                rad += QUARTER_PI;\n                xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);\n                xOffset = Math.cos(rad) * radius;\n                yOffset = Math.sin(rad) * radius;\n                yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);\n                ctx.moveTo(x - xOffsetW, y - yOffset);\n                ctx.lineTo(x + xOffsetW, y + yOffset);\n                ctx.moveTo(x + yOffsetW, y - xOffset);\n                ctx.lineTo(x - yOffsetW, y + xOffset);\n                break;\n            case 'line':\n                xOffset = w ? w / 2 : Math.cos(rad) * radius;\n                yOffset = Math.sin(rad) * radius;\n                ctx.moveTo(x - xOffset, y - yOffset);\n                ctx.lineTo(x + xOffset, y + yOffset);\n                break;\n            case 'dash':\n                ctx.moveTo(x, y);\n                ctx.lineTo(x + Math.cos(rad) * (w ? w / 2 : radius), y + Math.sin(rad) * radius);\n                break;\n            case false:\n                ctx.closePath();\n                break;\n        }\n        ctx.fill();\n        if (options.borderWidth > 0) {\n            ctx.stroke();\n        }\n    }\n    /**\n     * Returns true if the point is inside the rectangle\n     * @param point - The point to test\n     * @param area - The rectangle\n     * @param margin - allowed margin\n     * @private\n     */ function _isPointInArea(point, area, margin) {\n        margin = margin || 0.5; // margin - default is to match rounded decimals\n        return !area || point && point.x > area.left - margin && point.x < area.right + margin && point.y > area.top - margin && point.y < area.bottom + margin;\n    }\n    function clipArea(ctx, area) {\n        ctx.save();\n        ctx.beginPath();\n        ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);\n        ctx.clip();\n    }\n    function unclipArea(ctx) {\n        ctx.restore();\n    }\n    /**\n     * @private\n     */ function _steppedLineTo(ctx, previous, target, flip, mode) {\n        if (!previous) {\n            return ctx.lineTo(target.x, target.y);\n        }\n        if (mode === 'middle') {\n            const midpoint = (previous.x + target.x) / 2.0;\n            ctx.lineTo(midpoint, previous.y);\n            ctx.lineTo(midpoint, target.y);\n        } else if (mode === 'after' !== !!flip) {\n            ctx.lineTo(previous.x, target.y);\n        } else {\n            ctx.lineTo(target.x, previous.y);\n        }\n        ctx.lineTo(target.x, target.y);\n    }\n    /**\n     * @private\n     */ function _bezierCurveTo(ctx, previous, target, flip) {\n        if (!previous) {\n            return ctx.lineTo(target.x, target.y);\n        }\n        ctx.bezierCurveTo(flip ? previous.cp1x : previous.cp2x, flip ? previous.cp1y : previous.cp2y, flip ? target.cp2x : target.cp1x, flip ? target.cp2y : target.cp1y, target.x, target.y);\n    }\n    function setRenderOpts(ctx, opts) {\n        if (opts.translation) {\n            ctx.translate(opts.translation[0], opts.translation[1]);\n        }\n        if (!isNullOrUndef(opts.rotation)) {\n            ctx.rotate(opts.rotation);\n        }\n        if (opts.color) {\n            ctx.fillStyle = opts.color;\n        }\n        if (opts.textAlign) {\n            ctx.textAlign = opts.textAlign;\n        }\n        if (opts.textBaseline) {\n            ctx.textBaseline = opts.textBaseline;\n        }\n    }\n    function decorateText(ctx, x, y, line, opts) {\n        if (opts.strikethrough || opts.underline) {\n            /**\n             * Now that IE11 support has been dropped, we can use more\n             * of the TextMetrics object. The actual bounding boxes\n             * are unflagged in Chrome, Firefox, Edge, and Safari so they\n             * can be safely used.\n             * See https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics#Browser_compatibility\n             */ const metrics = ctx.measureText(line);\n            const left = x - metrics.actualBoundingBoxLeft;\n            const right = x + metrics.actualBoundingBoxRight;\n            const top = y - metrics.actualBoundingBoxAscent;\n            const bottom = y + metrics.actualBoundingBoxDescent;\n            const yDecoration = opts.strikethrough ? (top + bottom) / 2 : bottom;\n            ctx.strokeStyle = ctx.fillStyle;\n            ctx.beginPath();\n            ctx.lineWidth = opts.decorationWidth || 2;\n            ctx.moveTo(left, yDecoration);\n            ctx.lineTo(right, yDecoration);\n            ctx.stroke();\n        }\n    }\n    function drawBackdrop(ctx, opts) {\n        const oldColor = ctx.fillStyle;\n        ctx.fillStyle = opts.color;\n        ctx.fillRect(opts.left, opts.top, opts.width, opts.height);\n        ctx.fillStyle = oldColor;\n    }\n    /**\n     * Render text onto the canvas\n     */ function renderText(ctx, text, x, y, font, opts = {}) {\n        const lines = isArray(text) ? text : [\n            text\n        ];\n        const stroke = opts.strokeWidth > 0 && opts.strokeColor !== '';\n        let i, line;\n        ctx.save();\n        ctx.font = font.string;\n        setRenderOpts(ctx, opts);\n        for(i = 0; i < lines.length; ++i){\n            line = lines[i];\n            if (opts.backdrop) {\n                drawBackdrop(ctx, opts.backdrop);\n            }\n            if (stroke) {\n                if (opts.strokeColor) {\n                    ctx.strokeStyle = opts.strokeColor;\n                }\n                if (!isNullOrUndef(opts.strokeWidth)) {\n                    ctx.lineWidth = opts.strokeWidth;\n                }\n                ctx.strokeText(line, x, y, opts.maxWidth);\n            }\n            ctx.fillText(line, x, y, opts.maxWidth);\n            decorateText(ctx, x, y, line, opts);\n            y += Number(font.lineHeight);\n        }\n        ctx.restore();\n    }\n    /**\n     * Add a path of a rectangle with rounded corners to the current sub-path\n     * @param ctx - Context\n     * @param rect - Bounding rect\n     */ function addRoundedRectPath(ctx, rect) {\n        const { x , y , w , h , radius  } = rect;\n        // top left arc\n        ctx.arc(x + radius.topLeft, y + radius.topLeft, radius.topLeft, 1.5 * PI, PI, true);\n        // line from top left to bottom left\n        ctx.lineTo(x, y + h - radius.bottomLeft);\n        // bottom left arc\n        ctx.arc(x + radius.bottomLeft, y + h - radius.bottomLeft, radius.bottomLeft, PI, HALF_PI, true);\n        // line from bottom left to bottom right\n        ctx.lineTo(x + w - radius.bottomRight, y + h);\n        // bottom right arc\n        ctx.arc(x + w - radius.bottomRight, y + h - radius.bottomRight, radius.bottomRight, HALF_PI, 0, true);\n        // line from bottom right to top right\n        ctx.lineTo(x + w, y + radius.topRight);\n        // top right arc\n        ctx.arc(x + w - radius.topRight, y + radius.topRight, radius.topRight, 0, -HALF_PI, true);\n        // line from top right to top left\n        ctx.lineTo(x + radius.topLeft, y);\n    }\n\n    /**\n     * Creates a Proxy for resolving raw values for options.\n     * @param scopes - The option scopes to look for values, in resolution order\n     * @param prefixes - The prefixes for values, in resolution order.\n     * @param rootScopes - The root option scopes\n     * @param fallback - Parent scopes fallback\n     * @param getTarget - callback for getting the target for changed values\n     * @returns Proxy\n     * @private\n     */ function _createResolver(scopes, prefixes = [\n        ''\n    ], rootScopes, fallback, getTarget = ()=>scopes[0]) {\n        const finalRootScopes = rootScopes || scopes;\n        if (typeof fallback === 'undefined') {\n            fallback = _resolve('_fallback', scopes);\n        }\n        const cache = {\n            [Symbol.toStringTag]: 'Object',\n            _cacheable: true,\n            _scopes: scopes,\n            _rootScopes: finalRootScopes,\n            _fallback: fallback,\n            _getTarget: getTarget,\n            override: (scope)=>_createResolver([\n                scope,\n                ...scopes\n            ], prefixes, finalRootScopes, fallback)\n        };\n        return new Proxy(cache, {\n            /**\n             * A trap for the delete operator.\n             */ deleteProperty (target, prop) {\n                delete target[prop]; // remove from cache\n                delete target._keys; // remove cached keys\n                delete scopes[0][prop]; // remove from top level scope\n                return true;\n            },\n            /**\n             * A trap for getting property values.\n             */ get (target, prop) {\n                return _cached(target, prop, ()=>_resolveWithPrefixes(prop, prefixes, scopes, target));\n            },\n            /**\n             * A trap for Object.getOwnPropertyDescriptor.\n             * Also used by Object.hasOwnProperty.\n             */ getOwnPropertyDescriptor (target, prop) {\n                return Reflect.getOwnPropertyDescriptor(target._scopes[0], prop);\n            },\n            /**\n             * A trap for Object.getPrototypeOf.\n             */ getPrototypeOf () {\n                return Reflect.getPrototypeOf(scopes[0]);\n            },\n            /**\n             * A trap for the in operator.\n             */ has (target, prop) {\n                return getKeysFromAllScopes(target).includes(prop);\n            },\n            /**\n             * A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols.\n             */ ownKeys (target) {\n                return getKeysFromAllScopes(target);\n            },\n            /**\n             * A trap for setting property values.\n             */ set (target, prop, value) {\n                const storage = target._storage || (target._storage = getTarget());\n                target[prop] = storage[prop] = value; // set to top level scope + cache\n                delete target._keys; // remove cached keys\n                return true;\n            }\n        });\n    }\n    /**\n     * Returns an Proxy for resolving option values with context.\n     * @param proxy - The Proxy returned by `_createResolver`\n     * @param context - Context object for scriptable/indexable options\n     * @param subProxy - The proxy provided for scriptable options\n     * @param descriptorDefaults - Defaults for descriptors\n     * @private\n     */ function _attachContext(proxy, context, subProxy, descriptorDefaults) {\n        const cache = {\n            _cacheable: false,\n            _proxy: proxy,\n            _context: context,\n            _subProxy: subProxy,\n            _stack: new Set(),\n            _descriptors: _descriptors(proxy, descriptorDefaults),\n            setContext: (ctx)=>_attachContext(proxy, ctx, subProxy, descriptorDefaults),\n            override: (scope)=>_attachContext(proxy.override(scope), context, subProxy, descriptorDefaults)\n        };\n        return new Proxy(cache, {\n            /**\n             * A trap for the delete operator.\n             */ deleteProperty (target, prop) {\n                delete target[prop]; // remove from cache\n                delete proxy[prop]; // remove from proxy\n                return true;\n            },\n            /**\n             * A trap for getting property values.\n             */ get (target, prop, receiver) {\n                return _cached(target, prop, ()=>_resolveWithContext(target, prop, receiver));\n            },\n            /**\n             * A trap for Object.getOwnPropertyDescriptor.\n             * Also used by Object.hasOwnProperty.\n             */ getOwnPropertyDescriptor (target, prop) {\n                return target._descriptors.allKeys ? Reflect.has(proxy, prop) ? {\n                    enumerable: true,\n                    configurable: true\n                } : undefined : Reflect.getOwnPropertyDescriptor(proxy, prop);\n            },\n            /**\n             * A trap for Object.getPrototypeOf.\n             */ getPrototypeOf () {\n                return Reflect.getPrototypeOf(proxy);\n            },\n            /**\n             * A trap for the in operator.\n             */ has (target, prop) {\n                return Reflect.has(proxy, prop);\n            },\n            /**\n             * A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols.\n             */ ownKeys () {\n                return Reflect.ownKeys(proxy);\n            },\n            /**\n             * A trap for setting property values.\n             */ set (target, prop, value) {\n                proxy[prop] = value; // set to proxy\n                delete target[prop]; // remove from cache\n                return true;\n            }\n        });\n    }\n    /**\n     * @private\n     */ function _descriptors(proxy, defaults = {\n        scriptable: true,\n        indexable: true\n    }) {\n        const { _scriptable =defaults.scriptable , _indexable =defaults.indexable , _allKeys =defaults.allKeys  } = proxy;\n        return {\n            allKeys: _allKeys,\n            scriptable: _scriptable,\n            indexable: _indexable,\n            isScriptable: isFunction(_scriptable) ? _scriptable : ()=>_scriptable,\n            isIndexable: isFunction(_indexable) ? _indexable : ()=>_indexable\n        };\n    }\n    const readKey = (prefix, name)=>prefix ? prefix + _capitalize(name) : name;\n    const needsSubResolver = (prop, value)=>isObject(value) && prop !== 'adapters' && (Object.getPrototypeOf(value) === null || value.constructor === Object);\n    function _cached(target, prop, resolve) {\n        if (Object.prototype.hasOwnProperty.call(target, prop)) {\n            return target[prop];\n        }\n        const value = resolve();\n        // cache the resolved value\n        target[prop] = value;\n        return value;\n    }\n    function _resolveWithContext(target, prop, receiver) {\n        const { _proxy , _context , _subProxy , _descriptors: descriptors  } = target;\n        let value = _proxy[prop]; // resolve from proxy\n        // resolve with context\n        if (isFunction(value) && descriptors.isScriptable(prop)) {\n            value = _resolveScriptable(prop, value, target, receiver);\n        }\n        if (isArray(value) && value.length) {\n            value = _resolveArray(prop, value, target, descriptors.isIndexable);\n        }\n        if (needsSubResolver(prop, value)) {\n            // if the resolved value is an object, create a sub resolver for it\n            value = _attachContext(value, _context, _subProxy && _subProxy[prop], descriptors);\n        }\n        return value;\n    }\n    function _resolveScriptable(prop, getValue, target, receiver) {\n        const { _proxy , _context , _subProxy , _stack  } = target;\n        if (_stack.has(prop)) {\n            throw new Error('Recursion detected: ' + Array.from(_stack).join('->') + '->' + prop);\n        }\n        _stack.add(prop);\n        let value = getValue(_context, _subProxy || receiver);\n        _stack.delete(prop);\n        if (needsSubResolver(prop, value)) {\n            // When scriptable option returns an object, create a resolver on that.\n            value = createSubResolver(_proxy._scopes, _proxy, prop, value);\n        }\n        return value;\n    }\n    function _resolveArray(prop, value, target, isIndexable) {\n        const { _proxy , _context , _subProxy , _descriptors: descriptors  } = target;\n        if (typeof _context.index !== 'undefined' && isIndexable(prop)) {\n            return value[_context.index % value.length];\n        } else if (isObject(value[0])) {\n            // Array of objects, return array or resolvers\n            const arr = value;\n            const scopes = _proxy._scopes.filter((s)=>s !== arr);\n            value = [];\n            for (const item of arr){\n                const resolver = createSubResolver(scopes, _proxy, prop, item);\n                value.push(_attachContext(resolver, _context, _subProxy && _subProxy[prop], descriptors));\n            }\n        }\n        return value;\n    }\n    function resolveFallback(fallback, prop, value) {\n        return isFunction(fallback) ? fallback(prop, value) : fallback;\n    }\n    const getScope = (key, parent)=>key === true ? parent : typeof key === 'string' ? resolveObjectKey(parent, key) : undefined;\n    function addScopes(set, parentScopes, key, parentFallback, value) {\n        for (const parent of parentScopes){\n            const scope = getScope(key, parent);\n            if (scope) {\n                set.add(scope);\n                const fallback = resolveFallback(scope._fallback, key, value);\n                if (typeof fallback !== 'undefined' && fallback !== key && fallback !== parentFallback) {\n                    // When we reach the descriptor that defines a new _fallback, return that.\n                    // The fallback will resume to that new scope.\n                    return fallback;\n                }\n            } else if (scope === false && typeof parentFallback !== 'undefined' && key !== parentFallback) {\n                // Fallback to `false` results to `false`, when falling back to different key.\n                // For example `interaction` from `hover` or `plugins.tooltip` and `animation` from `animations`\n                return null;\n            }\n        }\n        return false;\n    }\n    function createSubResolver(parentScopes, resolver, prop, value) {\n        const rootScopes = resolver._rootScopes;\n        const fallback = resolveFallback(resolver._fallback, prop, value);\n        const allScopes = [\n            ...parentScopes,\n            ...rootScopes\n        ];\n        const set = new Set();\n        set.add(value);\n        let key = addScopesFromKey(set, allScopes, prop, fallback || prop, value);\n        if (key === null) {\n            return false;\n        }\n        if (typeof fallback !== 'undefined' && fallback !== prop) {\n            key = addScopesFromKey(set, allScopes, fallback, key, value);\n            if (key === null) {\n                return false;\n            }\n        }\n        return _createResolver(Array.from(set), [\n            ''\n        ], rootScopes, fallback, ()=>subGetTarget(resolver, prop, value));\n    }\n    function addScopesFromKey(set, allScopes, key, fallback, item) {\n        while(key){\n            key = addScopes(set, allScopes, key, fallback, item);\n        }\n        return key;\n    }\n    function subGetTarget(resolver, prop, value) {\n        const parent = resolver._getTarget();\n        if (!(prop in parent)) {\n            parent[prop] = {};\n        }\n        const target = parent[prop];\n        if (isArray(target) && isObject(value)) {\n            // For array of objects, the object is used to store updated values\n            return value;\n        }\n        return target || {};\n    }\n    function _resolveWithPrefixes(prop, prefixes, scopes, proxy) {\n        let value;\n        for (const prefix of prefixes){\n            value = _resolve(readKey(prefix, prop), scopes);\n            if (typeof value !== 'undefined') {\n                return needsSubResolver(prop, value) ? createSubResolver(scopes, proxy, prop, value) : value;\n            }\n        }\n    }\n    function _resolve(key, scopes) {\n        for (const scope of scopes){\n            if (!scope) {\n                continue;\n            }\n            const value = scope[key];\n            if (typeof value !== 'undefined') {\n                return value;\n            }\n        }\n    }\n    function getKeysFromAllScopes(target) {\n        let keys = target._keys;\n        if (!keys) {\n            keys = target._keys = resolveKeysFromAllScopes(target._scopes);\n        }\n        return keys;\n    }\n    function resolveKeysFromAllScopes(scopes) {\n        const set = new Set();\n        for (const scope of scopes){\n            for (const key of Object.keys(scope).filter((k)=>!k.startsWith('_'))){\n                set.add(key);\n            }\n        }\n        return Array.from(set);\n    }\n    function _parseObjectDataRadialScale(meta, data, start, count) {\n        const { iScale  } = meta;\n        const { key ='r'  } = this._parsing;\n        const parsed = new Array(count);\n        let i, ilen, index, item;\n        for(i = 0, ilen = count; i < ilen; ++i){\n            index = i + start;\n            item = data[index];\n            parsed[i] = {\n                r: iScale.parse(resolveObjectKey(item, key), index)\n            };\n        }\n        return parsed;\n    }\n\n    const EPSILON = Number.EPSILON || 1e-14;\n    const getPoint = (points, i)=>i < points.length && !points[i].skip && points[i];\n    const getValueAxis = (indexAxis)=>indexAxis === 'x' ? 'y' : 'x';\n    function splineCurve(firstPoint, middlePoint, afterPoint, t) {\n        // Props to Rob Spencer at scaled innovation for his post on splining between points\n        // http://scaledinnovation.com/analytics/splines/aboutSplines.html\n        // This function must also respect \"skipped\" points\n        const previous = firstPoint.skip ? middlePoint : firstPoint;\n        const current = middlePoint;\n        const next = afterPoint.skip ? middlePoint : afterPoint;\n        const d01 = distanceBetweenPoints(current, previous);\n        const d12 = distanceBetweenPoints(next, current);\n        let s01 = d01 / (d01 + d12);\n        let s12 = d12 / (d01 + d12);\n        // If all points are the same, s01 & s02 will be inf\n        s01 = isNaN(s01) ? 0 : s01;\n        s12 = isNaN(s12) ? 0 : s12;\n        const fa = t * s01; // scaling factor for triangle Ta\n        const fb = t * s12;\n        return {\n            previous: {\n                x: current.x - fa * (next.x - previous.x),\n                y: current.y - fa * (next.y - previous.y)\n            },\n            next: {\n                x: current.x + fb * (next.x - previous.x),\n                y: current.y + fb * (next.y - previous.y)\n            }\n        };\n    }\n    /**\n     * Adjust tangents to ensure monotonic properties\n     */ function monotoneAdjust(points, deltaK, mK) {\n        const pointsLen = points.length;\n        let alphaK, betaK, tauK, squaredMagnitude, pointCurrent;\n        let pointAfter = getPoint(points, 0);\n        for(let i = 0; i < pointsLen - 1; ++i){\n            pointCurrent = pointAfter;\n            pointAfter = getPoint(points, i + 1);\n            if (!pointCurrent || !pointAfter) {\n                continue;\n            }\n            if (almostEquals(deltaK[i], 0, EPSILON)) {\n                mK[i] = mK[i + 1] = 0;\n                continue;\n            }\n            alphaK = mK[i] / deltaK[i];\n            betaK = mK[i + 1] / deltaK[i];\n            squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);\n            if (squaredMagnitude <= 9) {\n                continue;\n            }\n            tauK = 3 / Math.sqrt(squaredMagnitude);\n            mK[i] = alphaK * tauK * deltaK[i];\n            mK[i + 1] = betaK * tauK * deltaK[i];\n        }\n    }\n    function monotoneCompute(points, mK, indexAxis = 'x') {\n        const valueAxis = getValueAxis(indexAxis);\n        const pointsLen = points.length;\n        let delta, pointBefore, pointCurrent;\n        let pointAfter = getPoint(points, 0);\n        for(let i = 0; i < pointsLen; ++i){\n            pointBefore = pointCurrent;\n            pointCurrent = pointAfter;\n            pointAfter = getPoint(points, i + 1);\n            if (!pointCurrent) {\n                continue;\n            }\n            const iPixel = pointCurrent[indexAxis];\n            const vPixel = pointCurrent[valueAxis];\n            if (pointBefore) {\n                delta = (iPixel - pointBefore[indexAxis]) / 3;\n                pointCurrent[`cp1${indexAxis}`] = iPixel - delta;\n                pointCurrent[`cp1${valueAxis}`] = vPixel - delta * mK[i];\n            }\n            if (pointAfter) {\n                delta = (pointAfter[indexAxis] - iPixel) / 3;\n                pointCurrent[`cp2${indexAxis}`] = iPixel + delta;\n                pointCurrent[`cp2${valueAxis}`] = vPixel + delta * mK[i];\n            }\n        }\n    }\n    /**\n     * This function calculates B\u00e9zier control points in a similar way than |splineCurve|,\n     * but preserves monotonicity of the provided data and ensures no local extremums are added\n     * between the dataset discrete points due to the interpolation.\n     * See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation\n     */ function splineCurveMonotone(points, indexAxis = 'x') {\n        const valueAxis = getValueAxis(indexAxis);\n        const pointsLen = points.length;\n        const deltaK = Array(pointsLen).fill(0);\n        const mK = Array(pointsLen);\n        // Calculate slopes (deltaK) and initialize tangents (mK)\n        let i, pointBefore, pointCurrent;\n        let pointAfter = getPoint(points, 0);\n        for(i = 0; i < pointsLen; ++i){\n            pointBefore = pointCurrent;\n            pointCurrent = pointAfter;\n            pointAfter = getPoint(points, i + 1);\n            if (!pointCurrent) {\n                continue;\n            }\n            if (pointAfter) {\n                const slopeDelta = pointAfter[indexAxis] - pointCurrent[indexAxis];\n                // In the case of two points that appear at the same x pixel, slopeDeltaX is 0\n                deltaK[i] = slopeDelta !== 0 ? (pointAfter[valueAxis] - pointCurrent[valueAxis]) / slopeDelta : 0;\n            }\n            mK[i] = !pointBefore ? deltaK[i] : !pointAfter ? deltaK[i - 1] : sign(deltaK[i - 1]) !== sign(deltaK[i]) ? 0 : (deltaK[i - 1] + deltaK[i]) / 2;\n        }\n        monotoneAdjust(points, deltaK, mK);\n        monotoneCompute(points, mK, indexAxis);\n    }\n    function capControlPoint(pt, min, max) {\n        return Math.max(Math.min(pt, max), min);\n    }\n    function capBezierPoints(points, area) {\n        let i, ilen, point, inArea, inAreaPrev;\n        let inAreaNext = _isPointInArea(points[0], area);\n        for(i = 0, ilen = points.length; i < ilen; ++i){\n            inAreaPrev = inArea;\n            inArea = inAreaNext;\n            inAreaNext = i < ilen - 1 && _isPointInArea(points[i + 1], area);\n            if (!inArea) {\n                continue;\n            }\n            point = points[i];\n            if (inAreaPrev) {\n                point.cp1x = capControlPoint(point.cp1x, area.left, area.right);\n                point.cp1y = capControlPoint(point.cp1y, area.top, area.bottom);\n            }\n            if (inAreaNext) {\n                point.cp2x = capControlPoint(point.cp2x, area.left, area.right);\n                point.cp2y = capControlPoint(point.cp2y, area.top, area.bottom);\n            }\n        }\n    }\n    /**\n     * @private\n     */ function _updateBezierControlPoints(points, options, area, loop, indexAxis) {\n        let i, ilen, point, controlPoints;\n        // Only consider points that are drawn in case the spanGaps option is used\n        if (options.spanGaps) {\n            points = points.filter((pt)=>!pt.skip);\n        }\n        if (options.cubicInterpolationMode === 'monotone') {\n            splineCurveMonotone(points, indexAxis);\n        } else {\n            let prev = loop ? points[points.length - 1] : points[0];\n            for(i = 0, ilen = points.length; i < ilen; ++i){\n                point = points[i];\n                controlPoints = splineCurve(prev, point, points[Math.min(i + 1, ilen - (loop ? 0 : 1)) % ilen], options.tension);\n                point.cp1x = controlPoints.previous.x;\n                point.cp1y = controlPoints.previous.y;\n                point.cp2x = controlPoints.next.x;\n                point.cp2y = controlPoints.next.y;\n                prev = point;\n            }\n        }\n        if (options.capBezierPoints) {\n            capBezierPoints(points, area);\n        }\n    }\n\n    const atEdge = (t)=>t === 0 || t === 1;\n    const elasticIn = (t, s, p)=>-(Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * TAU / p));\n    const elasticOut = (t, s, p)=>Math.pow(2, -10 * t) * Math.sin((t - s) * TAU / p) + 1;\n    /**\n     * Easing functions adapted from Robert Penner's easing equations.\n     * @namespace Chart.helpers.easing.effects\n     * @see http://www.robertpenner.com/easing/\n     */ const effects = {\n        linear: (t)=>t,\n        easeInQuad: (t)=>t * t,\n        easeOutQuad: (t)=>-t * (t - 2),\n        easeInOutQuad: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t : -0.5 * (--t * (t - 2) - 1),\n        easeInCubic: (t)=>t * t * t,\n        easeOutCubic: (t)=>(t -= 1) * t * t + 1,\n        easeInOutCubic: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t : 0.5 * ((t -= 2) * t * t + 2),\n        easeInQuart: (t)=>t * t * t * t,\n        easeOutQuart: (t)=>-((t -= 1) * t * t * t - 1),\n        easeInOutQuart: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t * t : -0.5 * ((t -= 2) * t * t * t - 2),\n        easeInQuint: (t)=>t * t * t * t * t,\n        easeOutQuint: (t)=>(t -= 1) * t * t * t * t + 1,\n        easeInOutQuint: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t * t * t : 0.5 * ((t -= 2) * t * t * t * t + 2),\n        easeInSine: (t)=>-Math.cos(t * HALF_PI) + 1,\n        easeOutSine: (t)=>Math.sin(t * HALF_PI),\n        easeInOutSine: (t)=>-0.5 * (Math.cos(PI * t) - 1),\n        easeInExpo: (t)=>t === 0 ? 0 : Math.pow(2, 10 * (t - 1)),\n        easeOutExpo: (t)=>t === 1 ? 1 : -Math.pow(2, -10 * t) + 1,\n        easeInOutExpo: (t)=>atEdge(t) ? t : t < 0.5 ? 0.5 * Math.pow(2, 10 * (t * 2 - 1)) : 0.5 * (-Math.pow(2, -10 * (t * 2 - 1)) + 2),\n        easeInCirc: (t)=>t >= 1 ? t : -(Math.sqrt(1 - t * t) - 1),\n        easeOutCirc: (t)=>Math.sqrt(1 - (t -= 1) * t),\n        easeInOutCirc: (t)=>(t /= 0.5) < 1 ? -0.5 * (Math.sqrt(1 - t * t) - 1) : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1),\n        easeInElastic: (t)=>atEdge(t) ? t : elasticIn(t, 0.075, 0.3),\n        easeOutElastic: (t)=>atEdge(t) ? t : elasticOut(t, 0.075, 0.3),\n        easeInOutElastic (t) {\n            const s = 0.1125;\n            const p = 0.45;\n            return atEdge(t) ? t : t < 0.5 ? 0.5 * elasticIn(t * 2, s, p) : 0.5 + 0.5 * elasticOut(t * 2 - 1, s, p);\n        },\n        easeInBack (t) {\n            const s = 1.70158;\n            return t * t * ((s + 1) * t - s);\n        },\n        easeOutBack (t) {\n            const s = 1.70158;\n            return (t -= 1) * t * ((s + 1) * t + s) + 1;\n        },\n        easeInOutBack (t) {\n            let s = 1.70158;\n            if ((t /= 0.5) < 1) {\n                return 0.5 * (t * t * (((s *= 1.525) + 1) * t - s));\n            }\n            return 0.5 * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2);\n        },\n        easeInBounce: (t)=>1 - effects.easeOutBounce(1 - t),\n        easeOutBounce (t) {\n            const m = 7.5625;\n            const d = 2.75;\n            if (t < 1 / d) {\n                return m * t * t;\n            }\n            if (t < 2 / d) {\n                return m * (t -= 1.5 / d) * t + 0.75;\n            }\n            if (t < 2.5 / d) {\n                return m * (t -= 2.25 / d) * t + 0.9375;\n            }\n            return m * (t -= 2.625 / d) * t + 0.984375;\n        },\n        easeInOutBounce: (t)=>t < 0.5 ? effects.easeInBounce(t * 2) * 0.5 : effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5\n    };\n\n    /**\n     * @private\n     */ function _pointInLine(p1, p2, t, mode) {\n        return {\n            x: p1.x + t * (p2.x - p1.x),\n            y: p1.y + t * (p2.y - p1.y)\n        };\n    }\n    /**\n     * @private\n     */ function _steppedInterpolation(p1, p2, t, mode) {\n        return {\n            x: p1.x + t * (p2.x - p1.x),\n            y: mode === 'middle' ? t < 0.5 ? p1.y : p2.y : mode === 'after' ? t < 1 ? p1.y : p2.y : t > 0 ? p2.y : p1.y\n        };\n    }\n    /**\n     * @private\n     */ function _bezierInterpolation(p1, p2, t, mode) {\n        const cp1 = {\n            x: p1.cp2x,\n            y: p1.cp2y\n        };\n        const cp2 = {\n            x: p2.cp1x,\n            y: p2.cp1y\n        };\n        const a = _pointInLine(p1, cp1, t);\n        const b = _pointInLine(cp1, cp2, t);\n        const c = _pointInLine(cp2, p2, t);\n        const d = _pointInLine(a, b, t);\n        const e = _pointInLine(b, c, t);\n        return _pointInLine(d, e, t);\n    }\n\n    const LINE_HEIGHT = /^(normal|(\\d+(?:\\.\\d+)?)(px|em|%)?)$/;\n    const FONT_STYLE = /^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/;\n    /**\n     * @alias Chart.helpers.options\n     * @namespace\n     */ /**\n     * Converts the given line height `value` in pixels for a specific font `size`.\n     * @param value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').\n     * @param size - The font size (in pixels) used to resolve relative `value`.\n     * @returns The effective line height in pixels (size * 1.2 if value is invalid).\n     * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height\n     * @since 2.7.0\n     */ function toLineHeight(value, size) {\n        const matches = ('' + value).match(LINE_HEIGHT);\n        if (!matches || matches[1] === 'normal') {\n            return size * 1.2;\n        }\n        value = +matches[2];\n        switch(matches[3]){\n            case 'px':\n                return value;\n            case '%':\n                value /= 100;\n                break;\n        }\n        return size * value;\n    }\n    const numberOrZero = (v)=>+v || 0;\n    function _readValueToProps(value, props) {\n        const ret = {};\n        const objProps = isObject(props);\n        const keys = objProps ? Object.keys(props) : props;\n        const read = isObject(value) ? objProps ? (prop)=>valueOrDefault(value[prop], value[props[prop]]) : (prop)=>value[prop] : ()=>value;\n        for (const prop of keys){\n            ret[prop] = numberOrZero(read(prop));\n        }\n        return ret;\n    }\n    /**\n     * Converts the given value into a TRBL object.\n     * @param value - If a number, set the value to all TRBL component,\n     *  else, if an object, use defined properties and sets undefined ones to 0.\n     *  x / y are shorthands for same value for left/right and top/bottom.\n     * @returns The padding values (top, right, bottom, left)\n     * @since 3.0.0\n     */ function toTRBL(value) {\n        return _readValueToProps(value, {\n            top: 'y',\n            right: 'x',\n            bottom: 'y',\n            left: 'x'\n        });\n    }\n    /**\n     * Converts the given value into a TRBL corners object (similar with css border-radius).\n     * @param value - If a number, set the value to all TRBL corner components,\n     *  else, if an object, use defined properties and sets undefined ones to 0.\n     * @returns The TRBL corner values (topLeft, topRight, bottomLeft, bottomRight)\n     * @since 3.0.0\n     */ function toTRBLCorners(value) {\n        return _readValueToProps(value, [\n            'topLeft',\n            'topRight',\n            'bottomLeft',\n            'bottomRight'\n        ]);\n    }\n    /**\n     * Converts the given value into a padding object with pre-computed width/height.\n     * @param value - If a number, set the value to all TRBL component,\n     *  else, if an object, use defined properties and sets undefined ones to 0.\n     *  x / y are shorthands for same value for left/right and top/bottom.\n     * @returns The padding values (top, right, bottom, left, width, height)\n     * @since 2.7.0\n     */ function toPadding(value) {\n        const obj = toTRBL(value);\n        obj.width = obj.left + obj.right;\n        obj.height = obj.top + obj.bottom;\n        return obj;\n    }\n    /**\n     * Parses font options and returns the font object.\n     * @param options - A object that contains font options to be parsed.\n     * @param fallback - A object that contains fallback font options.\n     * @return The font object.\n     * @private\n     */ function toFont(options, fallback) {\n        options = options || {};\n        fallback = fallback || defaults.font;\n        let size = valueOrDefault(options.size, fallback.size);\n        if (typeof size === 'string') {\n            size = parseInt(size, 10);\n        }\n        let style = valueOrDefault(options.style, fallback.style);\n        if (style && !('' + style).match(FONT_STYLE)) {\n            console.warn('Invalid font style specified: \"' + style + '\"');\n            style = undefined;\n        }\n        const font = {\n            family: valueOrDefault(options.family, fallback.family),\n            lineHeight: toLineHeight(valueOrDefault(options.lineHeight, fallback.lineHeight), size),\n            size,\n            style,\n            weight: valueOrDefault(options.weight, fallback.weight),\n            string: ''\n        };\n        font.string = toFontString(font);\n        return font;\n    }\n    /**\n     * Evaluates the given `inputs` sequentially and returns the first defined value.\n     * @param inputs - An array of values, falling back to the last value.\n     * @param context - If defined and the current value is a function, the value\n     * is called with `context` as first argument and the result becomes the new input.\n     * @param index - If defined and the current value is an array, the value\n     * at `index` become the new input.\n     * @param info - object to return information about resolution in\n     * @param info.cacheable - Will be set to `false` if option is not cacheable.\n     * @since 2.7.0\n     */ function resolve(inputs, context, index, info) {\n        let cacheable = true;\n        let i, ilen, value;\n        for(i = 0, ilen = inputs.length; i < ilen; ++i){\n            value = inputs[i];\n            if (value === undefined) {\n                continue;\n            }\n            if (context !== undefined && typeof value === 'function') {\n                value = value(context);\n                cacheable = false;\n            }\n            if (index !== undefined && isArray(value)) {\n                value = value[index % value.length];\n                cacheable = false;\n            }\n            if (value !== undefined) {\n                if (info && !cacheable) {\n                    info.cacheable = false;\n                }\n                return value;\n            }\n        }\n    }\n    /**\n     * @param minmax\n     * @param grace\n     * @param beginAtZero\n     * @private\n     */ function _addGrace(minmax, grace, beginAtZero) {\n        const { min , max  } = minmax;\n        const change = toDimension(grace, (max - min) / 2);\n        const keepZero = (value, add)=>beginAtZero && value === 0 ? 0 : value + add;\n        return {\n            min: keepZero(min, -Math.abs(change)),\n            max: keepZero(max, change)\n        };\n    }\n    function createContext(parentContext, context) {\n        return Object.assign(Object.create(parentContext), context);\n    }\n\n    const getRightToLeftAdapter = function(rectX, width) {\n        return {\n            x (x) {\n                return rectX + rectX + width - x;\n            },\n            setWidth (w) {\n                width = w;\n            },\n            textAlign (align) {\n                if (align === 'center') {\n                    return align;\n                }\n                return align === 'right' ? 'left' : 'right';\n            },\n            xPlus (x, value) {\n                return x - value;\n            },\n            leftForLtr (x, itemWidth) {\n                return x - itemWidth;\n            }\n        };\n    };\n    const getLeftToRightAdapter = function() {\n        return {\n            x (x) {\n                return x;\n            },\n            setWidth (w) {},\n            textAlign (align) {\n                return align;\n            },\n            xPlus (x, value) {\n                return x + value;\n            },\n            leftForLtr (x, _itemWidth) {\n                return x;\n            }\n        };\n    };\n    function getRtlAdapter(rtl, rectX, width) {\n        return rtl ? getRightToLeftAdapter(rectX, width) : getLeftToRightAdapter();\n    }\n    function overrideTextDirection(ctx, direction) {\n        let style, original;\n        if (direction === 'ltr' || direction === 'rtl') {\n            style = ctx.canvas.style;\n            original = [\n                style.getPropertyValue('direction'),\n                style.getPropertyPriority('direction')\n            ];\n            style.setProperty('direction', direction, 'important');\n            ctx.prevTextDirection = original;\n        }\n    }\n    function restoreTextDirection(ctx, original) {\n        if (original !== undefined) {\n            delete ctx.prevTextDirection;\n            ctx.canvas.style.setProperty('direction', original[0], original[1]);\n        }\n    }\n\n    function propertyFn(property) {\n        if (property === 'angle') {\n            return {\n                between: _angleBetween,\n                compare: _angleDiff,\n                normalize: _normalizeAngle\n            };\n        }\n        return {\n            between: _isBetween,\n            compare: (a, b)=>a - b,\n            normalize: (x)=>x\n        };\n    }\n    function normalizeSegment({ start , end , count , loop , style  }) {\n        return {\n            start: start % count,\n            end: end % count,\n            loop: loop && (end - start + 1) % count === 0,\n            style\n        };\n    }\n    function getSegment(segment, points, bounds) {\n        const { property , start: startBound , end: endBound  } = bounds;\n        const { between , normalize  } = propertyFn(property);\n        const count = points.length;\n        let { start , end , loop  } = segment;\n        let i, ilen;\n        if (loop) {\n            start += count;\n            end += count;\n            for(i = 0, ilen = count; i < ilen; ++i){\n                if (!between(normalize(points[start % count][property]), startBound, endBound)) {\n                    break;\n                }\n                start--;\n                end--;\n            }\n            start %= count;\n            end %= count;\n        }\n        if (end < start) {\n            end += count;\n        }\n        return {\n            start,\n            end,\n            loop,\n            style: segment.style\n        };\n    }\n    function _boundSegment(segment, points, bounds) {\n        if (!bounds) {\n            return [\n                segment\n            ];\n        }\n        const { property , start: startBound , end: endBound  } = bounds;\n        const count = points.length;\n        const { compare , between , normalize  } = propertyFn(property);\n        const { start , end , loop , style  } = getSegment(segment, points, bounds);\n        const result = [];\n        let inside = false;\n        let subStart = null;\n        let value, point, prevValue;\n        const startIsBefore = ()=>between(startBound, prevValue, value) && compare(startBound, prevValue) !== 0;\n        const endIsBefore = ()=>compare(endBound, value) === 0 || between(endBound, prevValue, value);\n        const shouldStart = ()=>inside || startIsBefore();\n        const shouldStop = ()=>!inside || endIsBefore();\n        for(let i = start, prev = start; i <= end; ++i){\n            point = points[i % count];\n            if (point.skip) {\n                continue;\n            }\n            value = normalize(point[property]);\n            if (value === prevValue) {\n                continue;\n            }\n            inside = between(value, startBound, endBound);\n            if (subStart === null && shouldStart()) {\n                subStart = compare(value, startBound) === 0 ? i : prev;\n            }\n            if (subStart !== null && shouldStop()) {\n                result.push(normalizeSegment({\n                    start: subStart,\n                    end: i,\n                    loop,\n                    count,\n                    style\n                }));\n                subStart = null;\n            }\n            prev = i;\n            prevValue = value;\n        }\n        if (subStart !== null) {\n            result.push(normalizeSegment({\n                start: subStart,\n                end,\n                loop,\n                count,\n                style\n            }));\n        }\n        return result;\n    }\n    function _boundSegments(line, bounds) {\n        const result = [];\n        const segments = line.segments;\n        for(let i = 0; i < segments.length; i++){\n            const sub = _boundSegment(segments[i], line.points, bounds);\n            if (sub.length) {\n                result.push(...sub);\n            }\n        }\n        return result;\n    }\n    function findStartAndEnd(points, count, loop, spanGaps) {\n        let start = 0;\n        let end = count - 1;\n        if (loop && !spanGaps) {\n            while(start < count && !points[start].skip){\n                start++;\n            }\n        }\n        while(start < count && points[start].skip){\n            start++;\n        }\n        start %= count;\n        if (loop) {\n            end += start;\n        }\n        while(end > start && points[end % count].skip){\n            end--;\n        }\n        end %= count;\n        return {\n            start,\n            end\n        };\n    }\n    function solidSegments(points, start, max, loop) {\n        const count = points.length;\n        const result = [];\n        let last = start;\n        let prev = points[start];\n        let end;\n        for(end = start + 1; end <= max; ++end){\n            const cur = points[end % count];\n            if (cur.skip || cur.stop) {\n                if (!prev.skip) {\n                    loop = false;\n                    result.push({\n                        start: start % count,\n                        end: (end - 1) % count,\n                        loop\n                    });\n                    start = last = cur.stop ? end : null;\n                }\n            } else {\n                last = end;\n                if (prev.skip) {\n                    start = end;\n                }\n            }\n            prev = cur;\n        }\n        if (last !== null) {\n            result.push({\n                start: start % count,\n                end: last % count,\n                loop\n            });\n        }\n        return result;\n    }\n    function _computeSegments(line, segmentOptions) {\n        const points = line.points;\n        const spanGaps = line.options.spanGaps;\n        const count = points.length;\n        if (!count) {\n            return [];\n        }\n        const loop = !!line._loop;\n        const { start , end  } = findStartAndEnd(points, count, loop, spanGaps);\n        if (spanGaps === true) {\n            return splitByStyles(line, [\n                {\n                    start,\n                    end,\n                    loop\n                }\n            ], points, segmentOptions);\n        }\n        const max = end < start ? end + count : end;\n        const completeLoop = !!line._fullLoop && start === 0 && end === count - 1;\n        return splitByStyles(line, solidSegments(points, start, max, completeLoop), points, segmentOptions);\n    }\n    function splitByStyles(line, segments, points, segmentOptions) {\n        if (!segmentOptions || !segmentOptions.setContext || !points) {\n            return segments;\n        }\n        return doSplitByStyles(line, segments, points, segmentOptions);\n    }\n    function doSplitByStyles(line, segments, points, segmentOptions) {\n        const chartContext = line._chart.getContext();\n        const baseStyle = readStyle(line.options);\n        const { _datasetIndex: datasetIndex , options: { spanGaps  }  } = line;\n        const count = points.length;\n        const result = [];\n        let prevStyle = baseStyle;\n        let start = segments[0].start;\n        let i = start;\n        function addStyle(s, e, l, st) {\n            const dir = spanGaps ? -1 : 1;\n            if (s === e) {\n                return;\n            }\n            s += count;\n            while(points[s % count].skip){\n                s -= dir;\n            }\n            while(points[e % count].skip){\n                e += dir;\n            }\n            if (s % count !== e % count) {\n                result.push({\n                    start: s % count,\n                    end: e % count,\n                    loop: l,\n                    style: st\n                });\n                prevStyle = st;\n                start = e % count;\n            }\n        }\n        for (const segment of segments){\n            start = spanGaps ? start : segment.start;\n            let prev = points[start % count];\n            let style;\n            for(i = start + 1; i <= segment.end; i++){\n                const pt = points[i % count];\n                style = readStyle(segmentOptions.setContext(createContext(chartContext, {\n                    type: 'segment',\n                    p0: prev,\n                    p1: pt,\n                    p0DataIndex: (i - 1) % count,\n                    p1DataIndex: i % count,\n                    datasetIndex\n                })));\n                if (styleChanged(style, prevStyle)) {\n                    addStyle(start, i - 1, segment.loop, prevStyle);\n                }\n                prev = pt;\n                prevStyle = style;\n            }\n            if (start < i - 1) {\n                addStyle(start, i - 1, segment.loop, prevStyle);\n            }\n        }\n        return result;\n    }\n    function readStyle(options) {\n        return {\n            backgroundColor: options.backgroundColor,\n            borderCapStyle: options.borderCapStyle,\n            borderDash: options.borderDash,\n            borderDashOffset: options.borderDashOffset,\n            borderJoinStyle: options.borderJoinStyle,\n            borderWidth: options.borderWidth,\n            borderColor: options.borderColor\n        };\n    }\n    function styleChanged(style, prevStyle) {\n        if (!prevStyle) {\n            return false;\n        }\n        const cache = [];\n        const replacer = function(key, value) {\n            if (!isPatternOrGradient(value)) {\n                return value;\n            }\n            if (!cache.includes(value)) {\n                cache.push(value);\n            }\n            return cache.indexOf(value);\n        };\n        return JSON.stringify(style, replacer) !== JSON.stringify(prevStyle, replacer);\n    }\n\n    var helpers = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        HALF_PI: HALF_PI,\n        INFINITY: INFINITY,\n        PI: PI,\n        PITAU: PITAU,\n        QUARTER_PI: QUARTER_PI,\n        RAD_PER_DEG: RAD_PER_DEG,\n        TAU: TAU,\n        TWO_THIRDS_PI: TWO_THIRDS_PI,\n        _addGrace: _addGrace,\n        _alignPixel: _alignPixel,\n        _alignStartEnd: _alignStartEnd,\n        _angleBetween: _angleBetween,\n        _angleDiff: _angleDiff,\n        _arrayUnique: _arrayUnique,\n        _attachContext: _attachContext,\n        _bezierCurveTo: _bezierCurveTo,\n        _bezierInterpolation: _bezierInterpolation,\n        _boundSegment: _boundSegment,\n        _boundSegments: _boundSegments,\n        _capitalize: _capitalize,\n        _computeSegments: _computeSegments,\n        _createResolver: _createResolver,\n        _decimalPlaces: _decimalPlaces,\n        _deprecated: _deprecated,\n        _descriptors: _descriptors,\n        _elementsEqual: _elementsEqual,\n        _factorize: _factorize,\n        _filterBetween: _filterBetween,\n        _getParentNode: _getParentNode,\n        _getStartAndCountOfVisiblePoints: _getStartAndCountOfVisiblePoints,\n        _int16Range: _int16Range,\n        _isBetween: _isBetween,\n        _isClickEvent: _isClickEvent,\n        _isDomSupported: _isDomSupported,\n        _isPointInArea: _isPointInArea,\n        _limitValue: _limitValue,\n        _longestText: _longestText,\n        _lookup: _lookup,\n        _lookupByKey: _lookupByKey,\n        _measureText: _measureText,\n        _merger: _merger,\n        _mergerIf: _mergerIf,\n        _normalizeAngle: _normalizeAngle,\n        _parseObjectDataRadialScale: _parseObjectDataRadialScale,\n        _pointInLine: _pointInLine,\n        _readValueToProps: _readValueToProps,\n        _rlookupByKey: _rlookupByKey,\n        _scaleRangesChanged: _scaleRangesChanged,\n        _setMinAndMaxByKey: _setMinAndMaxByKey,\n        _splitKey: _splitKey,\n        _steppedInterpolation: _steppedInterpolation,\n        _steppedLineTo: _steppedLineTo,\n        _textX: _textX,\n        _toLeftRightCenter: _toLeftRightCenter,\n        _updateBezierControlPoints: _updateBezierControlPoints,\n        addRoundedRectPath: addRoundedRectPath,\n        almostEquals: almostEquals,\n        almostWhole: almostWhole,\n        callback: callback,\n        clearCanvas: clearCanvas,\n        clipArea: clipArea,\n        clone: clone$1,\n        color: color,\n        createContext: createContext,\n        debounce: debounce,\n        defined: defined,\n        distanceBetweenPoints: distanceBetweenPoints,\n        drawPoint: drawPoint,\n        drawPointLegend: drawPointLegend,\n        each: each,\n        easingEffects: effects,\n        finiteOrDefault: finiteOrDefault,\n        fontString: fontString,\n        formatNumber: formatNumber,\n        getAngleFromPoint: getAngleFromPoint,\n        getHoverColor: getHoverColor,\n        getMaximumSize: getMaximumSize,\n        getRelativePosition: getRelativePosition,\n        getRtlAdapter: getRtlAdapter,\n        getStyle: getStyle,\n        isArray: isArray,\n        isFinite: isNumberFinite,\n        isFunction: isFunction,\n        isNullOrUndef: isNullOrUndef,\n        isNumber: isNumber,\n        isObject: isObject,\n        isPatternOrGradient: isPatternOrGradient,\n        listenArrayEvents: listenArrayEvents,\n        log10: log10,\n        merge: merge,\n        mergeIf: mergeIf,\n        niceNum: niceNum,\n        noop: noop,\n        overrideTextDirection: overrideTextDirection,\n        readUsedSize: readUsedSize,\n        renderText: renderText,\n        requestAnimFrame: requestAnimFrame,\n        resolve: resolve,\n        resolveObjectKey: resolveObjectKey,\n        restoreTextDirection: restoreTextDirection,\n        retinaScale: retinaScale,\n        setsEqual: setsEqual,\n        sign: sign,\n        splineCurve: splineCurve,\n        splineCurveMonotone: splineCurveMonotone,\n        supportsEventListenerOptions: supportsEventListenerOptions,\n        throttled: throttled,\n        toDegrees: toDegrees,\n        toDimension: toDimension,\n        toFont: toFont,\n        toFontString: toFontString,\n        toLineHeight: toLineHeight,\n        toPadding: toPadding,\n        toPercentage: toPercentage,\n        toRadians: toRadians,\n        toTRBL: toTRBL,\n        toTRBLCorners: toTRBLCorners,\n        uid: uid,\n        unclipArea: unclipArea,\n        unlistenArrayEvents: unlistenArrayEvents,\n        valueOrDefault: valueOrDefault\n    });\n\n    function binarySearch(metaset, axis, value, intersect) {\n        const { controller , data , _sorted  } = metaset;\n        const iScale = controller._cachedMeta.iScale;\n        if (iScale && axis === iScale.axis && axis !== 'r' && _sorted && data.length) {\n            const lookupMethod = iScale._reversePixels ? _rlookupByKey : _lookupByKey;\n            if (!intersect) {\n                return lookupMethod(data, axis, value);\n            } else if (controller._sharedOptions) {\n                const el = data[0];\n                const range = typeof el.getRange === 'function' && el.getRange(axis);\n                if (range) {\n                    const start = lookupMethod(data, axis, value - range);\n                    const end = lookupMethod(data, axis, value + range);\n                    return {\n                        lo: start.lo,\n                        hi: end.hi\n                    };\n                }\n            }\n        }\n        return {\n            lo: 0,\n            hi: data.length - 1\n        };\n    }\n    function evaluateInteractionItems(chart, axis, position, handler, intersect) {\n        const metasets = chart.getSortedVisibleDatasetMetas();\n        const value = position[axis];\n        for(let i = 0, ilen = metasets.length; i < ilen; ++i){\n            const { index , data  } = metasets[i];\n            const { lo , hi  } = binarySearch(metasets[i], axis, value, intersect);\n            for(let j = lo; j <= hi; ++j){\n                const element = data[j];\n                if (!element.skip) {\n                    handler(element, index, j);\n                }\n            }\n        }\n    }\n    function getDistanceMetricForAxis(axis) {\n        const useX = axis.indexOf('x') !== -1;\n        const useY = axis.indexOf('y') !== -1;\n        return function(pt1, pt2) {\n            const deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;\n            const deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;\n            return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));\n        };\n    }\n    function getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) {\n        const items = [];\n        if (!includeInvisible && !chart.isPointInArea(position)) {\n            return items;\n        }\n        const evaluationFunc = function(element, datasetIndex, index) {\n            if (!includeInvisible && !_isPointInArea(element, chart.chartArea, 0)) {\n                return;\n            }\n            if (element.inRange(position.x, position.y, useFinalPosition)) {\n                items.push({\n                    element,\n                    datasetIndex,\n                    index\n                });\n            }\n        };\n        evaluateInteractionItems(chart, axis, position, evaluationFunc, true);\n        return items;\n    }\n    function getNearestRadialItems(chart, position, axis, useFinalPosition) {\n        let items = [];\n        function evaluationFunc(element, datasetIndex, index) {\n            const { startAngle , endAngle  } = element.getProps([\n                'startAngle',\n                'endAngle'\n            ], useFinalPosition);\n            const { angle  } = getAngleFromPoint(element, {\n                x: position.x,\n                y: position.y\n            });\n            if (_angleBetween(angle, startAngle, endAngle)) {\n                items.push({\n                    element,\n                    datasetIndex,\n                    index\n                });\n            }\n        }\n        evaluateInteractionItems(chart, axis, position, evaluationFunc);\n        return items;\n    }\n    function getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition, includeInvisible) {\n        let items = [];\n        const distanceMetric = getDistanceMetricForAxis(axis);\n        let minDistance = Number.POSITIVE_INFINITY;\n        function evaluationFunc(element, datasetIndex, index) {\n            const inRange = element.inRange(position.x, position.y, useFinalPosition);\n            if (intersect && !inRange) {\n                return;\n            }\n            const center = element.getCenterPoint(useFinalPosition);\n            const pointInArea = !!includeInvisible || chart.isPointInArea(center);\n            if (!pointInArea && !inRange) {\n                return;\n            }\n            const distance = distanceMetric(position, center);\n            if (distance < minDistance) {\n                items = [\n                    {\n                        element,\n                        datasetIndex,\n                        index\n                    }\n                ];\n                minDistance = distance;\n            } else if (distance === minDistance) {\n                items.push({\n                    element,\n                    datasetIndex,\n                    index\n                });\n            }\n        }\n        evaluateInteractionItems(chart, axis, position, evaluationFunc);\n        return items;\n    }\n    function getNearestItems(chart, position, axis, intersect, useFinalPosition, includeInvisible) {\n        if (!includeInvisible && !chart.isPointInArea(position)) {\n            return [];\n        }\n        return axis === 'r' && !intersect ? getNearestRadialItems(chart, position, axis, useFinalPosition) : getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition, includeInvisible);\n    }\n    function getAxisItems(chart, position, axis, intersect, useFinalPosition) {\n        const items = [];\n        const rangeMethod = axis === 'x' ? 'inXRange' : 'inYRange';\n        let intersectsItem = false;\n        evaluateInteractionItems(chart, axis, position, (element, datasetIndex, index)=>{\n            if (element[rangeMethod](position[axis], useFinalPosition)) {\n                items.push({\n                    element,\n                    datasetIndex,\n                    index\n                });\n                intersectsItem = intersectsItem || element.inRange(position.x, position.y, useFinalPosition);\n            }\n        });\n        if (intersect && !intersectsItem) {\n            return [];\n        }\n        return items;\n    }\n    var Interaction = {\n        evaluateInteractionItems,\n        modes: {\n            index (chart, e, options, useFinalPosition) {\n                const position = getRelativePosition(e, chart);\n                const axis = options.axis || 'x';\n                const includeInvisible = options.includeInvisible || false;\n                const items = options.intersect ? getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) : getNearestItems(chart, position, axis, false, useFinalPosition, includeInvisible);\n                const elements = [];\n                if (!items.length) {\n                    return [];\n                }\n                chart.getSortedVisibleDatasetMetas().forEach((meta)=>{\n                    const index = items[0].index;\n                    const element = meta.data[index];\n                    if (element && !element.skip) {\n                        elements.push({\n                            element,\n                            datasetIndex: meta.index,\n                            index\n                        });\n                    }\n                });\n                return elements;\n            },\n            dataset (chart, e, options, useFinalPosition) {\n                const position = getRelativePosition(e, chart);\n                const axis = options.axis || 'xy';\n                const includeInvisible = options.includeInvisible || false;\n                let items = options.intersect ? getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) : getNearestItems(chart, position, axis, false, useFinalPosition, includeInvisible);\n                if (items.length > 0) {\n                    const datasetIndex = items[0].datasetIndex;\n                    const data = chart.getDatasetMeta(datasetIndex).data;\n                    items = [];\n                    for(let i = 0; i < data.length; ++i){\n                        items.push({\n                            element: data[i],\n                            datasetIndex,\n                            index: i\n                        });\n                    }\n                }\n                return items;\n            },\n            point (chart, e, options, useFinalPosition) {\n                const position = getRelativePosition(e, chart);\n                const axis = options.axis || 'xy';\n                const includeInvisible = options.includeInvisible || false;\n                return getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible);\n            },\n            nearest (chart, e, options, useFinalPosition) {\n                const position = getRelativePosition(e, chart);\n                const axis = options.axis || 'xy';\n                const includeInvisible = options.includeInvisible || false;\n                return getNearestItems(chart, position, axis, options.intersect, useFinalPosition, includeInvisible);\n            },\n            x (chart, e, options, useFinalPosition) {\n                const position = getRelativePosition(e, chart);\n                return getAxisItems(chart, position, 'x', options.intersect, useFinalPosition);\n            },\n            y (chart, e, options, useFinalPosition) {\n                const position = getRelativePosition(e, chart);\n                return getAxisItems(chart, position, 'y', options.intersect, useFinalPosition);\n            }\n        }\n    };\n\n    const STATIC_POSITIONS = [\n        'left',\n        'top',\n        'right',\n        'bottom'\n    ];\n    function filterByPosition(array, position) {\n        return array.filter((v)=>v.pos === position);\n    }\n    function filterDynamicPositionByAxis(array, axis) {\n        return array.filter((v)=>STATIC_POSITIONS.indexOf(v.pos) === -1 && v.box.axis === axis);\n    }\n    function sortByWeight(array, reverse) {\n        return array.sort((a, b)=>{\n            const v0 = reverse ? b : a;\n            const v1 = reverse ? a : b;\n            return v0.weight === v1.weight ? v0.index - v1.index : v0.weight - v1.weight;\n        });\n    }\n    function wrapBoxes(boxes) {\n        const layoutBoxes = [];\n        let i, ilen, box, pos, stack, stackWeight;\n        for(i = 0, ilen = (boxes || []).length; i < ilen; ++i){\n            box = boxes[i];\n            ({ position: pos , options: { stack , stackWeight =1  }  } = box);\n            layoutBoxes.push({\n                index: i,\n                box,\n                pos,\n                horizontal: box.isHorizontal(),\n                weight: box.weight,\n                stack: stack && pos + stack,\n                stackWeight\n            });\n        }\n        return layoutBoxes;\n    }\n    function buildStacks(layouts) {\n        const stacks = {};\n        for (const wrap of layouts){\n            const { stack , pos , stackWeight  } = wrap;\n            if (!stack || !STATIC_POSITIONS.includes(pos)) {\n                continue;\n            }\n            const _stack = stacks[stack] || (stacks[stack] = {\n                count: 0,\n                placed: 0,\n                weight: 0,\n                size: 0\n            });\n            _stack.count++;\n            _stack.weight += stackWeight;\n        }\n        return stacks;\n    }\n    function setLayoutDims(layouts, params) {\n        const stacks = buildStacks(layouts);\n        const { vBoxMaxWidth , hBoxMaxHeight  } = params;\n        let i, ilen, layout;\n        for(i = 0, ilen = layouts.length; i < ilen; ++i){\n            layout = layouts[i];\n            const { fullSize  } = layout.box;\n            const stack = stacks[layout.stack];\n            const factor = stack && layout.stackWeight / stack.weight;\n            if (layout.horizontal) {\n                layout.width = factor ? factor * vBoxMaxWidth : fullSize && params.availableWidth;\n                layout.height = hBoxMaxHeight;\n            } else {\n                layout.width = vBoxMaxWidth;\n                layout.height = factor ? factor * hBoxMaxHeight : fullSize && params.availableHeight;\n            }\n        }\n        return stacks;\n    }\n    function buildLayoutBoxes(boxes) {\n        const layoutBoxes = wrapBoxes(boxes);\n        const fullSize = sortByWeight(layoutBoxes.filter((wrap)=>wrap.box.fullSize), true);\n        const left = sortByWeight(filterByPosition(layoutBoxes, 'left'), true);\n        const right = sortByWeight(filterByPosition(layoutBoxes, 'right'));\n        const top = sortByWeight(filterByPosition(layoutBoxes, 'top'), true);\n        const bottom = sortByWeight(filterByPosition(layoutBoxes, 'bottom'));\n        const centerHorizontal = filterDynamicPositionByAxis(layoutBoxes, 'x');\n        const centerVertical = filterDynamicPositionByAxis(layoutBoxes, 'y');\n        return {\n            fullSize,\n            leftAndTop: left.concat(top),\n            rightAndBottom: right.concat(centerVertical).concat(bottom).concat(centerHorizontal),\n            chartArea: filterByPosition(layoutBoxes, 'chartArea'),\n            vertical: left.concat(right).concat(centerVertical),\n            horizontal: top.concat(bottom).concat(centerHorizontal)\n        };\n    }\n    function getCombinedMax(maxPadding, chartArea, a, b) {\n        return Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]);\n    }\n    function updateMaxPadding(maxPadding, boxPadding) {\n        maxPadding.top = Math.max(maxPadding.top, boxPadding.top);\n        maxPadding.left = Math.max(maxPadding.left, boxPadding.left);\n        maxPadding.bottom = Math.max(maxPadding.bottom, boxPadding.bottom);\n        maxPadding.right = Math.max(maxPadding.right, boxPadding.right);\n    }\n    function updateDims(chartArea, params, layout, stacks) {\n        const { pos , box  } = layout;\n        const maxPadding = chartArea.maxPadding;\n        if (!isObject(pos)) {\n            if (layout.size) {\n                chartArea[pos] -= layout.size;\n            }\n            const stack = stacks[layout.stack] || {\n                size: 0,\n                count: 1\n            };\n            stack.size = Math.max(stack.size, layout.horizontal ? box.height : box.width);\n            layout.size = stack.size / stack.count;\n            chartArea[pos] += layout.size;\n        }\n        if (box.getPadding) {\n            updateMaxPadding(maxPadding, box.getPadding());\n        }\n        const newWidth = Math.max(0, params.outerWidth - getCombinedMax(maxPadding, chartArea, 'left', 'right'));\n        const newHeight = Math.max(0, params.outerHeight - getCombinedMax(maxPadding, chartArea, 'top', 'bottom'));\n        const widthChanged = newWidth !== chartArea.w;\n        const heightChanged = newHeight !== chartArea.h;\n        chartArea.w = newWidth;\n        chartArea.h = newHeight;\n        return layout.horizontal ? {\n            same: widthChanged,\n            other: heightChanged\n        } : {\n            same: heightChanged,\n            other: widthChanged\n        };\n    }\n    function handleMaxPadding(chartArea) {\n        const maxPadding = chartArea.maxPadding;\n        function updatePos(pos) {\n            const change = Math.max(maxPadding[pos] - chartArea[pos], 0);\n            chartArea[pos] += change;\n            return change;\n        }\n        chartArea.y += updatePos('top');\n        chartArea.x += updatePos('left');\n        updatePos('right');\n        updatePos('bottom');\n    }\n    function getMargins(horizontal, chartArea) {\n        const maxPadding = chartArea.maxPadding;\n        function marginForPositions(positions) {\n            const margin = {\n                left: 0,\n                top: 0,\n                right: 0,\n                bottom: 0\n            };\n            positions.forEach((pos)=>{\n                margin[pos] = Math.max(chartArea[pos], maxPadding[pos]);\n            });\n            return margin;\n        }\n        return horizontal ? marginForPositions([\n            'left',\n            'right'\n        ]) : marginForPositions([\n            'top',\n            'bottom'\n        ]);\n    }\n    function fitBoxes(boxes, chartArea, params, stacks) {\n        const refitBoxes = [];\n        let i, ilen, layout, box, refit, changed;\n        for(i = 0, ilen = boxes.length, refit = 0; i < ilen; ++i){\n            layout = boxes[i];\n            box = layout.box;\n            box.update(layout.width || chartArea.w, layout.height || chartArea.h, getMargins(layout.horizontal, chartArea));\n            const { same , other  } = updateDims(chartArea, params, layout, stacks);\n            refit |= same && refitBoxes.length;\n            changed = changed || other;\n            if (!box.fullSize) {\n                refitBoxes.push(layout);\n            }\n        }\n        return refit && fitBoxes(refitBoxes, chartArea, params, stacks) || changed;\n    }\n    function setBoxDims(box, left, top, width, height) {\n        box.top = top;\n        box.left = left;\n        box.right = left + width;\n        box.bottom = top + height;\n        box.width = width;\n        box.height = height;\n    }\n    function placeBoxes(boxes, chartArea, params, stacks) {\n        const userPadding = params.padding;\n        let { x , y  } = chartArea;\n        for (const layout of boxes){\n            const box = layout.box;\n            const stack = stacks[layout.stack] || {\n                count: 1,\n                placed: 0,\n                weight: 1\n            };\n            const weight = layout.stackWeight / stack.weight || 1;\n            if (layout.horizontal) {\n                const width = chartArea.w * weight;\n                const height = stack.size || box.height;\n                if (defined(stack.start)) {\n                    y = stack.start;\n                }\n                if (box.fullSize) {\n                    setBoxDims(box, userPadding.left, y, params.outerWidth - userPadding.right - userPadding.left, height);\n                } else {\n                    setBoxDims(box, chartArea.left + stack.placed, y, width, height);\n                }\n                stack.start = y;\n                stack.placed += width;\n                y = box.bottom;\n            } else {\n                const height = chartArea.h * weight;\n                const width = stack.size || box.width;\n                if (defined(stack.start)) {\n                    x = stack.start;\n                }\n                if (box.fullSize) {\n                    setBoxDims(box, x, userPadding.top, width, params.outerHeight - userPadding.bottom - userPadding.top);\n                } else {\n                    setBoxDims(box, x, chartArea.top + stack.placed, width, height);\n                }\n                stack.start = x;\n                stack.placed += height;\n                x = box.right;\n            }\n        }\n        chartArea.x = x;\n        chartArea.y = y;\n    }\n    var layouts = {\n        addBox (chart, item) {\n            if (!chart.boxes) {\n                chart.boxes = [];\n            }\n            item.fullSize = item.fullSize || false;\n            item.position = item.position || 'top';\n            item.weight = item.weight || 0;\n            item._layers = item._layers || function() {\n                return [\n                    {\n                        z: 0,\n                        draw (chartArea) {\n                            item.draw(chartArea);\n                        }\n                    }\n                ];\n            };\n            chart.boxes.push(item);\n        },\n        removeBox (chart, layoutItem) {\n            const index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1;\n            if (index !== -1) {\n                chart.boxes.splice(index, 1);\n            }\n        },\n        configure (chart, item, options) {\n            item.fullSize = options.fullSize;\n            item.position = options.position;\n            item.weight = options.weight;\n        },\n        update (chart, width, height, minPadding) {\n            if (!chart) {\n                return;\n            }\n            const padding = toPadding(chart.options.layout.padding);\n            const availableWidth = Math.max(width - padding.width, 0);\n            const availableHeight = Math.max(height - padding.height, 0);\n            const boxes = buildLayoutBoxes(chart.boxes);\n            const verticalBoxes = boxes.vertical;\n            const horizontalBoxes = boxes.horizontal;\n            each(chart.boxes, (box)=>{\n                if (typeof box.beforeLayout === 'function') {\n                    box.beforeLayout();\n                }\n            });\n            const visibleVerticalBoxCount = verticalBoxes.reduce((total, wrap)=>wrap.box.options && wrap.box.options.display === false ? total : total + 1, 0) || 1;\n            const params = Object.freeze({\n                outerWidth: width,\n                outerHeight: height,\n                padding,\n                availableWidth,\n                availableHeight,\n                vBoxMaxWidth: availableWidth / 2 / visibleVerticalBoxCount,\n                hBoxMaxHeight: availableHeight / 2\n            });\n            const maxPadding = Object.assign({}, padding);\n            updateMaxPadding(maxPadding, toPadding(minPadding));\n            const chartArea = Object.assign({\n                maxPadding,\n                w: availableWidth,\n                h: availableHeight,\n                x: padding.left,\n                y: padding.top\n            }, padding);\n            const stacks = setLayoutDims(verticalBoxes.concat(horizontalBoxes), params);\n            fitBoxes(boxes.fullSize, chartArea, params, stacks);\n            fitBoxes(verticalBoxes, chartArea, params, stacks);\n            if (fitBoxes(horizontalBoxes, chartArea, params, stacks)) {\n                fitBoxes(verticalBoxes, chartArea, params, stacks);\n            }\n            handleMaxPadding(chartArea);\n            placeBoxes(boxes.leftAndTop, chartArea, params, stacks);\n            chartArea.x += chartArea.w;\n            chartArea.y += chartArea.h;\n            placeBoxes(boxes.rightAndBottom, chartArea, params, stacks);\n            chart.chartArea = {\n                left: chartArea.left,\n                top: chartArea.top,\n                right: chartArea.left + chartArea.w,\n                bottom: chartArea.top + chartArea.h,\n                height: chartArea.h,\n                width: chartArea.w\n            };\n            each(boxes.chartArea, (layout)=>{\n                const box = layout.box;\n                Object.assign(box, chart.chartArea);\n                box.update(chartArea.w, chartArea.h, {\n                    left: 0,\n                    top: 0,\n                    right: 0,\n                    bottom: 0\n                });\n            });\n        }\n    };\n\n    class BasePlatform {\n        acquireContext(canvas, aspectRatio) {}\n        releaseContext(context) {\n            return false;\n        }\n        addEventListener(chart, type, listener) {}\n        removeEventListener(chart, type, listener) {}\n        getDevicePixelRatio() {\n            return 1;\n        }\n        getMaximumSize(element, width, height, aspectRatio) {\n            width = Math.max(0, width || element.width);\n            height = height || element.height;\n            return {\n                width,\n                height: Math.max(0, aspectRatio ? Math.floor(width / aspectRatio) : height)\n            };\n        }\n        isAttached(canvas) {\n            return true;\n        }\n        updateConfig(config) {\n        }\n    }\n\n    class BasicPlatform extends BasePlatform {\n        acquireContext(item) {\n            return item && item.getContext && item.getContext('2d') || null;\n        }\n        updateConfig(config) {\n            config.options.animation = false;\n        }\n    }\n\n    const EXPANDO_KEY = '$chartjs';\n    const EVENT_TYPES = {\n        touchstart: 'mousedown',\n        touchmove: 'mousemove',\n        touchend: 'mouseup',\n        pointerenter: 'mouseenter',\n        pointerdown: 'mousedown',\n        pointermove: 'mousemove',\n        pointerup: 'mouseup',\n        pointerleave: 'mouseout',\n        pointerout: 'mouseout'\n    };\n    const isNullOrEmpty = (value)=>value === null || value === '';\n    function initCanvas(canvas, aspectRatio) {\n        const style = canvas.style;\n        const renderHeight = canvas.getAttribute('height');\n        const renderWidth = canvas.getAttribute('width');\n        canvas[EXPANDO_KEY] = {\n            initial: {\n                height: renderHeight,\n                width: renderWidth,\n                style: {\n                    display: style.display,\n                    height: style.height,\n                    width: style.width\n                }\n            }\n        };\n        style.display = style.display || 'block';\n        style.boxSizing = style.boxSizing || 'border-box';\n        if (isNullOrEmpty(renderWidth)) {\n            const displayWidth = readUsedSize(canvas, 'width');\n            if (displayWidth !== undefined) {\n                canvas.width = displayWidth;\n            }\n        }\n        if (isNullOrEmpty(renderHeight)) {\n            if (canvas.style.height === '') {\n                canvas.height = canvas.width / (aspectRatio || 2);\n            } else {\n                const displayHeight = readUsedSize(canvas, 'height');\n                if (displayHeight !== undefined) {\n                    canvas.height = displayHeight;\n                }\n            }\n        }\n        return canvas;\n    }\n    const eventListenerOptions = supportsEventListenerOptions ? {\n        passive: true\n    } : false;\n    function addListener(node, type, listener) {\n        node.addEventListener(type, listener, eventListenerOptions);\n    }\n    function removeListener(chart, type, listener) {\n        chart.canvas.removeEventListener(type, listener, eventListenerOptions);\n    }\n    function fromNativeEvent(event, chart) {\n        const type = EVENT_TYPES[event.type] || event.type;\n        const { x , y  } = getRelativePosition(event, chart);\n        return {\n            type,\n            chart,\n            native: event,\n            x: x !== undefined ? x : null,\n            y: y !== undefined ? y : null\n        };\n    }\n    function nodeListContains(nodeList, canvas) {\n        for (const node of nodeList){\n            if (node === canvas || node.contains(canvas)) {\n                return true;\n            }\n        }\n    }\n    function createAttachObserver(chart, type, listener) {\n        const canvas = chart.canvas;\n        const observer = new MutationObserver((entries)=>{\n            let trigger = false;\n            for (const entry of entries){\n                trigger = trigger || nodeListContains(entry.addedNodes, canvas);\n                trigger = trigger && !nodeListContains(entry.removedNodes, canvas);\n            }\n            if (trigger) {\n                listener();\n            }\n        });\n        observer.observe(document, {\n            childList: true,\n            subtree: true\n        });\n        return observer;\n    }\n    function createDetachObserver(chart, type, listener) {\n        const canvas = chart.canvas;\n        const observer = new MutationObserver((entries)=>{\n            let trigger = false;\n            for (const entry of entries){\n                trigger = trigger || nodeListContains(entry.removedNodes, canvas);\n                trigger = trigger && !nodeListContains(entry.addedNodes, canvas);\n            }\n            if (trigger) {\n                listener();\n            }\n        });\n        observer.observe(document, {\n            childList: true,\n            subtree: true\n        });\n        return observer;\n    }\n    const drpListeningCharts = new Map();\n    let oldDevicePixelRatio = 0;\n    function onWindowResize() {\n        const dpr = window.devicePixelRatio;\n        if (dpr === oldDevicePixelRatio) {\n            return;\n        }\n        oldDevicePixelRatio = dpr;\n        drpListeningCharts.forEach((resize, chart)=>{\n            if (chart.currentDevicePixelRatio !== dpr) {\n                resize();\n            }\n        });\n    }\n    function listenDevicePixelRatioChanges(chart, resize) {\n        if (!drpListeningCharts.size) {\n            window.addEventListener('resize', onWindowResize);\n        }\n        drpListeningCharts.set(chart, resize);\n    }\n    function unlistenDevicePixelRatioChanges(chart) {\n        drpListeningCharts.delete(chart);\n        if (!drpListeningCharts.size) {\n            window.removeEventListener('resize', onWindowResize);\n        }\n    }\n    function createResizeObserver(chart, type, listener) {\n        const canvas = chart.canvas;\n        const container = canvas && _getParentNode(canvas);\n        if (!container) {\n            return;\n        }\n        const resize = throttled((width, height)=>{\n            const w = container.clientWidth;\n            listener(width, height);\n            if (w < container.clientWidth) {\n                listener();\n            }\n        }, window);\n        const observer = new ResizeObserver((entries)=>{\n            const entry = entries[0];\n            const width = entry.contentRect.width;\n            const height = entry.contentRect.height;\n            if (width === 0 && height === 0) {\n                return;\n            }\n            resize(width, height);\n        });\n        observer.observe(container);\n        listenDevicePixelRatioChanges(chart, resize);\n        return observer;\n    }\n    function releaseObserver(chart, type, observer) {\n        if (observer) {\n            observer.disconnect();\n        }\n        if (type === 'resize') {\n            unlistenDevicePixelRatioChanges(chart);\n        }\n    }\n    function createProxyAndListen(chart, type, listener) {\n        const canvas = chart.canvas;\n        const proxy = throttled((event)=>{\n            if (chart.ctx !== null) {\n                listener(fromNativeEvent(event, chart));\n            }\n        }, chart);\n        addListener(canvas, type, proxy);\n        return proxy;\n    }\n    class DomPlatform extends BasePlatform {\n        acquireContext(canvas, aspectRatio) {\n            const context = canvas && canvas.getContext && canvas.getContext('2d');\n            if (context && context.canvas === canvas) {\n                initCanvas(canvas, aspectRatio);\n                return context;\n            }\n            return null;\n        }\n        releaseContext(context) {\n            const canvas = context.canvas;\n            if (!canvas[EXPANDO_KEY]) {\n                return false;\n            }\n            const initial = canvas[EXPANDO_KEY].initial;\n            [\n                'height',\n                'width'\n            ].forEach((prop)=>{\n                const value = initial[prop];\n                if (isNullOrUndef(value)) {\n                    canvas.removeAttribute(prop);\n                } else {\n                    canvas.setAttribute(prop, value);\n                }\n            });\n            const style = initial.style || {};\n            Object.keys(style).forEach((key)=>{\n                canvas.style[key] = style[key];\n            });\n            canvas.width = canvas.width;\n            delete canvas[EXPANDO_KEY];\n            return true;\n        }\n        addEventListener(chart, type, listener) {\n            this.removeEventListener(chart, type);\n            const proxies = chart.$proxies || (chart.$proxies = {});\n            const handlers = {\n                attach: createAttachObserver,\n                detach: createDetachObserver,\n                resize: createResizeObserver\n            };\n            const handler = handlers[type] || createProxyAndListen;\n            proxies[type] = handler(chart, type, listener);\n        }\n        removeEventListener(chart, type) {\n            const proxies = chart.$proxies || (chart.$proxies = {});\n            const proxy = proxies[type];\n            if (!proxy) {\n                return;\n            }\n            const handlers = {\n                attach: releaseObserver,\n                detach: releaseObserver,\n                resize: releaseObserver\n            };\n            const handler = handlers[type] || removeListener;\n            handler(chart, type, proxy);\n            proxies[type] = undefined;\n        }\n        getDevicePixelRatio() {\n            return window.devicePixelRatio;\n        }\n        getMaximumSize(canvas, width, height, aspectRatio) {\n            return getMaximumSize(canvas, width, height, aspectRatio);\n        }\n        isAttached(canvas) {\n            const container = _getParentNode(canvas);\n            return !!(container && container.isConnected);\n        }\n    }\n\n    function _detectPlatform(canvas) {\n        if (!_isDomSupported() || typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas) {\n            return BasicPlatform;\n        }\n        return DomPlatform;\n    }\n\n    var platforms = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        BasePlatform: BasePlatform,\n        BasicPlatform: BasicPlatform,\n        DomPlatform: DomPlatform,\n        _detectPlatform: _detectPlatform\n    });\n\n    const transparent = 'transparent';\n    const interpolators = {\n        boolean (from, to, factor) {\n            return factor > 0.5 ? to : from;\n        },\n        color (from, to, factor) {\n            const c0 = color(from || transparent);\n            const c1 = c0.valid && color(to || transparent);\n            return c1 && c1.valid ? c1.mix(c0, factor).hexString() : to;\n        },\n        number (from, to, factor) {\n            return from + (to - from) * factor;\n        }\n    };\n    class Animation {\n        constructor(cfg, target, prop, to){\n            const currentValue = target[prop];\n            to = resolve([\n                cfg.to,\n                to,\n                currentValue,\n                cfg.from\n            ]);\n            const from = resolve([\n                cfg.from,\n                currentValue,\n                to\n            ]);\n            this._active = true;\n            this._fn = cfg.fn || interpolators[cfg.type || typeof from];\n            this._easing = effects[cfg.easing] || effects.linear;\n            this._start = Math.floor(Date.now() + (cfg.delay || 0));\n            this._duration = this._total = Math.floor(cfg.duration);\n            this._loop = !!cfg.loop;\n            this._target = target;\n            this._prop = prop;\n            this._from = from;\n            this._to = to;\n            this._promises = undefined;\n        }\n        active() {\n            return this._active;\n        }\n        update(cfg, to, date) {\n            if (this._active) {\n                this._notify(false);\n                const currentValue = this._target[this._prop];\n                const elapsed = date - this._start;\n                const remain = this._duration - elapsed;\n                this._start = date;\n                this._duration = Math.floor(Math.max(remain, cfg.duration));\n                this._total += elapsed;\n                this._loop = !!cfg.loop;\n                this._to = resolve([\n                    cfg.to,\n                    to,\n                    currentValue,\n                    cfg.from\n                ]);\n                this._from = resolve([\n                    cfg.from,\n                    currentValue,\n                    to\n                ]);\n            }\n        }\n        cancel() {\n            if (this._active) {\n                this.tick(Date.now());\n                this._active = false;\n                this._notify(false);\n            }\n        }\n        tick(date) {\n            const elapsed = date - this._start;\n            const duration = this._duration;\n            const prop = this._prop;\n            const from = this._from;\n            const loop = this._loop;\n            const to = this._to;\n            let factor;\n            this._active = from !== to && (loop || elapsed < duration);\n            if (!this._active) {\n                this._target[prop] = to;\n                this._notify(true);\n                return;\n            }\n            if (elapsed < 0) {\n                this._target[prop] = from;\n                return;\n            }\n            factor = elapsed / duration % 2;\n            factor = loop && factor > 1 ? 2 - factor : factor;\n            factor = this._easing(Math.min(1, Math.max(0, factor)));\n            this._target[prop] = this._fn(from, to, factor);\n        }\n        wait() {\n            const promises = this._promises || (this._promises = []);\n            return new Promise((res, rej)=>{\n                promises.push({\n                    res,\n                    rej\n                });\n            });\n        }\n        _notify(resolved) {\n            const method = resolved ? 'res' : 'rej';\n            const promises = this._promises || [];\n            for(let i = 0; i < promises.length; i++){\n                promises[i][method]();\n            }\n        }\n    }\n\n    class Animations {\n        constructor(chart, config){\n            this._chart = chart;\n            this._properties = new Map();\n            this.configure(config);\n        }\n        configure(config) {\n            if (!isObject(config)) {\n                return;\n            }\n            const animationOptions = Object.keys(defaults.animation);\n            const animatedProps = this._properties;\n            Object.getOwnPropertyNames(config).forEach((key)=>{\n                const cfg = config[key];\n                if (!isObject(cfg)) {\n                    return;\n                }\n                const resolved = {};\n                for (const option of animationOptions){\n                    resolved[option] = cfg[option];\n                }\n                (isArray(cfg.properties) && cfg.properties || [\n                    key\n                ]).forEach((prop)=>{\n                    if (prop === key || !animatedProps.has(prop)) {\n                        animatedProps.set(prop, resolved);\n                    }\n                });\n            });\n        }\n        _animateOptions(target, values) {\n            const newOptions = values.options;\n            const options = resolveTargetOptions(target, newOptions);\n            if (!options) {\n                return [];\n            }\n            const animations = this._createAnimations(options, newOptions);\n            if (newOptions.$shared) {\n                awaitAll(target.options.$animations, newOptions).then(()=>{\n                    target.options = newOptions;\n                }, ()=>{\n                });\n            }\n            return animations;\n        }\n        _createAnimations(target, values) {\n            const animatedProps = this._properties;\n            const animations = [];\n            const running = target.$animations || (target.$animations = {});\n            const props = Object.keys(values);\n            const date = Date.now();\n            let i;\n            for(i = props.length - 1; i >= 0; --i){\n                const prop = props[i];\n                if (prop.charAt(0) === '$') {\n                    continue;\n                }\n                if (prop === 'options') {\n                    animations.push(...this._animateOptions(target, values));\n                    continue;\n                }\n                const value = values[prop];\n                let animation = running[prop];\n                const cfg = animatedProps.get(prop);\n                if (animation) {\n                    if (cfg && animation.active()) {\n                        animation.update(cfg, value, date);\n                        continue;\n                    } else {\n                        animation.cancel();\n                    }\n                }\n                if (!cfg || !cfg.duration) {\n                    target[prop] = value;\n                    continue;\n                }\n                running[prop] = animation = new Animation(cfg, target, prop, value);\n                animations.push(animation);\n            }\n            return animations;\n        }\n        update(target, values) {\n            if (this._properties.size === 0) {\n                Object.assign(target, values);\n                return;\n            }\n            const animations = this._createAnimations(target, values);\n            if (animations.length) {\n                animator.add(this._chart, animations);\n                return true;\n            }\n        }\n    }\n    function awaitAll(animations, properties) {\n        const running = [];\n        const keys = Object.keys(properties);\n        for(let i = 0; i < keys.length; i++){\n            const anim = animations[keys[i]];\n            if (anim && anim.active()) {\n                running.push(anim.wait());\n            }\n        }\n        return Promise.all(running);\n    }\n    function resolveTargetOptions(target, newOptions) {\n        if (!newOptions) {\n            return;\n        }\n        let options = target.options;\n        if (!options) {\n            target.options = newOptions;\n            return;\n        }\n        if (options.$shared) {\n            target.options = options = Object.assign({}, options, {\n                $shared: false,\n                $animations: {}\n            });\n        }\n        return options;\n    }\n\n    function scaleClip(scale, allowedOverflow) {\n        const opts = scale && scale.options || {};\n        const reverse = opts.reverse;\n        const min = opts.min === undefined ? allowedOverflow : 0;\n        const max = opts.max === undefined ? allowedOverflow : 0;\n        return {\n            start: reverse ? max : min,\n            end: reverse ? min : max\n        };\n    }\n    function defaultClip(xScale, yScale, allowedOverflow) {\n        if (allowedOverflow === false) {\n            return false;\n        }\n        const x = scaleClip(xScale, allowedOverflow);\n        const y = scaleClip(yScale, allowedOverflow);\n        return {\n            top: y.end,\n            right: x.end,\n            bottom: y.start,\n            left: x.start\n        };\n    }\n    function toClip(value) {\n        let t, r, b, l;\n        if (isObject(value)) {\n            t = value.top;\n            r = value.right;\n            b = value.bottom;\n            l = value.left;\n        } else {\n            t = r = b = l = value;\n        }\n        return {\n            top: t,\n            right: r,\n            bottom: b,\n            left: l,\n            disabled: value === false\n        };\n    }\n    function getSortedDatasetIndices(chart, filterVisible) {\n        const keys = [];\n        const metasets = chart._getSortedDatasetMetas(filterVisible);\n        let i, ilen;\n        for(i = 0, ilen = metasets.length; i < ilen; ++i){\n            keys.push(metasets[i].index);\n        }\n        return keys;\n    }\n    function applyStack(stack, value, dsIndex, options = {}) {\n        const keys = stack.keys;\n        const singleMode = options.mode === 'single';\n        let i, ilen, datasetIndex, otherValue;\n        if (value === null) {\n            return;\n        }\n        for(i = 0, ilen = keys.length; i < ilen; ++i){\n            datasetIndex = +keys[i];\n            if (datasetIndex === dsIndex) {\n                if (options.all) {\n                    continue;\n                }\n                break;\n            }\n            otherValue = stack.values[datasetIndex];\n            if (isNumberFinite(otherValue) && (singleMode || value === 0 || sign(value) === sign(otherValue))) {\n                value += otherValue;\n            }\n        }\n        return value;\n    }\n    function convertObjectDataToArray(data) {\n        const keys = Object.keys(data);\n        const adata = new Array(keys.length);\n        let i, ilen, key;\n        for(i = 0, ilen = keys.length; i < ilen; ++i){\n            key = keys[i];\n            adata[i] = {\n                x: key,\n                y: data[key]\n            };\n        }\n        return adata;\n    }\n    function isStacked(scale, meta) {\n        const stacked = scale && scale.options.stacked;\n        return stacked || stacked === undefined && meta.stack !== undefined;\n    }\n    function getStackKey(indexScale, valueScale, meta) {\n        return `${indexScale.id}.${valueScale.id}.${meta.stack || meta.type}`;\n    }\n    function getUserBounds(scale) {\n        const { min , max , minDefined , maxDefined  } = scale.getUserBounds();\n        return {\n            min: minDefined ? min : Number.NEGATIVE_INFINITY,\n            max: maxDefined ? max : Number.POSITIVE_INFINITY\n        };\n    }\n    function getOrCreateStack(stacks, stackKey, indexValue) {\n        const subStack = stacks[stackKey] || (stacks[stackKey] = {});\n        return subStack[indexValue] || (subStack[indexValue] = {});\n    }\n    function getLastIndexInStack(stack, vScale, positive, type) {\n        for (const meta of vScale.getMatchingVisibleMetas(type).reverse()){\n            const value = stack[meta.index];\n            if (positive && value > 0 || !positive && value < 0) {\n                return meta.index;\n            }\n        }\n        return null;\n    }\n    function updateStacks(controller, parsed) {\n        const { chart , _cachedMeta: meta  } = controller;\n        const stacks = chart._stacks || (chart._stacks = {});\n        const { iScale , vScale , index: datasetIndex  } = meta;\n        const iAxis = iScale.axis;\n        const vAxis = vScale.axis;\n        const key = getStackKey(iScale, vScale, meta);\n        const ilen = parsed.length;\n        let stack;\n        for(let i = 0; i < ilen; ++i){\n            const item = parsed[i];\n            const { [iAxis]: index , [vAxis]: value  } = item;\n            const itemStacks = item._stacks || (item._stacks = {});\n            stack = itemStacks[vAxis] = getOrCreateStack(stacks, key, index);\n            stack[datasetIndex] = value;\n            stack._top = getLastIndexInStack(stack, vScale, true, meta.type);\n            stack._bottom = getLastIndexInStack(stack, vScale, false, meta.type);\n            const visualValues = stack._visualValues || (stack._visualValues = {});\n            visualValues[datasetIndex] = value;\n        }\n    }\n    function getFirstScaleId(chart, axis) {\n        const scales = chart.scales;\n        return Object.keys(scales).filter((key)=>scales[key].axis === axis).shift();\n    }\n    function createDatasetContext(parent, index) {\n        return createContext(parent, {\n            active: false,\n            dataset: undefined,\n            datasetIndex: index,\n            index,\n            mode: 'default',\n            type: 'dataset'\n        });\n    }\n    function createDataContext(parent, index, element) {\n        return createContext(parent, {\n            active: false,\n            dataIndex: index,\n            parsed: undefined,\n            raw: undefined,\n            element,\n            index,\n            mode: 'default',\n            type: 'data'\n        });\n    }\n    function clearStacks(meta, items) {\n        const datasetIndex = meta.controller.index;\n        const axis = meta.vScale && meta.vScale.axis;\n        if (!axis) {\n            return;\n        }\n        items = items || meta._parsed;\n        for (const parsed of items){\n            const stacks = parsed._stacks;\n            if (!stacks || stacks[axis] === undefined || stacks[axis][datasetIndex] === undefined) {\n                return;\n            }\n            delete stacks[axis][datasetIndex];\n            if (stacks[axis]._visualValues !== undefined && stacks[axis]._visualValues[datasetIndex] !== undefined) {\n                delete stacks[axis]._visualValues[datasetIndex];\n            }\n        }\n    }\n    const isDirectUpdateMode = (mode)=>mode === 'reset' || mode === 'none';\n    const cloneIfNotShared = (cached, shared)=>shared ? cached : Object.assign({}, cached);\n    const createStack = (canStack, meta, chart)=>canStack && !meta.hidden && meta._stacked && {\n        keys: getSortedDatasetIndices(chart, true),\n        values: null\n    };\n    class DatasetController {\n        static defaults = {};\n        static datasetElementType = null;\n        static dataElementType = null;\n        constructor(chart, datasetIndex){\n            this.chart = chart;\n            this._ctx = chart.ctx;\n            this.index = datasetIndex;\n            this._cachedDataOpts = {};\n            this._cachedMeta = this.getMeta();\n            this._type = this._cachedMeta.type;\n            this.options = undefined;\n            this._parsing = false;\n            this._data = undefined;\n            this._objectData = undefined;\n            this._sharedOptions = undefined;\n            this._drawStart = undefined;\n            this._drawCount = undefined;\n            this.enableOptionSharing = false;\n            this.supportsDecimation = false;\n            this.$context = undefined;\n            this._syncList = [];\n            this.datasetElementType = new.target.datasetElementType;\n            this.dataElementType = new.target.dataElementType;\n            this.initialize();\n        }\n        initialize() {\n            const meta = this._cachedMeta;\n            this.configure();\n            this.linkScales();\n            meta._stacked = isStacked(meta.vScale, meta);\n            this.addElements();\n            if (this.options.fill && !this.chart.isPluginEnabled('filler')) {\n                console.warn(\"Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options\");\n            }\n        }\n        updateIndex(datasetIndex) {\n            if (this.index !== datasetIndex) {\n                clearStacks(this._cachedMeta);\n            }\n            this.index = datasetIndex;\n        }\n        linkScales() {\n            const chart = this.chart;\n            const meta = this._cachedMeta;\n            const dataset = this.getDataset();\n            const chooseId = (axis, x, y, r)=>axis === 'x' ? x : axis === 'r' ? r : y;\n            const xid = meta.xAxisID = valueOrDefault(dataset.xAxisID, getFirstScaleId(chart, 'x'));\n            const yid = meta.yAxisID = valueOrDefault(dataset.yAxisID, getFirstScaleId(chart, 'y'));\n            const rid = meta.rAxisID = valueOrDefault(dataset.rAxisID, getFirstScaleId(chart, 'r'));\n            const indexAxis = meta.indexAxis;\n            const iid = meta.iAxisID = chooseId(indexAxis, xid, yid, rid);\n            const vid = meta.vAxisID = chooseId(indexAxis, yid, xid, rid);\n            meta.xScale = this.getScaleForId(xid);\n            meta.yScale = this.getScaleForId(yid);\n            meta.rScale = this.getScaleForId(rid);\n            meta.iScale = this.getScaleForId(iid);\n            meta.vScale = this.getScaleForId(vid);\n        }\n        getDataset() {\n            return this.chart.data.datasets[this.index];\n        }\n        getMeta() {\n            return this.chart.getDatasetMeta(this.index);\n        }\n        getScaleForId(scaleID) {\n            return this.chart.scales[scaleID];\n        }\n        _getOtherScale(scale) {\n            const meta = this._cachedMeta;\n            return scale === meta.iScale ? meta.vScale : meta.iScale;\n        }\n        reset() {\n            this._update('reset');\n        }\n        _destroy() {\n            const meta = this._cachedMeta;\n            if (this._data) {\n                unlistenArrayEvents(this._data, this);\n            }\n            if (meta._stacked) {\n                clearStacks(meta);\n            }\n        }\n        _dataCheck() {\n            const dataset = this.getDataset();\n            const data = dataset.data || (dataset.data = []);\n            const _data = this._data;\n            if (isObject(data)) {\n                this._data = convertObjectDataToArray(data);\n            } else if (_data !== data) {\n                if (_data) {\n                    unlistenArrayEvents(_data, this);\n                    const meta = this._cachedMeta;\n                    clearStacks(meta);\n                    meta._parsed = [];\n                }\n                if (data && Object.isExtensible(data)) {\n                    listenArrayEvents(data, this);\n                }\n                this._syncList = [];\n                this._data = data;\n            }\n        }\n        addElements() {\n            const meta = this._cachedMeta;\n            this._dataCheck();\n            if (this.datasetElementType) {\n                meta.dataset = new this.datasetElementType();\n            }\n        }\n        buildOrUpdateElements(resetNewElements) {\n            const meta = this._cachedMeta;\n            const dataset = this.getDataset();\n            let stackChanged = false;\n            this._dataCheck();\n            const oldStacked = meta._stacked;\n            meta._stacked = isStacked(meta.vScale, meta);\n            if (meta.stack !== dataset.stack) {\n                stackChanged = true;\n                clearStacks(meta);\n                meta.stack = dataset.stack;\n            }\n            this._resyncElements(resetNewElements);\n            if (stackChanged || oldStacked !== meta._stacked) {\n                updateStacks(this, meta._parsed);\n            }\n        }\n        configure() {\n            const config = this.chart.config;\n            const scopeKeys = config.datasetScopeKeys(this._type);\n            const scopes = config.getOptionScopes(this.getDataset(), scopeKeys, true);\n            this.options = config.createResolver(scopes, this.getContext());\n            this._parsing = this.options.parsing;\n            this._cachedDataOpts = {};\n        }\n        parse(start, count) {\n            const { _cachedMeta: meta , _data: data  } = this;\n            const { iScale , _stacked  } = meta;\n            const iAxis = iScale.axis;\n            let sorted = start === 0 && count === data.length ? true : meta._sorted;\n            let prev = start > 0 && meta._parsed[start - 1];\n            let i, cur, parsed;\n            if (this._parsing === false) {\n                meta._parsed = data;\n                meta._sorted = true;\n                parsed = data;\n            } else {\n                if (isArray(data[start])) {\n                    parsed = this.parseArrayData(meta, data, start, count);\n                } else if (isObject(data[start])) {\n                    parsed = this.parseObjectData(meta, data, start, count);\n                } else {\n                    parsed = this.parsePrimitiveData(meta, data, start, count);\n                }\n                const isNotInOrderComparedToPrev = ()=>cur[iAxis] === null || prev && cur[iAxis] < prev[iAxis];\n                for(i = 0; i < count; ++i){\n                    meta._parsed[i + start] = cur = parsed[i];\n                    if (sorted) {\n                        if (isNotInOrderComparedToPrev()) {\n                            sorted = false;\n                        }\n                        prev = cur;\n                    }\n                }\n                meta._sorted = sorted;\n            }\n            if (_stacked) {\n                updateStacks(this, parsed);\n            }\n        }\n        parsePrimitiveData(meta, data, start, count) {\n            const { iScale , vScale  } = meta;\n            const iAxis = iScale.axis;\n            const vAxis = vScale.axis;\n            const labels = iScale.getLabels();\n            const singleScale = iScale === vScale;\n            const parsed = new Array(count);\n            let i, ilen, index;\n            for(i = 0, ilen = count; i < ilen; ++i){\n                index = i + start;\n                parsed[i] = {\n                    [iAxis]: singleScale || iScale.parse(labels[index], index),\n                    [vAxis]: vScale.parse(data[index], index)\n                };\n            }\n            return parsed;\n        }\n        parseArrayData(meta, data, start, count) {\n            const { xScale , yScale  } = meta;\n            const parsed = new Array(count);\n            let i, ilen, index, item;\n            for(i = 0, ilen = count; i < ilen; ++i){\n                index = i + start;\n                item = data[index];\n                parsed[i] = {\n                    x: xScale.parse(item[0], index),\n                    y: yScale.parse(item[1], index)\n                };\n            }\n            return parsed;\n        }\n        parseObjectData(meta, data, start, count) {\n            const { xScale , yScale  } = meta;\n            const { xAxisKey ='x' , yAxisKey ='y'  } = this._parsing;\n            const parsed = new Array(count);\n            let i, ilen, index, item;\n            for(i = 0, ilen = count; i < ilen; ++i){\n                index = i + start;\n                item = data[index];\n                parsed[i] = {\n                    x: xScale.parse(resolveObjectKey(item, xAxisKey), index),\n                    y: yScale.parse(resolveObjectKey(item, yAxisKey), index)\n                };\n            }\n            return parsed;\n        }\n        getParsed(index) {\n            return this._cachedMeta._parsed[index];\n        }\n        getDataElement(index) {\n            return this._cachedMeta.data[index];\n        }\n        applyStack(scale, parsed, mode) {\n            const chart = this.chart;\n            const meta = this._cachedMeta;\n            const value = parsed[scale.axis];\n            const stack = {\n                keys: getSortedDatasetIndices(chart, true),\n                values: parsed._stacks[scale.axis]._visualValues\n            };\n            return applyStack(stack, value, meta.index, {\n                mode\n            });\n        }\n        updateRangeFromParsed(range, scale, parsed, stack) {\n            const parsedValue = parsed[scale.axis];\n            let value = parsedValue === null ? NaN : parsedValue;\n            const values = stack && parsed._stacks[scale.axis];\n            if (stack && values) {\n                stack.values = values;\n                value = applyStack(stack, parsedValue, this._cachedMeta.index);\n            }\n            range.min = Math.min(range.min, value);\n            range.max = Math.max(range.max, value);\n        }\n        getMinMax(scale, canStack) {\n            const meta = this._cachedMeta;\n            const _parsed = meta._parsed;\n            const sorted = meta._sorted && scale === meta.iScale;\n            const ilen = _parsed.length;\n            const otherScale = this._getOtherScale(scale);\n            const stack = createStack(canStack, meta, this.chart);\n            const range = {\n                min: Number.POSITIVE_INFINITY,\n                max: Number.NEGATIVE_INFINITY\n            };\n            const { min: otherMin , max: otherMax  } = getUserBounds(otherScale);\n            let i, parsed;\n            function _skip() {\n                parsed = _parsed[i];\n                const otherValue = parsed[otherScale.axis];\n                return !isNumberFinite(parsed[scale.axis]) || otherMin > otherValue || otherMax < otherValue;\n            }\n            for(i = 0; i < ilen; ++i){\n                if (_skip()) {\n                    continue;\n                }\n                this.updateRangeFromParsed(range, scale, parsed, stack);\n                if (sorted) {\n                    break;\n                }\n            }\n            if (sorted) {\n                for(i = ilen - 1; i >= 0; --i){\n                    if (_skip()) {\n                        continue;\n                    }\n                    this.updateRangeFromParsed(range, scale, parsed, stack);\n                    break;\n                }\n            }\n            return range;\n        }\n        getAllParsedValues(scale) {\n            const parsed = this._cachedMeta._parsed;\n            const values = [];\n            let i, ilen, value;\n            for(i = 0, ilen = parsed.length; i < ilen; ++i){\n                value = parsed[i][scale.axis];\n                if (isNumberFinite(value)) {\n                    values.push(value);\n                }\n            }\n            return values;\n        }\n        getMaxOverflow() {\n            return false;\n        }\n        getLabelAndValue(index) {\n            const meta = this._cachedMeta;\n            const iScale = meta.iScale;\n            const vScale = meta.vScale;\n            const parsed = this.getParsed(index);\n            return {\n                label: iScale ? '' + iScale.getLabelForValue(parsed[iScale.axis]) : '',\n                value: vScale ? '' + vScale.getLabelForValue(parsed[vScale.axis]) : ''\n            };\n        }\n        _update(mode) {\n            const meta = this._cachedMeta;\n            this.update(mode || 'default');\n            meta._clip = toClip(valueOrDefault(this.options.clip, defaultClip(meta.xScale, meta.yScale, this.getMaxOverflow())));\n        }\n        update(mode) {}\n        draw() {\n            const ctx = this._ctx;\n            const chart = this.chart;\n            const meta = this._cachedMeta;\n            const elements = meta.data || [];\n            const area = chart.chartArea;\n            const active = [];\n            const start = this._drawStart || 0;\n            const count = this._drawCount || elements.length - start;\n            const drawActiveElementsOnTop = this.options.drawActiveElementsOnTop;\n            let i;\n            if (meta.dataset) {\n                meta.dataset.draw(ctx, area, start, count);\n            }\n            for(i = start; i < start + count; ++i){\n                const element = elements[i];\n                if (element.hidden) {\n                    continue;\n                }\n                if (element.active && drawActiveElementsOnTop) {\n                    active.push(element);\n                } else {\n                    element.draw(ctx, area);\n                }\n            }\n            for(i = 0; i < active.length; ++i){\n                active[i].draw(ctx, area);\n            }\n        }\n        getStyle(index, active) {\n            const mode = active ? 'active' : 'default';\n            return index === undefined && this._cachedMeta.dataset ? this.resolveDatasetElementOptions(mode) : this.resolveDataElementOptions(index || 0, mode);\n        }\n        getContext(index, active, mode) {\n            const dataset = this.getDataset();\n            let context;\n            if (index >= 0 && index < this._cachedMeta.data.length) {\n                const element = this._cachedMeta.data[index];\n                context = element.$context || (element.$context = createDataContext(this.getContext(), index, element));\n                context.parsed = this.getParsed(index);\n                context.raw = dataset.data[index];\n                context.index = context.dataIndex = index;\n            } else {\n                context = this.$context || (this.$context = createDatasetContext(this.chart.getContext(), this.index));\n                context.dataset = dataset;\n                context.index = context.datasetIndex = this.index;\n            }\n            context.active = !!active;\n            context.mode = mode;\n            return context;\n        }\n        resolveDatasetElementOptions(mode) {\n            return this._resolveElementOptions(this.datasetElementType.id, mode);\n        }\n        resolveDataElementOptions(index, mode) {\n            return this._resolveElementOptions(this.dataElementType.id, mode, index);\n        }\n        _resolveElementOptions(elementType, mode = 'default', index) {\n            const active = mode === 'active';\n            const cache = this._cachedDataOpts;\n            const cacheKey = elementType + '-' + mode;\n            const cached = cache[cacheKey];\n            const sharing = this.enableOptionSharing && defined(index);\n            if (cached) {\n                return cloneIfNotShared(cached, sharing);\n            }\n            const config = this.chart.config;\n            const scopeKeys = config.datasetElementScopeKeys(this._type, elementType);\n            const prefixes = active ? [\n                `${elementType}Hover`,\n                'hover',\n                elementType,\n                ''\n            ] : [\n                elementType,\n                ''\n            ];\n            const scopes = config.getOptionScopes(this.getDataset(), scopeKeys);\n            const names = Object.keys(defaults.elements[elementType]);\n            const context = ()=>this.getContext(index, active, mode);\n            const values = config.resolveNamedOptions(scopes, names, context, prefixes);\n            if (values.$shared) {\n                values.$shared = sharing;\n                cache[cacheKey] = Object.freeze(cloneIfNotShared(values, sharing));\n            }\n            return values;\n        }\n        _resolveAnimations(index, transition, active) {\n            const chart = this.chart;\n            const cache = this._cachedDataOpts;\n            const cacheKey = `animation-${transition}`;\n            const cached = cache[cacheKey];\n            if (cached) {\n                return cached;\n            }\n            let options;\n            if (chart.options.animation !== false) {\n                const config = this.chart.config;\n                const scopeKeys = config.datasetAnimationScopeKeys(this._type, transition);\n                const scopes = config.getOptionScopes(this.getDataset(), scopeKeys);\n                options = config.createResolver(scopes, this.getContext(index, active, transition));\n            }\n            const animations = new Animations(chart, options && options.animations);\n            if (options && options._cacheable) {\n                cache[cacheKey] = Object.freeze(animations);\n            }\n            return animations;\n        }\n        getSharedOptions(options) {\n            if (!options.$shared) {\n                return;\n            }\n            return this._sharedOptions || (this._sharedOptions = Object.assign({}, options));\n        }\n        includeOptions(mode, sharedOptions) {\n            return !sharedOptions || isDirectUpdateMode(mode) || this.chart._animationsDisabled;\n        }\n        _getSharedOptions(start, mode) {\n            const firstOpts = this.resolveDataElementOptions(start, mode);\n            const previouslySharedOptions = this._sharedOptions;\n            const sharedOptions = this.getSharedOptions(firstOpts);\n            const includeOptions = this.includeOptions(mode, sharedOptions) || sharedOptions !== previouslySharedOptions;\n            this.updateSharedOptions(sharedOptions, mode, firstOpts);\n            return {\n                sharedOptions,\n                includeOptions\n            };\n        }\n        updateElement(element, index, properties, mode) {\n            if (isDirectUpdateMode(mode)) {\n                Object.assign(element, properties);\n            } else {\n                this._resolveAnimations(index, mode).update(element, properties);\n            }\n        }\n        updateSharedOptions(sharedOptions, mode, newOptions) {\n            if (sharedOptions && !isDirectUpdateMode(mode)) {\n                this._resolveAnimations(undefined, mode).update(sharedOptions, newOptions);\n            }\n        }\n        _setStyle(element, index, mode, active) {\n            element.active = active;\n            const options = this.getStyle(index, active);\n            this._resolveAnimations(index, mode, active).update(element, {\n                options: !active && this.getSharedOptions(options) || options\n            });\n        }\n        removeHoverStyle(element, datasetIndex, index) {\n            this._setStyle(element, index, 'active', false);\n        }\n        setHoverStyle(element, datasetIndex, index) {\n            this._setStyle(element, index, 'active', true);\n        }\n        _removeDatasetHoverStyle() {\n            const element = this._cachedMeta.dataset;\n            if (element) {\n                this._setStyle(element, undefined, 'active', false);\n            }\n        }\n        _setDatasetHoverStyle() {\n            const element = this._cachedMeta.dataset;\n            if (element) {\n                this._setStyle(element, undefined, 'active', true);\n            }\n        }\n        _resyncElements(resetNewElements) {\n            const data = this._data;\n            const elements = this._cachedMeta.data;\n            for (const [method, arg1, arg2] of this._syncList){\n                this[method](arg1, arg2);\n            }\n            this._syncList = [];\n            const numMeta = elements.length;\n            const numData = data.length;\n            const count = Math.min(numData, numMeta);\n            if (count) {\n                this.parse(0, count);\n            }\n            if (numData > numMeta) {\n                this._insertElements(numMeta, numData - numMeta, resetNewElements);\n            } else if (numData < numMeta) {\n                this._removeElements(numData, numMeta - numData);\n            }\n        }\n        _insertElements(start, count, resetNewElements = true) {\n            const meta = this._cachedMeta;\n            const data = meta.data;\n            const end = start + count;\n            let i;\n            const move = (arr)=>{\n                arr.length += count;\n                for(i = arr.length - 1; i >= end; i--){\n                    arr[i] = arr[i - count];\n                }\n            };\n            move(data);\n            for(i = start; i < end; ++i){\n                data[i] = new this.dataElementType();\n            }\n            if (this._parsing) {\n                move(meta._parsed);\n            }\n            this.parse(start, count);\n            if (resetNewElements) {\n                this.updateElements(data, start, count, 'reset');\n            }\n        }\n        updateElements(element, start, count, mode) {}\n        _removeElements(start, count) {\n            const meta = this._cachedMeta;\n            if (this._parsing) {\n                const removed = meta._parsed.splice(start, count);\n                if (meta._stacked) {\n                    clearStacks(meta, removed);\n                }\n            }\n            meta.data.splice(start, count);\n        }\n        _sync(args) {\n            if (this._parsing) {\n                this._syncList.push(args);\n            } else {\n                const [method, arg1, arg2] = args;\n                this[method](arg1, arg2);\n            }\n            this.chart._dataChanges.push([\n                this.index,\n                ...args\n            ]);\n        }\n        _onDataPush() {\n            const count = arguments.length;\n            this._sync([\n                '_insertElements',\n                this.getDataset().data.length - count,\n                count\n            ]);\n        }\n        _onDataPop() {\n            this._sync([\n                '_removeElements',\n                this._cachedMeta.data.length - 1,\n                1\n            ]);\n        }\n        _onDataShift() {\n            this._sync([\n                '_removeElements',\n                0,\n                1\n            ]);\n        }\n        _onDataSplice(start, count) {\n            if (count) {\n                this._sync([\n                    '_removeElements',\n                    start,\n                    count\n                ]);\n            }\n            const newCount = arguments.length - 2;\n            if (newCount) {\n                this._sync([\n                    '_insertElements',\n                    start,\n                    newCount\n                ]);\n            }\n        }\n        _onDataUnshift() {\n            this._sync([\n                '_insertElements',\n                0,\n                arguments.length\n            ]);\n        }\n    }\n\n    class Element {\n        static defaults = {};\n        static defaultRoutes = undefined;\n        x;\n        y;\n        active = false;\n        options;\n        $animations;\n        tooltipPosition(useFinalPosition) {\n            const { x , y  } = this.getProps([\n                'x',\n                'y'\n            ], useFinalPosition);\n            return {\n                x,\n                y\n            };\n        }\n        hasValue() {\n            return isNumber(this.x) && isNumber(this.y);\n        }\n        getProps(props, final) {\n            const anims = this.$animations;\n            if (!final || !anims) {\n                // let's not create an object, if not needed\n                return this;\n            }\n            const ret = {};\n            props.forEach((prop)=>{\n                ret[prop] = anims[prop] && anims[prop].active() ? anims[prop]._to : this[prop];\n            });\n            return ret;\n        }\n    }\n\n    function autoSkip(scale, ticks) {\n        const tickOpts = scale.options.ticks;\n        const determinedMaxTicks = determineMaxTicks(scale);\n        const ticksLimit = Math.min(tickOpts.maxTicksLimit || determinedMaxTicks, determinedMaxTicks);\n        const majorIndices = tickOpts.major.enabled ? getMajorIndices(ticks) : [];\n        const numMajorIndices = majorIndices.length;\n        const first = majorIndices[0];\n        const last = majorIndices[numMajorIndices - 1];\n        const newTicks = [];\n        if (numMajorIndices > ticksLimit) {\n            skipMajors(ticks, newTicks, majorIndices, numMajorIndices / ticksLimit);\n            return newTicks;\n        }\n        const spacing = calculateSpacing(majorIndices, ticks, ticksLimit);\n        if (numMajorIndices > 0) {\n            let i, ilen;\n            const avgMajorSpacing = numMajorIndices > 1 ? Math.round((last - first) / (numMajorIndices - 1)) : null;\n            skip(ticks, newTicks, spacing, isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first);\n            for(i = 0, ilen = numMajorIndices - 1; i < ilen; i++){\n                skip(ticks, newTicks, spacing, majorIndices[i], majorIndices[i + 1]);\n            }\n            skip(ticks, newTicks, spacing, last, isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing);\n            return newTicks;\n        }\n        skip(ticks, newTicks, spacing);\n        return newTicks;\n    }\n    function determineMaxTicks(scale) {\n        const offset = scale.options.offset;\n        const tickLength = scale._tickSize();\n        const maxScale = scale._length / tickLength + (offset ? 0 : 1);\n        const maxChart = scale._maxLength / tickLength;\n        return Math.floor(Math.min(maxScale, maxChart));\n    }\n    function calculateSpacing(majorIndices, ticks, ticksLimit) {\n        const evenMajorSpacing = getEvenSpacing(majorIndices);\n        const spacing = ticks.length / ticksLimit;\n        if (!evenMajorSpacing) {\n            return Math.max(spacing, 1);\n        }\n        const factors = _factorize(evenMajorSpacing);\n        for(let i = 0, ilen = factors.length - 1; i < ilen; i++){\n            const factor = factors[i];\n            if (factor > spacing) {\n                return factor;\n            }\n        }\n        return Math.max(spacing, 1);\n    }\n    function getMajorIndices(ticks) {\n        const result = [];\n        let i, ilen;\n        for(i = 0, ilen = ticks.length; i < ilen; i++){\n            if (ticks[i].major) {\n                result.push(i);\n            }\n        }\n        return result;\n    }\n    function skipMajors(ticks, newTicks, majorIndices, spacing) {\n        let count = 0;\n        let next = majorIndices[0];\n        let i;\n        spacing = Math.ceil(spacing);\n        for(i = 0; i < ticks.length; i++){\n            if (i === next) {\n                newTicks.push(ticks[i]);\n                count++;\n                next = majorIndices[count * spacing];\n            }\n        }\n    }\n    function skip(ticks, newTicks, spacing, majorStart, majorEnd) {\n        const start = valueOrDefault(majorStart, 0);\n        const end = Math.min(valueOrDefault(majorEnd, ticks.length), ticks.length);\n        let count = 0;\n        let length, i, next;\n        spacing = Math.ceil(spacing);\n        if (majorEnd) {\n            length = majorEnd - majorStart;\n            spacing = length / Math.floor(length / spacing);\n        }\n        next = start;\n        while(next < 0){\n            count++;\n            next = Math.round(start + count * spacing);\n        }\n        for(i = Math.max(start, 0); i < end; i++){\n            if (i === next) {\n                newTicks.push(ticks[i]);\n                count++;\n                next = Math.round(start + count * spacing);\n            }\n        }\n    }\n    function getEvenSpacing(arr) {\n        const len = arr.length;\n        let i, diff;\n        if (len < 2) {\n            return false;\n        }\n        for(diff = arr[0], i = 1; i < len; ++i){\n            if (arr[i] - arr[i - 1] !== diff) {\n                return false;\n            }\n        }\n        return diff;\n    }\n\n    const reverseAlign = (align)=>align === 'left' ? 'right' : align === 'right' ? 'left' : align;\n    const offsetFromEdge = (scale, edge, offset)=>edge === 'top' || edge === 'left' ? scale[edge] + offset : scale[edge] - offset;\n    const getTicksLimit = (ticksLength, maxTicksLimit)=>Math.min(maxTicksLimit || ticksLength, ticksLength);\n    function sample(arr, numItems) {\n        const result = [];\n        const increment = arr.length / numItems;\n        const len = arr.length;\n        let i = 0;\n        for(; i < len; i += increment){\n            result.push(arr[Math.floor(i)]);\n        }\n        return result;\n    }\n    function getPixelForGridLine(scale, index, offsetGridLines) {\n        const length = scale.ticks.length;\n        const validIndex = Math.min(index, length - 1);\n        const start = scale._startPixel;\n        const end = scale._endPixel;\n        const epsilon = 1e-6;\n        let lineValue = scale.getPixelForTick(validIndex);\n        let offset;\n        if (offsetGridLines) {\n            if (length === 1) {\n                offset = Math.max(lineValue - start, end - lineValue);\n            } else if (index === 0) {\n                offset = (scale.getPixelForTick(1) - lineValue) / 2;\n            } else {\n                offset = (lineValue - scale.getPixelForTick(validIndex - 1)) / 2;\n            }\n            lineValue += validIndex < index ? offset : -offset;\n            if (lineValue < start - epsilon || lineValue > end + epsilon) {\n                return;\n            }\n        }\n        return lineValue;\n    }\n    function garbageCollect(caches, length) {\n        each(caches, (cache)=>{\n            const gc = cache.gc;\n            const gcLen = gc.length / 2;\n            let i;\n            if (gcLen > length) {\n                for(i = 0; i < gcLen; ++i){\n                    delete cache.data[gc[i]];\n                }\n                gc.splice(0, gcLen);\n            }\n        });\n    }\n    function getTickMarkLength(options) {\n        return options.drawTicks ? options.tickLength : 0;\n    }\n    function getTitleHeight(options, fallback) {\n        if (!options.display) {\n            return 0;\n        }\n        const font = toFont(options.font, fallback);\n        const padding = toPadding(options.padding);\n        const lines = isArray(options.text) ? options.text.length : 1;\n        return lines * font.lineHeight + padding.height;\n    }\n    function createScaleContext(parent, scale) {\n        return createContext(parent, {\n            scale,\n            type: 'scale'\n        });\n    }\n    function createTickContext(parent, index, tick) {\n        return createContext(parent, {\n            tick,\n            index,\n            type: 'tick'\n        });\n    }\n    function titleAlign(align, position, reverse) {\n        let ret = _toLeftRightCenter(align);\n        if (reverse && position !== 'right' || !reverse && position === 'right') {\n            ret = reverseAlign(ret);\n        }\n        return ret;\n    }\n    function titleArgs(scale, offset, position, align) {\n        const { top , left , bottom , right , chart  } = scale;\n        const { chartArea , scales  } = chart;\n        let rotation = 0;\n        let maxWidth, titleX, titleY;\n        const height = bottom - top;\n        const width = right - left;\n        if (scale.isHorizontal()) {\n            titleX = _alignStartEnd(align, left, right);\n            if (isObject(position)) {\n                const positionAxisID = Object.keys(position)[0];\n                const value = position[positionAxisID];\n                titleY = scales[positionAxisID].getPixelForValue(value) + height - offset;\n            } else if (position === 'center') {\n                titleY = (chartArea.bottom + chartArea.top) / 2 + height - offset;\n            } else {\n                titleY = offsetFromEdge(scale, position, offset);\n            }\n            maxWidth = right - left;\n        } else {\n            if (isObject(position)) {\n                const positionAxisID = Object.keys(position)[0];\n                const value = position[positionAxisID];\n                titleX = scales[positionAxisID].getPixelForValue(value) - width + offset;\n            } else if (position === 'center') {\n                titleX = (chartArea.left + chartArea.right) / 2 - width + offset;\n            } else {\n                titleX = offsetFromEdge(scale, position, offset);\n            }\n            titleY = _alignStartEnd(align, bottom, top);\n            rotation = position === 'left' ? -HALF_PI : HALF_PI;\n        }\n        return {\n            titleX,\n            titleY,\n            maxWidth,\n            rotation\n        };\n    }\n    class Scale extends Element {\n        constructor(cfg){\n            super();\n            this.id = cfg.id;\n            this.type = cfg.type;\n            this.options = undefined;\n            this.ctx = cfg.ctx;\n            this.chart = cfg.chart;\n            this.top = undefined;\n            this.bottom = undefined;\n            this.left = undefined;\n            this.right = undefined;\n            this.width = undefined;\n            this.height = undefined;\n            this._margins = {\n                left: 0,\n                right: 0,\n                top: 0,\n                bottom: 0\n            };\n            this.maxWidth = undefined;\n            this.maxHeight = undefined;\n            this.paddingTop = undefined;\n            this.paddingBottom = undefined;\n            this.paddingLeft = undefined;\n            this.paddingRight = undefined;\n            this.axis = undefined;\n            this.labelRotation = undefined;\n            this.min = undefined;\n            this.max = undefined;\n            this._range = undefined;\n            this.ticks = [];\n            this._gridLineItems = null;\n            this._labelItems = null;\n            this._labelSizes = null;\n            this._length = 0;\n            this._maxLength = 0;\n            this._longestTextCache = {};\n            this._startPixel = undefined;\n            this._endPixel = undefined;\n            this._reversePixels = false;\n            this._userMax = undefined;\n            this._userMin = undefined;\n            this._suggestedMax = undefined;\n            this._suggestedMin = undefined;\n            this._ticksLength = 0;\n            this._borderValue = 0;\n            this._cache = {};\n            this._dataLimitsCached = false;\n            this.$context = undefined;\n        }\n        init(options) {\n            this.options = options.setContext(this.getContext());\n            this.axis = options.axis;\n            this._userMin = this.parse(options.min);\n            this._userMax = this.parse(options.max);\n            this._suggestedMin = this.parse(options.suggestedMin);\n            this._suggestedMax = this.parse(options.suggestedMax);\n        }\n        parse(raw, index) {\n            return raw;\n        }\n        getUserBounds() {\n            let { _userMin , _userMax , _suggestedMin , _suggestedMax  } = this;\n            _userMin = finiteOrDefault(_userMin, Number.POSITIVE_INFINITY);\n            _userMax = finiteOrDefault(_userMax, Number.NEGATIVE_INFINITY);\n            _suggestedMin = finiteOrDefault(_suggestedMin, Number.POSITIVE_INFINITY);\n            _suggestedMax = finiteOrDefault(_suggestedMax, Number.NEGATIVE_INFINITY);\n            return {\n                min: finiteOrDefault(_userMin, _suggestedMin),\n                max: finiteOrDefault(_userMax, _suggestedMax),\n                minDefined: isNumberFinite(_userMin),\n                maxDefined: isNumberFinite(_userMax)\n            };\n        }\n        getMinMax(canStack) {\n            let { min , max , minDefined , maxDefined  } = this.getUserBounds();\n            let range;\n            if (minDefined && maxDefined) {\n                return {\n                    min,\n                    max\n                };\n            }\n            const metas = this.getMatchingVisibleMetas();\n            for(let i = 0, ilen = metas.length; i < ilen; ++i){\n                range = metas[i].controller.getMinMax(this, canStack);\n                if (!minDefined) {\n                    min = Math.min(min, range.min);\n                }\n                if (!maxDefined) {\n                    max = Math.max(max, range.max);\n                }\n            }\n            min = maxDefined && min > max ? max : min;\n            max = minDefined && min > max ? min : max;\n            return {\n                min: finiteOrDefault(min, finiteOrDefault(max, min)),\n                max: finiteOrDefault(max, finiteOrDefault(min, max))\n            };\n        }\n        getPadding() {\n            return {\n                left: this.paddingLeft || 0,\n                top: this.paddingTop || 0,\n                right: this.paddingRight || 0,\n                bottom: this.paddingBottom || 0\n            };\n        }\n        getTicks() {\n            return this.ticks;\n        }\n        getLabels() {\n            const data = this.chart.data;\n            return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels || [];\n        }\n        getLabelItems(chartArea = this.chart.chartArea) {\n            const items = this._labelItems || (this._labelItems = this._computeLabelItems(chartArea));\n            return items;\n        }\n        beforeLayout() {\n            this._cache = {};\n            this._dataLimitsCached = false;\n        }\n        beforeUpdate() {\n            callback(this.options.beforeUpdate, [\n                this\n            ]);\n        }\n        update(maxWidth, maxHeight, margins) {\n            const { beginAtZero , grace , ticks: tickOpts  } = this.options;\n            const sampleSize = tickOpts.sampleSize;\n            this.beforeUpdate();\n            this.maxWidth = maxWidth;\n            this.maxHeight = maxHeight;\n            this._margins = margins = Object.assign({\n                left: 0,\n                right: 0,\n                top: 0,\n                bottom: 0\n            }, margins);\n            this.ticks = null;\n            this._labelSizes = null;\n            this._gridLineItems = null;\n            this._labelItems = null;\n            this.beforeSetDimensions();\n            this.setDimensions();\n            this.afterSetDimensions();\n            this._maxLength = this.isHorizontal() ? this.width + margins.left + margins.right : this.height + margins.top + margins.bottom;\n            if (!this._dataLimitsCached) {\n                this.beforeDataLimits();\n                this.determineDataLimits();\n                this.afterDataLimits();\n                this._range = _addGrace(this, grace, beginAtZero);\n                this._dataLimitsCached = true;\n            }\n            this.beforeBuildTicks();\n            this.ticks = this.buildTicks() || [];\n            this.afterBuildTicks();\n            const samplingEnabled = sampleSize < this.ticks.length;\n            this._convertTicksToLabels(samplingEnabled ? sample(this.ticks, sampleSize) : this.ticks);\n            this.configure();\n            this.beforeCalculateLabelRotation();\n            this.calculateLabelRotation();\n            this.afterCalculateLabelRotation();\n            if (tickOpts.display && (tickOpts.autoSkip || tickOpts.source === 'auto')) {\n                this.ticks = autoSkip(this, this.ticks);\n                this._labelSizes = null;\n                this.afterAutoSkip();\n            }\n            if (samplingEnabled) {\n                this._convertTicksToLabels(this.ticks);\n            }\n            this.beforeFit();\n            this.fit();\n            this.afterFit();\n            this.afterUpdate();\n        }\n        configure() {\n            let reversePixels = this.options.reverse;\n            let startPixel, endPixel;\n            if (this.isHorizontal()) {\n                startPixel = this.left;\n                endPixel = this.right;\n            } else {\n                startPixel = this.top;\n                endPixel = this.bottom;\n                reversePixels = !reversePixels;\n            }\n            this._startPixel = startPixel;\n            this._endPixel = endPixel;\n            this._reversePixels = reversePixels;\n            this._length = endPixel - startPixel;\n            this._alignToPixels = this.options.alignToPixels;\n        }\n        afterUpdate() {\n            callback(this.options.afterUpdate, [\n                this\n            ]);\n        }\n        beforeSetDimensions() {\n            callback(this.options.beforeSetDimensions, [\n                this\n            ]);\n        }\n        setDimensions() {\n            if (this.isHorizontal()) {\n                this.width = this.maxWidth;\n                this.left = 0;\n                this.right = this.width;\n            } else {\n                this.height = this.maxHeight;\n                this.top = 0;\n                this.bottom = this.height;\n            }\n            this.paddingLeft = 0;\n            this.paddingTop = 0;\n            this.paddingRight = 0;\n            this.paddingBottom = 0;\n        }\n        afterSetDimensions() {\n            callback(this.options.afterSetDimensions, [\n                this\n            ]);\n        }\n        _callHooks(name) {\n            this.chart.notifyPlugins(name, this.getContext());\n            callback(this.options[name], [\n                this\n            ]);\n        }\n        beforeDataLimits() {\n            this._callHooks('beforeDataLimits');\n        }\n        determineDataLimits() {}\n        afterDataLimits() {\n            this._callHooks('afterDataLimits');\n        }\n        beforeBuildTicks() {\n            this._callHooks('beforeBuildTicks');\n        }\n        buildTicks() {\n            return [];\n        }\n        afterBuildTicks() {\n            this._callHooks('afterBuildTicks');\n        }\n        beforeTickToLabelConversion() {\n            callback(this.options.beforeTickToLabelConversion, [\n                this\n            ]);\n        }\n        generateTickLabels(ticks) {\n            const tickOpts = this.options.ticks;\n            let i, ilen, tick;\n            for(i = 0, ilen = ticks.length; i < ilen; i++){\n                tick = ticks[i];\n                tick.label = callback(tickOpts.callback, [\n                    tick.value,\n                    i,\n                    ticks\n                ], this);\n            }\n        }\n        afterTickToLabelConversion() {\n            callback(this.options.afterTickToLabelConversion, [\n                this\n            ]);\n        }\n        beforeCalculateLabelRotation() {\n            callback(this.options.beforeCalculateLabelRotation, [\n                this\n            ]);\n        }\n        calculateLabelRotation() {\n            const options = this.options;\n            const tickOpts = options.ticks;\n            const numTicks = getTicksLimit(this.ticks.length, options.ticks.maxTicksLimit);\n            const minRotation = tickOpts.minRotation || 0;\n            const maxRotation = tickOpts.maxRotation;\n            let labelRotation = minRotation;\n            let tickWidth, maxHeight, maxLabelDiagonal;\n            if (!this._isVisible() || !tickOpts.display || minRotation >= maxRotation || numTicks <= 1 || !this.isHorizontal()) {\n                this.labelRotation = minRotation;\n                return;\n            }\n            const labelSizes = this._getLabelSizes();\n            const maxLabelWidth = labelSizes.widest.width;\n            const maxLabelHeight = labelSizes.highest.height;\n            const maxWidth = _limitValue(this.chart.width - maxLabelWidth, 0, this.maxWidth);\n            tickWidth = options.offset ? this.maxWidth / numTicks : maxWidth / (numTicks - 1);\n            if (maxLabelWidth + 6 > tickWidth) {\n                tickWidth = maxWidth / (numTicks - (options.offset ? 0.5 : 1));\n                maxHeight = this.maxHeight - getTickMarkLength(options.grid) - tickOpts.padding - getTitleHeight(options.title, this.chart.options.font);\n                maxLabelDiagonal = Math.sqrt(maxLabelWidth * maxLabelWidth + maxLabelHeight * maxLabelHeight);\n                labelRotation = toDegrees(Math.min(Math.asin(_limitValue((labelSizes.highest.height + 6) / tickWidth, -1, 1)), Math.asin(_limitValue(maxHeight / maxLabelDiagonal, -1, 1)) - Math.asin(_limitValue(maxLabelHeight / maxLabelDiagonal, -1, 1))));\n                labelRotation = Math.max(minRotation, Math.min(maxRotation, labelRotation));\n            }\n            this.labelRotation = labelRotation;\n        }\n        afterCalculateLabelRotation() {\n            callback(this.options.afterCalculateLabelRotation, [\n                this\n            ]);\n        }\n        afterAutoSkip() {}\n        beforeFit() {\n            callback(this.options.beforeFit, [\n                this\n            ]);\n        }\n        fit() {\n            const minSize = {\n                width: 0,\n                height: 0\n            };\n            const { chart , options: { ticks: tickOpts , title: titleOpts , grid: gridOpts  }  } = this;\n            const display = this._isVisible();\n            const isHorizontal = this.isHorizontal();\n            if (display) {\n                const titleHeight = getTitleHeight(titleOpts, chart.options.font);\n                if (isHorizontal) {\n                    minSize.width = this.maxWidth;\n                    minSize.height = getTickMarkLength(gridOpts) + titleHeight;\n                } else {\n                    minSize.height = this.maxHeight;\n                    minSize.width = getTickMarkLength(gridOpts) + titleHeight;\n                }\n                if (tickOpts.display && this.ticks.length) {\n                    const { first , last , widest , highest  } = this._getLabelSizes();\n                    const tickPadding = tickOpts.padding * 2;\n                    const angleRadians = toRadians(this.labelRotation);\n                    const cos = Math.cos(angleRadians);\n                    const sin = Math.sin(angleRadians);\n                    if (isHorizontal) {\n                        const labelHeight = tickOpts.mirror ? 0 : sin * widest.width + cos * highest.height;\n                        minSize.height = Math.min(this.maxHeight, minSize.height + labelHeight + tickPadding);\n                    } else {\n                        const labelWidth = tickOpts.mirror ? 0 : cos * widest.width + sin * highest.height;\n                        minSize.width = Math.min(this.maxWidth, minSize.width + labelWidth + tickPadding);\n                    }\n                    this._calculatePadding(first, last, sin, cos);\n                }\n            }\n            this._handleMargins();\n            if (isHorizontal) {\n                this.width = this._length = chart.width - this._margins.left - this._margins.right;\n                this.height = minSize.height;\n            } else {\n                this.width = minSize.width;\n                this.height = this._length = chart.height - this._margins.top - this._margins.bottom;\n            }\n        }\n        _calculatePadding(first, last, sin, cos) {\n            const { ticks: { align , padding  } , position  } = this.options;\n            const isRotated = this.labelRotation !== 0;\n            const labelsBelowTicks = position !== 'top' && this.axis === 'x';\n            if (this.isHorizontal()) {\n                const offsetLeft = this.getPixelForTick(0) - this.left;\n                const offsetRight = this.right - this.getPixelForTick(this.ticks.length - 1);\n                let paddingLeft = 0;\n                let paddingRight = 0;\n                if (isRotated) {\n                    if (labelsBelowTicks) {\n                        paddingLeft = cos * first.width;\n                        paddingRight = sin * last.height;\n                    } else {\n                        paddingLeft = sin * first.height;\n                        paddingRight = cos * last.width;\n                    }\n                } else if (align === 'start') {\n                    paddingRight = last.width;\n                } else if (align === 'end') {\n                    paddingLeft = first.width;\n                } else if (align !== 'inner') {\n                    paddingLeft = first.width / 2;\n                    paddingRight = last.width / 2;\n                }\n                this.paddingLeft = Math.max((paddingLeft - offsetLeft + padding) * this.width / (this.width - offsetLeft), 0);\n                this.paddingRight = Math.max((paddingRight - offsetRight + padding) * this.width / (this.width - offsetRight), 0);\n            } else {\n                let paddingTop = last.height / 2;\n                let paddingBottom = first.height / 2;\n                if (align === 'start') {\n                    paddingTop = 0;\n                    paddingBottom = first.height;\n                } else if (align === 'end') {\n                    paddingTop = last.height;\n                    paddingBottom = 0;\n                }\n                this.paddingTop = paddingTop + padding;\n                this.paddingBottom = paddingBottom + padding;\n            }\n        }\n        _handleMargins() {\n            if (this._margins) {\n                this._margins.left = Math.max(this.paddingLeft, this._margins.left);\n                this._margins.top = Math.max(this.paddingTop, this._margins.top);\n                this._margins.right = Math.max(this.paddingRight, this._margins.right);\n                this._margins.bottom = Math.max(this.paddingBottom, this._margins.bottom);\n            }\n        }\n        afterFit() {\n            callback(this.options.afterFit, [\n                this\n            ]);\n        }\n        isHorizontal() {\n            const { axis , position  } = this.options;\n            return position === 'top' || position === 'bottom' || axis === 'x';\n        }\n        isFullSize() {\n            return this.options.fullSize;\n        }\n        _convertTicksToLabels(ticks) {\n            this.beforeTickToLabelConversion();\n            this.generateTickLabels(ticks);\n            let i, ilen;\n            for(i = 0, ilen = ticks.length; i < ilen; i++){\n                if (isNullOrUndef(ticks[i].label)) {\n                    ticks.splice(i, 1);\n                    ilen--;\n                    i--;\n                }\n            }\n            this.afterTickToLabelConversion();\n        }\n        _getLabelSizes() {\n            let labelSizes = this._labelSizes;\n            if (!labelSizes) {\n                const sampleSize = this.options.ticks.sampleSize;\n                let ticks = this.ticks;\n                if (sampleSize < ticks.length) {\n                    ticks = sample(ticks, sampleSize);\n                }\n                this._labelSizes = labelSizes = this._computeLabelSizes(ticks, ticks.length, this.options.ticks.maxTicksLimit);\n            }\n            return labelSizes;\n        }\n        _computeLabelSizes(ticks, length, maxTicksLimit) {\n            const { ctx , _longestTextCache: caches  } = this;\n            const widths = [];\n            const heights = [];\n            const increment = Math.floor(length / getTicksLimit(length, maxTicksLimit));\n            let widestLabelSize = 0;\n            let highestLabelSize = 0;\n            let i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel;\n            for(i = 0; i < length; i += increment){\n                label = ticks[i].label;\n                tickFont = this._resolveTickFontOptions(i);\n                ctx.font = fontString = tickFont.string;\n                cache = caches[fontString] = caches[fontString] || {\n                    data: {},\n                    gc: []\n                };\n                lineHeight = tickFont.lineHeight;\n                width = height = 0;\n                if (!isNullOrUndef(label) && !isArray(label)) {\n                    width = _measureText(ctx, cache.data, cache.gc, width, label);\n                    height = lineHeight;\n                } else if (isArray(label)) {\n                    for(j = 0, jlen = label.length; j < jlen; ++j){\n                        nestedLabel =  label[j];\n                        if (!isNullOrUndef(nestedLabel) && !isArray(nestedLabel)) {\n                            width = _measureText(ctx, cache.data, cache.gc, width, nestedLabel);\n                            height += lineHeight;\n                        }\n                    }\n                }\n                widths.push(width);\n                heights.push(height);\n                widestLabelSize = Math.max(width, widestLabelSize);\n                highestLabelSize = Math.max(height, highestLabelSize);\n            }\n            garbageCollect(caches, length);\n            const widest = widths.indexOf(widestLabelSize);\n            const highest = heights.indexOf(highestLabelSize);\n            const valueAt = (idx)=>({\n                width: widths[idx] || 0,\n                height: heights[idx] || 0\n            });\n            return {\n                first: valueAt(0),\n                last: valueAt(length - 1),\n                widest: valueAt(widest),\n                highest: valueAt(highest),\n                widths,\n                heights\n            };\n        }\n        getLabelForValue(value) {\n            return value;\n        }\n        getPixelForValue(value, index) {\n            return NaN;\n        }\n        getValueForPixel(pixel) {}\n        getPixelForTick(index) {\n            const ticks = this.ticks;\n            if (index < 0 || index > ticks.length - 1) {\n                return null;\n            }\n            return this.getPixelForValue(ticks[index].value);\n        }\n        getPixelForDecimal(decimal) {\n            if (this._reversePixels) {\n                decimal = 1 - decimal;\n            }\n            const pixel = this._startPixel + decimal * this._length;\n            return _int16Range(this._alignToPixels ? _alignPixel(this.chart, pixel, 0) : pixel);\n        }\n        getDecimalForPixel(pixel) {\n            const decimal = (pixel - this._startPixel) / this._length;\n            return this._reversePixels ? 1 - decimal : decimal;\n        }\n        getBasePixel() {\n            return this.getPixelForValue(this.getBaseValue());\n        }\n        getBaseValue() {\n            const { min , max  } = this;\n            return min < 0 && max < 0 ? max : min > 0 && max > 0 ? min : 0;\n        }\n        getContext(index) {\n            const ticks = this.ticks || [];\n            if (index >= 0 && index < ticks.length) {\n                const tick = ticks[index];\n                return tick.$context || (tick.$context = createTickContext(this.getContext(), index, tick));\n            }\n            return this.$context || (this.$context = createScaleContext(this.chart.getContext(), this));\n        }\n        _tickSize() {\n            const optionTicks = this.options.ticks;\n            const rot = toRadians(this.labelRotation);\n            const cos = Math.abs(Math.cos(rot));\n            const sin = Math.abs(Math.sin(rot));\n            const labelSizes = this._getLabelSizes();\n            const padding = optionTicks.autoSkipPadding || 0;\n            const w = labelSizes ? labelSizes.widest.width + padding : 0;\n            const h = labelSizes ? labelSizes.highest.height + padding : 0;\n            return this.isHorizontal() ? h * cos > w * sin ? w / cos : h / sin : h * sin < w * cos ? h / cos : w / sin;\n        }\n        _isVisible() {\n            const display = this.options.display;\n            if (display !== 'auto') {\n                return !!display;\n            }\n            return this.getMatchingVisibleMetas().length > 0;\n        }\n        _computeGridLineItems(chartArea) {\n            const axis = this.axis;\n            const chart = this.chart;\n            const options = this.options;\n            const { grid , position , border  } = options;\n            const offset = grid.offset;\n            const isHorizontal = this.isHorizontal();\n            const ticks = this.ticks;\n            const ticksLength = ticks.length + (offset ? 1 : 0);\n            const tl = getTickMarkLength(grid);\n            const items = [];\n            const borderOpts = border.setContext(this.getContext());\n            const axisWidth = borderOpts.display ? borderOpts.width : 0;\n            const axisHalfWidth = axisWidth / 2;\n            const alignBorderValue = function(pixel) {\n                return _alignPixel(chart, pixel, axisWidth);\n            };\n            let borderValue, i, lineValue, alignedLineValue;\n            let tx1, ty1, tx2, ty2, x1, y1, x2, y2;\n            if (position === 'top') {\n                borderValue = alignBorderValue(this.bottom);\n                ty1 = this.bottom - tl;\n                ty2 = borderValue - axisHalfWidth;\n                y1 = alignBorderValue(chartArea.top) + axisHalfWidth;\n                y2 = chartArea.bottom;\n            } else if (position === 'bottom') {\n                borderValue = alignBorderValue(this.top);\n                y1 = chartArea.top;\n                y2 = alignBorderValue(chartArea.bottom) - axisHalfWidth;\n                ty1 = borderValue + axisHalfWidth;\n                ty2 = this.top + tl;\n            } else if (position === 'left') {\n                borderValue = alignBorderValue(this.right);\n                tx1 = this.right - tl;\n                tx2 = borderValue - axisHalfWidth;\n                x1 = alignBorderValue(chartArea.left) + axisHalfWidth;\n                x2 = chartArea.right;\n            } else if (position === 'right') {\n                borderValue = alignBorderValue(this.left);\n                x1 = chartArea.left;\n                x2 = alignBorderValue(chartArea.right) - axisHalfWidth;\n                tx1 = borderValue + axisHalfWidth;\n                tx2 = this.left + tl;\n            } else if (axis === 'x') {\n                if (position === 'center') {\n                    borderValue = alignBorderValue((chartArea.top + chartArea.bottom) / 2 + 0.5);\n                } else if (isObject(position)) {\n                    const positionAxisID = Object.keys(position)[0];\n                    const value = position[positionAxisID];\n                    borderValue = alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value));\n                }\n                y1 = chartArea.top;\n                y2 = chartArea.bottom;\n                ty1 = borderValue + axisHalfWidth;\n                ty2 = ty1 + tl;\n            } else if (axis === 'y') {\n                if (position === 'center') {\n                    borderValue = alignBorderValue((chartArea.left + chartArea.right) / 2);\n                } else if (isObject(position)) {\n                    const positionAxisID = Object.keys(position)[0];\n                    const value = position[positionAxisID];\n                    borderValue = alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value));\n                }\n                tx1 = borderValue - axisHalfWidth;\n                tx2 = tx1 - tl;\n                x1 = chartArea.left;\n                x2 = chartArea.right;\n            }\n            const limit = valueOrDefault(options.ticks.maxTicksLimit, ticksLength);\n            const step = Math.max(1, Math.ceil(ticksLength / limit));\n            for(i = 0; i < ticksLength; i += step){\n                const context = this.getContext(i);\n                const optsAtIndex = grid.setContext(context);\n                const optsAtIndexBorder = border.setContext(context);\n                const lineWidth = optsAtIndex.lineWidth;\n                const lineColor = optsAtIndex.color;\n                const borderDash = optsAtIndexBorder.dash || [];\n                const borderDashOffset = optsAtIndexBorder.dashOffset;\n                const tickWidth = optsAtIndex.tickWidth;\n                const tickColor = optsAtIndex.tickColor;\n                const tickBorderDash = optsAtIndex.tickBorderDash || [];\n                const tickBorderDashOffset = optsAtIndex.tickBorderDashOffset;\n                lineValue = getPixelForGridLine(this, i, offset);\n                if (lineValue === undefined) {\n                    continue;\n                }\n                alignedLineValue = _alignPixel(chart, lineValue, lineWidth);\n                if (isHorizontal) {\n                    tx1 = tx2 = x1 = x2 = alignedLineValue;\n                } else {\n                    ty1 = ty2 = y1 = y2 = alignedLineValue;\n                }\n                items.push({\n                    tx1,\n                    ty1,\n                    tx2,\n                    ty2,\n                    x1,\n                    y1,\n                    x2,\n                    y2,\n                    width: lineWidth,\n                    color: lineColor,\n                    borderDash,\n                    borderDashOffset,\n                    tickWidth,\n                    tickColor,\n                    tickBorderDash,\n                    tickBorderDashOffset\n                });\n            }\n            this._ticksLength = ticksLength;\n            this._borderValue = borderValue;\n            return items;\n        }\n        _computeLabelItems(chartArea) {\n            const axis = this.axis;\n            const options = this.options;\n            const { position , ticks: optionTicks  } = options;\n            const isHorizontal = this.isHorizontal();\n            const ticks = this.ticks;\n            const { align , crossAlign , padding , mirror  } = optionTicks;\n            const tl = getTickMarkLength(options.grid);\n            const tickAndPadding = tl + padding;\n            const hTickAndPadding = mirror ? -padding : tickAndPadding;\n            const rotation = -toRadians(this.labelRotation);\n            const items = [];\n            let i, ilen, tick, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset;\n            let textBaseline = 'middle';\n            if (position === 'top') {\n                y = this.bottom - hTickAndPadding;\n                textAlign = this._getXAxisLabelAlignment();\n            } else if (position === 'bottom') {\n                y = this.top + hTickAndPadding;\n                textAlign = this._getXAxisLabelAlignment();\n            } else if (position === 'left') {\n                const ret = this._getYAxisLabelAlignment(tl);\n                textAlign = ret.textAlign;\n                x = ret.x;\n            } else if (position === 'right') {\n                const ret = this._getYAxisLabelAlignment(tl);\n                textAlign = ret.textAlign;\n                x = ret.x;\n            } else if (axis === 'x') {\n                if (position === 'center') {\n                    y = (chartArea.top + chartArea.bottom) / 2 + tickAndPadding;\n                } else if (isObject(position)) {\n                    const positionAxisID = Object.keys(position)[0];\n                    const value = position[positionAxisID];\n                    y = this.chart.scales[positionAxisID].getPixelForValue(value) + tickAndPadding;\n                }\n                textAlign = this._getXAxisLabelAlignment();\n            } else if (axis === 'y') {\n                if (position === 'center') {\n                    x = (chartArea.left + chartArea.right) / 2 - tickAndPadding;\n                } else if (isObject(position)) {\n                    const positionAxisID = Object.keys(position)[0];\n                    const value = position[positionAxisID];\n                    x = this.chart.scales[positionAxisID].getPixelForValue(value);\n                }\n                textAlign = this._getYAxisLabelAlignment(tl).textAlign;\n            }\n            if (axis === 'y') {\n                if (align === 'start') {\n                    textBaseline = 'top';\n                } else if (align === 'end') {\n                    textBaseline = 'bottom';\n                }\n            }\n            const labelSizes = this._getLabelSizes();\n            for(i = 0, ilen = ticks.length; i < ilen; ++i){\n                tick = ticks[i];\n                label = tick.label;\n                const optsAtIndex = optionTicks.setContext(this.getContext(i));\n                pixel = this.getPixelForTick(i) + optionTicks.labelOffset;\n                font = this._resolveTickFontOptions(i);\n                lineHeight = font.lineHeight;\n                lineCount = isArray(label) ? label.length : 1;\n                const halfCount = lineCount / 2;\n                const color = optsAtIndex.color;\n                const strokeColor = optsAtIndex.textStrokeColor;\n                const strokeWidth = optsAtIndex.textStrokeWidth;\n                let tickTextAlign = textAlign;\n                if (isHorizontal) {\n                    x = pixel;\n                    if (textAlign === 'inner') {\n                        if (i === ilen - 1) {\n                            tickTextAlign = !this.options.reverse ? 'right' : 'left';\n                        } else if (i === 0) {\n                            tickTextAlign = !this.options.reverse ? 'left' : 'right';\n                        } else {\n                            tickTextAlign = 'center';\n                        }\n                    }\n                    if (position === 'top') {\n                        if (crossAlign === 'near' || rotation !== 0) {\n                            textOffset = -lineCount * lineHeight + lineHeight / 2;\n                        } else if (crossAlign === 'center') {\n                            textOffset = -labelSizes.highest.height / 2 - halfCount * lineHeight + lineHeight;\n                        } else {\n                            textOffset = -labelSizes.highest.height + lineHeight / 2;\n                        }\n                    } else {\n                        if (crossAlign === 'near' || rotation !== 0) {\n                            textOffset = lineHeight / 2;\n                        } else if (crossAlign === 'center') {\n                            textOffset = labelSizes.highest.height / 2 - halfCount * lineHeight;\n                        } else {\n                            textOffset = labelSizes.highest.height - lineCount * lineHeight;\n                        }\n                    }\n                    if (mirror) {\n                        textOffset *= -1;\n                    }\n                    if (rotation !== 0 && !optsAtIndex.showLabelBackdrop) {\n                        x += lineHeight / 2 * Math.sin(rotation);\n                    }\n                } else {\n                    y = pixel;\n                    textOffset = (1 - lineCount) * lineHeight / 2;\n                }\n                let backdrop;\n                if (optsAtIndex.showLabelBackdrop) {\n                    const labelPadding = toPadding(optsAtIndex.backdropPadding);\n                    const height = labelSizes.heights[i];\n                    const width = labelSizes.widths[i];\n                    let top = textOffset - labelPadding.top;\n                    let left = 0 - labelPadding.left;\n                    switch(textBaseline){\n                        case 'middle':\n                            top -= height / 2;\n                            break;\n                        case 'bottom':\n                            top -= height;\n                            break;\n                    }\n                    switch(textAlign){\n                        case 'center':\n                            left -= width / 2;\n                            break;\n                        case 'right':\n                            left -= width;\n                            break;\n                        case 'inner':\n                            if (i === ilen - 1) {\n                                left -= width;\n                            } else if (i > 0) {\n                                left -= width / 2;\n                            }\n                            break;\n                    }\n                    backdrop = {\n                        left,\n                        top,\n                        width: width + labelPadding.width,\n                        height: height + labelPadding.height,\n                        color: optsAtIndex.backdropColor\n                    };\n                }\n                items.push({\n                    label,\n                    font,\n                    textOffset,\n                    options: {\n                        rotation,\n                        color,\n                        strokeColor,\n                        strokeWidth,\n                        textAlign: tickTextAlign,\n                        textBaseline,\n                        translation: [\n                            x,\n                            y\n                        ],\n                        backdrop\n                    }\n                });\n            }\n            return items;\n        }\n        _getXAxisLabelAlignment() {\n            const { position , ticks  } = this.options;\n            const rotation = -toRadians(this.labelRotation);\n            if (rotation) {\n                return position === 'top' ? 'left' : 'right';\n            }\n            let align = 'center';\n            if (ticks.align === 'start') {\n                align = 'left';\n            } else if (ticks.align === 'end') {\n                align = 'right';\n            } else if (ticks.align === 'inner') {\n                align = 'inner';\n            }\n            return align;\n        }\n        _getYAxisLabelAlignment(tl) {\n            const { position , ticks: { crossAlign , mirror , padding  }  } = this.options;\n            const labelSizes = this._getLabelSizes();\n            const tickAndPadding = tl + padding;\n            const widest = labelSizes.widest.width;\n            let textAlign;\n            let x;\n            if (position === 'left') {\n                if (mirror) {\n                    x = this.right + padding;\n                    if (crossAlign === 'near') {\n                        textAlign = 'left';\n                    } else if (crossAlign === 'center') {\n                        textAlign = 'center';\n                        x += widest / 2;\n                    } else {\n                        textAlign = 'right';\n                        x += widest;\n                    }\n                } else {\n                    x = this.right - tickAndPadding;\n                    if (crossAlign === 'near') {\n                        textAlign = 'right';\n                    } else if (crossAlign === 'center') {\n                        textAlign = 'center';\n                        x -= widest / 2;\n                    } else {\n                        textAlign = 'left';\n                        x = this.left;\n                    }\n                }\n            } else if (position === 'right') {\n                if (mirror) {\n                    x = this.left + padding;\n                    if (crossAlign === 'near') {\n                        textAlign = 'right';\n                    } else if (crossAlign === 'center') {\n                        textAlign = 'center';\n                        x -= widest / 2;\n                    } else {\n                        textAlign = 'left';\n                        x -= widest;\n                    }\n                } else {\n                    x = this.left + tickAndPadding;\n                    if (crossAlign === 'near') {\n                        textAlign = 'left';\n                    } else if (crossAlign === 'center') {\n                        textAlign = 'center';\n                        x += widest / 2;\n                    } else {\n                        textAlign = 'right';\n                        x = this.right;\n                    }\n                }\n            } else {\n                textAlign = 'right';\n            }\n            return {\n                textAlign,\n                x\n            };\n        }\n        _computeLabelArea() {\n            if (this.options.ticks.mirror) {\n                return;\n            }\n            const chart = this.chart;\n            const position = this.options.position;\n            if (position === 'left' || position === 'right') {\n                return {\n                    top: 0,\n                    left: this.left,\n                    bottom: chart.height,\n                    right: this.right\n                };\n            }\n            if (position === 'top' || position === 'bottom') {\n                return {\n                    top: this.top,\n                    left: 0,\n                    bottom: this.bottom,\n                    right: chart.width\n                };\n            }\n        }\n        drawBackground() {\n            const { ctx , options: { backgroundColor  } , left , top , width , height  } = this;\n            if (backgroundColor) {\n                ctx.save();\n                ctx.fillStyle = backgroundColor;\n                ctx.fillRect(left, top, width, height);\n                ctx.restore();\n            }\n        }\n        getLineWidthForValue(value) {\n            const grid = this.options.grid;\n            if (!this._isVisible() || !grid.display) {\n                return 0;\n            }\n            const ticks = this.ticks;\n            const index = ticks.findIndex((t)=>t.value === value);\n            if (index >= 0) {\n                const opts = grid.setContext(this.getContext(index));\n                return opts.lineWidth;\n            }\n            return 0;\n        }\n        drawGrid(chartArea) {\n            const grid = this.options.grid;\n            const ctx = this.ctx;\n            const items = this._gridLineItems || (this._gridLineItems = this._computeGridLineItems(chartArea));\n            let i, ilen;\n            const drawLine = (p1, p2, style)=>{\n                if (!style.width || !style.color) {\n                    return;\n                }\n                ctx.save();\n                ctx.lineWidth = style.width;\n                ctx.strokeStyle = style.color;\n                ctx.setLineDash(style.borderDash || []);\n                ctx.lineDashOffset = style.borderDashOffset;\n                ctx.beginPath();\n                ctx.moveTo(p1.x, p1.y);\n                ctx.lineTo(p2.x, p2.y);\n                ctx.stroke();\n                ctx.restore();\n            };\n            if (grid.display) {\n                for(i = 0, ilen = items.length; i < ilen; ++i){\n                    const item = items[i];\n                    if (grid.drawOnChartArea) {\n                        drawLine({\n                            x: item.x1,\n                            y: item.y1\n                        }, {\n                            x: item.x2,\n                            y: item.y2\n                        }, item);\n                    }\n                    if (grid.drawTicks) {\n                        drawLine({\n                            x: item.tx1,\n                            y: item.ty1\n                        }, {\n                            x: item.tx2,\n                            y: item.ty2\n                        }, {\n                            color: item.tickColor,\n                            width: item.tickWidth,\n                            borderDash: item.tickBorderDash,\n                            borderDashOffset: item.tickBorderDashOffset\n                        });\n                    }\n                }\n            }\n        }\n        drawBorder() {\n            const { chart , ctx , options: { border , grid  }  } = this;\n            const borderOpts = border.setContext(this.getContext());\n            const axisWidth = border.display ? borderOpts.width : 0;\n            if (!axisWidth) {\n                return;\n            }\n            const lastLineWidth = grid.setContext(this.getContext(0)).lineWidth;\n            const borderValue = this._borderValue;\n            let x1, x2, y1, y2;\n            if (this.isHorizontal()) {\n                x1 = _alignPixel(chart, this.left, axisWidth) - axisWidth / 2;\n                x2 = _alignPixel(chart, this.right, lastLineWidth) + lastLineWidth / 2;\n                y1 = y2 = borderValue;\n            } else {\n                y1 = _alignPixel(chart, this.top, axisWidth) - axisWidth / 2;\n                y2 = _alignPixel(chart, this.bottom, lastLineWidth) + lastLineWidth / 2;\n                x1 = x2 = borderValue;\n            }\n            ctx.save();\n            ctx.lineWidth = borderOpts.width;\n            ctx.strokeStyle = borderOpts.color;\n            ctx.beginPath();\n            ctx.moveTo(x1, y1);\n            ctx.lineTo(x2, y2);\n            ctx.stroke();\n            ctx.restore();\n        }\n        drawLabels(chartArea) {\n            const optionTicks = this.options.ticks;\n            if (!optionTicks.display) {\n                return;\n            }\n            const ctx = this.ctx;\n            const area = this._computeLabelArea();\n            if (area) {\n                clipArea(ctx, area);\n            }\n            const items = this.getLabelItems(chartArea);\n            for (const item of items){\n                const renderTextOptions = item.options;\n                const tickFont = item.font;\n                const label = item.label;\n                const y = item.textOffset;\n                renderText(ctx, label, 0, y, tickFont, renderTextOptions);\n            }\n            if (area) {\n                unclipArea(ctx);\n            }\n        }\n        drawTitle() {\n            const { ctx , options: { position , title , reverse  }  } = this;\n            if (!title.display) {\n                return;\n            }\n            const font = toFont(title.font);\n            const padding = toPadding(title.padding);\n            const align = title.align;\n            let offset = font.lineHeight / 2;\n            if (position === 'bottom' || position === 'center' || isObject(position)) {\n                offset += padding.bottom;\n                if (isArray(title.text)) {\n                    offset += font.lineHeight * (title.text.length - 1);\n                }\n            } else {\n                offset += padding.top;\n            }\n            const { titleX , titleY , maxWidth , rotation  } = titleArgs(this, offset, position, align);\n            renderText(ctx, title.text, 0, 0, font, {\n                color: title.color,\n                maxWidth,\n                rotation,\n                textAlign: titleAlign(align, position, reverse),\n                textBaseline: 'middle',\n                translation: [\n                    titleX,\n                    titleY\n                ]\n            });\n        }\n        draw(chartArea) {\n            if (!this._isVisible()) {\n                return;\n            }\n            this.drawBackground();\n            this.drawGrid(chartArea);\n            this.drawBorder();\n            this.drawTitle();\n            this.drawLabels(chartArea);\n        }\n        _layers() {\n            const opts = this.options;\n            const tz = opts.ticks && opts.ticks.z || 0;\n            const gz = valueOrDefault(opts.grid && opts.grid.z, -1);\n            const bz = valueOrDefault(opts.border && opts.border.z, 0);\n            if (!this._isVisible() || this.draw !== Scale.prototype.draw) {\n                return [\n                    {\n                        z: tz,\n                        draw: (chartArea)=>{\n                            this.draw(chartArea);\n                        }\n                    }\n                ];\n            }\n            return [\n                {\n                    z: gz,\n                    draw: (chartArea)=>{\n                        this.drawBackground();\n                        this.drawGrid(chartArea);\n                        this.drawTitle();\n                    }\n                },\n                {\n                    z: bz,\n                    draw: ()=>{\n                        this.drawBorder();\n                    }\n                },\n                {\n                    z: tz,\n                    draw: (chartArea)=>{\n                        this.drawLabels(chartArea);\n                    }\n                }\n            ];\n        }\n        getMatchingVisibleMetas(type) {\n            const metas = this.chart.getSortedVisibleDatasetMetas();\n            const axisID = this.axis + 'AxisID';\n            const result = [];\n            let i, ilen;\n            for(i = 0, ilen = metas.length; i < ilen; ++i){\n                const meta = metas[i];\n                if (meta[axisID] === this.id && (!type || meta.type === type)) {\n                    result.push(meta);\n                }\n            }\n            return result;\n        }\n        _resolveTickFontOptions(index) {\n            const opts = this.options.ticks.setContext(this.getContext(index));\n            return toFont(opts.font);\n        }\n        _maxDigits() {\n            const fontSize = this._resolveTickFontOptions(0).lineHeight;\n            return (this.isHorizontal() ? this.width : this.height) / fontSize;\n        }\n    }\n\n    class TypedRegistry {\n        constructor(type, scope, override){\n            this.type = type;\n            this.scope = scope;\n            this.override = override;\n            this.items = Object.create(null);\n        }\n        isForType(type) {\n            return Object.prototype.isPrototypeOf.call(this.type.prototype, type.prototype);\n        }\n        register(item) {\n            const proto = Object.getPrototypeOf(item);\n            let parentScope;\n            if (isIChartComponent(proto)) {\n                parentScope = this.register(proto);\n            }\n            const items = this.items;\n            const id = item.id;\n            const scope = this.scope + '.' + id;\n            if (!id) {\n                throw new Error('class does not have id: ' + item);\n            }\n            if (id in items) {\n                return scope;\n            }\n            items[id] = item;\n            registerDefaults(item, scope, parentScope);\n            if (this.override) {\n                defaults.override(item.id, item.overrides);\n            }\n            return scope;\n        }\n        get(id) {\n            return this.items[id];\n        }\n        unregister(item) {\n            const items = this.items;\n            const id = item.id;\n            const scope = this.scope;\n            if (id in items) {\n                delete items[id];\n            }\n            if (scope && id in defaults[scope]) {\n                delete defaults[scope][id];\n                if (this.override) {\n                    delete overrides[id];\n                }\n            }\n        }\n    }\n    function registerDefaults(item, scope, parentScope) {\n        const itemDefaults = merge(Object.create(null), [\n            parentScope ? defaults.get(parentScope) : {},\n            defaults.get(scope),\n            item.defaults\n        ]);\n        defaults.set(scope, itemDefaults);\n        if (item.defaultRoutes) {\n            routeDefaults(scope, item.defaultRoutes);\n        }\n        if (item.descriptors) {\n            defaults.describe(scope, item.descriptors);\n        }\n    }\n    function routeDefaults(scope, routes) {\n        Object.keys(routes).forEach((property)=>{\n            const propertyParts = property.split('.');\n            const sourceName = propertyParts.pop();\n            const sourceScope = [\n                scope\n            ].concat(propertyParts).join('.');\n            const parts = routes[property].split('.');\n            const targetName = parts.pop();\n            const targetScope = parts.join('.');\n            defaults.route(sourceScope, sourceName, targetScope, targetName);\n        });\n    }\n    function isIChartComponent(proto) {\n        return 'id' in proto && 'defaults' in proto;\n    }\n\n    class Registry {\n        constructor(){\n            this.controllers = new TypedRegistry(DatasetController, 'datasets', true);\n            this.elements = new TypedRegistry(Element, 'elements');\n            this.plugins = new TypedRegistry(Object, 'plugins');\n            this.scales = new TypedRegistry(Scale, 'scales');\n            this._typedRegistries = [\n                this.controllers,\n                this.scales,\n                this.elements\n            ];\n        }\n        add(...args) {\n            this._each('register', args);\n        }\n        remove(...args) {\n            this._each('unregister', args);\n        }\n        addControllers(...args) {\n            this._each('register', args, this.controllers);\n        }\n        addElements(...args) {\n            this._each('register', args, this.elements);\n        }\n        addPlugins(...args) {\n            this._each('register', args, this.plugins);\n        }\n        addScales(...args) {\n            this._each('register', args, this.scales);\n        }\n        getController(id) {\n            return this._get(id, this.controllers, 'controller');\n        }\n        getElement(id) {\n            return this._get(id, this.elements, 'element');\n        }\n        getPlugin(id) {\n            return this._get(id, this.plugins, 'plugin');\n        }\n        getScale(id) {\n            return this._get(id, this.scales, 'scale');\n        }\n        removeControllers(...args) {\n            this._each('unregister', args, this.controllers);\n        }\n        removeElements(...args) {\n            this._each('unregister', args, this.elements);\n        }\n        removePlugins(...args) {\n            this._each('unregister', args, this.plugins);\n        }\n        removeScales(...args) {\n            this._each('unregister', args, this.scales);\n        }\n        _each(method, args, typedRegistry) {\n            [\n                ...args\n            ].forEach((arg)=>{\n                const reg = typedRegistry || this._getRegistryForType(arg);\n                if (typedRegistry || reg.isForType(arg) || reg === this.plugins && arg.id) {\n                    this._exec(method, reg, arg);\n                } else {\n                    each(arg, (item)=>{\n                        const itemReg = typedRegistry || this._getRegistryForType(item);\n                        this._exec(method, itemReg, item);\n                    });\n                }\n            });\n        }\n        _exec(method, registry, component) {\n            const camelMethod = _capitalize(method);\n            callback(component['before' + camelMethod], [], component);\n            registry[method](component);\n            callback(component['after' + camelMethod], [], component);\n        }\n        _getRegistryForType(type) {\n            for(let i = 0; i < this._typedRegistries.length; i++){\n                const reg = this._typedRegistries[i];\n                if (reg.isForType(type)) {\n                    return reg;\n                }\n            }\n            return this.plugins;\n        }\n        _get(id, typedRegistry, type) {\n            const item = typedRegistry.get(id);\n            if (item === undefined) {\n                throw new Error('\"' + id + '\" is not a registered ' + type + '.');\n            }\n            return item;\n        }\n    }\n    var registry = /* #__PURE__ */ new Registry();\n\n    class PluginService {\n        constructor(){\n            this._init = [];\n        }\n        notify(chart, hook, args, filter) {\n            if (hook === 'beforeInit') {\n                this._init = this._createDescriptors(chart, true);\n                this._notify(this._init, chart, 'install');\n            }\n            const descriptors = filter ? this._descriptors(chart).filter(filter) : this._descriptors(chart);\n            const result = this._notify(descriptors, chart, hook, args);\n            if (hook === 'afterDestroy') {\n                this._notify(descriptors, chart, 'stop');\n                this._notify(this._init, chart, 'uninstall');\n            }\n            return result;\n        }\n        _notify(descriptors, chart, hook, args) {\n            args = args || {};\n            for (const descriptor of descriptors){\n                const plugin = descriptor.plugin;\n                const method = plugin[hook];\n                const params = [\n                    chart,\n                    args,\n                    descriptor.options\n                ];\n                if (callback(method, params, plugin) === false && args.cancelable) {\n                    return false;\n                }\n            }\n            return true;\n        }\n        invalidate() {\n            if (!isNullOrUndef(this._cache)) {\n                this._oldCache = this._cache;\n                this._cache = undefined;\n            }\n        }\n        _descriptors(chart) {\n            if (this._cache) {\n                return this._cache;\n            }\n            const descriptors = this._cache = this._createDescriptors(chart);\n            this._notifyStateChanges(chart);\n            return descriptors;\n        }\n        _createDescriptors(chart, all) {\n            const config = chart && chart.config;\n            const options = valueOrDefault(config.options && config.options.plugins, {});\n            const plugins = allPlugins(config);\n            return options === false && !all ? [] : createDescriptors(chart, plugins, options, all);\n        }\n        _notifyStateChanges(chart) {\n            const previousDescriptors = this._oldCache || [];\n            const descriptors = this._cache;\n            const diff = (a, b)=>a.filter((x)=>!b.some((y)=>x.plugin.id === y.plugin.id));\n            this._notify(diff(previousDescriptors, descriptors), chart, 'stop');\n            this._notify(diff(descriptors, previousDescriptors), chart, 'start');\n        }\n    }\n    function allPlugins(config) {\n        const localIds = {};\n        const plugins = [];\n        const keys = Object.keys(registry.plugins.items);\n        for(let i = 0; i < keys.length; i++){\n            plugins.push(registry.getPlugin(keys[i]));\n        }\n        const local = config.plugins || [];\n        for(let i = 0; i < local.length; i++){\n            const plugin = local[i];\n            if (plugins.indexOf(plugin) === -1) {\n                plugins.push(plugin);\n                localIds[plugin.id] = true;\n            }\n        }\n        return {\n            plugins,\n            localIds\n        };\n    }\n    function getOpts(options, all) {\n        if (!all && options === false) {\n            return null;\n        }\n        if (options === true) {\n            return {};\n        }\n        return options;\n    }\n    function createDescriptors(chart, { plugins , localIds  }, options, all) {\n        const result = [];\n        const context = chart.getContext();\n        for (const plugin of plugins){\n            const id = plugin.id;\n            const opts = getOpts(options[id], all);\n            if (opts === null) {\n                continue;\n            }\n            result.push({\n                plugin,\n                options: pluginOpts(chart.config, {\n                    plugin,\n                    local: localIds[id]\n                }, opts, context)\n            });\n        }\n        return result;\n    }\n    function pluginOpts(config, { plugin , local  }, opts, context) {\n        const keys = config.pluginScopeKeys(plugin);\n        const scopes = config.getOptionScopes(opts, keys);\n        if (local && plugin.defaults) {\n            scopes.push(plugin.defaults);\n        }\n        return config.createResolver(scopes, context, [\n            ''\n        ], {\n            scriptable: false,\n            indexable: false,\n            allKeys: true\n        });\n    }\n\n    function getIndexAxis(type, options) {\n        const datasetDefaults = defaults.datasets[type] || {};\n        const datasetOptions = (options.datasets || {})[type] || {};\n        return datasetOptions.indexAxis || options.indexAxis || datasetDefaults.indexAxis || 'x';\n    }\n    function getAxisFromDefaultScaleID(id, indexAxis) {\n        let axis = id;\n        if (id === '_index_') {\n            axis = indexAxis;\n        } else if (id === '_value_') {\n            axis = indexAxis === 'x' ? 'y' : 'x';\n        }\n        return axis;\n    }\n    function getDefaultScaleIDFromAxis(axis, indexAxis) {\n        return axis === indexAxis ? '_index_' : '_value_';\n    }\n    function idMatchesAxis(id) {\n        if (id === 'x' || id === 'y' || id === 'r') {\n            return id;\n        }\n    }\n    function axisFromPosition(position) {\n        if (position === 'top' || position === 'bottom') {\n            return 'x';\n        }\n        if (position === 'left' || position === 'right') {\n            return 'y';\n        }\n    }\n    function determineAxis(id, ...scaleOptions) {\n        if (idMatchesAxis(id)) {\n            return id;\n        }\n        for (const opts of scaleOptions){\n            const axis = opts.axis || axisFromPosition(opts.position) || id.length > 1 && idMatchesAxis(id[0].toLowerCase());\n            if (axis) {\n                return axis;\n            }\n        }\n        throw new Error(`Cannot determine type of '${id}' axis. Please provide 'axis' or 'position' option.`);\n    }\n    function getAxisFromDataset(id, axis, dataset) {\n        if (dataset[axis + 'AxisID'] === id) {\n            return {\n                axis\n            };\n        }\n    }\n    function retrieveAxisFromDatasets(id, config) {\n        if (config.data && config.data.datasets) {\n            const boundDs = config.data.datasets.filter((d)=>d.xAxisID === id || d.yAxisID === id);\n            if (boundDs.length) {\n                return getAxisFromDataset(id, 'x', boundDs[0]) || getAxisFromDataset(id, 'y', boundDs[0]);\n            }\n        }\n        return {};\n    }\n    function mergeScaleConfig(config, options) {\n        const chartDefaults = overrides[config.type] || {\n            scales: {}\n        };\n        const configScales = options.scales || {};\n        const chartIndexAxis = getIndexAxis(config.type, options);\n        const scales = Object.create(null);\n        Object.keys(configScales).forEach((id)=>{\n            const scaleConf = configScales[id];\n            if (!isObject(scaleConf)) {\n                return console.error(`Invalid scale configuration for scale: ${id}`);\n            }\n            if (scaleConf._proxy) {\n                return console.warn(`Ignoring resolver passed as options for scale: ${id}`);\n            }\n            const axis = determineAxis(id, scaleConf, retrieveAxisFromDatasets(id, config), defaults.scales[scaleConf.type]);\n            const defaultId = getDefaultScaleIDFromAxis(axis, chartIndexAxis);\n            const defaultScaleOptions = chartDefaults.scales || {};\n            scales[id] = mergeIf(Object.create(null), [\n                {\n                    axis\n                },\n                scaleConf,\n                defaultScaleOptions[axis],\n                defaultScaleOptions[defaultId]\n            ]);\n        });\n        config.data.datasets.forEach((dataset)=>{\n            const type = dataset.type || config.type;\n            const indexAxis = dataset.indexAxis || getIndexAxis(type, options);\n            const datasetDefaults = overrides[type] || {};\n            const defaultScaleOptions = datasetDefaults.scales || {};\n            Object.keys(defaultScaleOptions).forEach((defaultID)=>{\n                const axis = getAxisFromDefaultScaleID(defaultID, indexAxis);\n                const id = dataset[axis + 'AxisID'] || axis;\n                scales[id] = scales[id] || Object.create(null);\n                mergeIf(scales[id], [\n                    {\n                        axis\n                    },\n                    configScales[id],\n                    defaultScaleOptions[defaultID]\n                ]);\n            });\n        });\n        Object.keys(scales).forEach((key)=>{\n            const scale = scales[key];\n            mergeIf(scale, [\n                defaults.scales[scale.type],\n                defaults.scale\n            ]);\n        });\n        return scales;\n    }\n    function initOptions(config) {\n        const options = config.options || (config.options = {});\n        options.plugins = valueOrDefault(options.plugins, {});\n        options.scales = mergeScaleConfig(config, options);\n    }\n    function initData(data) {\n        data = data || {};\n        data.datasets = data.datasets || [];\n        data.labels = data.labels || [];\n        return data;\n    }\n    function initConfig(config) {\n        config = config || {};\n        config.data = initData(config.data);\n        initOptions(config);\n        return config;\n    }\n    const keyCache = new Map();\n    const keysCached = new Set();\n    function cachedKeys(cacheKey, generate) {\n        let keys = keyCache.get(cacheKey);\n        if (!keys) {\n            keys = generate();\n            keyCache.set(cacheKey, keys);\n            keysCached.add(keys);\n        }\n        return keys;\n    }\n    const addIfFound = (set, obj, key)=>{\n        const opts = resolveObjectKey(obj, key);\n        if (opts !== undefined) {\n            set.add(opts);\n        }\n    };\n    class Config {\n        constructor(config){\n            this._config = initConfig(config);\n            this._scopeCache = new Map();\n            this._resolverCache = new Map();\n        }\n        get platform() {\n            return this._config.platform;\n        }\n        get type() {\n            return this._config.type;\n        }\n        set type(type) {\n            this._config.type = type;\n        }\n        get data() {\n            return this._config.data;\n        }\n        set data(data) {\n            this._config.data = initData(data);\n        }\n        get options() {\n            return this._config.options;\n        }\n        set options(options) {\n            this._config.options = options;\n        }\n        get plugins() {\n            return this._config.plugins;\n        }\n        update() {\n            const config = this._config;\n            this.clearCache();\n            initOptions(config);\n        }\n        clearCache() {\n            this._scopeCache.clear();\n            this._resolverCache.clear();\n        }\n        datasetScopeKeys(datasetType) {\n            return cachedKeys(datasetType, ()=>[\n                [\n                    `datasets.${datasetType}`,\n                    ''\n                ]\n            ]);\n        }\n        datasetAnimationScopeKeys(datasetType, transition) {\n            return cachedKeys(`${datasetType}.transition.${transition}`, ()=>[\n                [\n                    `datasets.${datasetType}.transitions.${transition}`,\n                    `transitions.${transition}`\n                ],\n                [\n                    `datasets.${datasetType}`,\n                    ''\n                ]\n            ]);\n        }\n        datasetElementScopeKeys(datasetType, elementType) {\n            return cachedKeys(`${datasetType}-${elementType}`, ()=>[\n                [\n                    `datasets.${datasetType}.elements.${elementType}`,\n                    `datasets.${datasetType}`,\n                    `elements.${elementType}`,\n                    ''\n                ]\n            ]);\n        }\n        pluginScopeKeys(plugin) {\n            const id = plugin.id;\n            const type = this.type;\n            return cachedKeys(`${type}-plugin-${id}`, ()=>[\n                [\n                    `plugins.${id}`,\n                    ...plugin.additionalOptionScopes || []\n                ]\n            ]);\n        }\n        _cachedScopes(mainScope, resetCache) {\n            const _scopeCache = this._scopeCache;\n            let cache = _scopeCache.get(mainScope);\n            if (!cache || resetCache) {\n                cache = new Map();\n                _scopeCache.set(mainScope, cache);\n            }\n            return cache;\n        }\n        getOptionScopes(mainScope, keyLists, resetCache) {\n            const { options , type  } = this;\n            const cache = this._cachedScopes(mainScope, resetCache);\n            const cached = cache.get(keyLists);\n            if (cached) {\n                return cached;\n            }\n            const scopes = new Set();\n            keyLists.forEach((keys)=>{\n                if (mainScope) {\n                    scopes.add(mainScope);\n                    keys.forEach((key)=>addIfFound(scopes, mainScope, key));\n                }\n                keys.forEach((key)=>addIfFound(scopes, options, key));\n                keys.forEach((key)=>addIfFound(scopes, overrides[type] || {}, key));\n                keys.forEach((key)=>addIfFound(scopes, defaults, key));\n                keys.forEach((key)=>addIfFound(scopes, descriptors, key));\n            });\n            const array = Array.from(scopes);\n            if (array.length === 0) {\n                array.push(Object.create(null));\n            }\n            if (keysCached.has(keyLists)) {\n                cache.set(keyLists, array);\n            }\n            return array;\n        }\n        chartOptionScopes() {\n            const { options , type  } = this;\n            return [\n                options,\n                overrides[type] || {},\n                defaults.datasets[type] || {},\n                {\n                    type\n                },\n                defaults,\n                descriptors\n            ];\n        }\n        resolveNamedOptions(scopes, names, context, prefixes = [\n            ''\n        ]) {\n            const result = {\n                $shared: true\n            };\n            const { resolver , subPrefixes  } = getResolver(this._resolverCache, scopes, prefixes);\n            let options = resolver;\n            if (needContext(resolver, names)) {\n                result.$shared = false;\n                context = isFunction(context) ? context() : context;\n                const subResolver = this.createResolver(scopes, context, subPrefixes);\n                options = _attachContext(resolver, context, subResolver);\n            }\n            for (const prop of names){\n                result[prop] = options[prop];\n            }\n            return result;\n        }\n        createResolver(scopes, context, prefixes = [\n            ''\n        ], descriptorDefaults) {\n            const { resolver  } = getResolver(this._resolverCache, scopes, prefixes);\n            return isObject(context) ? _attachContext(resolver, context, undefined, descriptorDefaults) : resolver;\n        }\n    }\n    function getResolver(resolverCache, scopes, prefixes) {\n        let cache = resolverCache.get(scopes);\n        if (!cache) {\n            cache = new Map();\n            resolverCache.set(scopes, cache);\n        }\n        const cacheKey = prefixes.join();\n        let cached = cache.get(cacheKey);\n        if (!cached) {\n            const resolver = _createResolver(scopes, prefixes);\n            cached = {\n                resolver,\n                subPrefixes: prefixes.filter((p)=>!p.toLowerCase().includes('hover'))\n            };\n            cache.set(cacheKey, cached);\n        }\n        return cached;\n    }\n    const hasFunction = (value)=>isObject(value) && Object.getOwnPropertyNames(value).some((key)=>isFunction(value[key]));\n    function needContext(proxy, names) {\n        const { isScriptable , isIndexable  } = _descriptors(proxy);\n        for (const prop of names){\n            const scriptable = isScriptable(prop);\n            const indexable = isIndexable(prop);\n            const value = (indexable || scriptable) && proxy[prop];\n            if (scriptable && (isFunction(value) || hasFunction(value)) || indexable && isArray(value)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    var version = \"4.4.1\";\n\n    const KNOWN_POSITIONS = [\n        'top',\n        'bottom',\n        'left',\n        'right',\n        'chartArea'\n    ];\n    function positionIsHorizontal(position, axis) {\n        return position === 'top' || position === 'bottom' || KNOWN_POSITIONS.indexOf(position) === -1 && axis === 'x';\n    }\n    function compare2Level(l1, l2) {\n        return function(a, b) {\n            return a[l1] === b[l1] ? a[l2] - b[l2] : a[l1] - b[l1];\n        };\n    }\n    function onAnimationsComplete(context) {\n        const chart = context.chart;\n        const animationOptions = chart.options.animation;\n        chart.notifyPlugins('afterRender');\n        callback(animationOptions && animationOptions.onComplete, [\n            context\n        ], chart);\n    }\n    function onAnimationProgress(context) {\n        const chart = context.chart;\n        const animationOptions = chart.options.animation;\n        callback(animationOptions && animationOptions.onProgress, [\n            context\n        ], chart);\n    }\n    function getCanvas(item) {\n        if (_isDomSupported() && typeof item === 'string') {\n            item = document.getElementById(item);\n        } else if (item && item.length) {\n            item = item[0];\n        }\n        if (item && item.canvas) {\n            item = item.canvas;\n        }\n        return item;\n    }\n    const instances = {};\n    const getChart = (key)=>{\n        const canvas = getCanvas(key);\n        return Object.values(instances).filter((c)=>c.canvas === canvas).pop();\n    };\n    function moveNumericKeys(obj, start, move) {\n        const keys = Object.keys(obj);\n        for (const key of keys){\n            const intKey = +key;\n            if (intKey >= start) {\n                const value = obj[key];\n                delete obj[key];\n                if (move > 0 || intKey > start) {\n                    obj[intKey + move] = value;\n                }\n            }\n        }\n    }\n    function determineLastEvent(e, lastEvent, inChartArea, isClick) {\n        if (!inChartArea || e.type === 'mouseout') {\n            return null;\n        }\n        if (isClick) {\n            return lastEvent;\n        }\n        return e;\n    }\n    function getSizeForArea(scale, chartArea, field) {\n        return scale.options.clip ? scale[field] : chartArea[field];\n    }\n    function getDatasetArea(meta, chartArea) {\n        const { xScale , yScale  } = meta;\n        if (xScale && yScale) {\n            return {\n                left: getSizeForArea(xScale, chartArea, 'left'),\n                right: getSizeForArea(xScale, chartArea, 'right'),\n                top: getSizeForArea(yScale, chartArea, 'top'),\n                bottom: getSizeForArea(yScale, chartArea, 'bottom')\n            };\n        }\n        return chartArea;\n    }\n    class Chart {\n        static defaults = defaults;\n        static instances = instances;\n        static overrides = overrides;\n        static registry = registry;\n        static version = version;\n        static getChart = getChart;\n        static register(...items) {\n            registry.add(...items);\n            invalidatePlugins();\n        }\n        static unregister(...items) {\n            registry.remove(...items);\n            invalidatePlugins();\n        }\n        constructor(item, userConfig){\n            const config = this.config = new Config(userConfig);\n            const initialCanvas = getCanvas(item);\n            const existingChart = getChart(initialCanvas);\n            if (existingChart) {\n                throw new Error('Canvas is already in use. Chart with ID \\'' + existingChart.id + '\\'' + ' must be destroyed before the canvas with ID \\'' + existingChart.canvas.id + '\\' can be reused.');\n            }\n            const options = config.createResolver(config.chartOptionScopes(), this.getContext());\n            this.platform = new (config.platform || _detectPlatform(initialCanvas))();\n            this.platform.updateConfig(config);\n            const context = this.platform.acquireContext(initialCanvas, options.aspectRatio);\n            const canvas = context && context.canvas;\n            const height = canvas && canvas.height;\n            const width = canvas && canvas.width;\n            this.id = uid();\n            this.ctx = context;\n            this.canvas = canvas;\n            this.width = width;\n            this.height = height;\n            this._options = options;\n            this._aspectRatio = this.aspectRatio;\n            this._layers = [];\n            this._metasets = [];\n            this._stacks = undefined;\n            this.boxes = [];\n            this.currentDevicePixelRatio = undefined;\n            this.chartArea = undefined;\n            this._active = [];\n            this._lastEvent = undefined;\n            this._listeners = {};\n            this._responsiveListeners = undefined;\n            this._sortedMetasets = [];\n            this.scales = {};\n            this._plugins = new PluginService();\n            this.$proxies = {};\n            this._hiddenIndices = {};\n            this.attached = false;\n            this._animationsDisabled = undefined;\n            this.$context = undefined;\n            this._doResize = debounce((mode)=>this.update(mode), options.resizeDelay || 0);\n            this._dataChanges = [];\n            instances[this.id] = this;\n            if (!context || !canvas) {\n                console.error(\"Failed to create chart: can't acquire context from the given item\");\n                return;\n            }\n            animator.listen(this, 'complete', onAnimationsComplete);\n            animator.listen(this, 'progress', onAnimationProgress);\n            this._initialize();\n            if (this.attached) {\n                this.update();\n            }\n        }\n        get aspectRatio() {\n            const { options: { aspectRatio , maintainAspectRatio  } , width , height , _aspectRatio  } = this;\n            if (!isNullOrUndef(aspectRatio)) {\n                return aspectRatio;\n            }\n            if (maintainAspectRatio && _aspectRatio) {\n                return _aspectRatio;\n            }\n            return height ? width / height : null;\n        }\n        get data() {\n            return this.config.data;\n        }\n        set data(data) {\n            this.config.data = data;\n        }\n        get options() {\n            return this._options;\n        }\n        set options(options) {\n            this.config.options = options;\n        }\n        get registry() {\n            return registry;\n        }\n        _initialize() {\n            this.notifyPlugins('beforeInit');\n            if (this.options.responsive) {\n                this.resize();\n            } else {\n                retinaScale(this, this.options.devicePixelRatio);\n            }\n            this.bindEvents();\n            this.notifyPlugins('afterInit');\n            return this;\n        }\n        clear() {\n            clearCanvas(this.canvas, this.ctx);\n            return this;\n        }\n        stop() {\n            animator.stop(this);\n            return this;\n        }\n        resize(width, height) {\n            if (!animator.running(this)) {\n                this._resize(width, height);\n            } else {\n                this._resizeBeforeDraw = {\n                    width,\n                    height\n                };\n            }\n        }\n        _resize(width, height) {\n            const options = this.options;\n            const canvas = this.canvas;\n            const aspectRatio = options.maintainAspectRatio && this.aspectRatio;\n            const newSize = this.platform.getMaximumSize(canvas, width, height, aspectRatio);\n            const newRatio = options.devicePixelRatio || this.platform.getDevicePixelRatio();\n            const mode = this.width ? 'resize' : 'attach';\n            this.width = newSize.width;\n            this.height = newSize.height;\n            this._aspectRatio = this.aspectRatio;\n            if (!retinaScale(this, newRatio, true)) {\n                return;\n            }\n            this.notifyPlugins('resize', {\n                size: newSize\n            });\n            callback(options.onResize, [\n                this,\n                newSize\n            ], this);\n            if (this.attached) {\n                if (this._doResize(mode)) {\n                    this.render();\n                }\n            }\n        }\n        ensureScalesHaveIDs() {\n            const options = this.options;\n            const scalesOptions = options.scales || {};\n            each(scalesOptions, (axisOptions, axisID)=>{\n                axisOptions.id = axisID;\n            });\n        }\n        buildOrUpdateScales() {\n            const options = this.options;\n            const scaleOpts = options.scales;\n            const scales = this.scales;\n            const updated = Object.keys(scales).reduce((obj, id)=>{\n                obj[id] = false;\n                return obj;\n            }, {});\n            let items = [];\n            if (scaleOpts) {\n                items = items.concat(Object.keys(scaleOpts).map((id)=>{\n                    const scaleOptions = scaleOpts[id];\n                    const axis = determineAxis(id, scaleOptions);\n                    const isRadial = axis === 'r';\n                    const isHorizontal = axis === 'x';\n                    return {\n                        options: scaleOptions,\n                        dposition: isRadial ? 'chartArea' : isHorizontal ? 'bottom' : 'left',\n                        dtype: isRadial ? 'radialLinear' : isHorizontal ? 'category' : 'linear'\n                    };\n                }));\n            }\n            each(items, (item)=>{\n                const scaleOptions = item.options;\n                const id = scaleOptions.id;\n                const axis = determineAxis(id, scaleOptions);\n                const scaleType = valueOrDefault(scaleOptions.type, item.dtype);\n                if (scaleOptions.position === undefined || positionIsHorizontal(scaleOptions.position, axis) !== positionIsHorizontal(item.dposition)) {\n                    scaleOptions.position = item.dposition;\n                }\n                updated[id] = true;\n                let scale = null;\n                if (id in scales && scales[id].type === scaleType) {\n                    scale = scales[id];\n                } else {\n                    const scaleClass = registry.getScale(scaleType);\n                    scale = new scaleClass({\n                        id,\n                        type: scaleType,\n                        ctx: this.ctx,\n                        chart: this\n                    });\n                    scales[scale.id] = scale;\n                }\n                scale.init(scaleOptions, options);\n            });\n            each(updated, (hasUpdated, id)=>{\n                if (!hasUpdated) {\n                    delete scales[id];\n                }\n            });\n            each(scales, (scale)=>{\n                layouts.configure(this, scale, scale.options);\n                layouts.addBox(this, scale);\n            });\n        }\n        _updateMetasets() {\n            const metasets = this._metasets;\n            const numData = this.data.datasets.length;\n            const numMeta = metasets.length;\n            metasets.sort((a, b)=>a.index - b.index);\n            if (numMeta > numData) {\n                for(let i = numData; i < numMeta; ++i){\n                    this._destroyDatasetMeta(i);\n                }\n                metasets.splice(numData, numMeta - numData);\n            }\n            this._sortedMetasets = metasets.slice(0).sort(compare2Level('order', 'index'));\n        }\n        _removeUnreferencedMetasets() {\n            const { _metasets: metasets , data: { datasets  }  } = this;\n            if (metasets.length > datasets.length) {\n                delete this._stacks;\n            }\n            metasets.forEach((meta, index)=>{\n                if (datasets.filter((x)=>x === meta._dataset).length === 0) {\n                    this._destroyDatasetMeta(index);\n                }\n            });\n        }\n        buildOrUpdateControllers() {\n            const newControllers = [];\n            const datasets = this.data.datasets;\n            let i, ilen;\n            this._removeUnreferencedMetasets();\n            for(i = 0, ilen = datasets.length; i < ilen; i++){\n                const dataset = datasets[i];\n                let meta = this.getDatasetMeta(i);\n                const type = dataset.type || this.config.type;\n                if (meta.type && meta.type !== type) {\n                    this._destroyDatasetMeta(i);\n                    meta = this.getDatasetMeta(i);\n                }\n                meta.type = type;\n                meta.indexAxis = dataset.indexAxis || getIndexAxis(type, this.options);\n                meta.order = dataset.order || 0;\n                meta.index = i;\n                meta.label = '' + dataset.label;\n                meta.visible = this.isDatasetVisible(i);\n                if (meta.controller) {\n                    meta.controller.updateIndex(i);\n                    meta.controller.linkScales();\n                } else {\n                    const ControllerClass = registry.getController(type);\n                    const { datasetElementType , dataElementType  } = defaults.datasets[type];\n                    Object.assign(ControllerClass, {\n                        dataElementType: registry.getElement(dataElementType),\n                        datasetElementType: datasetElementType && registry.getElement(datasetElementType)\n                    });\n                    meta.controller = new ControllerClass(this, i);\n                    newControllers.push(meta.controller);\n                }\n            }\n            this._updateMetasets();\n            return newControllers;\n        }\n        _resetElements() {\n            each(this.data.datasets, (dataset, datasetIndex)=>{\n                this.getDatasetMeta(datasetIndex).controller.reset();\n            }, this);\n        }\n        reset() {\n            this._resetElements();\n            this.notifyPlugins('reset');\n        }\n        update(mode) {\n            const config = this.config;\n            config.update();\n            const options = this._options = config.createResolver(config.chartOptionScopes(), this.getContext());\n            const animsDisabled = this._animationsDisabled = !options.animation;\n            this._updateScales();\n            this._checkEventBindings();\n            this._updateHiddenIndices();\n            this._plugins.invalidate();\n            if (this.notifyPlugins('beforeUpdate', {\n                mode,\n                cancelable: true\n            }) === false) {\n                return;\n            }\n            const newControllers = this.buildOrUpdateControllers();\n            this.notifyPlugins('beforeElementsUpdate');\n            let minPadding = 0;\n            for(let i = 0, ilen = this.data.datasets.length; i < ilen; i++){\n                const { controller  } = this.getDatasetMeta(i);\n                const reset = !animsDisabled && newControllers.indexOf(controller) === -1;\n                controller.buildOrUpdateElements(reset);\n                minPadding = Math.max(+controller.getMaxOverflow(), minPadding);\n            }\n            minPadding = this._minPadding = options.layout.autoPadding ? minPadding : 0;\n            this._updateLayout(minPadding);\n            if (!animsDisabled) {\n                each(newControllers, (controller)=>{\n                    controller.reset();\n                });\n            }\n            this._updateDatasets(mode);\n            this.notifyPlugins('afterUpdate', {\n                mode\n            });\n            this._layers.sort(compare2Level('z', '_idx'));\n            const { _active , _lastEvent  } = this;\n            if (_lastEvent) {\n                this._eventHandler(_lastEvent, true);\n            } else if (_active.length) {\n                this._updateHoverStyles(_active, _active, true);\n            }\n            this.render();\n        }\n        _updateScales() {\n            each(this.scales, (scale)=>{\n                layouts.removeBox(this, scale);\n            });\n            this.ensureScalesHaveIDs();\n            this.buildOrUpdateScales();\n        }\n        _checkEventBindings() {\n            const options = this.options;\n            const existingEvents = new Set(Object.keys(this._listeners));\n            const newEvents = new Set(options.events);\n            if (!setsEqual(existingEvents, newEvents) || !!this._responsiveListeners !== options.responsive) {\n                this.unbindEvents();\n                this.bindEvents();\n            }\n        }\n        _updateHiddenIndices() {\n            const { _hiddenIndices  } = this;\n            const changes = this._getUniformDataChanges() || [];\n            for (const { method , start , count  } of changes){\n                const move = method === '_removeElements' ? -count : count;\n                moveNumericKeys(_hiddenIndices, start, move);\n            }\n        }\n        _getUniformDataChanges() {\n            const _dataChanges = this._dataChanges;\n            if (!_dataChanges || !_dataChanges.length) {\n                return;\n            }\n            this._dataChanges = [];\n            const datasetCount = this.data.datasets.length;\n            const makeSet = (idx)=>new Set(_dataChanges.filter((c)=>c[0] === idx).map((c, i)=>i + ',' + c.splice(1).join(',')));\n            const changeSet = makeSet(0);\n            for(let i = 1; i < datasetCount; i++){\n                if (!setsEqual(changeSet, makeSet(i))) {\n                    return;\n                }\n            }\n            return Array.from(changeSet).map((c)=>c.split(',')).map((a)=>({\n                method: a[1],\n                start: +a[2],\n                count: +a[3]\n            }));\n        }\n        _updateLayout(minPadding) {\n            if (this.notifyPlugins('beforeLayout', {\n                cancelable: true\n            }) === false) {\n                return;\n            }\n            layouts.update(this, this.width, this.height, minPadding);\n            const area = this.chartArea;\n            const noArea = area.width <= 0 || area.height <= 0;\n            this._layers = [];\n            each(this.boxes, (box)=>{\n                if (noArea && box.position === 'chartArea') {\n                    return;\n                }\n                if (box.configure) {\n                    box.configure();\n                }\n                this._layers.push(...box._layers());\n            }, this);\n            this._layers.forEach((item, index)=>{\n                item._idx = index;\n            });\n            this.notifyPlugins('afterLayout');\n        }\n        _updateDatasets(mode) {\n            if (this.notifyPlugins('beforeDatasetsUpdate', {\n                mode,\n                cancelable: true\n            }) === false) {\n                return;\n            }\n            for(let i = 0, ilen = this.data.datasets.length; i < ilen; ++i){\n                this.getDatasetMeta(i).controller.configure();\n            }\n            for(let i = 0, ilen = this.data.datasets.length; i < ilen; ++i){\n                this._updateDataset(i, isFunction(mode) ? mode({\n                    datasetIndex: i\n                }) : mode);\n            }\n            this.notifyPlugins('afterDatasetsUpdate', {\n                mode\n            });\n        }\n        _updateDataset(index, mode) {\n            const meta = this.getDatasetMeta(index);\n            const args = {\n                meta,\n                index,\n                mode,\n                cancelable: true\n            };\n            if (this.notifyPlugins('beforeDatasetUpdate', args) === false) {\n                return;\n            }\n            meta.controller._update(mode);\n            args.cancelable = false;\n            this.notifyPlugins('afterDatasetUpdate', args);\n        }\n        render() {\n            if (this.notifyPlugins('beforeRender', {\n                cancelable: true\n            }) === false) {\n                return;\n            }\n            if (animator.has(this)) {\n                if (this.attached && !animator.running(this)) {\n                    animator.start(this);\n                }\n            } else {\n                this.draw();\n                onAnimationsComplete({\n                    chart: this\n                });\n            }\n        }\n        draw() {\n            let i;\n            if (this._resizeBeforeDraw) {\n                const { width , height  } = this._resizeBeforeDraw;\n                this._resize(width, height);\n                this._resizeBeforeDraw = null;\n            }\n            this.clear();\n            if (this.width <= 0 || this.height <= 0) {\n                return;\n            }\n            if (this.notifyPlugins('beforeDraw', {\n                cancelable: true\n            }) === false) {\n                return;\n            }\n            const layers = this._layers;\n            for(i = 0; i < layers.length && layers[i].z <= 0; ++i){\n                layers[i].draw(this.chartArea);\n            }\n            this._drawDatasets();\n            for(; i < layers.length; ++i){\n                layers[i].draw(this.chartArea);\n            }\n            this.notifyPlugins('afterDraw');\n        }\n        _getSortedDatasetMetas(filterVisible) {\n            const metasets = this._sortedMetasets;\n            const result = [];\n            let i, ilen;\n            for(i = 0, ilen = metasets.length; i < ilen; ++i){\n                const meta = metasets[i];\n                if (!filterVisible || meta.visible) {\n                    result.push(meta);\n                }\n            }\n            return result;\n        }\n        getSortedVisibleDatasetMetas() {\n            return this._getSortedDatasetMetas(true);\n        }\n        _drawDatasets() {\n            if (this.notifyPlugins('beforeDatasetsDraw', {\n                cancelable: true\n            }) === false) {\n                return;\n            }\n            const metasets = this.getSortedVisibleDatasetMetas();\n            for(let i = metasets.length - 1; i >= 0; --i){\n                this._drawDataset(metasets[i]);\n            }\n            this.notifyPlugins('afterDatasetsDraw');\n        }\n        _drawDataset(meta) {\n            const ctx = this.ctx;\n            const clip = meta._clip;\n            const useClip = !clip.disabled;\n            const area = getDatasetArea(meta, this.chartArea);\n            const args = {\n                meta,\n                index: meta.index,\n                cancelable: true\n            };\n            if (this.notifyPlugins('beforeDatasetDraw', args) === false) {\n                return;\n            }\n            if (useClip) {\n                clipArea(ctx, {\n                    left: clip.left === false ? 0 : area.left - clip.left,\n                    right: clip.right === false ? this.width : area.right + clip.right,\n                    top: clip.top === false ? 0 : area.top - clip.top,\n                    bottom: clip.bottom === false ? this.height : area.bottom + clip.bottom\n                });\n            }\n            meta.controller.draw();\n            if (useClip) {\n                unclipArea(ctx);\n            }\n            args.cancelable = false;\n            this.notifyPlugins('afterDatasetDraw', args);\n        }\n        isPointInArea(point) {\n            return _isPointInArea(point, this.chartArea, this._minPadding);\n        }\n        getElementsAtEventForMode(e, mode, options, useFinalPosition) {\n            const method = Interaction.modes[mode];\n            if (typeof method === 'function') {\n                return method(this, e, options, useFinalPosition);\n            }\n            return [];\n        }\n        getDatasetMeta(datasetIndex) {\n            const dataset = this.data.datasets[datasetIndex];\n            const metasets = this._metasets;\n            let meta = metasets.filter((x)=>x && x._dataset === dataset).pop();\n            if (!meta) {\n                meta = {\n                    type: null,\n                    data: [],\n                    dataset: null,\n                    controller: null,\n                    hidden: null,\n                    xAxisID: null,\n                    yAxisID: null,\n                    order: dataset && dataset.order || 0,\n                    index: datasetIndex,\n                    _dataset: dataset,\n                    _parsed: [],\n                    _sorted: false\n                };\n                metasets.push(meta);\n            }\n            return meta;\n        }\n        getContext() {\n            return this.$context || (this.$context = createContext(null, {\n                chart: this,\n                type: 'chart'\n            }));\n        }\n        getVisibleDatasetCount() {\n            return this.getSortedVisibleDatasetMetas().length;\n        }\n        isDatasetVisible(datasetIndex) {\n            const dataset = this.data.datasets[datasetIndex];\n            if (!dataset) {\n                return false;\n            }\n            const meta = this.getDatasetMeta(datasetIndex);\n            return typeof meta.hidden === 'boolean' ? !meta.hidden : !dataset.hidden;\n        }\n        setDatasetVisibility(datasetIndex, visible) {\n            const meta = this.getDatasetMeta(datasetIndex);\n            meta.hidden = !visible;\n        }\n        toggleDataVisibility(index) {\n            this._hiddenIndices[index] = !this._hiddenIndices[index];\n        }\n        getDataVisibility(index) {\n            return !this._hiddenIndices[index];\n        }\n        _updateVisibility(datasetIndex, dataIndex, visible) {\n            const mode = visible ? 'show' : 'hide';\n            const meta = this.getDatasetMeta(datasetIndex);\n            const anims = meta.controller._resolveAnimations(undefined, mode);\n            if (defined(dataIndex)) {\n                meta.data[dataIndex].hidden = !visible;\n                this.update();\n            } else {\n                this.setDatasetVisibility(datasetIndex, visible);\n                anims.update(meta, {\n                    visible\n                });\n                this.update((ctx)=>ctx.datasetIndex === datasetIndex ? mode : undefined);\n            }\n        }\n        hide(datasetIndex, dataIndex) {\n            this._updateVisibility(datasetIndex, dataIndex, false);\n        }\n        show(datasetIndex, dataIndex) {\n            this._updateVisibility(datasetIndex, dataIndex, true);\n        }\n        _destroyDatasetMeta(datasetIndex) {\n            const meta = this._metasets[datasetIndex];\n            if (meta && meta.controller) {\n                meta.controller._destroy();\n            }\n            delete this._metasets[datasetIndex];\n        }\n        _stop() {\n            let i, ilen;\n            this.stop();\n            animator.remove(this);\n            for(i = 0, ilen = this.data.datasets.length; i < ilen; ++i){\n                this._destroyDatasetMeta(i);\n            }\n        }\n        destroy() {\n            this.notifyPlugins('beforeDestroy');\n            const { canvas , ctx  } = this;\n            this._stop();\n            this.config.clearCache();\n            if (canvas) {\n                this.unbindEvents();\n                clearCanvas(canvas, ctx);\n                this.platform.releaseContext(ctx);\n                this.canvas = null;\n                this.ctx = null;\n            }\n            delete instances[this.id];\n            this.notifyPlugins('afterDestroy');\n        }\n        toBase64Image(...args) {\n            return this.canvas.toDataURL(...args);\n        }\n        bindEvents() {\n            this.bindUserEvents();\n            if (this.options.responsive) {\n                this.bindResponsiveEvents();\n            } else {\n                this.attached = true;\n            }\n        }\n        bindUserEvents() {\n            const listeners = this._listeners;\n            const platform = this.platform;\n            const _add = (type, listener)=>{\n                platform.addEventListener(this, type, listener);\n                listeners[type] = listener;\n            };\n            const listener = (e, x, y)=>{\n                e.offsetX = x;\n                e.offsetY = y;\n                this._eventHandler(e);\n            };\n            each(this.options.events, (type)=>_add(type, listener));\n        }\n        bindResponsiveEvents() {\n            if (!this._responsiveListeners) {\n                this._responsiveListeners = {};\n            }\n            const listeners = this._responsiveListeners;\n            const platform = this.platform;\n            const _add = (type, listener)=>{\n                platform.addEventListener(this, type, listener);\n                listeners[type] = listener;\n            };\n            const _remove = (type, listener)=>{\n                if (listeners[type]) {\n                    platform.removeEventListener(this, type, listener);\n                    delete listeners[type];\n                }\n            };\n            const listener = (width, height)=>{\n                if (this.canvas) {\n                    this.resize(width, height);\n                }\n            };\n            let detached;\n            const attached = ()=>{\n                _remove('attach', attached);\n                this.attached = true;\n                this.resize();\n                _add('resize', listener);\n                _add('detach', detached);\n            };\n            detached = ()=>{\n                this.attached = false;\n                _remove('resize', listener);\n                this._stop();\n                this._resize(0, 0);\n                _add('attach', attached);\n            };\n            if (platform.isAttached(this.canvas)) {\n                attached();\n            } else {\n                detached();\n            }\n        }\n        unbindEvents() {\n            each(this._listeners, (listener, type)=>{\n                this.platform.removeEventListener(this, type, listener);\n            });\n            this._listeners = {};\n            each(this._responsiveListeners, (listener, type)=>{\n                this.platform.removeEventListener(this, type, listener);\n            });\n            this._responsiveListeners = undefined;\n        }\n        updateHoverStyle(items, mode, enabled) {\n            const prefix = enabled ? 'set' : 'remove';\n            let meta, item, i, ilen;\n            if (mode === 'dataset') {\n                meta = this.getDatasetMeta(items[0].datasetIndex);\n                meta.controller['_' + prefix + 'DatasetHoverStyle']();\n            }\n            for(i = 0, ilen = items.length; i < ilen; ++i){\n                item = items[i];\n                const controller = item && this.getDatasetMeta(item.datasetIndex).controller;\n                if (controller) {\n                    controller[prefix + 'HoverStyle'](item.element, item.datasetIndex, item.index);\n                }\n            }\n        }\n        getActiveElements() {\n            return this._active || [];\n        }\n        setActiveElements(activeElements) {\n            const lastActive = this._active || [];\n            const active = activeElements.map(({ datasetIndex , index  })=>{\n                const meta = this.getDatasetMeta(datasetIndex);\n                if (!meta) {\n                    throw new Error('No dataset found at index ' + datasetIndex);\n                }\n                return {\n                    datasetIndex,\n                    element: meta.data[index],\n                    index\n                };\n            });\n            const changed = !_elementsEqual(active, lastActive);\n            if (changed) {\n                this._active = active;\n                this._lastEvent = null;\n                this._updateHoverStyles(active, lastActive);\n            }\n        }\n        notifyPlugins(hook, args, filter) {\n            return this._plugins.notify(this, hook, args, filter);\n        }\n        isPluginEnabled(pluginId) {\n            return this._plugins._cache.filter((p)=>p.plugin.id === pluginId).length === 1;\n        }\n        _updateHoverStyles(active, lastActive, replay) {\n            const hoverOptions = this.options.hover;\n            const diff = (a, b)=>a.filter((x)=>!b.some((y)=>x.datasetIndex === y.datasetIndex && x.index === y.index));\n            const deactivated = diff(lastActive, active);\n            const activated = replay ? active : diff(active, lastActive);\n            if (deactivated.length) {\n                this.updateHoverStyle(deactivated, hoverOptions.mode, false);\n            }\n            if (activated.length && hoverOptions.mode) {\n                this.updateHoverStyle(activated, hoverOptions.mode, true);\n            }\n        }\n        _eventHandler(e, replay) {\n            const args = {\n                event: e,\n                replay,\n                cancelable: true,\n                inChartArea: this.isPointInArea(e)\n            };\n            const eventFilter = (plugin)=>(plugin.options.events || this.options.events).includes(e.native.type);\n            if (this.notifyPlugins('beforeEvent', args, eventFilter) === false) {\n                return;\n            }\n            const changed = this._handleEvent(e, replay, args.inChartArea);\n            args.cancelable = false;\n            this.notifyPlugins('afterEvent', args, eventFilter);\n            if (changed || args.changed) {\n                this.render();\n            }\n            return this;\n        }\n        _handleEvent(e, replay, inChartArea) {\n            const { _active: lastActive = [] , options  } = this;\n            const useFinalPosition = replay;\n            const active = this._getActiveElements(e, lastActive, inChartArea, useFinalPosition);\n            const isClick = _isClickEvent(e);\n            const lastEvent = determineLastEvent(e, this._lastEvent, inChartArea, isClick);\n            if (inChartArea) {\n                this._lastEvent = null;\n                callback(options.onHover, [\n                    e,\n                    active,\n                    this\n                ], this);\n                if (isClick) {\n                    callback(options.onClick, [\n                        e,\n                        active,\n                        this\n                    ], this);\n                }\n            }\n            const changed = !_elementsEqual(active, lastActive);\n            if (changed || replay) {\n                this._active = active;\n                this._updateHoverStyles(active, lastActive, replay);\n            }\n            this._lastEvent = lastEvent;\n            return changed;\n        }\n        _getActiveElements(e, lastActive, inChartArea, useFinalPosition) {\n            if (e.type === 'mouseout') {\n                return [];\n            }\n            if (!inChartArea) {\n                return lastActive;\n            }\n            const hoverOptions = this.options.hover;\n            return this.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions, useFinalPosition);\n        }\n    }\n    function invalidatePlugins() {\n        return each(Chart.instances, (chart)=>chart._plugins.invalidate());\n    }\n\n    /**\n     * @namespace Chart._adapters\n     * @since 2.8.0\n     * @private\n     */ function abstract() {\n        throw new Error('This method is not implemented: Check that a complete date adapter is provided.');\n    }\n    /**\n     * Date adapter (current used by the time scale)\n     * @namespace Chart._adapters._date\n     * @memberof Chart._adapters\n     * @private\n     */ class DateAdapterBase {\n        /**\n         * Override default date adapter methods.\n         * Accepts type parameter to define options type.\n         * @example\n         * Chart._adapters._date.override<{myAdapterOption: string}>({\n         *   init() {\n         *     console.log(this.options.myAdapterOption);\n         *   }\n         * })\n         */ static override(members) {\n            Object.assign(DateAdapterBase.prototype, members);\n        }\n        options;\n        constructor(options){\n            this.options = options || {};\n        }\n        // eslint-disable-next-line @typescript-eslint/no-empty-function\n        init() {}\n        formats() {\n            return abstract();\n        }\n        parse() {\n            return abstract();\n        }\n        format() {\n            return abstract();\n        }\n        add() {\n            return abstract();\n        }\n        diff() {\n            return abstract();\n        }\n        startOf() {\n            return abstract();\n        }\n        endOf() {\n            return abstract();\n        }\n    }\n    var _adapters = {\n        _date: DateAdapterBase\n    };\n\n    function getAllScaleValues(scale, type) {\n        if (!scale._cache.$bar) {\n            const visibleMetas = scale.getMatchingVisibleMetas(type);\n            let values = [];\n            for(let i = 0, ilen = visibleMetas.length; i < ilen; i++){\n                values = values.concat(visibleMetas[i].controller.getAllParsedValues(scale));\n            }\n            scale._cache.$bar = _arrayUnique(values.sort((a, b)=>a - b));\n        }\n        return scale._cache.$bar;\n    }\n    function computeMinSampleSize(meta) {\n        const scale = meta.iScale;\n        const values = getAllScaleValues(scale, meta.type);\n        let min = scale._length;\n        let i, ilen, curr, prev;\n        const updateMinAndPrev = ()=>{\n            if (curr === 32767 || curr === -32768) {\n                return;\n            }\n            if (defined(prev)) {\n                min = Math.min(min, Math.abs(curr - prev) || min);\n            }\n            prev = curr;\n        };\n        for(i = 0, ilen = values.length; i < ilen; ++i){\n            curr = scale.getPixelForValue(values[i]);\n            updateMinAndPrev();\n        }\n        prev = undefined;\n        for(i = 0, ilen = scale.ticks.length; i < ilen; ++i){\n            curr = scale.getPixelForTick(i);\n            updateMinAndPrev();\n        }\n        return min;\n    }\n    function computeFitCategoryTraits(index, ruler, options, stackCount) {\n        const thickness = options.barThickness;\n        let size, ratio;\n        if (isNullOrUndef(thickness)) {\n            size = ruler.min * options.categoryPercentage;\n            ratio = options.barPercentage;\n        } else {\n            size = thickness * stackCount;\n            ratio = 1;\n        }\n        return {\n            chunk: size / stackCount,\n            ratio,\n            start: ruler.pixels[index] - size / 2\n        };\n    }\n    function computeFlexCategoryTraits(index, ruler, options, stackCount) {\n        const pixels = ruler.pixels;\n        const curr = pixels[index];\n        let prev = index > 0 ? pixels[index - 1] : null;\n        let next = index < pixels.length - 1 ? pixels[index + 1] : null;\n        const percent = options.categoryPercentage;\n        if (prev === null) {\n            prev = curr - (next === null ? ruler.end - ruler.start : next - curr);\n        }\n        if (next === null) {\n            next = curr + curr - prev;\n        }\n        const start = curr - (curr - Math.min(prev, next)) / 2 * percent;\n        const size = Math.abs(next - prev) / 2 * percent;\n        return {\n            chunk: size / stackCount,\n            ratio: options.barPercentage,\n            start\n        };\n    }\n    function parseFloatBar(entry, item, vScale, i) {\n        const startValue = vScale.parse(entry[0], i);\n        const endValue = vScale.parse(entry[1], i);\n        const min = Math.min(startValue, endValue);\n        const max = Math.max(startValue, endValue);\n        let barStart = min;\n        let barEnd = max;\n        if (Math.abs(min) > Math.abs(max)) {\n            barStart = max;\n            barEnd = min;\n        }\n        item[vScale.axis] = barEnd;\n        item._custom = {\n            barStart,\n            barEnd,\n            start: startValue,\n            end: endValue,\n            min,\n            max\n        };\n    }\n    function parseValue(entry, item, vScale, i) {\n        if (isArray(entry)) {\n            parseFloatBar(entry, item, vScale, i);\n        } else {\n            item[vScale.axis] = vScale.parse(entry, i);\n        }\n        return item;\n    }\n    function parseArrayOrPrimitive(meta, data, start, count) {\n        const iScale = meta.iScale;\n        const vScale = meta.vScale;\n        const labels = iScale.getLabels();\n        const singleScale = iScale === vScale;\n        const parsed = [];\n        let i, ilen, item, entry;\n        for(i = start, ilen = start + count; i < ilen; ++i){\n            entry = data[i];\n            item = {};\n            item[iScale.axis] = singleScale || iScale.parse(labels[i], i);\n            parsed.push(parseValue(entry, item, vScale, i));\n        }\n        return parsed;\n    }\n    function isFloatBar(custom) {\n        return custom && custom.barStart !== undefined && custom.barEnd !== undefined;\n    }\n    function barSign(size, vScale, actualBase) {\n        if (size !== 0) {\n            return sign(size);\n        }\n        return (vScale.isHorizontal() ? 1 : -1) * (vScale.min >= actualBase ? 1 : -1);\n    }\n    function borderProps(properties) {\n        let reverse, start, end, top, bottom;\n        if (properties.horizontal) {\n            reverse = properties.base > properties.x;\n            start = 'left';\n            end = 'right';\n        } else {\n            reverse = properties.base < properties.y;\n            start = 'bottom';\n            end = 'top';\n        }\n        if (reverse) {\n            top = 'end';\n            bottom = 'start';\n        } else {\n            top = 'start';\n            bottom = 'end';\n        }\n        return {\n            start,\n            end,\n            reverse,\n            top,\n            bottom\n        };\n    }\n    function setBorderSkipped(properties, options, stack, index) {\n        let edge = options.borderSkipped;\n        const res = {};\n        if (!edge) {\n            properties.borderSkipped = res;\n            return;\n        }\n        if (edge === true) {\n            properties.borderSkipped = {\n                top: true,\n                right: true,\n                bottom: true,\n                left: true\n            };\n            return;\n        }\n        const { start , end , reverse , top , bottom  } = borderProps(properties);\n        if (edge === 'middle' && stack) {\n            properties.enableBorderRadius = true;\n            if ((stack._top || 0) === index) {\n                edge = top;\n            } else if ((stack._bottom || 0) === index) {\n                edge = bottom;\n            } else {\n                res[parseEdge(bottom, start, end, reverse)] = true;\n                edge = top;\n            }\n        }\n        res[parseEdge(edge, start, end, reverse)] = true;\n        properties.borderSkipped = res;\n    }\n    function parseEdge(edge, a, b, reverse) {\n        if (reverse) {\n            edge = swap(edge, a, b);\n            edge = startEnd(edge, b, a);\n        } else {\n            edge = startEnd(edge, a, b);\n        }\n        return edge;\n    }\n    function swap(orig, v1, v2) {\n        return orig === v1 ? v2 : orig === v2 ? v1 : orig;\n    }\n    function startEnd(v, start, end) {\n        return v === 'start' ? start : v === 'end' ? end : v;\n    }\n    function setInflateAmount(properties, { inflateAmount  }, ratio) {\n        properties.inflateAmount = inflateAmount === 'auto' ? ratio === 1 ? 0.33 : 0 : inflateAmount;\n    }\n    class BarController extends DatasetController {\n        static id = 'bar';\n        static defaults = {\n            datasetElementType: false,\n            dataElementType: 'bar',\n            categoryPercentage: 0.8,\n            barPercentage: 0.9,\n            grouped: true,\n            animations: {\n                numbers: {\n                    type: 'number',\n                    properties: [\n                        'x',\n                        'y',\n                        'base',\n                        'width',\n                        'height'\n                    ]\n                }\n            }\n        };\n        static overrides = {\n            scales: {\n                _index_: {\n                    type: 'category',\n                    offset: true,\n                    grid: {\n                        offset: true\n                    }\n                },\n                _value_: {\n                    type: 'linear',\n                    beginAtZero: true\n                }\n            }\n        };\n        parsePrimitiveData(meta, data, start, count) {\n            return parseArrayOrPrimitive(meta, data, start, count);\n        }\n        parseArrayData(meta, data, start, count) {\n            return parseArrayOrPrimitive(meta, data, start, count);\n        }\n        parseObjectData(meta, data, start, count) {\n            const { iScale , vScale  } = meta;\n            const { xAxisKey ='x' , yAxisKey ='y'  } = this._parsing;\n            const iAxisKey = iScale.axis === 'x' ? xAxisKey : yAxisKey;\n            const vAxisKey = vScale.axis === 'x' ? xAxisKey : yAxisKey;\n            const parsed = [];\n            let i, ilen, item, obj;\n            for(i = start, ilen = start + count; i < ilen; ++i){\n                obj = data[i];\n                item = {};\n                item[iScale.axis] = iScale.parse(resolveObjectKey(obj, iAxisKey), i);\n                parsed.push(parseValue(resolveObjectKey(obj, vAxisKey), item, vScale, i));\n            }\n            return parsed;\n        }\n        updateRangeFromParsed(range, scale, parsed, stack) {\n            super.updateRangeFromParsed(range, scale, parsed, stack);\n            const custom = parsed._custom;\n            if (custom && scale === this._cachedMeta.vScale) {\n                range.min = Math.min(range.min, custom.min);\n                range.max = Math.max(range.max, custom.max);\n            }\n        }\n        getMaxOverflow() {\n            return 0;\n        }\n        getLabelAndValue(index) {\n            const meta = this._cachedMeta;\n            const { iScale , vScale  } = meta;\n            const parsed = this.getParsed(index);\n            const custom = parsed._custom;\n            const value = isFloatBar(custom) ? '[' + custom.start + ', ' + custom.end + ']' : '' + vScale.getLabelForValue(parsed[vScale.axis]);\n            return {\n                label: '' + iScale.getLabelForValue(parsed[iScale.axis]),\n                value\n            };\n        }\n        initialize() {\n            this.enableOptionSharing = true;\n            super.initialize();\n            const meta = this._cachedMeta;\n            meta.stack = this.getDataset().stack;\n        }\n        update(mode) {\n            const meta = this._cachedMeta;\n            this.updateElements(meta.data, 0, meta.data.length, mode);\n        }\n        updateElements(bars, start, count, mode) {\n            const reset = mode === 'reset';\n            const { index , _cachedMeta: { vScale  }  } = this;\n            const base = vScale.getBasePixel();\n            const horizontal = vScale.isHorizontal();\n            const ruler = this._getRuler();\n            const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);\n            for(let i = start; i < start + count; i++){\n                const parsed = this.getParsed(i);\n                const vpixels = reset || isNullOrUndef(parsed[vScale.axis]) ? {\n                    base,\n                    head: base\n                } : this._calculateBarValuePixels(i);\n                const ipixels = this._calculateBarIndexPixels(i, ruler);\n                const stack = (parsed._stacks || {})[vScale.axis];\n                const properties = {\n                    horizontal,\n                    base: vpixels.base,\n                    enableBorderRadius: !stack || isFloatBar(parsed._custom) || index === stack._top || index === stack._bottom,\n                    x: horizontal ? vpixels.head : ipixels.center,\n                    y: horizontal ? ipixels.center : vpixels.head,\n                    height: horizontal ? ipixels.size : Math.abs(vpixels.size),\n                    width: horizontal ? Math.abs(vpixels.size) : ipixels.size\n                };\n                if (includeOptions) {\n                    properties.options = sharedOptions || this.resolveDataElementOptions(i, bars[i].active ? 'active' : mode);\n                }\n                const options = properties.options || bars[i].options;\n                setBorderSkipped(properties, options, stack, index);\n                setInflateAmount(properties, options, ruler.ratio);\n                this.updateElement(bars[i], i, properties, mode);\n            }\n        }\n        _getStacks(last, dataIndex) {\n            const { iScale  } = this._cachedMeta;\n            const metasets = iScale.getMatchingVisibleMetas(this._type).filter((meta)=>meta.controller.options.grouped);\n            const stacked = iScale.options.stacked;\n            const stacks = [];\n            const skipNull = (meta)=>{\n                const parsed = meta.controller.getParsed(dataIndex);\n                const val = parsed && parsed[meta.vScale.axis];\n                if (isNullOrUndef(val) || isNaN(val)) {\n                    return true;\n                }\n            };\n            for (const meta of metasets){\n                if (dataIndex !== undefined && skipNull(meta)) {\n                    continue;\n                }\n                if (stacked === false || stacks.indexOf(meta.stack) === -1 || stacked === undefined && meta.stack === undefined) {\n                    stacks.push(meta.stack);\n                }\n                if (meta.index === last) {\n                    break;\n                }\n            }\n            if (!stacks.length) {\n                stacks.push(undefined);\n            }\n            return stacks;\n        }\n        _getStackCount(index) {\n            return this._getStacks(undefined, index).length;\n        }\n        _getStackIndex(datasetIndex, name, dataIndex) {\n            const stacks = this._getStacks(datasetIndex, dataIndex);\n            const index = name !== undefined ? stacks.indexOf(name) : -1;\n            return index === -1 ? stacks.length - 1 : index;\n        }\n        _getRuler() {\n            const opts = this.options;\n            const meta = this._cachedMeta;\n            const iScale = meta.iScale;\n            const pixels = [];\n            let i, ilen;\n            for(i = 0, ilen = meta.data.length; i < ilen; ++i){\n                pixels.push(iScale.getPixelForValue(this.getParsed(i)[iScale.axis], i));\n            }\n            const barThickness = opts.barThickness;\n            const min = barThickness || computeMinSampleSize(meta);\n            return {\n                min,\n                pixels,\n                start: iScale._startPixel,\n                end: iScale._endPixel,\n                stackCount: this._getStackCount(),\n                scale: iScale,\n                grouped: opts.grouped,\n                ratio: barThickness ? 1 : opts.categoryPercentage * opts.barPercentage\n            };\n        }\n        _calculateBarValuePixels(index) {\n            const { _cachedMeta: { vScale , _stacked , index: datasetIndex  } , options: { base: baseValue , minBarLength  }  } = this;\n            const actualBase = baseValue || 0;\n            const parsed = this.getParsed(index);\n            const custom = parsed._custom;\n            const floating = isFloatBar(custom);\n            let value = parsed[vScale.axis];\n            let start = 0;\n            let length = _stacked ? this.applyStack(vScale, parsed, _stacked) : value;\n            let head, size;\n            if (length !== value) {\n                start = length - value;\n                length = value;\n            }\n            if (floating) {\n                value = custom.barStart;\n                length = custom.barEnd - custom.barStart;\n                if (value !== 0 && sign(value) !== sign(custom.barEnd)) {\n                    start = 0;\n                }\n                start += value;\n            }\n            const startValue = !isNullOrUndef(baseValue) && !floating ? baseValue : start;\n            let base = vScale.getPixelForValue(startValue);\n            if (this.chart.getDataVisibility(index)) {\n                head = vScale.getPixelForValue(start + length);\n            } else {\n                head = base;\n            }\n            size = head - base;\n            if (Math.abs(size) < minBarLength) {\n                size = barSign(size, vScale, actualBase) * minBarLength;\n                if (value === actualBase) {\n                    base -= size / 2;\n                }\n                const startPixel = vScale.getPixelForDecimal(0);\n                const endPixel = vScale.getPixelForDecimal(1);\n                const min = Math.min(startPixel, endPixel);\n                const max = Math.max(startPixel, endPixel);\n                base = Math.max(Math.min(base, max), min);\n                head = base + size;\n                if (_stacked && !floating) {\n                    parsed._stacks[vScale.axis]._visualValues[datasetIndex] = vScale.getValueForPixel(head) - vScale.getValueForPixel(base);\n                }\n            }\n            if (base === vScale.getPixelForValue(actualBase)) {\n                const halfGrid = sign(size) * vScale.getLineWidthForValue(actualBase) / 2;\n                base += halfGrid;\n                size -= halfGrid;\n            }\n            return {\n                size,\n                base,\n                head,\n                center: head + size / 2\n            };\n        }\n        _calculateBarIndexPixels(index, ruler) {\n            const scale = ruler.scale;\n            const options = this.options;\n            const skipNull = options.skipNull;\n            const maxBarThickness = valueOrDefault(options.maxBarThickness, Infinity);\n            let center, size;\n            if (ruler.grouped) {\n                const stackCount = skipNull ? this._getStackCount(index) : ruler.stackCount;\n                const range = options.barThickness === 'flex' ? computeFlexCategoryTraits(index, ruler, options, stackCount) : computeFitCategoryTraits(index, ruler, options, stackCount);\n                const stackIndex = this._getStackIndex(this.index, this._cachedMeta.stack, skipNull ? index : undefined);\n                center = range.start + range.chunk * stackIndex + range.chunk / 2;\n                size = Math.min(maxBarThickness, range.chunk * range.ratio);\n            } else {\n                center = scale.getPixelForValue(this.getParsed(index)[scale.axis], index);\n                size = Math.min(maxBarThickness, ruler.min * ruler.ratio);\n            }\n            return {\n                base: center - size / 2,\n                head: center + size / 2,\n                center,\n                size\n            };\n        }\n        draw() {\n            const meta = this._cachedMeta;\n            const vScale = meta.vScale;\n            const rects = meta.data;\n            const ilen = rects.length;\n            let i = 0;\n            for(; i < ilen; ++i){\n                if (this.getParsed(i)[vScale.axis] !== null) {\n                    rects[i].draw(this._ctx);\n                }\n            }\n        }\n    }\n\n    class BubbleController extends DatasetController {\n        static id = 'bubble';\n        static defaults = {\n            datasetElementType: false,\n            dataElementType: 'point',\n            animations: {\n                numbers: {\n                    type: 'number',\n                    properties: [\n                        'x',\n                        'y',\n                        'borderWidth',\n                        'radius'\n                    ]\n                }\n            }\n        };\n        static overrides = {\n            scales: {\n                x: {\n                    type: 'linear'\n                },\n                y: {\n                    type: 'linear'\n                }\n            }\n        };\n        initialize() {\n            this.enableOptionSharing = true;\n            super.initialize();\n        }\n        parsePrimitiveData(meta, data, start, count) {\n            const parsed = super.parsePrimitiveData(meta, data, start, count);\n            for(let i = 0; i < parsed.length; i++){\n                parsed[i]._custom = this.resolveDataElementOptions(i + start).radius;\n            }\n            return parsed;\n        }\n        parseArrayData(meta, data, start, count) {\n            const parsed = super.parseArrayData(meta, data, start, count);\n            for(let i = 0; i < parsed.length; i++){\n                const item = data[start + i];\n                parsed[i]._custom = valueOrDefault(item[2], this.resolveDataElementOptions(i + start).radius);\n            }\n            return parsed;\n        }\n        parseObjectData(meta, data, start, count) {\n            const parsed = super.parseObjectData(meta, data, start, count);\n            for(let i = 0; i < parsed.length; i++){\n                const item = data[start + i];\n                parsed[i]._custom = valueOrDefault(item && item.r && +item.r, this.resolveDataElementOptions(i + start).radius);\n            }\n            return parsed;\n        }\n        getMaxOverflow() {\n            const data = this._cachedMeta.data;\n            let max = 0;\n            for(let i = data.length - 1; i >= 0; --i){\n                max = Math.max(max, data[i].size(this.resolveDataElementOptions(i)) / 2);\n            }\n            return max > 0 && max;\n        }\n        getLabelAndValue(index) {\n            const meta = this._cachedMeta;\n            const labels = this.chart.data.labels || [];\n            const { xScale , yScale  } = meta;\n            const parsed = this.getParsed(index);\n            const x = xScale.getLabelForValue(parsed.x);\n            const y = yScale.getLabelForValue(parsed.y);\n            const r = parsed._custom;\n            return {\n                label: labels[index] || '',\n                value: '(' + x + ', ' + y + (r ? ', ' + r : '') + ')'\n            };\n        }\n        update(mode) {\n            const points = this._cachedMeta.data;\n            this.updateElements(points, 0, points.length, mode);\n        }\n        updateElements(points, start, count, mode) {\n            const reset = mode === 'reset';\n            const { iScale , vScale  } = this._cachedMeta;\n            const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);\n            const iAxis = iScale.axis;\n            const vAxis = vScale.axis;\n            for(let i = start; i < start + count; i++){\n                const point = points[i];\n                const parsed = !reset && this.getParsed(i);\n                const properties = {};\n                const iPixel = properties[iAxis] = reset ? iScale.getPixelForDecimal(0.5) : iScale.getPixelForValue(parsed[iAxis]);\n                const vPixel = properties[vAxis] = reset ? vScale.getBasePixel() : vScale.getPixelForValue(parsed[vAxis]);\n                properties.skip = isNaN(iPixel) || isNaN(vPixel);\n                if (includeOptions) {\n                    properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode);\n                    if (reset) {\n                        properties.options.radius = 0;\n                    }\n                }\n                this.updateElement(point, i, properties, mode);\n            }\n        }\n        resolveDataElementOptions(index, mode) {\n            const parsed = this.getParsed(index);\n            let values = super.resolveDataElementOptions(index, mode);\n            if (values.$shared) {\n                values = Object.assign({}, values, {\n                    $shared: false\n                });\n            }\n            const radius = values.radius;\n            if (mode !== 'active') {\n                values.radius = 0;\n            }\n            values.radius += valueOrDefault(parsed && parsed._custom, radius);\n            return values;\n        }\n    }\n\n    function getRatioAndOffset(rotation, circumference, cutout) {\n        let ratioX = 1;\n        let ratioY = 1;\n        let offsetX = 0;\n        let offsetY = 0;\n        if (circumference < TAU) {\n            const startAngle = rotation;\n            const endAngle = startAngle + circumference;\n            const startX = Math.cos(startAngle);\n            const startY = Math.sin(startAngle);\n            const endX = Math.cos(endAngle);\n            const endY = Math.sin(endAngle);\n            const calcMax = (angle, a, b)=>_angleBetween(angle, startAngle, endAngle, true) ? 1 : Math.max(a, a * cutout, b, b * cutout);\n            const calcMin = (angle, a, b)=>_angleBetween(angle, startAngle, endAngle, true) ? -1 : Math.min(a, a * cutout, b, b * cutout);\n            const maxX = calcMax(0, startX, endX);\n            const maxY = calcMax(HALF_PI, startY, endY);\n            const minX = calcMin(PI, startX, endX);\n            const minY = calcMin(PI + HALF_PI, startY, endY);\n            ratioX = (maxX - minX) / 2;\n            ratioY = (maxY - minY) / 2;\n            offsetX = -(maxX + minX) / 2;\n            offsetY = -(maxY + minY) / 2;\n        }\n        return {\n            ratioX,\n            ratioY,\n            offsetX,\n            offsetY\n        };\n    }\n    class DoughnutController extends DatasetController {\n        static id = 'doughnut';\n        static defaults = {\n            datasetElementType: false,\n            dataElementType: 'arc',\n            animation: {\n                animateRotate: true,\n                animateScale: false\n            },\n            animations: {\n                numbers: {\n                    type: 'number',\n                    properties: [\n                        'circumference',\n                        'endAngle',\n                        'innerRadius',\n                        'outerRadius',\n                        'startAngle',\n                        'x',\n                        'y',\n                        'offset',\n                        'borderWidth',\n                        'spacing'\n                    ]\n                }\n            },\n            cutout: '50%',\n            rotation: 0,\n            circumference: 360,\n            radius: '100%',\n            spacing: 0,\n            indexAxis: 'r'\n        };\n        static descriptors = {\n            _scriptable: (name)=>name !== 'spacing',\n            _indexable: (name)=>name !== 'spacing' && !name.startsWith('borderDash') && !name.startsWith('hoverBorderDash')\n        };\n        static overrides = {\n            aspectRatio: 1,\n            plugins: {\n                legend: {\n                    labels: {\n                        generateLabels (chart) {\n                            const data = chart.data;\n                            if (data.labels.length && data.datasets.length) {\n                                const { labels: { pointStyle , color  }  } = chart.legend.options;\n                                return data.labels.map((label, i)=>{\n                                    const meta = chart.getDatasetMeta(0);\n                                    const style = meta.controller.getStyle(i);\n                                    return {\n                                        text: label,\n                                        fillStyle: style.backgroundColor,\n                                        strokeStyle: style.borderColor,\n                                        fontColor: color,\n                                        lineWidth: style.borderWidth,\n                                        pointStyle: pointStyle,\n                                        hidden: !chart.getDataVisibility(i),\n                                        index: i\n                                    };\n                                });\n                            }\n                            return [];\n                        }\n                    },\n                    onClick (e, legendItem, legend) {\n                        legend.chart.toggleDataVisibility(legendItem.index);\n                        legend.chart.update();\n                    }\n                }\n            }\n        };\n        constructor(chart, datasetIndex){\n            super(chart, datasetIndex);\n            this.enableOptionSharing = true;\n            this.innerRadius = undefined;\n            this.outerRadius = undefined;\n            this.offsetX = undefined;\n            this.offsetY = undefined;\n        }\n        linkScales() {}\n        parse(start, count) {\n            const data = this.getDataset().data;\n            const meta = this._cachedMeta;\n            if (this._parsing === false) {\n                meta._parsed = data;\n            } else {\n                let getter = (i)=>+data[i];\n                if (isObject(data[start])) {\n                    const { key ='value'  } = this._parsing;\n                    getter = (i)=>+resolveObjectKey(data[i], key);\n                }\n                let i, ilen;\n                for(i = start, ilen = start + count; i < ilen; ++i){\n                    meta._parsed[i] = getter(i);\n                }\n            }\n        }\n        _getRotation() {\n            return toRadians(this.options.rotation - 90);\n        }\n        _getCircumference() {\n            return toRadians(this.options.circumference);\n        }\n        _getRotationExtents() {\n            let min = TAU;\n            let max = -TAU;\n            for(let i = 0; i < this.chart.data.datasets.length; ++i){\n                if (this.chart.isDatasetVisible(i) && this.chart.getDatasetMeta(i).type === this._type) {\n                    const controller = this.chart.getDatasetMeta(i).controller;\n                    const rotation = controller._getRotation();\n                    const circumference = controller._getCircumference();\n                    min = Math.min(min, rotation);\n                    max = Math.max(max, rotation + circumference);\n                }\n            }\n            return {\n                rotation: min,\n                circumference: max - min\n            };\n        }\n        update(mode) {\n            const chart = this.chart;\n            const { chartArea  } = chart;\n            const meta = this._cachedMeta;\n            const arcs = meta.data;\n            const spacing = this.getMaxBorderWidth() + this.getMaxOffset(arcs) + this.options.spacing;\n            const maxSize = Math.max((Math.min(chartArea.width, chartArea.height) - spacing) / 2, 0);\n            const cutout = Math.min(toPercentage(this.options.cutout, maxSize), 1);\n            const chartWeight = this._getRingWeight(this.index);\n            const { circumference , rotation  } = this._getRotationExtents();\n            const { ratioX , ratioY , offsetX , offsetY  } = getRatioAndOffset(rotation, circumference, cutout);\n            const maxWidth = (chartArea.width - spacing) / ratioX;\n            const maxHeight = (chartArea.height - spacing) / ratioY;\n            const maxRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0);\n            const outerRadius = toDimension(this.options.radius, maxRadius);\n            const innerRadius = Math.max(outerRadius * cutout, 0);\n            const radiusLength = (outerRadius - innerRadius) / this._getVisibleDatasetWeightTotal();\n            this.offsetX = offsetX * outerRadius;\n            this.offsetY = offsetY * outerRadius;\n            meta.total = this.calculateTotal();\n            this.outerRadius = outerRadius - radiusLength * this._getRingWeightOffset(this.index);\n            this.innerRadius = Math.max(this.outerRadius - radiusLength * chartWeight, 0);\n            this.updateElements(arcs, 0, arcs.length, mode);\n        }\n        _circumference(i, reset) {\n            const opts = this.options;\n            const meta = this._cachedMeta;\n            const circumference = this._getCircumference();\n            if (reset && opts.animation.animateRotate || !this.chart.getDataVisibility(i) || meta._parsed[i] === null || meta.data[i].hidden) {\n                return 0;\n            }\n            return this.calculateCircumference(meta._parsed[i] * circumference / TAU);\n        }\n        updateElements(arcs, start, count, mode) {\n            const reset = mode === 'reset';\n            const chart = this.chart;\n            const chartArea = chart.chartArea;\n            const opts = chart.options;\n            const animationOpts = opts.animation;\n            const centerX = (chartArea.left + chartArea.right) / 2;\n            const centerY = (chartArea.top + chartArea.bottom) / 2;\n            const animateScale = reset && animationOpts.animateScale;\n            const innerRadius = animateScale ? 0 : this.innerRadius;\n            const outerRadius = animateScale ? 0 : this.outerRadius;\n            const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);\n            let startAngle = this._getRotation();\n            let i;\n            for(i = 0; i < start; ++i){\n                startAngle += this._circumference(i, reset);\n            }\n            for(i = start; i < start + count; ++i){\n                const circumference = this._circumference(i, reset);\n                const arc = arcs[i];\n                const properties = {\n                    x: centerX + this.offsetX,\n                    y: centerY + this.offsetY,\n                    startAngle,\n                    endAngle: startAngle + circumference,\n                    circumference,\n                    outerRadius,\n                    innerRadius\n                };\n                if (includeOptions) {\n                    properties.options = sharedOptions || this.resolveDataElementOptions(i, arc.active ? 'active' : mode);\n                }\n                startAngle += circumference;\n                this.updateElement(arc, i, properties, mode);\n            }\n        }\n        calculateTotal() {\n            const meta = this._cachedMeta;\n            const metaData = meta.data;\n            let total = 0;\n            let i;\n            for(i = 0; i < metaData.length; i++){\n                const value = meta._parsed[i];\n                if (value !== null && !isNaN(value) && this.chart.getDataVisibility(i) && !metaData[i].hidden) {\n                    total += Math.abs(value);\n                }\n            }\n            return total;\n        }\n        calculateCircumference(value) {\n            const total = this._cachedMeta.total;\n            if (total > 0 && !isNaN(value)) {\n                return TAU * (Math.abs(value) / total);\n            }\n            return 0;\n        }\n        getLabelAndValue(index) {\n            const meta = this._cachedMeta;\n            const chart = this.chart;\n            const labels = chart.data.labels || [];\n            const value = formatNumber(meta._parsed[index], chart.options.locale);\n            return {\n                label: labels[index] || '',\n                value\n            };\n        }\n        getMaxBorderWidth(arcs) {\n            let max = 0;\n            const chart = this.chart;\n            let i, ilen, meta, controller, options;\n            if (!arcs) {\n                for(i = 0, ilen = chart.data.datasets.length; i < ilen; ++i){\n                    if (chart.isDatasetVisible(i)) {\n                        meta = chart.getDatasetMeta(i);\n                        arcs = meta.data;\n                        controller = meta.controller;\n                        break;\n                    }\n                }\n            }\n            if (!arcs) {\n                return 0;\n            }\n            for(i = 0, ilen = arcs.length; i < ilen; ++i){\n                options = controller.resolveDataElementOptions(i);\n                if (options.borderAlign !== 'inner') {\n                    max = Math.max(max, options.borderWidth || 0, options.hoverBorderWidth || 0);\n                }\n            }\n            return max;\n        }\n        getMaxOffset(arcs) {\n            let max = 0;\n            for(let i = 0, ilen = arcs.length; i < ilen; ++i){\n                const options = this.resolveDataElementOptions(i);\n                max = Math.max(max, options.offset || 0, options.hoverOffset || 0);\n            }\n            return max;\n        }\n        _getRingWeightOffset(datasetIndex) {\n            let ringWeightOffset = 0;\n            for(let i = 0; i < datasetIndex; ++i){\n                if (this.chart.isDatasetVisible(i)) {\n                    ringWeightOffset += this._getRingWeight(i);\n                }\n            }\n            return ringWeightOffset;\n        }\n        _getRingWeight(datasetIndex) {\n            return Math.max(valueOrDefault(this.chart.data.datasets[datasetIndex].weight, 1), 0);\n        }\n        _getVisibleDatasetWeightTotal() {\n            return this._getRingWeightOffset(this.chart.data.datasets.length) || 1;\n        }\n    }\n\n    class LineController extends DatasetController {\n        static id = 'line';\n        static defaults = {\n            datasetElementType: 'line',\n            dataElementType: 'point',\n            showLine: true,\n            spanGaps: false\n        };\n        static overrides = {\n            scales: {\n                _index_: {\n                    type: 'category'\n                },\n                _value_: {\n                    type: 'linear'\n                }\n            }\n        };\n        initialize() {\n            this.enableOptionSharing = true;\n            this.supportsDecimation = true;\n            super.initialize();\n        }\n        update(mode) {\n            const meta = this._cachedMeta;\n            const { dataset: line , data: points = [] , _dataset  } = meta;\n            const animationsDisabled = this.chart._animationsDisabled;\n            let { start , count  } = _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);\n            this._drawStart = start;\n            this._drawCount = count;\n            if (_scaleRangesChanged(meta)) {\n                start = 0;\n                count = points.length;\n            }\n            line._chart = this.chart;\n            line._datasetIndex = this.index;\n            line._decimated = !!_dataset._decimated;\n            line.points = points;\n            const options = this.resolveDatasetElementOptions(mode);\n            if (!this.options.showLine) {\n                options.borderWidth = 0;\n            }\n            options.segment = this.options.segment;\n            this.updateElement(line, undefined, {\n                animated: !animationsDisabled,\n                options\n            }, mode);\n            this.updateElements(points, start, count, mode);\n        }\n        updateElements(points, start, count, mode) {\n            const reset = mode === 'reset';\n            const { iScale , vScale , _stacked , _dataset  } = this._cachedMeta;\n            const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);\n            const iAxis = iScale.axis;\n            const vAxis = vScale.axis;\n            const { spanGaps , segment  } = this.options;\n            const maxGapLength = isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;\n            const directUpdate = this.chart._animationsDisabled || reset || mode === 'none';\n            const end = start + count;\n            const pointsCount = points.length;\n            let prevParsed = start > 0 && this.getParsed(start - 1);\n            for(let i = 0; i < pointsCount; ++i){\n                const point = points[i];\n                const properties = directUpdate ? point : {};\n                if (i < start || i >= end) {\n                    properties.skip = true;\n                    continue;\n                }\n                const parsed = this.getParsed(i);\n                const nullData = isNullOrUndef(parsed[vAxis]);\n                const iPixel = properties[iAxis] = iScale.getPixelForValue(parsed[iAxis], i);\n                const vPixel = properties[vAxis] = reset || nullData ? vScale.getBasePixel() : vScale.getPixelForValue(_stacked ? this.applyStack(vScale, parsed, _stacked) : parsed[vAxis], i);\n                properties.skip = isNaN(iPixel) || isNaN(vPixel) || nullData;\n                properties.stop = i > 0 && Math.abs(parsed[iAxis] - prevParsed[iAxis]) > maxGapLength;\n                if (segment) {\n                    properties.parsed = parsed;\n                    properties.raw = _dataset.data[i];\n                }\n                if (includeOptions) {\n                    properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode);\n                }\n                if (!directUpdate) {\n                    this.updateElement(point, i, properties, mode);\n                }\n                prevParsed = parsed;\n            }\n        }\n        getMaxOverflow() {\n            const meta = this._cachedMeta;\n            const dataset = meta.dataset;\n            const border = dataset.options && dataset.options.borderWidth || 0;\n            const data = meta.data || [];\n            if (!data.length) {\n                return border;\n            }\n            const firstPoint = data[0].size(this.resolveDataElementOptions(0));\n            const lastPoint = data[data.length - 1].size(this.resolveDataElementOptions(data.length - 1));\n            return Math.max(border, firstPoint, lastPoint) / 2;\n        }\n        draw() {\n            const meta = this._cachedMeta;\n            meta.dataset.updateControlPoints(this.chart.chartArea, meta.iScale.axis);\n            super.draw();\n        }\n    }\n\n    class PolarAreaController extends DatasetController {\n        static id = 'polarArea';\n        static defaults = {\n            dataElementType: 'arc',\n            animation: {\n                animateRotate: true,\n                animateScale: true\n            },\n            animations: {\n                numbers: {\n                    type: 'number',\n                    properties: [\n                        'x',\n                        'y',\n                        'startAngle',\n                        'endAngle',\n                        'innerRadius',\n                        'outerRadius'\n                    ]\n                }\n            },\n            indexAxis: 'r',\n            startAngle: 0\n        };\n        static overrides = {\n            aspectRatio: 1,\n            plugins: {\n                legend: {\n                    labels: {\n                        generateLabels (chart) {\n                            const data = chart.data;\n                            if (data.labels.length && data.datasets.length) {\n                                const { labels: { pointStyle , color  }  } = chart.legend.options;\n                                return data.labels.map((label, i)=>{\n                                    const meta = chart.getDatasetMeta(0);\n                                    const style = meta.controller.getStyle(i);\n                                    return {\n                                        text: label,\n                                        fillStyle: style.backgroundColor,\n                                        strokeStyle: style.borderColor,\n                                        fontColor: color,\n                                        lineWidth: style.borderWidth,\n                                        pointStyle: pointStyle,\n                                        hidden: !chart.getDataVisibility(i),\n                                        index: i\n                                    };\n                                });\n                            }\n                            return [];\n                        }\n                    },\n                    onClick (e, legendItem, legend) {\n                        legend.chart.toggleDataVisibility(legendItem.index);\n                        legend.chart.update();\n                    }\n                }\n            },\n            scales: {\n                r: {\n                    type: 'radialLinear',\n                    angleLines: {\n                        display: false\n                    },\n                    beginAtZero: true,\n                    grid: {\n                        circular: true\n                    },\n                    pointLabels: {\n                        display: false\n                    },\n                    startAngle: 0\n                }\n            }\n        };\n        constructor(chart, datasetIndex){\n            super(chart, datasetIndex);\n            this.innerRadius = undefined;\n            this.outerRadius = undefined;\n        }\n        getLabelAndValue(index) {\n            const meta = this._cachedMeta;\n            const chart = this.chart;\n            const labels = chart.data.labels || [];\n            const value = formatNumber(meta._parsed[index].r, chart.options.locale);\n            return {\n                label: labels[index] || '',\n                value\n            };\n        }\n        parseObjectData(meta, data, start, count) {\n            return _parseObjectDataRadialScale.bind(this)(meta, data, start, count);\n        }\n        update(mode) {\n            const arcs = this._cachedMeta.data;\n            this._updateRadius();\n            this.updateElements(arcs, 0, arcs.length, mode);\n        }\n        getMinMax() {\n            const meta = this._cachedMeta;\n            const range = {\n                min: Number.POSITIVE_INFINITY,\n                max: Number.NEGATIVE_INFINITY\n            };\n            meta.data.forEach((element, index)=>{\n                const parsed = this.getParsed(index).r;\n                if (!isNaN(parsed) && this.chart.getDataVisibility(index)) {\n                    if (parsed < range.min) {\n                        range.min = parsed;\n                    }\n                    if (parsed > range.max) {\n                        range.max = parsed;\n                    }\n                }\n            });\n            return range;\n        }\n        _updateRadius() {\n            const chart = this.chart;\n            const chartArea = chart.chartArea;\n            const opts = chart.options;\n            const minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);\n            const outerRadius = Math.max(minSize / 2, 0);\n            const innerRadius = Math.max(opts.cutoutPercentage ? outerRadius / 100 * opts.cutoutPercentage : 1, 0);\n            const radiusLength = (outerRadius - innerRadius) / chart.getVisibleDatasetCount();\n            this.outerRadius = outerRadius - radiusLength * this.index;\n            this.innerRadius = this.outerRadius - radiusLength;\n        }\n        updateElements(arcs, start, count, mode) {\n            const reset = mode === 'reset';\n            const chart = this.chart;\n            const opts = chart.options;\n            const animationOpts = opts.animation;\n            const scale = this._cachedMeta.rScale;\n            const centerX = scale.xCenter;\n            const centerY = scale.yCenter;\n            const datasetStartAngle = scale.getIndexAngle(0) - 0.5 * PI;\n            let angle = datasetStartAngle;\n            let i;\n            const defaultAngle = 360 / this.countVisibleElements();\n            for(i = 0; i < start; ++i){\n                angle += this._computeAngle(i, mode, defaultAngle);\n            }\n            for(i = start; i < start + count; i++){\n                const arc = arcs[i];\n                let startAngle = angle;\n                let endAngle = angle + this._computeAngle(i, mode, defaultAngle);\n                let outerRadius = chart.getDataVisibility(i) ? scale.getDistanceFromCenterForValue(this.getParsed(i).r) : 0;\n                angle = endAngle;\n                if (reset) {\n                    if (animationOpts.animateScale) {\n                        outerRadius = 0;\n                    }\n                    if (animationOpts.animateRotate) {\n                        startAngle = endAngle = datasetStartAngle;\n                    }\n                }\n                const properties = {\n                    x: centerX,\n                    y: centerY,\n                    innerRadius: 0,\n                    outerRadius,\n                    startAngle,\n                    endAngle,\n                    options: this.resolveDataElementOptions(i, arc.active ? 'active' : mode)\n                };\n                this.updateElement(arc, i, properties, mode);\n            }\n        }\n        countVisibleElements() {\n            const meta = this._cachedMeta;\n            let count = 0;\n            meta.data.forEach((element, index)=>{\n                if (!isNaN(this.getParsed(index).r) && this.chart.getDataVisibility(index)) {\n                    count++;\n                }\n            });\n            return count;\n        }\n        _computeAngle(index, mode, defaultAngle) {\n            return this.chart.getDataVisibility(index) ? toRadians(this.resolveDataElementOptions(index, mode).angle || defaultAngle) : 0;\n        }\n    }\n\n    class PieController extends DoughnutController {\n        static id = 'pie';\n        static defaults = {\n            cutout: 0,\n            rotation: 0,\n            circumference: 360,\n            radius: '100%'\n        };\n    }\n\n    class RadarController extends DatasetController {\n        static id = 'radar';\n        static defaults = {\n            datasetElementType: 'line',\n            dataElementType: 'point',\n            indexAxis: 'r',\n            showLine: true,\n            elements: {\n                line: {\n                    fill: 'start'\n                }\n            }\n        };\n        static overrides = {\n            aspectRatio: 1,\n            scales: {\n                r: {\n                    type: 'radialLinear'\n                }\n            }\n        };\n        getLabelAndValue(index) {\n            const vScale = this._cachedMeta.vScale;\n            const parsed = this.getParsed(index);\n            return {\n                label: vScale.getLabels()[index],\n                value: '' + vScale.getLabelForValue(parsed[vScale.axis])\n            };\n        }\n        parseObjectData(meta, data, start, count) {\n            return _parseObjectDataRadialScale.bind(this)(meta, data, start, count);\n        }\n        update(mode) {\n            const meta = this._cachedMeta;\n            const line = meta.dataset;\n            const points = meta.data || [];\n            const labels = meta.iScale.getLabels();\n            line.points = points;\n            if (mode !== 'resize') {\n                const options = this.resolveDatasetElementOptions(mode);\n                if (!this.options.showLine) {\n                    options.borderWidth = 0;\n                }\n                const properties = {\n                    _loop: true,\n                    _fullLoop: labels.length === points.length,\n                    options\n                };\n                this.updateElement(line, undefined, properties, mode);\n            }\n            this.updateElements(points, 0, points.length, mode);\n        }\n        updateElements(points, start, count, mode) {\n            const scale = this._cachedMeta.rScale;\n            const reset = mode === 'reset';\n            for(let i = start; i < start + count; i++){\n                const point = points[i];\n                const options = this.resolveDataElementOptions(i, point.active ? 'active' : mode);\n                const pointPosition = scale.getPointPositionForValue(i, this.getParsed(i).r);\n                const x = reset ? scale.xCenter : pointPosition.x;\n                const y = reset ? scale.yCenter : pointPosition.y;\n                const properties = {\n                    x,\n                    y,\n                    angle: pointPosition.angle,\n                    skip: isNaN(x) || isNaN(y),\n                    options\n                };\n                this.updateElement(point, i, properties, mode);\n            }\n        }\n    }\n\n    class ScatterController extends DatasetController {\n        static id = 'scatter';\n        static defaults = {\n            datasetElementType: false,\n            dataElementType: 'point',\n            showLine: false,\n            fill: false\n        };\n        static overrides = {\n            interaction: {\n                mode: 'point'\n            },\n            scales: {\n                x: {\n                    type: 'linear'\n                },\n                y: {\n                    type: 'linear'\n                }\n            }\n        };\n        getLabelAndValue(index) {\n            const meta = this._cachedMeta;\n            const labels = this.chart.data.labels || [];\n            const { xScale , yScale  } = meta;\n            const parsed = this.getParsed(index);\n            const x = xScale.getLabelForValue(parsed.x);\n            const y = yScale.getLabelForValue(parsed.y);\n            return {\n                label: labels[index] || '',\n                value: '(' + x + ', ' + y + ')'\n            };\n        }\n        update(mode) {\n            const meta = this._cachedMeta;\n            const { data: points = []  } = meta;\n            const animationsDisabled = this.chart._animationsDisabled;\n            let { start , count  } = _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);\n            this._drawStart = start;\n            this._drawCount = count;\n            if (_scaleRangesChanged(meta)) {\n                start = 0;\n                count = points.length;\n            }\n            if (this.options.showLine) {\n                if (!this.datasetElementType) {\n                    this.addElements();\n                }\n                const { dataset: line , _dataset  } = meta;\n                line._chart = this.chart;\n                line._datasetIndex = this.index;\n                line._decimated = !!_dataset._decimated;\n                line.points = points;\n                const options = this.resolveDatasetElementOptions(mode);\n                options.segment = this.options.segment;\n                this.updateElement(line, undefined, {\n                    animated: !animationsDisabled,\n                    options\n                }, mode);\n            } else if (this.datasetElementType) {\n                delete meta.dataset;\n                this.datasetElementType = false;\n            }\n            this.updateElements(points, start, count, mode);\n        }\n        addElements() {\n            const { showLine  } = this.options;\n            if (!this.datasetElementType && showLine) {\n                this.datasetElementType = this.chart.registry.getElement('line');\n            }\n            super.addElements();\n        }\n        updateElements(points, start, count, mode) {\n            const reset = mode === 'reset';\n            const { iScale , vScale , _stacked , _dataset  } = this._cachedMeta;\n            const firstOpts = this.resolveDataElementOptions(start, mode);\n            const sharedOptions = this.getSharedOptions(firstOpts);\n            const includeOptions = this.includeOptions(mode, sharedOptions);\n            const iAxis = iScale.axis;\n            const vAxis = vScale.axis;\n            const { spanGaps , segment  } = this.options;\n            const maxGapLength = isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;\n            const directUpdate = this.chart._animationsDisabled || reset || mode === 'none';\n            let prevParsed = start > 0 && this.getParsed(start - 1);\n            for(let i = start; i < start + count; ++i){\n                const point = points[i];\n                const parsed = this.getParsed(i);\n                const properties = directUpdate ? point : {};\n                const nullData = isNullOrUndef(parsed[vAxis]);\n                const iPixel = properties[iAxis] = iScale.getPixelForValue(parsed[iAxis], i);\n                const vPixel = properties[vAxis] = reset || nullData ? vScale.getBasePixel() : vScale.getPixelForValue(_stacked ? this.applyStack(vScale, parsed, _stacked) : parsed[vAxis], i);\n                properties.skip = isNaN(iPixel) || isNaN(vPixel) || nullData;\n                properties.stop = i > 0 && Math.abs(parsed[iAxis] - prevParsed[iAxis]) > maxGapLength;\n                if (segment) {\n                    properties.parsed = parsed;\n                    properties.raw = _dataset.data[i];\n                }\n                if (includeOptions) {\n                    properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode);\n                }\n                if (!directUpdate) {\n                    this.updateElement(point, i, properties, mode);\n                }\n                prevParsed = parsed;\n            }\n            this.updateSharedOptions(sharedOptions, mode, firstOpts);\n        }\n        getMaxOverflow() {\n            const meta = this._cachedMeta;\n            const data = meta.data || [];\n            if (!this.options.showLine) {\n                let max = 0;\n                for(let i = data.length - 1; i >= 0; --i){\n                    max = Math.max(max, data[i].size(this.resolveDataElementOptions(i)) / 2);\n                }\n                return max > 0 && max;\n            }\n            const dataset = meta.dataset;\n            const border = dataset.options && dataset.options.borderWidth || 0;\n            if (!data.length) {\n                return border;\n            }\n            const firstPoint = data[0].size(this.resolveDataElementOptions(0));\n            const lastPoint = data[data.length - 1].size(this.resolveDataElementOptions(data.length - 1));\n            return Math.max(border, firstPoint, lastPoint) / 2;\n        }\n    }\n\n    var controllers = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        BarController: BarController,\n        BubbleController: BubbleController,\n        DoughnutController: DoughnutController,\n        LineController: LineController,\n        PieController: PieController,\n        PolarAreaController: PolarAreaController,\n        RadarController: RadarController,\n        ScatterController: ScatterController\n    });\n\n    function clipArc(ctx, element, endAngle) {\n        const { startAngle , pixelMargin , x , y , outerRadius , innerRadius  } = element;\n        let angleMargin = pixelMargin / outerRadius;\n        // Draw an inner border by clipping the arc and drawing a double-width border\n        // Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders\n        ctx.beginPath();\n        ctx.arc(x, y, outerRadius, startAngle - angleMargin, endAngle + angleMargin);\n        if (innerRadius > pixelMargin) {\n            angleMargin = pixelMargin / innerRadius;\n            ctx.arc(x, y, innerRadius, endAngle + angleMargin, startAngle - angleMargin, true);\n        } else {\n            ctx.arc(x, y, pixelMargin, endAngle + HALF_PI, startAngle - HALF_PI);\n        }\n        ctx.closePath();\n        ctx.clip();\n    }\n    function toRadiusCorners(value) {\n        return _readValueToProps(value, [\n            'outerStart',\n            'outerEnd',\n            'innerStart',\n            'innerEnd'\n        ]);\n    }\n    /**\n     * Parse border radius from the provided options\n     */ function parseBorderRadius$1(arc, innerRadius, outerRadius, angleDelta) {\n        const o = toRadiusCorners(arc.options.borderRadius);\n        const halfThickness = (outerRadius - innerRadius) / 2;\n        const innerLimit = Math.min(halfThickness, angleDelta * innerRadius / 2);\n        // Outer limits are complicated. We want to compute the available angular distance at\n        // a radius of outerRadius - borderRadius because for small angular distances, this term limits.\n        // We compute at r = outerRadius - borderRadius because this circle defines the center of the border corners.\n        //\n        // If the borderRadius is large, that value can become negative.\n        // This causes the outer borders to lose their radius entirely, which is rather unexpected. To solve that, if borderRadius > outerRadius\n        // we know that the thickness term will dominate and compute the limits at that point\n        const computeOuterLimit = (val)=>{\n            const outerArcLimit = (outerRadius - Math.min(halfThickness, val)) * angleDelta / 2;\n            return _limitValue(val, 0, Math.min(halfThickness, outerArcLimit));\n        };\n        return {\n            outerStart: computeOuterLimit(o.outerStart),\n            outerEnd: computeOuterLimit(o.outerEnd),\n            innerStart: _limitValue(o.innerStart, 0, innerLimit),\n            innerEnd: _limitValue(o.innerEnd, 0, innerLimit)\n        };\n    }\n    /**\n     * Convert (r, \ud835\udf03) to (x, y)\n     */ function rThetaToXY(r, theta, x, y) {\n        return {\n            x: x + r * Math.cos(theta),\n            y: y + r * Math.sin(theta)\n        };\n    }\n    /**\n     * Path the arc, respecting border radius by separating into left and right halves.\n     *\n     *   Start      End\n     *\n     *    1--->a--->2    Outer\n     *   /           \\\n     *   8           3\n     *   |           |\n     *   |           |\n     *   7           4\n     *   \\           /\n     *    6<---b<---5    Inner\n     */ function pathArc(ctx, element, offset, spacing, end, circular) {\n        const { x , y , startAngle: start , pixelMargin , innerRadius: innerR  } = element;\n        const outerRadius = Math.max(element.outerRadius + spacing + offset - pixelMargin, 0);\n        const innerRadius = innerR > 0 ? innerR + spacing + offset + pixelMargin : 0;\n        let spacingOffset = 0;\n        const alpha = end - start;\n        if (spacing) {\n            // When spacing is present, it is the same for all items\n            // So we adjust the start and end angle of the arc such that\n            // the distance is the same as it would be without the spacing\n            const noSpacingInnerRadius = innerR > 0 ? innerR - spacing : 0;\n            const noSpacingOuterRadius = outerRadius > 0 ? outerRadius - spacing : 0;\n            const avNogSpacingRadius = (noSpacingInnerRadius + noSpacingOuterRadius) / 2;\n            const adjustedAngle = avNogSpacingRadius !== 0 ? alpha * avNogSpacingRadius / (avNogSpacingRadius + spacing) : alpha;\n            spacingOffset = (alpha - adjustedAngle) / 2;\n        }\n        const beta = Math.max(0.001, alpha * outerRadius - offset / PI) / outerRadius;\n        const angleOffset = (alpha - beta) / 2;\n        const startAngle = start + angleOffset + spacingOffset;\n        const endAngle = end - angleOffset - spacingOffset;\n        const { outerStart , outerEnd , innerStart , innerEnd  } = parseBorderRadius$1(element, innerRadius, outerRadius, endAngle - startAngle);\n        const outerStartAdjustedRadius = outerRadius - outerStart;\n        const outerEndAdjustedRadius = outerRadius - outerEnd;\n        const outerStartAdjustedAngle = startAngle + outerStart / outerStartAdjustedRadius;\n        const outerEndAdjustedAngle = endAngle - outerEnd / outerEndAdjustedRadius;\n        const innerStartAdjustedRadius = innerRadius + innerStart;\n        const innerEndAdjustedRadius = innerRadius + innerEnd;\n        const innerStartAdjustedAngle = startAngle + innerStart / innerStartAdjustedRadius;\n        const innerEndAdjustedAngle = endAngle - innerEnd / innerEndAdjustedRadius;\n        ctx.beginPath();\n        if (circular) {\n            // The first arc segments from point 1 to point a to point 2\n            const outerMidAdjustedAngle = (outerStartAdjustedAngle + outerEndAdjustedAngle) / 2;\n            ctx.arc(x, y, outerRadius, outerStartAdjustedAngle, outerMidAdjustedAngle);\n            ctx.arc(x, y, outerRadius, outerMidAdjustedAngle, outerEndAdjustedAngle);\n            // The corner segment from point 2 to point 3\n            if (outerEnd > 0) {\n                const pCenter = rThetaToXY(outerEndAdjustedRadius, outerEndAdjustedAngle, x, y);\n                ctx.arc(pCenter.x, pCenter.y, outerEnd, outerEndAdjustedAngle, endAngle + HALF_PI);\n            }\n            // The line from point 3 to point 4\n            const p4 = rThetaToXY(innerEndAdjustedRadius, endAngle, x, y);\n            ctx.lineTo(p4.x, p4.y);\n            // The corner segment from point 4 to point 5\n            if (innerEnd > 0) {\n                const pCenter = rThetaToXY(innerEndAdjustedRadius, innerEndAdjustedAngle, x, y);\n                ctx.arc(pCenter.x, pCenter.y, innerEnd, endAngle + HALF_PI, innerEndAdjustedAngle + Math.PI);\n            }\n            // The inner arc from point 5 to point b to point 6\n            const innerMidAdjustedAngle = (endAngle - innerEnd / innerRadius + (startAngle + innerStart / innerRadius)) / 2;\n            ctx.arc(x, y, innerRadius, endAngle - innerEnd / innerRadius, innerMidAdjustedAngle, true);\n            ctx.arc(x, y, innerRadius, innerMidAdjustedAngle, startAngle + innerStart / innerRadius, true);\n            // The corner segment from point 6 to point 7\n            if (innerStart > 0) {\n                const pCenter = rThetaToXY(innerStartAdjustedRadius, innerStartAdjustedAngle, x, y);\n                ctx.arc(pCenter.x, pCenter.y, innerStart, innerStartAdjustedAngle + Math.PI, startAngle - HALF_PI);\n            }\n            // The line from point 7 to point 8\n            const p8 = rThetaToXY(outerStartAdjustedRadius, startAngle, x, y);\n            ctx.lineTo(p8.x, p8.y);\n            // The corner segment from point 8 to point 1\n            if (outerStart > 0) {\n                const pCenter = rThetaToXY(outerStartAdjustedRadius, outerStartAdjustedAngle, x, y);\n                ctx.arc(pCenter.x, pCenter.y, outerStart, startAngle - HALF_PI, outerStartAdjustedAngle);\n            }\n        } else {\n            ctx.moveTo(x, y);\n            const outerStartX = Math.cos(outerStartAdjustedAngle) * outerRadius + x;\n            const outerStartY = Math.sin(outerStartAdjustedAngle) * outerRadius + y;\n            ctx.lineTo(outerStartX, outerStartY);\n            const outerEndX = Math.cos(outerEndAdjustedAngle) * outerRadius + x;\n            const outerEndY = Math.sin(outerEndAdjustedAngle) * outerRadius + y;\n            ctx.lineTo(outerEndX, outerEndY);\n        }\n        ctx.closePath();\n    }\n    function drawArc(ctx, element, offset, spacing, circular) {\n        const { fullCircles , startAngle , circumference  } = element;\n        let endAngle = element.endAngle;\n        if (fullCircles) {\n            pathArc(ctx, element, offset, spacing, endAngle, circular);\n            for(let i = 0; i < fullCircles; ++i){\n                ctx.fill();\n            }\n            if (!isNaN(circumference)) {\n                endAngle = startAngle + (circumference % TAU || TAU);\n            }\n        }\n        pathArc(ctx, element, offset, spacing, endAngle, circular);\n        ctx.fill();\n        return endAngle;\n    }\n    function drawBorder(ctx, element, offset, spacing, circular) {\n        const { fullCircles , startAngle , circumference , options  } = element;\n        const { borderWidth , borderJoinStyle , borderDash , borderDashOffset  } = options;\n        const inner = options.borderAlign === 'inner';\n        if (!borderWidth) {\n            return;\n        }\n        ctx.setLineDash(borderDash || []);\n        ctx.lineDashOffset = borderDashOffset;\n        if (inner) {\n            ctx.lineWidth = borderWidth * 2;\n            ctx.lineJoin = borderJoinStyle || 'round';\n        } else {\n            ctx.lineWidth = borderWidth;\n            ctx.lineJoin = borderJoinStyle || 'bevel';\n        }\n        let endAngle = element.endAngle;\n        if (fullCircles) {\n            pathArc(ctx, element, offset, spacing, endAngle, circular);\n            for(let i = 0; i < fullCircles; ++i){\n                ctx.stroke();\n            }\n            if (!isNaN(circumference)) {\n                endAngle = startAngle + (circumference % TAU || TAU);\n            }\n        }\n        if (inner) {\n            clipArc(ctx, element, endAngle);\n        }\n        if (!fullCircles) {\n            pathArc(ctx, element, offset, spacing, endAngle, circular);\n            ctx.stroke();\n        }\n    }\n    class ArcElement extends Element {\n        static id = 'arc';\n        static defaults = {\n            borderAlign: 'center',\n            borderColor: '#fff',\n            borderDash: [],\n            borderDashOffset: 0,\n            borderJoinStyle: undefined,\n            borderRadius: 0,\n            borderWidth: 2,\n            offset: 0,\n            spacing: 0,\n            angle: undefined,\n            circular: true\n        };\n        static defaultRoutes = {\n            backgroundColor: 'backgroundColor'\n        };\n        static descriptors = {\n            _scriptable: true,\n            _indexable: (name)=>name !== 'borderDash'\n        };\n        circumference;\n        endAngle;\n        fullCircles;\n        innerRadius;\n        outerRadius;\n        pixelMargin;\n        startAngle;\n        constructor(cfg){\n            super();\n            this.options = undefined;\n            this.circumference = undefined;\n            this.startAngle = undefined;\n            this.endAngle = undefined;\n            this.innerRadius = undefined;\n            this.outerRadius = undefined;\n            this.pixelMargin = 0;\n            this.fullCircles = 0;\n            if (cfg) {\n                Object.assign(this, cfg);\n            }\n        }\n        inRange(chartX, chartY, useFinalPosition) {\n            const point = this.getProps([\n                'x',\n                'y'\n            ], useFinalPosition);\n            const { angle , distance  } = getAngleFromPoint(point, {\n                x: chartX,\n                y: chartY\n            });\n            const { startAngle , endAngle , innerRadius , outerRadius , circumference  } = this.getProps([\n                'startAngle',\n                'endAngle',\n                'innerRadius',\n                'outerRadius',\n                'circumference'\n            ], useFinalPosition);\n            const rAdjust = (this.options.spacing + this.options.borderWidth) / 2;\n            const _circumference = valueOrDefault(circumference, endAngle - startAngle);\n            const betweenAngles = _circumference >= TAU || _angleBetween(angle, startAngle, endAngle);\n            const withinRadius = _isBetween(distance, innerRadius + rAdjust, outerRadius + rAdjust);\n            return betweenAngles && withinRadius;\n        }\n        getCenterPoint(useFinalPosition) {\n            const { x , y , startAngle , endAngle , innerRadius , outerRadius  } = this.getProps([\n                'x',\n                'y',\n                'startAngle',\n                'endAngle',\n                'innerRadius',\n                'outerRadius'\n            ], useFinalPosition);\n            const { offset , spacing  } = this.options;\n            const halfAngle = (startAngle + endAngle) / 2;\n            const halfRadius = (innerRadius + outerRadius + spacing + offset) / 2;\n            return {\n                x: x + Math.cos(halfAngle) * halfRadius,\n                y: y + Math.sin(halfAngle) * halfRadius\n            };\n        }\n        tooltipPosition(useFinalPosition) {\n            return this.getCenterPoint(useFinalPosition);\n        }\n        draw(ctx) {\n            const { options , circumference  } = this;\n            const offset = (options.offset || 0) / 4;\n            const spacing = (options.spacing || 0) / 2;\n            const circular = options.circular;\n            this.pixelMargin = options.borderAlign === 'inner' ? 0.33 : 0;\n            this.fullCircles = circumference > TAU ? Math.floor(circumference / TAU) : 0;\n            if (circumference === 0 || this.innerRadius < 0 || this.outerRadius < 0) {\n                return;\n            }\n            ctx.save();\n            const halfAngle = (this.startAngle + this.endAngle) / 2;\n            ctx.translate(Math.cos(halfAngle) * offset, Math.sin(halfAngle) * offset);\n            const fix = 1 - Math.sin(Math.min(PI, circumference || 0));\n            const radiusOffset = offset * fix;\n            ctx.fillStyle = options.backgroundColor;\n            ctx.strokeStyle = options.borderColor;\n            drawArc(ctx, this, radiusOffset, spacing, circular);\n            drawBorder(ctx, this, radiusOffset, spacing, circular);\n            ctx.restore();\n        }\n    }\n\n    function setStyle(ctx, options, style = options) {\n        ctx.lineCap = valueOrDefault(style.borderCapStyle, options.borderCapStyle);\n        ctx.setLineDash(valueOrDefault(style.borderDash, options.borderDash));\n        ctx.lineDashOffset = valueOrDefault(style.borderDashOffset, options.borderDashOffset);\n        ctx.lineJoin = valueOrDefault(style.borderJoinStyle, options.borderJoinStyle);\n        ctx.lineWidth = valueOrDefault(style.borderWidth, options.borderWidth);\n        ctx.strokeStyle = valueOrDefault(style.borderColor, options.borderColor);\n    }\n    function lineTo(ctx, previous, target) {\n        ctx.lineTo(target.x, target.y);\n    }\n    function getLineMethod(options) {\n        if (options.stepped) {\n            return _steppedLineTo;\n        }\n        if (options.tension || options.cubicInterpolationMode === 'monotone') {\n            return _bezierCurveTo;\n        }\n        return lineTo;\n    }\n    function pathVars(points, segment, params = {}) {\n        const count = points.length;\n        const { start: paramsStart = 0 , end: paramsEnd = count - 1  } = params;\n        const { start: segmentStart , end: segmentEnd  } = segment;\n        const start = Math.max(paramsStart, segmentStart);\n        const end = Math.min(paramsEnd, segmentEnd);\n        const outside = paramsStart < segmentStart && paramsEnd < segmentStart || paramsStart > segmentEnd && paramsEnd > segmentEnd;\n        return {\n            count,\n            start,\n            loop: segment.loop,\n            ilen: end < start && !outside ? count + end - start : end - start\n        };\n    }\n    function pathSegment(ctx, line, segment, params) {\n        const { points , options  } = line;\n        const { count , start , loop , ilen  } = pathVars(points, segment, params);\n        const lineMethod = getLineMethod(options);\n        let { move =true , reverse  } = params || {};\n        let i, point, prev;\n        for(i = 0; i <= ilen; ++i){\n            point = points[(start + (reverse ? ilen - i : i)) % count];\n            if (point.skip) {\n                continue;\n            } else if (move) {\n                ctx.moveTo(point.x, point.y);\n                move = false;\n            } else {\n                lineMethod(ctx, prev, point, reverse, options.stepped);\n            }\n            prev = point;\n        }\n        if (loop) {\n            point = points[(start + (reverse ? ilen : 0)) % count];\n            lineMethod(ctx, prev, point, reverse, options.stepped);\n        }\n        return !!loop;\n    }\n    function fastPathSegment(ctx, line, segment, params) {\n        const points = line.points;\n        const { count , start , ilen  } = pathVars(points, segment, params);\n        const { move =true , reverse  } = params || {};\n        let avgX = 0;\n        let countX = 0;\n        let i, point, prevX, minY, maxY, lastY;\n        const pointIndex = (index)=>(start + (reverse ? ilen - index : index)) % count;\n        const drawX = ()=>{\n            if (minY !== maxY) {\n                ctx.lineTo(avgX, maxY);\n                ctx.lineTo(avgX, minY);\n                ctx.lineTo(avgX, lastY);\n            }\n        };\n        if (move) {\n            point = points[pointIndex(0)];\n            ctx.moveTo(point.x, point.y);\n        }\n        for(i = 0; i <= ilen; ++i){\n            point = points[pointIndex(i)];\n            if (point.skip) {\n                continue;\n            }\n            const x = point.x;\n            const y = point.y;\n            const truncX = x | 0;\n            if (truncX === prevX) {\n                if (y < minY) {\n                    minY = y;\n                } else if (y > maxY) {\n                    maxY = y;\n                }\n                avgX = (countX * avgX + x) / ++countX;\n            } else {\n                drawX();\n                ctx.lineTo(x, y);\n                prevX = truncX;\n                countX = 0;\n                minY = maxY = y;\n            }\n            lastY = y;\n        }\n        drawX();\n    }\n    function _getSegmentMethod(line) {\n        const opts = line.options;\n        const borderDash = opts.borderDash && opts.borderDash.length;\n        const useFastPath = !line._decimated && !line._loop && !opts.tension && opts.cubicInterpolationMode !== 'monotone' && !opts.stepped && !borderDash;\n        return useFastPath ? fastPathSegment : pathSegment;\n    }\n    function _getInterpolationMethod(options) {\n        if (options.stepped) {\n            return _steppedInterpolation;\n        }\n        if (options.tension || options.cubicInterpolationMode === 'monotone') {\n            return _bezierInterpolation;\n        }\n        return _pointInLine;\n    }\n    function strokePathWithCache(ctx, line, start, count) {\n        let path = line._path;\n        if (!path) {\n            path = line._path = new Path2D();\n            if (line.path(path, start, count)) {\n                path.closePath();\n            }\n        }\n        setStyle(ctx, line.options);\n        ctx.stroke(path);\n    }\n    function strokePathDirect(ctx, line, start, count) {\n        const { segments , options  } = line;\n        const segmentMethod = _getSegmentMethod(line);\n        for (const segment of segments){\n            setStyle(ctx, options, segment.style);\n            ctx.beginPath();\n            if (segmentMethod(ctx, line, segment, {\n                start,\n                end: start + count - 1\n            })) {\n                ctx.closePath();\n            }\n            ctx.stroke();\n        }\n    }\n    const usePath2D = typeof Path2D === 'function';\n    function draw(ctx, line, start, count) {\n        if (usePath2D && !line.options.segment) {\n            strokePathWithCache(ctx, line, start, count);\n        } else {\n            strokePathDirect(ctx, line, start, count);\n        }\n    }\n    class LineElement extends Element {\n        static id = 'line';\n        static defaults = {\n            borderCapStyle: 'butt',\n            borderDash: [],\n            borderDashOffset: 0,\n            borderJoinStyle: 'miter',\n            borderWidth: 3,\n            capBezierPoints: true,\n            cubicInterpolationMode: 'default',\n            fill: false,\n            spanGaps: false,\n            stepped: false,\n            tension: 0\n        };\n        static defaultRoutes = {\n            backgroundColor: 'backgroundColor',\n            borderColor: 'borderColor'\n        };\n        static descriptors = {\n            _scriptable: true,\n            _indexable: (name)=>name !== 'borderDash' && name !== 'fill'\n        };\n        constructor(cfg){\n            super();\n            this.animated = true;\n            this.options = undefined;\n            this._chart = undefined;\n            this._loop = undefined;\n            this._fullLoop = undefined;\n            this._path = undefined;\n            this._points = undefined;\n            this._segments = undefined;\n            this._decimated = false;\n            this._pointsUpdated = false;\n            this._datasetIndex = undefined;\n            if (cfg) {\n                Object.assign(this, cfg);\n            }\n        }\n        updateControlPoints(chartArea, indexAxis) {\n            const options = this.options;\n            if ((options.tension || options.cubicInterpolationMode === 'monotone') && !options.stepped && !this._pointsUpdated) {\n                const loop = options.spanGaps ? this._loop : this._fullLoop;\n                _updateBezierControlPoints(this._points, options, chartArea, loop, indexAxis);\n                this._pointsUpdated = true;\n            }\n        }\n        set points(points) {\n            this._points = points;\n            delete this._segments;\n            delete this._path;\n            this._pointsUpdated = false;\n        }\n        get points() {\n            return this._points;\n        }\n        get segments() {\n            return this._segments || (this._segments = _computeSegments(this, this.options.segment));\n        }\n        first() {\n            const segments = this.segments;\n            const points = this.points;\n            return segments.length && points[segments[0].start];\n        }\n        last() {\n            const segments = this.segments;\n            const points = this.points;\n            const count = segments.length;\n            return count && points[segments[count - 1].end];\n        }\n        interpolate(point, property) {\n            const options = this.options;\n            const value = point[property];\n            const points = this.points;\n            const segments = _boundSegments(this, {\n                property,\n                start: value,\n                end: value\n            });\n            if (!segments.length) {\n                return;\n            }\n            const result = [];\n            const _interpolate = _getInterpolationMethod(options);\n            let i, ilen;\n            for(i = 0, ilen = segments.length; i < ilen; ++i){\n                const { start , end  } = segments[i];\n                const p1 = points[start];\n                const p2 = points[end];\n                if (p1 === p2) {\n                    result.push(p1);\n                    continue;\n                }\n                const t = Math.abs((value - p1[property]) / (p2[property] - p1[property]));\n                const interpolated = _interpolate(p1, p2, t, options.stepped);\n                interpolated[property] = point[property];\n                result.push(interpolated);\n            }\n            return result.length === 1 ? result[0] : result;\n        }\n        pathSegment(ctx, segment, params) {\n            const segmentMethod = _getSegmentMethod(this);\n            return segmentMethod(ctx, this, segment, params);\n        }\n        path(ctx, start, count) {\n            const segments = this.segments;\n            const segmentMethod = _getSegmentMethod(this);\n            let loop = this._loop;\n            start = start || 0;\n            count = count || this.points.length - start;\n            for (const segment of segments){\n                loop &= segmentMethod(ctx, this, segment, {\n                    start,\n                    end: start + count - 1\n                });\n            }\n            return !!loop;\n        }\n        draw(ctx, chartArea, start, count) {\n            const options = this.options || {};\n            const points = this.points || [];\n            if (points.length && options.borderWidth) {\n                ctx.save();\n                draw(ctx, this, start, count);\n                ctx.restore();\n            }\n            if (this.animated) {\n                this._pointsUpdated = false;\n                this._path = undefined;\n            }\n        }\n    }\n\n    function inRange$1(el, pos, axis, useFinalPosition) {\n        const options = el.options;\n        const { [axis]: value  } = el.getProps([\n            axis\n        ], useFinalPosition);\n        return Math.abs(pos - value) < options.radius + options.hitRadius;\n    }\n    class PointElement extends Element {\n        static id = 'point';\n        parsed;\n        skip;\n        stop;\n        /**\n         * @type {any}\n         */ static defaults = {\n            borderWidth: 1,\n            hitRadius: 1,\n            hoverBorderWidth: 1,\n            hoverRadius: 4,\n            pointStyle: 'circle',\n            radius: 3,\n            rotation: 0\n        };\n        /**\n         * @type {any}\n         */ static defaultRoutes = {\n            backgroundColor: 'backgroundColor',\n            borderColor: 'borderColor'\n        };\n        constructor(cfg){\n            super();\n            this.options = undefined;\n            this.parsed = undefined;\n            this.skip = undefined;\n            this.stop = undefined;\n            if (cfg) {\n                Object.assign(this, cfg);\n            }\n        }\n        inRange(mouseX, mouseY, useFinalPosition) {\n            const options = this.options;\n            const { x , y  } = this.getProps([\n                'x',\n                'y'\n            ], useFinalPosition);\n            return Math.pow(mouseX - x, 2) + Math.pow(mouseY - y, 2) < Math.pow(options.hitRadius + options.radius, 2);\n        }\n        inXRange(mouseX, useFinalPosition) {\n            return inRange$1(this, mouseX, 'x', useFinalPosition);\n        }\n        inYRange(mouseY, useFinalPosition) {\n            return inRange$1(this, mouseY, 'y', useFinalPosition);\n        }\n        getCenterPoint(useFinalPosition) {\n            const { x , y  } = this.getProps([\n                'x',\n                'y'\n            ], useFinalPosition);\n            return {\n                x,\n                y\n            };\n        }\n        size(options) {\n            options = options || this.options || {};\n            let radius = options.radius || 0;\n            radius = Math.max(radius, radius && options.hoverRadius || 0);\n            const borderWidth = radius && options.borderWidth || 0;\n            return (radius + borderWidth) * 2;\n        }\n        draw(ctx, area) {\n            const options = this.options;\n            if (this.skip || options.radius < 0.1 || !_isPointInArea(this, area, this.size(options) / 2)) {\n                return;\n            }\n            ctx.strokeStyle = options.borderColor;\n            ctx.lineWidth = options.borderWidth;\n            ctx.fillStyle = options.backgroundColor;\n            drawPoint(ctx, options, this.x, this.y);\n        }\n        getRange() {\n            const options = this.options || {};\n            // @ts-expect-error Fallbacks should never be hit in practice\n            return options.radius + options.hitRadius;\n        }\n    }\n\n    function getBarBounds(bar, useFinalPosition) {\n        const { x , y , base , width , height  } =  bar.getProps([\n            'x',\n            'y',\n            'base',\n            'width',\n            'height'\n        ], useFinalPosition);\n        let left, right, top, bottom, half;\n        if (bar.horizontal) {\n            half = height / 2;\n            left = Math.min(x, base);\n            right = Math.max(x, base);\n            top = y - half;\n            bottom = y + half;\n        } else {\n            half = width / 2;\n            left = x - half;\n            right = x + half;\n            top = Math.min(y, base);\n            bottom = Math.max(y, base);\n        }\n        return {\n            left,\n            top,\n            right,\n            bottom\n        };\n    }\n    function skipOrLimit(skip, value, min, max) {\n        return skip ? 0 : _limitValue(value, min, max);\n    }\n    function parseBorderWidth(bar, maxW, maxH) {\n        const value = bar.options.borderWidth;\n        const skip = bar.borderSkipped;\n        const o = toTRBL(value);\n        return {\n            t: skipOrLimit(skip.top, o.top, 0, maxH),\n            r: skipOrLimit(skip.right, o.right, 0, maxW),\n            b: skipOrLimit(skip.bottom, o.bottom, 0, maxH),\n            l: skipOrLimit(skip.left, o.left, 0, maxW)\n        };\n    }\n    function parseBorderRadius(bar, maxW, maxH) {\n        const { enableBorderRadius  } = bar.getProps([\n            'enableBorderRadius'\n        ]);\n        const value = bar.options.borderRadius;\n        const o = toTRBLCorners(value);\n        const maxR = Math.min(maxW, maxH);\n        const skip = bar.borderSkipped;\n        const enableBorder = enableBorderRadius || isObject(value);\n        return {\n            topLeft: skipOrLimit(!enableBorder || skip.top || skip.left, o.topLeft, 0, maxR),\n            topRight: skipOrLimit(!enableBorder || skip.top || skip.right, o.topRight, 0, maxR),\n            bottomLeft: skipOrLimit(!enableBorder || skip.bottom || skip.left, o.bottomLeft, 0, maxR),\n            bottomRight: skipOrLimit(!enableBorder || skip.bottom || skip.right, o.bottomRight, 0, maxR)\n        };\n    }\n    function boundingRects(bar) {\n        const bounds = getBarBounds(bar);\n        const width = bounds.right - bounds.left;\n        const height = bounds.bottom - bounds.top;\n        const border = parseBorderWidth(bar, width / 2, height / 2);\n        const radius = parseBorderRadius(bar, width / 2, height / 2);\n        return {\n            outer: {\n                x: bounds.left,\n                y: bounds.top,\n                w: width,\n                h: height,\n                radius\n            },\n            inner: {\n                x: bounds.left + border.l,\n                y: bounds.top + border.t,\n                w: width - border.l - border.r,\n                h: height - border.t - border.b,\n                radius: {\n                    topLeft: Math.max(0, radius.topLeft - Math.max(border.t, border.l)),\n                    topRight: Math.max(0, radius.topRight - Math.max(border.t, border.r)),\n                    bottomLeft: Math.max(0, radius.bottomLeft - Math.max(border.b, border.l)),\n                    bottomRight: Math.max(0, radius.bottomRight - Math.max(border.b, border.r))\n                }\n            }\n        };\n    }\n    function inRange(bar, x, y, useFinalPosition) {\n        const skipX = x === null;\n        const skipY = y === null;\n        const skipBoth = skipX && skipY;\n        const bounds = bar && !skipBoth && getBarBounds(bar, useFinalPosition);\n        return bounds && (skipX || _isBetween(x, bounds.left, bounds.right)) && (skipY || _isBetween(y, bounds.top, bounds.bottom));\n    }\n    function hasRadius(radius) {\n        return radius.topLeft || radius.topRight || radius.bottomLeft || radius.bottomRight;\n    }\n    function addNormalRectPath(ctx, rect) {\n        ctx.rect(rect.x, rect.y, rect.w, rect.h);\n    }\n    function inflateRect(rect, amount, refRect = {}) {\n        const x = rect.x !== refRect.x ? -amount : 0;\n        const y = rect.y !== refRect.y ? -amount : 0;\n        const w = (rect.x + rect.w !== refRect.x + refRect.w ? amount : 0) - x;\n        const h = (rect.y + rect.h !== refRect.y + refRect.h ? amount : 0) - y;\n        return {\n            x: rect.x + x,\n            y: rect.y + y,\n            w: rect.w + w,\n            h: rect.h + h,\n            radius: rect.radius\n        };\n    }\n    class BarElement extends Element {\n        static id = 'bar';\n        static defaults = {\n            borderSkipped: 'start',\n            borderWidth: 0,\n            borderRadius: 0,\n            inflateAmount: 'auto',\n            pointStyle: undefined\n        };\n        static defaultRoutes = {\n            backgroundColor: 'backgroundColor',\n            borderColor: 'borderColor'\n        };\n        constructor(cfg){\n            super();\n            this.options = undefined;\n            this.horizontal = undefined;\n            this.base = undefined;\n            this.width = undefined;\n            this.height = undefined;\n            this.inflateAmount = undefined;\n            if (cfg) {\n                Object.assign(this, cfg);\n            }\n        }\n        draw(ctx) {\n            const { inflateAmount , options: { borderColor , backgroundColor  }  } = this;\n            const { inner , outer  } = boundingRects(this);\n            const addRectPath = hasRadius(outer.radius) ? addRoundedRectPath : addNormalRectPath;\n            ctx.save();\n            if (outer.w !== inner.w || outer.h !== inner.h) {\n                ctx.beginPath();\n                addRectPath(ctx, inflateRect(outer, inflateAmount, inner));\n                ctx.clip();\n                addRectPath(ctx, inflateRect(inner, -inflateAmount, outer));\n                ctx.fillStyle = borderColor;\n                ctx.fill('evenodd');\n            }\n            ctx.beginPath();\n            addRectPath(ctx, inflateRect(inner, inflateAmount));\n            ctx.fillStyle = backgroundColor;\n            ctx.fill();\n            ctx.restore();\n        }\n        inRange(mouseX, mouseY, useFinalPosition) {\n            return inRange(this, mouseX, mouseY, useFinalPosition);\n        }\n        inXRange(mouseX, useFinalPosition) {\n            return inRange(this, mouseX, null, useFinalPosition);\n        }\n        inYRange(mouseY, useFinalPosition) {\n            return inRange(this, null, mouseY, useFinalPosition);\n        }\n        getCenterPoint(useFinalPosition) {\n            const { x , y , base , horizontal  } =  this.getProps([\n                'x',\n                'y',\n                'base',\n                'horizontal'\n            ], useFinalPosition);\n            return {\n                x: horizontal ? (x + base) / 2 : x,\n                y: horizontal ? y : (y + base) / 2\n            };\n        }\n        getRange(axis) {\n            return axis === 'x' ? this.width / 2 : this.height / 2;\n        }\n    }\n\n    var elements = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        ArcElement: ArcElement,\n        BarElement: BarElement,\n        LineElement: LineElement,\n        PointElement: PointElement\n    });\n\n    const addIfString = (labels, raw, index, addedLabels)=>{\n        if (typeof raw === 'string') {\n            index = labels.push(raw) - 1;\n            addedLabels.unshift({\n                index,\n                label: raw\n            });\n        } else if (isNaN(raw)) {\n            index = null;\n        }\n        return index;\n    };\n    function findOrAddLabel(labels, raw, index, addedLabels) {\n        const first = labels.indexOf(raw);\n        if (first === -1) {\n            return addIfString(labels, raw, index, addedLabels);\n        }\n        const last = labels.lastIndexOf(raw);\n        return first !== last ? index : first;\n    }\n    const validIndex = (index, max)=>index === null ? null : _limitValue(Math.round(index), 0, max);\n    function _getLabelForValue(value) {\n        const labels = this.getLabels();\n        if (value >= 0 && value < labels.length) {\n            return labels[value];\n        }\n        return value;\n    }\n    class CategoryScale extends Scale {\n        static id = 'category';\n        static defaults = {\n            ticks: {\n                callback: _getLabelForValue\n            }\n        };\n        constructor(cfg){\n            super(cfg);\n            this._startValue = undefined;\n            this._valueRange = 0;\n            this._addedLabels = [];\n        }\n        init(scaleOptions) {\n            const added = this._addedLabels;\n            if (added.length) {\n                const labels = this.getLabels();\n                for (const { index , label  } of added){\n                    if (labels[index] === label) {\n                        labels.splice(index, 1);\n                    }\n                }\n                this._addedLabels = [];\n            }\n            super.init(scaleOptions);\n        }\n        parse(raw, index) {\n            if (isNullOrUndef(raw)) {\n                return null;\n            }\n            const labels = this.getLabels();\n            index = isFinite(index) && labels[index] === raw ? index : findOrAddLabel(labels, raw, valueOrDefault(index, raw), this._addedLabels);\n            return validIndex(index, labels.length - 1);\n        }\n        determineDataLimits() {\n            const { minDefined , maxDefined  } = this.getUserBounds();\n            let { min , max  } = this.getMinMax(true);\n            if (this.options.bounds === 'ticks') {\n                if (!minDefined) {\n                    min = 0;\n                }\n                if (!maxDefined) {\n                    max = this.getLabels().length - 1;\n                }\n            }\n            this.min = min;\n            this.max = max;\n        }\n        buildTicks() {\n            const min = this.min;\n            const max = this.max;\n            const offset = this.options.offset;\n            const ticks = [];\n            let labels = this.getLabels();\n            labels = min === 0 && max === labels.length - 1 ? labels : labels.slice(min, max + 1);\n            this._valueRange = Math.max(labels.length - (offset ? 0 : 1), 1);\n            this._startValue = this.min - (offset ? 0.5 : 0);\n            for(let value = min; value <= max; value++){\n                ticks.push({\n                    value\n                });\n            }\n            return ticks;\n        }\n        getLabelForValue(value) {\n            return _getLabelForValue.call(this, value);\n        }\n        configure() {\n            super.configure();\n            if (!this.isHorizontal()) {\n                this._reversePixels = !this._reversePixels;\n            }\n        }\n        getPixelForValue(value) {\n            if (typeof value !== 'number') {\n                value = this.parse(value);\n            }\n            return value === null ? NaN : this.getPixelForDecimal((value - this._startValue) / this._valueRange);\n        }\n        getPixelForTick(index) {\n            const ticks = this.ticks;\n            if (index < 0 || index > ticks.length - 1) {\n                return null;\n            }\n            return this.getPixelForValue(ticks[index].value);\n        }\n        getValueForPixel(pixel) {\n            return Math.round(this._startValue + this.getDecimalForPixel(pixel) * this._valueRange);\n        }\n        getBasePixel() {\n            return this.bottom;\n        }\n    }\n\n    function generateTicks$1(generationOptions, dataRange) {\n        const ticks = [];\n        const MIN_SPACING = 1e-14;\n        const { bounds , step , min , max , precision , count , maxTicks , maxDigits , includeBounds  } = generationOptions;\n        const unit = step || 1;\n        const maxSpaces = maxTicks - 1;\n        const { min: rmin , max: rmax  } = dataRange;\n        const minDefined = !isNullOrUndef(min);\n        const maxDefined = !isNullOrUndef(max);\n        const countDefined = !isNullOrUndef(count);\n        const minSpacing = (rmax - rmin) / (maxDigits + 1);\n        let spacing = niceNum((rmax - rmin) / maxSpaces / unit) * unit;\n        let factor, niceMin, niceMax, numSpaces;\n        if (spacing < MIN_SPACING && !minDefined && !maxDefined) {\n            return [\n                {\n                    value: rmin\n                },\n                {\n                    value: rmax\n                }\n            ];\n        }\n        numSpaces = Math.ceil(rmax / spacing) - Math.floor(rmin / spacing);\n        if (numSpaces > maxSpaces) {\n            spacing = niceNum(numSpaces * spacing / maxSpaces / unit) * unit;\n        }\n        if (!isNullOrUndef(precision)) {\n            factor = Math.pow(10, precision);\n            spacing = Math.ceil(spacing * factor) / factor;\n        }\n        if (bounds === 'ticks') {\n            niceMin = Math.floor(rmin / spacing) * spacing;\n            niceMax = Math.ceil(rmax / spacing) * spacing;\n        } else {\n            niceMin = rmin;\n            niceMax = rmax;\n        }\n        if (minDefined && maxDefined && step && almostWhole((max - min) / step, spacing / 1000)) {\n            numSpaces = Math.round(Math.min((max - min) / spacing, maxTicks));\n            spacing = (max - min) / numSpaces;\n            niceMin = min;\n            niceMax = max;\n        } else if (countDefined) {\n            niceMin = minDefined ? min : niceMin;\n            niceMax = maxDefined ? max : niceMax;\n            numSpaces = count - 1;\n            spacing = (niceMax - niceMin) / numSpaces;\n        } else {\n            numSpaces = (niceMax - niceMin) / spacing;\n            if (almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {\n                numSpaces = Math.round(numSpaces);\n            } else {\n                numSpaces = Math.ceil(numSpaces);\n            }\n        }\n        const decimalPlaces = Math.max(_decimalPlaces(spacing), _decimalPlaces(niceMin));\n        factor = Math.pow(10, isNullOrUndef(precision) ? decimalPlaces : precision);\n        niceMin = Math.round(niceMin * factor) / factor;\n        niceMax = Math.round(niceMax * factor) / factor;\n        let j = 0;\n        if (minDefined) {\n            if (includeBounds && niceMin !== min) {\n                ticks.push({\n                    value: min\n                });\n                if (niceMin < min) {\n                    j++;\n                }\n                if (almostEquals(Math.round((niceMin + j * spacing) * factor) / factor, min, relativeLabelSize(min, minSpacing, generationOptions))) {\n                    j++;\n                }\n            } else if (niceMin < min) {\n                j++;\n            }\n        }\n        for(; j < numSpaces; ++j){\n            const tickValue = Math.round((niceMin + j * spacing) * factor) / factor;\n            if (maxDefined && tickValue > max) {\n                break;\n            }\n            ticks.push({\n                value: tickValue\n            });\n        }\n        if (maxDefined && includeBounds && niceMax !== max) {\n            if (ticks.length && almostEquals(ticks[ticks.length - 1].value, max, relativeLabelSize(max, minSpacing, generationOptions))) {\n                ticks[ticks.length - 1].value = max;\n            } else {\n                ticks.push({\n                    value: max\n                });\n            }\n        } else if (!maxDefined || niceMax === max) {\n            ticks.push({\n                value: niceMax\n            });\n        }\n        return ticks;\n    }\n    function relativeLabelSize(value, minSpacing, { horizontal , minRotation  }) {\n        const rad = toRadians(minRotation);\n        const ratio = (horizontal ? Math.sin(rad) : Math.cos(rad)) || 0.001;\n        const length = 0.75 * minSpacing * ('' + value).length;\n        return Math.min(minSpacing / ratio, length);\n    }\n    class LinearScaleBase extends Scale {\n        constructor(cfg){\n            super(cfg);\n            this.start = undefined;\n            this.end = undefined;\n            this._startValue = undefined;\n            this._endValue = undefined;\n            this._valueRange = 0;\n        }\n        parse(raw, index) {\n            if (isNullOrUndef(raw)) {\n                return null;\n            }\n            if ((typeof raw === 'number' || raw instanceof Number) && !isFinite(+raw)) {\n                return null;\n            }\n            return +raw;\n        }\n        handleTickRangeOptions() {\n            const { beginAtZero  } = this.options;\n            const { minDefined , maxDefined  } = this.getUserBounds();\n            let { min , max  } = this;\n            const setMin = (v)=>min = minDefined ? min : v;\n            const setMax = (v)=>max = maxDefined ? max : v;\n            if (beginAtZero) {\n                const minSign = sign(min);\n                const maxSign = sign(max);\n                if (minSign < 0 && maxSign < 0) {\n                    setMax(0);\n                } else if (minSign > 0 && maxSign > 0) {\n                    setMin(0);\n                }\n            }\n            if (min === max) {\n                let offset = max === 0 ? 1 : Math.abs(max * 0.05);\n                setMax(max + offset);\n                if (!beginAtZero) {\n                    setMin(min - offset);\n                }\n            }\n            this.min = min;\n            this.max = max;\n        }\n        getTickLimit() {\n            const tickOpts = this.options.ticks;\n            let { maxTicksLimit , stepSize  } = tickOpts;\n            let maxTicks;\n            if (stepSize) {\n                maxTicks = Math.ceil(this.max / stepSize) - Math.floor(this.min / stepSize) + 1;\n                if (maxTicks > 1000) {\n                    console.warn(`scales.${this.id}.ticks.stepSize: ${stepSize} would result generating up to ${maxTicks} ticks. Limiting to 1000.`);\n                    maxTicks = 1000;\n                }\n            } else {\n                maxTicks = this.computeTickLimit();\n                maxTicksLimit = maxTicksLimit || 11;\n            }\n            if (maxTicksLimit) {\n                maxTicks = Math.min(maxTicksLimit, maxTicks);\n            }\n            return maxTicks;\n        }\n        computeTickLimit() {\n            return Number.POSITIVE_INFINITY;\n        }\n        buildTicks() {\n            const opts = this.options;\n            const tickOpts = opts.ticks;\n            let maxTicks = this.getTickLimit();\n            maxTicks = Math.max(2, maxTicks);\n            const numericGeneratorOptions = {\n                maxTicks,\n                bounds: opts.bounds,\n                min: opts.min,\n                max: opts.max,\n                precision: tickOpts.precision,\n                step: tickOpts.stepSize,\n                count: tickOpts.count,\n                maxDigits: this._maxDigits(),\n                horizontal: this.isHorizontal(),\n                minRotation: tickOpts.minRotation || 0,\n                includeBounds: tickOpts.includeBounds !== false\n            };\n            const dataRange = this._range || this;\n            const ticks = generateTicks$1(numericGeneratorOptions, dataRange);\n            if (opts.bounds === 'ticks') {\n                _setMinAndMaxByKey(ticks, this, 'value');\n            }\n            if (opts.reverse) {\n                ticks.reverse();\n                this.start = this.max;\n                this.end = this.min;\n            } else {\n                this.start = this.min;\n                this.end = this.max;\n            }\n            return ticks;\n        }\n        configure() {\n            const ticks = this.ticks;\n            let start = this.min;\n            let end = this.max;\n            super.configure();\n            if (this.options.offset && ticks.length) {\n                const offset = (end - start) / Math.max(ticks.length - 1, 1) / 2;\n                start -= offset;\n                end += offset;\n            }\n            this._startValue = start;\n            this._endValue = end;\n            this._valueRange = end - start;\n        }\n        getLabelForValue(value) {\n            return formatNumber(value, this.chart.options.locale, this.options.ticks.format);\n        }\n    }\n\n    class LinearScale extends LinearScaleBase {\n        static id = 'linear';\n        static defaults = {\n            ticks: {\n                callback: Ticks.formatters.numeric\n            }\n        };\n        determineDataLimits() {\n            const { min , max  } = this.getMinMax(true);\n            this.min = isNumberFinite(min) ? min : 0;\n            this.max = isNumberFinite(max) ? max : 1;\n            this.handleTickRangeOptions();\n        }\n        computeTickLimit() {\n            const horizontal = this.isHorizontal();\n            const length = horizontal ? this.width : this.height;\n            const minRotation = toRadians(this.options.ticks.minRotation);\n            const ratio = (horizontal ? Math.sin(minRotation) : Math.cos(minRotation)) || 0.001;\n            const tickFont = this._resolveTickFontOptions(0);\n            return Math.ceil(length / Math.min(40, tickFont.lineHeight / ratio));\n        }\n        getPixelForValue(value) {\n            return value === null ? NaN : this.getPixelForDecimal((value - this._startValue) / this._valueRange);\n        }\n        getValueForPixel(pixel) {\n            return this._startValue + this.getDecimalForPixel(pixel) * this._valueRange;\n        }\n    }\n\n    const log10Floor = (v)=>Math.floor(log10(v));\n    const changeExponent = (v, m)=>Math.pow(10, log10Floor(v) + m);\n    function isMajor(tickVal) {\n        const remain = tickVal / Math.pow(10, log10Floor(tickVal));\n        return remain === 1;\n    }\n    function steps(min, max, rangeExp) {\n        const rangeStep = Math.pow(10, rangeExp);\n        const start = Math.floor(min / rangeStep);\n        const end = Math.ceil(max / rangeStep);\n        return end - start;\n    }\n    function startExp(min, max) {\n        const range = max - min;\n        let rangeExp = log10Floor(range);\n        while(steps(min, max, rangeExp) > 10){\n            rangeExp++;\n        }\n        while(steps(min, max, rangeExp) < 10){\n            rangeExp--;\n        }\n        return Math.min(rangeExp, log10Floor(min));\n    }\n    function generateTicks(generationOptions, { min , max  }) {\n        min = finiteOrDefault(generationOptions.min, min);\n        const ticks = [];\n        const minExp = log10Floor(min);\n        let exp = startExp(min, max);\n        let precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1;\n        const stepSize = Math.pow(10, exp);\n        const base = minExp > exp ? Math.pow(10, minExp) : 0;\n        const start = Math.round((min - base) * precision) / precision;\n        const offset = Math.floor((min - base) / stepSize / 10) * stepSize * 10;\n        let significand = Math.floor((start - offset) / Math.pow(10, exp));\n        let value = finiteOrDefault(generationOptions.min, Math.round((base + offset + significand * Math.pow(10, exp)) * precision) / precision);\n        while(value < max){\n            ticks.push({\n                value,\n                major: isMajor(value),\n                significand\n            });\n            if (significand >= 10) {\n                significand = significand < 15 ? 15 : 20;\n            } else {\n                significand++;\n            }\n            if (significand >= 20) {\n                exp++;\n                significand = 2;\n                precision = exp >= 0 ? 1 : precision;\n            }\n            value = Math.round((base + offset + significand * Math.pow(10, exp)) * precision) / precision;\n        }\n        const lastTick = finiteOrDefault(generationOptions.max, value);\n        ticks.push({\n            value: lastTick,\n            major: isMajor(lastTick),\n            significand\n        });\n        return ticks;\n    }\n    class LogarithmicScale extends Scale {\n        static id = 'logarithmic';\n        static defaults = {\n            ticks: {\n                callback: Ticks.formatters.logarithmic,\n                major: {\n                    enabled: true\n                }\n            }\n        };\n        constructor(cfg){\n            super(cfg);\n            this.start = undefined;\n            this.end = undefined;\n            this._startValue = undefined;\n            this._valueRange = 0;\n        }\n        parse(raw, index) {\n            const value = LinearScaleBase.prototype.parse.apply(this, [\n                raw,\n                index\n            ]);\n            if (value === 0) {\n                this._zero = true;\n                return undefined;\n            }\n            return isNumberFinite(value) && value > 0 ? value : null;\n        }\n        determineDataLimits() {\n            const { min , max  } = this.getMinMax(true);\n            this.min = isNumberFinite(min) ? Math.max(0, min) : null;\n            this.max = isNumberFinite(max) ? Math.max(0, max) : null;\n            if (this.options.beginAtZero) {\n                this._zero = true;\n            }\n            if (this._zero && this.min !== this._suggestedMin && !isNumberFinite(this._userMin)) {\n                this.min = min === changeExponent(this.min, 0) ? changeExponent(this.min, -1) : changeExponent(this.min, 0);\n            }\n            this.handleTickRangeOptions();\n        }\n        handleTickRangeOptions() {\n            const { minDefined , maxDefined  } = this.getUserBounds();\n            let min = this.min;\n            let max = this.max;\n            const setMin = (v)=>min = minDefined ? min : v;\n            const setMax = (v)=>max = maxDefined ? max : v;\n            if (min === max) {\n                if (min <= 0) {\n                    setMin(1);\n                    setMax(10);\n                } else {\n                    setMin(changeExponent(min, -1));\n                    setMax(changeExponent(max, +1));\n                }\n            }\n            if (min <= 0) {\n                setMin(changeExponent(max, -1));\n            }\n            if (max <= 0) {\n                setMax(changeExponent(min, +1));\n            }\n            this.min = min;\n            this.max = max;\n        }\n        buildTicks() {\n            const opts = this.options;\n            const generationOptions = {\n                min: this._userMin,\n                max: this._userMax\n            };\n            const ticks = generateTicks(generationOptions, this);\n            if (opts.bounds === 'ticks') {\n                _setMinAndMaxByKey(ticks, this, 'value');\n            }\n            if (opts.reverse) {\n                ticks.reverse();\n                this.start = this.max;\n                this.end = this.min;\n            } else {\n                this.start = this.min;\n                this.end = this.max;\n            }\n            return ticks;\n        }\n        getLabelForValue(value) {\n            return value === undefined ? '0' : formatNumber(value, this.chart.options.locale, this.options.ticks.format);\n        }\n        configure() {\n            const start = this.min;\n            super.configure();\n            this._startValue = log10(start);\n            this._valueRange = log10(this.max) - log10(start);\n        }\n        getPixelForValue(value) {\n            if (value === undefined || value === 0) {\n                value = this.min;\n            }\n            if (value === null || isNaN(value)) {\n                return NaN;\n            }\n            return this.getPixelForDecimal(value === this.min ? 0 : (log10(value) - this._startValue) / this._valueRange);\n        }\n        getValueForPixel(pixel) {\n            const decimal = this.getDecimalForPixel(pixel);\n            return Math.pow(10, this._startValue + decimal * this._valueRange);\n        }\n    }\n\n    function getTickBackdropHeight(opts) {\n        const tickOpts = opts.ticks;\n        if (tickOpts.display && opts.display) {\n            const padding = toPadding(tickOpts.backdropPadding);\n            return valueOrDefault(tickOpts.font && tickOpts.font.size, defaults.font.size) + padding.height;\n        }\n        return 0;\n    }\n    function measureLabelSize(ctx, font, label) {\n        label = isArray(label) ? label : [\n            label\n        ];\n        return {\n            w: _longestText(ctx, font.string, label),\n            h: label.length * font.lineHeight\n        };\n    }\n    function determineLimits(angle, pos, size, min, max) {\n        if (angle === min || angle === max) {\n            return {\n                start: pos - size / 2,\n                end: pos + size / 2\n            };\n        } else if (angle < min || angle > max) {\n            return {\n                start: pos - size,\n                end: pos\n            };\n        }\n        return {\n            start: pos,\n            end: pos + size\n        };\n    }\n    function fitWithPointLabels(scale) {\n        const orig = {\n            l: scale.left + scale._padding.left,\n            r: scale.right - scale._padding.right,\n            t: scale.top + scale._padding.top,\n            b: scale.bottom - scale._padding.bottom\n        };\n        const limits = Object.assign({}, orig);\n        const labelSizes = [];\n        const padding = [];\n        const valueCount = scale._pointLabels.length;\n        const pointLabelOpts = scale.options.pointLabels;\n        const additionalAngle = pointLabelOpts.centerPointLabels ? PI / valueCount : 0;\n        for(let i = 0; i < valueCount; i++){\n            const opts = pointLabelOpts.setContext(scale.getPointLabelContext(i));\n            padding[i] = opts.padding;\n            const pointPosition = scale.getPointPosition(i, scale.drawingArea + padding[i], additionalAngle);\n            const plFont = toFont(opts.font);\n            const textSize = measureLabelSize(scale.ctx, plFont, scale._pointLabels[i]);\n            labelSizes[i] = textSize;\n            const angleRadians = _normalizeAngle(scale.getIndexAngle(i) + additionalAngle);\n            const angle = Math.round(toDegrees(angleRadians));\n            const hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);\n            const vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);\n            updateLimits(limits, orig, angleRadians, hLimits, vLimits);\n        }\n        scale.setCenterPoint(orig.l - limits.l, limits.r - orig.r, orig.t - limits.t, limits.b - orig.b);\n        scale._pointLabelItems = buildPointLabelItems(scale, labelSizes, padding);\n    }\n    function updateLimits(limits, orig, angle, hLimits, vLimits) {\n        const sin = Math.abs(Math.sin(angle));\n        const cos = Math.abs(Math.cos(angle));\n        let x = 0;\n        let y = 0;\n        if (hLimits.start < orig.l) {\n            x = (orig.l - hLimits.start) / sin;\n            limits.l = Math.min(limits.l, orig.l - x);\n        } else if (hLimits.end > orig.r) {\n            x = (hLimits.end - orig.r) / sin;\n            limits.r = Math.max(limits.r, orig.r + x);\n        }\n        if (vLimits.start < orig.t) {\n            y = (orig.t - vLimits.start) / cos;\n            limits.t = Math.min(limits.t, orig.t - y);\n        } else if (vLimits.end > orig.b) {\n            y = (vLimits.end - orig.b) / cos;\n            limits.b = Math.max(limits.b, orig.b + y);\n        }\n    }\n    function createPointLabelItem(scale, index, itemOpts) {\n        const outerDistance = scale.drawingArea;\n        const { extra , additionalAngle , padding , size  } = itemOpts;\n        const pointLabelPosition = scale.getPointPosition(index, outerDistance + extra + padding, additionalAngle);\n        const angle = Math.round(toDegrees(_normalizeAngle(pointLabelPosition.angle + HALF_PI)));\n        const y = yForAngle(pointLabelPosition.y, size.h, angle);\n        const textAlign = getTextAlignForAngle(angle);\n        const left = leftForTextAlign(pointLabelPosition.x, size.w, textAlign);\n        return {\n            visible: true,\n            x: pointLabelPosition.x,\n            y,\n            textAlign,\n            left,\n            top: y,\n            right: left + size.w,\n            bottom: y + size.h\n        };\n    }\n    function isNotOverlapped(item, area) {\n        if (!area) {\n            return true;\n        }\n        const { left , top , right , bottom  } = item;\n        const apexesInArea = _isPointInArea({\n            x: left,\n            y: top\n        }, area) || _isPointInArea({\n            x: left,\n            y: bottom\n        }, area) || _isPointInArea({\n            x: right,\n            y: top\n        }, area) || _isPointInArea({\n            x: right,\n            y: bottom\n        }, area);\n        return !apexesInArea;\n    }\n    function buildPointLabelItems(scale, labelSizes, padding) {\n        const items = [];\n        const valueCount = scale._pointLabels.length;\n        const opts = scale.options;\n        const { centerPointLabels , display  } = opts.pointLabels;\n        const itemOpts = {\n            extra: getTickBackdropHeight(opts) / 2,\n            additionalAngle: centerPointLabels ? PI / valueCount : 0\n        };\n        let area;\n        for(let i = 0; i < valueCount; i++){\n            itemOpts.padding = padding[i];\n            itemOpts.size = labelSizes[i];\n            const item = createPointLabelItem(scale, i, itemOpts);\n            items.push(item);\n            if (display === 'auto') {\n                item.visible = isNotOverlapped(item, area);\n                if (item.visible) {\n                    area = item;\n                }\n            }\n        }\n        return items;\n    }\n    function getTextAlignForAngle(angle) {\n        if (angle === 0 || angle === 180) {\n            return 'center';\n        } else if (angle < 180) {\n            return 'left';\n        }\n        return 'right';\n    }\n    function leftForTextAlign(x, w, align) {\n        if (align === 'right') {\n            x -= w;\n        } else if (align === 'center') {\n            x -= w / 2;\n        }\n        return x;\n    }\n    function yForAngle(y, h, angle) {\n        if (angle === 90 || angle === 270) {\n            y -= h / 2;\n        } else if (angle > 270 || angle < 90) {\n            y -= h;\n        }\n        return y;\n    }\n    function drawPointLabelBox(ctx, opts, item) {\n        const { left , top , right , bottom  } = item;\n        const { backdropColor  } = opts;\n        if (!isNullOrUndef(backdropColor)) {\n            const borderRadius = toTRBLCorners(opts.borderRadius);\n            const padding = toPadding(opts.backdropPadding);\n            ctx.fillStyle = backdropColor;\n            const backdropLeft = left - padding.left;\n            const backdropTop = top - padding.top;\n            const backdropWidth = right - left + padding.width;\n            const backdropHeight = bottom - top + padding.height;\n            if (Object.values(borderRadius).some((v)=>v !== 0)) {\n                ctx.beginPath();\n                addRoundedRectPath(ctx, {\n                    x: backdropLeft,\n                    y: backdropTop,\n                    w: backdropWidth,\n                    h: backdropHeight,\n                    radius: borderRadius\n                });\n                ctx.fill();\n            } else {\n                ctx.fillRect(backdropLeft, backdropTop, backdropWidth, backdropHeight);\n            }\n        }\n    }\n    function drawPointLabels(scale, labelCount) {\n        const { ctx , options: { pointLabels  }  } = scale;\n        for(let i = labelCount - 1; i >= 0; i--){\n            const item = scale._pointLabelItems[i];\n            if (!item.visible) {\n                continue;\n            }\n            const optsAtIndex = pointLabels.setContext(scale.getPointLabelContext(i));\n            drawPointLabelBox(ctx, optsAtIndex, item);\n            const plFont = toFont(optsAtIndex.font);\n            const { x , y , textAlign  } = item;\n            renderText(ctx, scale._pointLabels[i], x, y + plFont.lineHeight / 2, plFont, {\n                color: optsAtIndex.color,\n                textAlign: textAlign,\n                textBaseline: 'middle'\n            });\n        }\n    }\n    function pathRadiusLine(scale, radius, circular, labelCount) {\n        const { ctx  } = scale;\n        if (circular) {\n            ctx.arc(scale.xCenter, scale.yCenter, radius, 0, TAU);\n        } else {\n            let pointPosition = scale.getPointPosition(0, radius);\n            ctx.moveTo(pointPosition.x, pointPosition.y);\n            for(let i = 1; i < labelCount; i++){\n                pointPosition = scale.getPointPosition(i, radius);\n                ctx.lineTo(pointPosition.x, pointPosition.y);\n            }\n        }\n    }\n    function drawRadiusLine(scale, gridLineOpts, radius, labelCount, borderOpts) {\n        const ctx = scale.ctx;\n        const circular = gridLineOpts.circular;\n        const { color , lineWidth  } = gridLineOpts;\n        if (!circular && !labelCount || !color || !lineWidth || radius < 0) {\n            return;\n        }\n        ctx.save();\n        ctx.strokeStyle = color;\n        ctx.lineWidth = lineWidth;\n        ctx.setLineDash(borderOpts.dash);\n        ctx.lineDashOffset = borderOpts.dashOffset;\n        ctx.beginPath();\n        pathRadiusLine(scale, radius, circular, labelCount);\n        ctx.closePath();\n        ctx.stroke();\n        ctx.restore();\n    }\n    function createPointLabelContext(parent, index, label) {\n        return createContext(parent, {\n            label,\n            index,\n            type: 'pointLabel'\n        });\n    }\n    class RadialLinearScale extends LinearScaleBase {\n        static id = 'radialLinear';\n        static defaults = {\n            display: true,\n            animate: true,\n            position: 'chartArea',\n            angleLines: {\n                display: true,\n                lineWidth: 1,\n                borderDash: [],\n                borderDashOffset: 0.0\n            },\n            grid: {\n                circular: false\n            },\n            startAngle: 0,\n            ticks: {\n                showLabelBackdrop: true,\n                callback: Ticks.formatters.numeric\n            },\n            pointLabels: {\n                backdropColor: undefined,\n                backdropPadding: 2,\n                display: true,\n                font: {\n                    size: 10\n                },\n                callback (label) {\n                    return label;\n                },\n                padding: 5,\n                centerPointLabels: false\n            }\n        };\n        static defaultRoutes = {\n            'angleLines.color': 'borderColor',\n            'pointLabels.color': 'color',\n            'ticks.color': 'color'\n        };\n        static descriptors = {\n            angleLines: {\n                _fallback: 'grid'\n            }\n        };\n        constructor(cfg){\n            super(cfg);\n            this.xCenter = undefined;\n            this.yCenter = undefined;\n            this.drawingArea = undefined;\n            this._pointLabels = [];\n            this._pointLabelItems = [];\n        }\n        setDimensions() {\n            const padding = this._padding = toPadding(getTickBackdropHeight(this.options) / 2);\n            const w = this.width = this.maxWidth - padding.width;\n            const h = this.height = this.maxHeight - padding.height;\n            this.xCenter = Math.floor(this.left + w / 2 + padding.left);\n            this.yCenter = Math.floor(this.top + h / 2 + padding.top);\n            this.drawingArea = Math.floor(Math.min(w, h) / 2);\n        }\n        determineDataLimits() {\n            const { min , max  } = this.getMinMax(false);\n            this.min = isNumberFinite(min) && !isNaN(min) ? min : 0;\n            this.max = isNumberFinite(max) && !isNaN(max) ? max : 0;\n            this.handleTickRangeOptions();\n        }\n        computeTickLimit() {\n            return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options));\n        }\n        generateTickLabels(ticks) {\n            LinearScaleBase.prototype.generateTickLabels.call(this, ticks);\n            this._pointLabels = this.getLabels().map((value, index)=>{\n                const label = callback(this.options.pointLabels.callback, [\n                    value,\n                    index\n                ], this);\n                return label || label === 0 ? label : '';\n            }).filter((v, i)=>this.chart.getDataVisibility(i));\n        }\n        fit() {\n            const opts = this.options;\n            if (opts.display && opts.pointLabels.display) {\n                fitWithPointLabels(this);\n            } else {\n                this.setCenterPoint(0, 0, 0, 0);\n            }\n        }\n        setCenterPoint(leftMovement, rightMovement, topMovement, bottomMovement) {\n            this.xCenter += Math.floor((leftMovement - rightMovement) / 2);\n            this.yCenter += Math.floor((topMovement - bottomMovement) / 2);\n            this.drawingArea -= Math.min(this.drawingArea / 2, Math.max(leftMovement, rightMovement, topMovement, bottomMovement));\n        }\n        getIndexAngle(index) {\n            const angleMultiplier = TAU / (this._pointLabels.length || 1);\n            const startAngle = this.options.startAngle || 0;\n            return _normalizeAngle(index * angleMultiplier + toRadians(startAngle));\n        }\n        getDistanceFromCenterForValue(value) {\n            if (isNullOrUndef(value)) {\n                return NaN;\n            }\n            const scalingFactor = this.drawingArea / (this.max - this.min);\n            if (this.options.reverse) {\n                return (this.max - value) * scalingFactor;\n            }\n            return (value - this.min) * scalingFactor;\n        }\n        getValueForDistanceFromCenter(distance) {\n            if (isNullOrUndef(distance)) {\n                return NaN;\n            }\n            const scaledDistance = distance / (this.drawingArea / (this.max - this.min));\n            return this.options.reverse ? this.max - scaledDistance : this.min + scaledDistance;\n        }\n        getPointLabelContext(index) {\n            const pointLabels = this._pointLabels || [];\n            if (index >= 0 && index < pointLabels.length) {\n                const pointLabel = pointLabels[index];\n                return createPointLabelContext(this.getContext(), index, pointLabel);\n            }\n        }\n        getPointPosition(index, distanceFromCenter, additionalAngle = 0) {\n            const angle = this.getIndexAngle(index) - HALF_PI + additionalAngle;\n            return {\n                x: Math.cos(angle) * distanceFromCenter + this.xCenter,\n                y: Math.sin(angle) * distanceFromCenter + this.yCenter,\n                angle\n            };\n        }\n        getPointPositionForValue(index, value) {\n            return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));\n        }\n        getBasePosition(index) {\n            return this.getPointPositionForValue(index || 0, this.getBaseValue());\n        }\n        getPointLabelPosition(index) {\n            const { left , top , right , bottom  } = this._pointLabelItems[index];\n            return {\n                left,\n                top,\n                right,\n                bottom\n            };\n        }\n        drawBackground() {\n            const { backgroundColor , grid: { circular  }  } = this.options;\n            if (backgroundColor) {\n                const ctx = this.ctx;\n                ctx.save();\n                ctx.beginPath();\n                pathRadiusLine(this, this.getDistanceFromCenterForValue(this._endValue), circular, this._pointLabels.length);\n                ctx.closePath();\n                ctx.fillStyle = backgroundColor;\n                ctx.fill();\n                ctx.restore();\n            }\n        }\n        drawGrid() {\n            const ctx = this.ctx;\n            const opts = this.options;\n            const { angleLines , grid , border  } = opts;\n            const labelCount = this._pointLabels.length;\n            let i, offset, position;\n            if (opts.pointLabels.display) {\n                drawPointLabels(this, labelCount);\n            }\n            if (grid.display) {\n                this.ticks.forEach((tick, index)=>{\n                    if (index !== 0) {\n                        offset = this.getDistanceFromCenterForValue(tick.value);\n                        const context = this.getContext(index);\n                        const optsAtIndex = grid.setContext(context);\n                        const optsAtIndexBorder = border.setContext(context);\n                        drawRadiusLine(this, optsAtIndex, offset, labelCount, optsAtIndexBorder);\n                    }\n                });\n            }\n            if (angleLines.display) {\n                ctx.save();\n                for(i = labelCount - 1; i >= 0; i--){\n                    const optsAtIndex = angleLines.setContext(this.getPointLabelContext(i));\n                    const { color , lineWidth  } = optsAtIndex;\n                    if (!lineWidth || !color) {\n                        continue;\n                    }\n                    ctx.lineWidth = lineWidth;\n                    ctx.strokeStyle = color;\n                    ctx.setLineDash(optsAtIndex.borderDash);\n                    ctx.lineDashOffset = optsAtIndex.borderDashOffset;\n                    offset = this.getDistanceFromCenterForValue(opts.ticks.reverse ? this.min : this.max);\n                    position = this.getPointPosition(i, offset);\n                    ctx.beginPath();\n                    ctx.moveTo(this.xCenter, this.yCenter);\n                    ctx.lineTo(position.x, position.y);\n                    ctx.stroke();\n                }\n                ctx.restore();\n            }\n        }\n        drawBorder() {}\n        drawLabels() {\n            const ctx = this.ctx;\n            const opts = this.options;\n            const tickOpts = opts.ticks;\n            if (!tickOpts.display) {\n                return;\n            }\n            const startAngle = this.getIndexAngle(0);\n            let offset, width;\n            ctx.save();\n            ctx.translate(this.xCenter, this.yCenter);\n            ctx.rotate(startAngle);\n            ctx.textAlign = 'center';\n            ctx.textBaseline = 'middle';\n            this.ticks.forEach((tick, index)=>{\n                if (index === 0 && !opts.reverse) {\n                    return;\n                }\n                const optsAtIndex = tickOpts.setContext(this.getContext(index));\n                const tickFont = toFont(optsAtIndex.font);\n                offset = this.getDistanceFromCenterForValue(this.ticks[index].value);\n                if (optsAtIndex.showLabelBackdrop) {\n                    ctx.font = tickFont.string;\n                    width = ctx.measureText(tick.label).width;\n                    ctx.fillStyle = optsAtIndex.backdropColor;\n                    const padding = toPadding(optsAtIndex.backdropPadding);\n                    ctx.fillRect(-width / 2 - padding.left, -offset - tickFont.size / 2 - padding.top, width + padding.width, tickFont.size + padding.height);\n                }\n                renderText(ctx, tick.label, 0, -offset, tickFont, {\n                    color: optsAtIndex.color,\n                    strokeColor: optsAtIndex.textStrokeColor,\n                    strokeWidth: optsAtIndex.textStrokeWidth\n                });\n            });\n            ctx.restore();\n        }\n        drawTitle() {}\n    }\n\n    const INTERVALS = {\n        millisecond: {\n            common: true,\n            size: 1,\n            steps: 1000\n        },\n        second: {\n            common: true,\n            size: 1000,\n            steps: 60\n        },\n        minute: {\n            common: true,\n            size: 60000,\n            steps: 60\n        },\n        hour: {\n            common: true,\n            size: 3600000,\n            steps: 24\n        },\n        day: {\n            common: true,\n            size: 86400000,\n            steps: 30\n        },\n        week: {\n            common: false,\n            size: 604800000,\n            steps: 4\n        },\n        month: {\n            common: true,\n            size: 2.628e9,\n            steps: 12\n        },\n        quarter: {\n            common: false,\n            size: 7.884e9,\n            steps: 4\n        },\n        year: {\n            common: true,\n            size: 3.154e10\n        }\n    };\n    const UNITS =  /* #__PURE__ */ Object.keys(INTERVALS);\n    function sorter(a, b) {\n        return a - b;\n    }\n    function parse(scale, input) {\n        if (isNullOrUndef(input)) {\n            return null;\n        }\n        const adapter = scale._adapter;\n        const { parser , round , isoWeekday  } = scale._parseOpts;\n        let value = input;\n        if (typeof parser === 'function') {\n            value = parser(value);\n        }\n        if (!isNumberFinite(value)) {\n            value = typeof parser === 'string' ? adapter.parse(value,  parser) : adapter.parse(value);\n        }\n        if (value === null) {\n            return null;\n        }\n        if (round) {\n            value = round === 'week' && (isNumber(isoWeekday) || isoWeekday === true) ? adapter.startOf(value, 'isoWeek', isoWeekday) : adapter.startOf(value, round);\n        }\n        return +value;\n    }\n    function determineUnitForAutoTicks(minUnit, min, max, capacity) {\n        const ilen = UNITS.length;\n        for(let i = UNITS.indexOf(minUnit); i < ilen - 1; ++i){\n            const interval = INTERVALS[UNITS[i]];\n            const factor = interval.steps ? interval.steps : Number.MAX_SAFE_INTEGER;\n            if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) {\n                return UNITS[i];\n            }\n        }\n        return UNITS[ilen - 1];\n    }\n    function determineUnitForFormatting(scale, numTicks, minUnit, min, max) {\n        for(let i = UNITS.length - 1; i >= UNITS.indexOf(minUnit); i--){\n            const unit = UNITS[i];\n            if (INTERVALS[unit].common && scale._adapter.diff(max, min, unit) >= numTicks - 1) {\n                return unit;\n            }\n        }\n        return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0];\n    }\n    function determineMajorUnit(unit) {\n        for(let i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i){\n            if (INTERVALS[UNITS[i]].common) {\n                return UNITS[i];\n            }\n        }\n    }\n    function addTick(ticks, time, timestamps) {\n        if (!timestamps) {\n            ticks[time] = true;\n        } else if (timestamps.length) {\n            const { lo , hi  } = _lookup(timestamps, time);\n            const timestamp = timestamps[lo] >= time ? timestamps[lo] : timestamps[hi];\n            ticks[timestamp] = true;\n        }\n    }\n    function setMajorTicks(scale, ticks, map, majorUnit) {\n        const adapter = scale._adapter;\n        const first = +adapter.startOf(ticks[0].value, majorUnit);\n        const last = ticks[ticks.length - 1].value;\n        let major, index;\n        for(major = first; major <= last; major = +adapter.add(major, 1, majorUnit)){\n            index = map[major];\n            if (index >= 0) {\n                ticks[index].major = true;\n            }\n        }\n        return ticks;\n    }\n    function ticksFromTimestamps(scale, values, majorUnit) {\n        const ticks = [];\n        const map = {};\n        const ilen = values.length;\n        let i, value;\n        for(i = 0; i < ilen; ++i){\n            value = values[i];\n            map[value] = i;\n            ticks.push({\n                value,\n                major: false\n            });\n        }\n        return ilen === 0 || !majorUnit ? ticks : setMajorTicks(scale, ticks, map, majorUnit);\n    }\n    class TimeScale extends Scale {\n        static id = 'time';\n        static defaults = {\n            bounds: 'data',\n            adapters: {},\n            time: {\n                parser: false,\n                unit: false,\n                round: false,\n                isoWeekday: false,\n                minUnit: 'millisecond',\n                displayFormats: {}\n            },\n            ticks: {\n                source: 'auto',\n                callback: false,\n                major: {\n                    enabled: false\n                }\n            }\n        };\n        constructor(props){\n            super(props);\n            this._cache = {\n                data: [],\n                labels: [],\n                all: []\n            };\n            this._unit = 'day';\n            this._majorUnit = undefined;\n            this._offsets = {};\n            this._normalized = false;\n            this._parseOpts = undefined;\n        }\n        init(scaleOpts, opts = {}) {\n            const time = scaleOpts.time || (scaleOpts.time = {});\n            const adapter = this._adapter = new _adapters._date(scaleOpts.adapters.date);\n            adapter.init(opts);\n            mergeIf(time.displayFormats, adapter.formats());\n            this._parseOpts = {\n                parser: time.parser,\n                round: time.round,\n                isoWeekday: time.isoWeekday\n            };\n            super.init(scaleOpts);\n            this._normalized = opts.normalized;\n        }\n        parse(raw, index) {\n            if (raw === undefined) {\n                return null;\n            }\n            return parse(this, raw);\n        }\n        beforeLayout() {\n            super.beforeLayout();\n            this._cache = {\n                data: [],\n                labels: [],\n                all: []\n            };\n        }\n        determineDataLimits() {\n            const options = this.options;\n            const adapter = this._adapter;\n            const unit = options.time.unit || 'day';\n            let { min , max , minDefined , maxDefined  } = this.getUserBounds();\n            function _applyBounds(bounds) {\n                if (!minDefined && !isNaN(bounds.min)) {\n                    min = Math.min(min, bounds.min);\n                }\n                if (!maxDefined && !isNaN(bounds.max)) {\n                    max = Math.max(max, bounds.max);\n                }\n            }\n            if (!minDefined || !maxDefined) {\n                _applyBounds(this._getLabelBounds());\n                if (options.bounds !== 'ticks' || options.ticks.source !== 'labels') {\n                    _applyBounds(this.getMinMax(false));\n                }\n            }\n            min = isNumberFinite(min) && !isNaN(min) ? min : +adapter.startOf(Date.now(), unit);\n            max = isNumberFinite(max) && !isNaN(max) ? max : +adapter.endOf(Date.now(), unit) + 1;\n            this.min = Math.min(min, max - 1);\n            this.max = Math.max(min + 1, max);\n        }\n        _getLabelBounds() {\n            const arr = this.getLabelTimestamps();\n            let min = Number.POSITIVE_INFINITY;\n            let max = Number.NEGATIVE_INFINITY;\n            if (arr.length) {\n                min = arr[0];\n                max = arr[arr.length - 1];\n            }\n            return {\n                min,\n                max\n            };\n        }\n        buildTicks() {\n            const options = this.options;\n            const timeOpts = options.time;\n            const tickOpts = options.ticks;\n            const timestamps = tickOpts.source === 'labels' ? this.getLabelTimestamps() : this._generate();\n            if (options.bounds === 'ticks' && timestamps.length) {\n                this.min = this._userMin || timestamps[0];\n                this.max = this._userMax || timestamps[timestamps.length - 1];\n            }\n            const min = this.min;\n            const max = this.max;\n            const ticks = _filterBetween(timestamps, min, max);\n            this._unit = timeOpts.unit || (tickOpts.autoSkip ? determineUnitForAutoTicks(timeOpts.minUnit, this.min, this.max, this._getLabelCapacity(min)) : determineUnitForFormatting(this, ticks.length, timeOpts.minUnit, this.min, this.max));\n            this._majorUnit = !tickOpts.major.enabled || this._unit === 'year' ? undefined : determineMajorUnit(this._unit);\n            this.initOffsets(timestamps);\n            if (options.reverse) {\n                ticks.reverse();\n            }\n            return ticksFromTimestamps(this, ticks, this._majorUnit);\n        }\n        afterAutoSkip() {\n            if (this.options.offsetAfterAutoskip) {\n                this.initOffsets(this.ticks.map((tick)=>+tick.value));\n            }\n        }\n        initOffsets(timestamps = []) {\n            let start = 0;\n            let end = 0;\n            let first, last;\n            if (this.options.offset && timestamps.length) {\n                first = this.getDecimalForValue(timestamps[0]);\n                if (timestamps.length === 1) {\n                    start = 1 - first;\n                } else {\n                    start = (this.getDecimalForValue(timestamps[1]) - first) / 2;\n                }\n                last = this.getDecimalForValue(timestamps[timestamps.length - 1]);\n                if (timestamps.length === 1) {\n                    end = last;\n                } else {\n                    end = (last - this.getDecimalForValue(timestamps[timestamps.length - 2])) / 2;\n                }\n            }\n            const limit = timestamps.length < 3 ? 0.5 : 0.25;\n            start = _limitValue(start, 0, limit);\n            end = _limitValue(end, 0, limit);\n            this._offsets = {\n                start,\n                end,\n                factor: 1 / (start + 1 + end)\n            };\n        }\n        _generate() {\n            const adapter = this._adapter;\n            const min = this.min;\n            const max = this.max;\n            const options = this.options;\n            const timeOpts = options.time;\n            const minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, this._getLabelCapacity(min));\n            const stepSize = valueOrDefault(options.ticks.stepSize, 1);\n            const weekday = minor === 'week' ? timeOpts.isoWeekday : false;\n            const hasWeekday = isNumber(weekday) || weekday === true;\n            const ticks = {};\n            let first = min;\n            let time, count;\n            if (hasWeekday) {\n                first = +adapter.startOf(first, 'isoWeek', weekday);\n            }\n            first = +adapter.startOf(first, hasWeekday ? 'day' : minor);\n            if (adapter.diff(max, min, minor) > 100000 * stepSize) {\n                throw new Error(min + ' and ' + max + ' are too far apart with stepSize of ' + stepSize + ' ' + minor);\n            }\n            const timestamps = options.ticks.source === 'data' && this.getDataTimestamps();\n            for(time = first, count = 0; time < max; time = +adapter.add(time, stepSize, minor), count++){\n                addTick(ticks, time, timestamps);\n            }\n            if (time === max || options.bounds === 'ticks' || count === 1) {\n                addTick(ticks, time, timestamps);\n            }\n            return Object.keys(ticks).sort(sorter).map((x)=>+x);\n        }\n        getLabelForValue(value) {\n            const adapter = this._adapter;\n            const timeOpts = this.options.time;\n            if (timeOpts.tooltipFormat) {\n                return adapter.format(value, timeOpts.tooltipFormat);\n            }\n            return adapter.format(value, timeOpts.displayFormats.datetime);\n        }\n        format(value, format) {\n            const options = this.options;\n            const formats = options.time.displayFormats;\n            const unit = this._unit;\n            const fmt = format || formats[unit];\n            return this._adapter.format(value, fmt);\n        }\n        _tickFormatFunction(time, index, ticks, format) {\n            const options = this.options;\n            const formatter = options.ticks.callback;\n            if (formatter) {\n                return callback(formatter, [\n                    time,\n                    index,\n                    ticks\n                ], this);\n            }\n            const formats = options.time.displayFormats;\n            const unit = this._unit;\n            const majorUnit = this._majorUnit;\n            const minorFormat = unit && formats[unit];\n            const majorFormat = majorUnit && formats[majorUnit];\n            const tick = ticks[index];\n            const major = majorUnit && majorFormat && tick && tick.major;\n            return this._adapter.format(time, format || (major ? majorFormat : minorFormat));\n        }\n        generateTickLabels(ticks) {\n            let i, ilen, tick;\n            for(i = 0, ilen = ticks.length; i < ilen; ++i){\n                tick = ticks[i];\n                tick.label = this._tickFormatFunction(tick.value, i, ticks);\n            }\n        }\n        getDecimalForValue(value) {\n            return value === null ? NaN : (value - this.min) / (this.max - this.min);\n        }\n        getPixelForValue(value) {\n            const offsets = this._offsets;\n            const pos = this.getDecimalForValue(value);\n            return this.getPixelForDecimal((offsets.start + pos) * offsets.factor);\n        }\n        getValueForPixel(pixel) {\n            const offsets = this._offsets;\n            const pos = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end;\n            return this.min + pos * (this.max - this.min);\n        }\n        _getLabelSize(label) {\n            const ticksOpts = this.options.ticks;\n            const tickLabelWidth = this.ctx.measureText(label).width;\n            const angle = toRadians(this.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation);\n            const cosRotation = Math.cos(angle);\n            const sinRotation = Math.sin(angle);\n            const tickFontSize = this._resolveTickFontOptions(0).size;\n            return {\n                w: tickLabelWidth * cosRotation + tickFontSize * sinRotation,\n                h: tickLabelWidth * sinRotation + tickFontSize * cosRotation\n            };\n        }\n        _getLabelCapacity(exampleTime) {\n            const timeOpts = this.options.time;\n            const displayFormats = timeOpts.displayFormats;\n            const format = displayFormats[timeOpts.unit] || displayFormats.millisecond;\n            const exampleLabel = this._tickFormatFunction(exampleTime, 0, ticksFromTimestamps(this, [\n                exampleTime\n            ], this._majorUnit), format);\n            const size = this._getLabelSize(exampleLabel);\n            const capacity = Math.floor(this.isHorizontal() ? this.width / size.w : this.height / size.h) - 1;\n            return capacity > 0 ? capacity : 1;\n        }\n        getDataTimestamps() {\n            let timestamps = this._cache.data || [];\n            let i, ilen;\n            if (timestamps.length) {\n                return timestamps;\n            }\n            const metas = this.getMatchingVisibleMetas();\n            if (this._normalized && metas.length) {\n                return this._cache.data = metas[0].controller.getAllParsedValues(this);\n            }\n            for(i = 0, ilen = metas.length; i < ilen; ++i){\n                timestamps = timestamps.concat(metas[i].controller.getAllParsedValues(this));\n            }\n            return this._cache.data = this.normalize(timestamps);\n        }\n        getLabelTimestamps() {\n            const timestamps = this._cache.labels || [];\n            let i, ilen;\n            if (timestamps.length) {\n                return timestamps;\n            }\n            const labels = this.getLabels();\n            for(i = 0, ilen = labels.length; i < ilen; ++i){\n                timestamps.push(parse(this, labels[i]));\n            }\n            return this._cache.labels = this._normalized ? timestamps : this.normalize(timestamps);\n        }\n        normalize(values) {\n            return _arrayUnique(values.sort(sorter));\n        }\n    }\n\n    function interpolate(table, val, reverse) {\n        let lo = 0;\n        let hi = table.length - 1;\n        let prevSource, nextSource, prevTarget, nextTarget;\n        if (reverse) {\n            if (val >= table[lo].pos && val <= table[hi].pos) {\n                ({ lo , hi  } = _lookupByKey(table, 'pos', val));\n            }\n            ({ pos: prevSource , time: prevTarget  } = table[lo]);\n            ({ pos: nextSource , time: nextTarget  } = table[hi]);\n        } else {\n            if (val >= table[lo].time && val <= table[hi].time) {\n                ({ lo , hi  } = _lookupByKey(table, 'time', val));\n            }\n            ({ time: prevSource , pos: prevTarget  } = table[lo]);\n            ({ time: nextSource , pos: nextTarget  } = table[hi]);\n        }\n        const span = nextSource - prevSource;\n        return span ? prevTarget + (nextTarget - prevTarget) * (val - prevSource) / span : prevTarget;\n    }\n    class TimeSeriesScale extends TimeScale {\n        static id = 'timeseries';\n        static defaults = TimeScale.defaults;\n        constructor(props){\n            super(props);\n            this._table = [];\n            this._minPos = undefined;\n            this._tableRange = undefined;\n        }\n        initOffsets() {\n            const timestamps = this._getTimestampsForTable();\n            const table = this._table = this.buildLookupTable(timestamps);\n            this._minPos = interpolate(table, this.min);\n            this._tableRange = interpolate(table, this.max) - this._minPos;\n            super.initOffsets(timestamps);\n        }\n        buildLookupTable(timestamps) {\n            const { min , max  } = this;\n            const items = [];\n            const table = [];\n            let i, ilen, prev, curr, next;\n            for(i = 0, ilen = timestamps.length; i < ilen; ++i){\n                curr = timestamps[i];\n                if (curr >= min && curr <= max) {\n                    items.push(curr);\n                }\n            }\n            if (items.length < 2) {\n                return [\n                    {\n                        time: min,\n                        pos: 0\n                    },\n                    {\n                        time: max,\n                        pos: 1\n                    }\n                ];\n            }\n            for(i = 0, ilen = items.length; i < ilen; ++i){\n                next = items[i + 1];\n                prev = items[i - 1];\n                curr = items[i];\n                if (Math.round((next + prev) / 2) !== curr) {\n                    table.push({\n                        time: curr,\n                        pos: i / (ilen - 1)\n                    });\n                }\n            }\n            return table;\n        }\n        _generate() {\n            const min = this.min;\n            const max = this.max;\n            let timestamps = super.getDataTimestamps();\n            if (!timestamps.includes(min) || !timestamps.length) {\n                timestamps.splice(0, 0, min);\n            }\n            if (!timestamps.includes(max) || timestamps.length === 1) {\n                timestamps.push(max);\n            }\n            return timestamps.sort((a, b)=>a - b);\n        }\n        _getTimestampsForTable() {\n            let timestamps = this._cache.all || [];\n            if (timestamps.length) {\n                return timestamps;\n            }\n            const data = this.getDataTimestamps();\n            const label = this.getLabelTimestamps();\n            if (data.length && label.length) {\n                timestamps = this.normalize(data.concat(label));\n            } else {\n                timestamps = data.length ? data : label;\n            }\n            timestamps = this._cache.all = timestamps;\n            return timestamps;\n        }\n        getDecimalForValue(value) {\n            return (interpolate(this._table, value) - this._minPos) / this._tableRange;\n        }\n        getValueForPixel(pixel) {\n            const offsets = this._offsets;\n            const decimal = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end;\n            return interpolate(this._table, decimal * this._tableRange + this._minPos, true);\n        }\n    }\n\n    var scales = /*#__PURE__*/Object.freeze({\n        __proto__: null,\n        CategoryScale: CategoryScale,\n        LinearScale: LinearScale,\n        LogarithmicScale: LogarithmicScale,\n        RadialLinearScale: RadialLinearScale,\n        TimeScale: TimeScale,\n        TimeSeriesScale: TimeSeriesScale\n    });\n\n    const BORDER_COLORS = [\n        'rgb(54, 162, 235)',\n        'rgb(255, 99, 132)',\n        'rgb(255, 159, 64)',\n        'rgb(255, 205, 86)',\n        'rgb(75, 192, 192)',\n        'rgb(153, 102, 255)',\n        'rgb(201, 203, 207)' // grey\n    ];\n    // Border colors with 50% transparency\n    const BACKGROUND_COLORS = /* #__PURE__ */ BORDER_COLORS.map((color)=>color.replace('rgb(', 'rgba(').replace(')', ', 0.5)'));\n    function getBorderColor(i) {\n        return BORDER_COLORS[i % BORDER_COLORS.length];\n    }\n    function getBackgroundColor(i) {\n        return BACKGROUND_COLORS[i % BACKGROUND_COLORS.length];\n    }\n    function colorizeDefaultDataset(dataset, i) {\n        dataset.borderColor = getBorderColor(i);\n        dataset.backgroundColor = getBackgroundColor(i);\n        return ++i;\n    }\n    function colorizeDoughnutDataset(dataset, i) {\n        dataset.backgroundColor = dataset.data.map(()=>getBorderColor(i++));\n        return i;\n    }\n    function colorizePolarAreaDataset(dataset, i) {\n        dataset.backgroundColor = dataset.data.map(()=>getBackgroundColor(i++));\n        return i;\n    }\n    function getColorizer(chart) {\n        let i = 0;\n        return (dataset, datasetIndex)=>{\n            const controller = chart.getDatasetMeta(datasetIndex).controller;\n            if (controller instanceof DoughnutController) {\n                i = colorizeDoughnutDataset(dataset, i);\n            } else if (controller instanceof PolarAreaController) {\n                i = colorizePolarAreaDataset(dataset, i);\n            } else if (controller) {\n                i = colorizeDefaultDataset(dataset, i);\n            }\n        };\n    }\n    function containsColorsDefinitions(descriptors) {\n        let k;\n        for(k in descriptors){\n            if (descriptors[k].borderColor || descriptors[k].backgroundColor) {\n                return true;\n            }\n        }\n        return false;\n    }\n    function containsColorsDefinition(descriptor) {\n        return descriptor && (descriptor.borderColor || descriptor.backgroundColor);\n    }\n    var plugin_colors = {\n        id: 'colors',\n        defaults: {\n            enabled: true,\n            forceOverride: false\n        },\n        beforeLayout (chart, _args, options) {\n            if (!options.enabled) {\n                return;\n            }\n            const { data: { datasets  } , options: chartOptions  } = chart.config;\n            const { elements  } = chartOptions;\n            if (!options.forceOverride && (containsColorsDefinitions(datasets) || containsColorsDefinition(chartOptions) || elements && containsColorsDefinitions(elements))) {\n                return;\n            }\n            const colorizer = getColorizer(chart);\n            datasets.forEach(colorizer);\n        }\n    };\n\n    function lttbDecimation(data, start, count, availableWidth, options) {\n        const samples = options.samples || availableWidth;\n        if (samples >= count) {\n            return data.slice(start, start + count);\n        }\n        const decimated = [];\n        const bucketWidth = (count - 2) / (samples - 2);\n        let sampledIndex = 0;\n        const endIndex = start + count - 1;\n        let a = start;\n        let i, maxAreaPoint, maxArea, area, nextA;\n        decimated[sampledIndex++] = data[a];\n        for(i = 0; i < samples - 2; i++){\n            let avgX = 0;\n            let avgY = 0;\n            let j;\n            const avgRangeStart = Math.floor((i + 1) * bucketWidth) + 1 + start;\n            const avgRangeEnd = Math.min(Math.floor((i + 2) * bucketWidth) + 1, count) + start;\n            const avgRangeLength = avgRangeEnd - avgRangeStart;\n            for(j = avgRangeStart; j < avgRangeEnd; j++){\n                avgX += data[j].x;\n                avgY += data[j].y;\n            }\n            avgX /= avgRangeLength;\n            avgY /= avgRangeLength;\n            const rangeOffs = Math.floor(i * bucketWidth) + 1 + start;\n            const rangeTo = Math.min(Math.floor((i + 1) * bucketWidth) + 1, count) + start;\n            const { x: pointAx , y: pointAy  } = data[a];\n            maxArea = area = -1;\n            for(j = rangeOffs; j < rangeTo; j++){\n                area = 0.5 * Math.abs((pointAx - avgX) * (data[j].y - pointAy) - (pointAx - data[j].x) * (avgY - pointAy));\n                if (area > maxArea) {\n                    maxArea = area;\n                    maxAreaPoint = data[j];\n                    nextA = j;\n                }\n            }\n            decimated[sampledIndex++] = maxAreaPoint;\n            a = nextA;\n        }\n        decimated[sampledIndex++] = data[endIndex];\n        return decimated;\n    }\n    function minMaxDecimation(data, start, count, availableWidth) {\n        let avgX = 0;\n        let countX = 0;\n        let i, point, x, y, prevX, minIndex, maxIndex, startIndex, minY, maxY;\n        const decimated = [];\n        const endIndex = start + count - 1;\n        const xMin = data[start].x;\n        const xMax = data[endIndex].x;\n        const dx = xMax - xMin;\n        for(i = start; i < start + count; ++i){\n            point = data[i];\n            x = (point.x - xMin) / dx * availableWidth;\n            y = point.y;\n            const truncX = x | 0;\n            if (truncX === prevX) {\n                if (y < minY) {\n                    minY = y;\n                    minIndex = i;\n                } else if (y > maxY) {\n                    maxY = y;\n                    maxIndex = i;\n                }\n                avgX = (countX * avgX + point.x) / ++countX;\n            } else {\n                const lastIndex = i - 1;\n                if (!isNullOrUndef(minIndex) && !isNullOrUndef(maxIndex)) {\n                    const intermediateIndex1 = Math.min(minIndex, maxIndex);\n                    const intermediateIndex2 = Math.max(minIndex, maxIndex);\n                    if (intermediateIndex1 !== startIndex && intermediateIndex1 !== lastIndex) {\n                        decimated.push({\n                            ...data[intermediateIndex1],\n                            x: avgX\n                        });\n                    }\n                    if (intermediateIndex2 !== startIndex && intermediateIndex2 !== lastIndex) {\n                        decimated.push({\n                            ...data[intermediateIndex2],\n                            x: avgX\n                        });\n                    }\n                }\n                if (i > 0 && lastIndex !== startIndex) {\n                    decimated.push(data[lastIndex]);\n                }\n                decimated.push(point);\n                prevX = truncX;\n                countX = 0;\n                minY = maxY = y;\n                minIndex = maxIndex = startIndex = i;\n            }\n        }\n        return decimated;\n    }\n    function cleanDecimatedDataset(dataset) {\n        if (dataset._decimated) {\n            const data = dataset._data;\n            delete dataset._decimated;\n            delete dataset._data;\n            Object.defineProperty(dataset, 'data', {\n                configurable: true,\n                enumerable: true,\n                writable: true,\n                value: data\n            });\n        }\n    }\n    function cleanDecimatedData(chart) {\n        chart.data.datasets.forEach((dataset)=>{\n            cleanDecimatedDataset(dataset);\n        });\n    }\n    function getStartAndCountOfVisiblePointsSimplified(meta, points) {\n        const pointCount = points.length;\n        let start = 0;\n        let count;\n        const { iScale  } = meta;\n        const { min , max , minDefined , maxDefined  } = iScale.getUserBounds();\n        if (minDefined) {\n            start = _limitValue(_lookupByKey(points, iScale.axis, min).lo, 0, pointCount - 1);\n        }\n        if (maxDefined) {\n            count = _limitValue(_lookupByKey(points, iScale.axis, max).hi + 1, start, pointCount) - start;\n        } else {\n            count = pointCount - start;\n        }\n        return {\n            start,\n            count\n        };\n    }\n    var plugin_decimation = {\n        id: 'decimation',\n        defaults: {\n            algorithm: 'min-max',\n            enabled: false\n        },\n        beforeElementsUpdate: (chart, args, options)=>{\n            if (!options.enabled) {\n                cleanDecimatedData(chart);\n                return;\n            }\n            const availableWidth = chart.width;\n            chart.data.datasets.forEach((dataset, datasetIndex)=>{\n                const { _data , indexAxis  } = dataset;\n                const meta = chart.getDatasetMeta(datasetIndex);\n                const data = _data || dataset.data;\n                if (resolve([\n                    indexAxis,\n                    chart.options.indexAxis\n                ]) === 'y') {\n                    return;\n                }\n                if (!meta.controller.supportsDecimation) {\n                    return;\n                }\n                const xAxis = chart.scales[meta.xAxisID];\n                if (xAxis.type !== 'linear' && xAxis.type !== 'time') {\n                    return;\n                }\n                if (chart.options.parsing) {\n                    return;\n                }\n                let { start , count  } = getStartAndCountOfVisiblePointsSimplified(meta, data);\n                const threshold = options.threshold || 4 * availableWidth;\n                if (count <= threshold) {\n                    cleanDecimatedDataset(dataset);\n                    return;\n                }\n                if (isNullOrUndef(_data)) {\n                    dataset._data = data;\n                    delete dataset.data;\n                    Object.defineProperty(dataset, 'data', {\n                        configurable: true,\n                        enumerable: true,\n                        get: function() {\n                            return this._decimated;\n                        },\n                        set: function(d) {\n                            this._data = d;\n                        }\n                    });\n                }\n                let decimated;\n                switch(options.algorithm){\n                    case 'lttb':\n                        decimated = lttbDecimation(data, start, count, availableWidth, options);\n                        break;\n                    case 'min-max':\n                        decimated = minMaxDecimation(data, start, count, availableWidth);\n                        break;\n                    default:\n                        throw new Error(`Unsupported decimation algorithm '${options.algorithm}'`);\n                }\n                dataset._decimated = decimated;\n            });\n        },\n        destroy (chart) {\n            cleanDecimatedData(chart);\n        }\n    };\n\n    function _segments(line, target, property) {\n        const segments = line.segments;\n        const points = line.points;\n        const tpoints = target.points;\n        const parts = [];\n        for (const segment of segments){\n            let { start , end  } = segment;\n            end = _findSegmentEnd(start, end, points);\n            const bounds = _getBounds(property, points[start], points[end], segment.loop);\n            if (!target.segments) {\n                parts.push({\n                    source: segment,\n                    target: bounds,\n                    start: points[start],\n                    end: points[end]\n                });\n                continue;\n            }\n            const targetSegments = _boundSegments(target, bounds);\n            for (const tgt of targetSegments){\n                const subBounds = _getBounds(property, tpoints[tgt.start], tpoints[tgt.end], tgt.loop);\n                const fillSources = _boundSegment(segment, points, subBounds);\n                for (const fillSource of fillSources){\n                    parts.push({\n                        source: fillSource,\n                        target: tgt,\n                        start: {\n                            [property]: _getEdge(bounds, subBounds, 'start', Math.max)\n                        },\n                        end: {\n                            [property]: _getEdge(bounds, subBounds, 'end', Math.min)\n                        }\n                    });\n                }\n            }\n        }\n        return parts;\n    }\n    function _getBounds(property, first, last, loop) {\n        if (loop) {\n            return;\n        }\n        let start = first[property];\n        let end = last[property];\n        if (property === 'angle') {\n            start = _normalizeAngle(start);\n            end = _normalizeAngle(end);\n        }\n        return {\n            property,\n            start,\n            end\n        };\n    }\n    function _pointsFromSegments(boundary, line) {\n        const { x =null , y =null  } = boundary || {};\n        const linePoints = line.points;\n        const points = [];\n        line.segments.forEach(({ start , end  })=>{\n            end = _findSegmentEnd(start, end, linePoints);\n            const first = linePoints[start];\n            const last = linePoints[end];\n            if (y !== null) {\n                points.push({\n                    x: first.x,\n                    y\n                });\n                points.push({\n                    x: last.x,\n                    y\n                });\n            } else if (x !== null) {\n                points.push({\n                    x,\n                    y: first.y\n                });\n                points.push({\n                    x,\n                    y: last.y\n                });\n            }\n        });\n        return points;\n    }\n    function _findSegmentEnd(start, end, points) {\n        for(; end > start; end--){\n            const point = points[end];\n            if (!isNaN(point.x) && !isNaN(point.y)) {\n                break;\n            }\n        }\n        return end;\n    }\n    function _getEdge(a, b, prop, fn) {\n        if (a && b) {\n            return fn(a[prop], b[prop]);\n        }\n        return a ? a[prop] : b ? b[prop] : 0;\n    }\n\n    function _createBoundaryLine(boundary, line) {\n        let points = [];\n        let _loop = false;\n        if (isArray(boundary)) {\n            _loop = true;\n            points = boundary;\n        } else {\n            points = _pointsFromSegments(boundary, line);\n        }\n        return points.length ? new LineElement({\n            points,\n            options: {\n                tension: 0\n            },\n            _loop,\n            _fullLoop: _loop\n        }) : null;\n    }\n    function _shouldApplyFill(source) {\n        return source && source.fill !== false;\n    }\n\n    function _resolveTarget(sources, index, propagate) {\n        const source = sources[index];\n        let fill = source.fill;\n        const visited = [\n            index\n        ];\n        let target;\n        if (!propagate) {\n            return fill;\n        }\n        while(fill !== false && visited.indexOf(fill) === -1){\n            if (!isNumberFinite(fill)) {\n                return fill;\n            }\n            target = sources[fill];\n            if (!target) {\n                return false;\n            }\n            if (target.visible) {\n                return fill;\n            }\n            visited.push(fill);\n            fill = target.fill;\n        }\n        return false;\n    }\n    function _decodeFill(line, index, count) {\n        const fill = parseFillOption(line);\n        if (isObject(fill)) {\n            return isNaN(fill.value) ? false : fill;\n        }\n        let target = parseFloat(fill);\n        if (isNumberFinite(target) && Math.floor(target) === target) {\n            return decodeTargetIndex(fill[0], index, target, count);\n        }\n        return [\n            'origin',\n            'start',\n            'end',\n            'stack',\n            'shape'\n        ].indexOf(fill) >= 0 && fill;\n    }\n    function decodeTargetIndex(firstCh, index, target, count) {\n        if (firstCh === '-' || firstCh === '+') {\n            target = index + target;\n        }\n        if (target === index || target < 0 || target >= count) {\n            return false;\n        }\n        return target;\n    }\n    function _getTargetPixel(fill, scale) {\n        let pixel = null;\n        if (fill === 'start') {\n            pixel = scale.bottom;\n        } else if (fill === 'end') {\n            pixel = scale.top;\n        } else if (isObject(fill)) {\n            pixel = scale.getPixelForValue(fill.value);\n        } else if (scale.getBasePixel) {\n            pixel = scale.getBasePixel();\n        }\n        return pixel;\n    }\n    function _getTargetValue(fill, scale, startValue) {\n        let value;\n        if (fill === 'start') {\n            value = startValue;\n        } else if (fill === 'end') {\n            value = scale.options.reverse ? scale.min : scale.max;\n        } else if (isObject(fill)) {\n            value = fill.value;\n        } else {\n            value = scale.getBaseValue();\n        }\n        return value;\n    }\n    function parseFillOption(line) {\n        const options = line.options;\n        const fillOption = options.fill;\n        let fill = valueOrDefault(fillOption && fillOption.target, fillOption);\n        if (fill === undefined) {\n            fill = !!options.backgroundColor;\n        }\n        if (fill === false || fill === null) {\n            return false;\n        }\n        if (fill === true) {\n            return 'origin';\n        }\n        return fill;\n    }\n\n    function _buildStackLine(source) {\n        const { scale , index , line  } = source;\n        const points = [];\n        const segments = line.segments;\n        const sourcePoints = line.points;\n        const linesBelow = getLinesBelow(scale, index);\n        linesBelow.push(_createBoundaryLine({\n            x: null,\n            y: scale.bottom\n        }, line));\n        for(let i = 0; i < segments.length; i++){\n            const segment = segments[i];\n            for(let j = segment.start; j <= segment.end; j++){\n                addPointsBelow(points, sourcePoints[j], linesBelow);\n            }\n        }\n        return new LineElement({\n            points,\n            options: {}\n        });\n    }\n    function getLinesBelow(scale, index) {\n        const below = [];\n        const metas = scale.getMatchingVisibleMetas('line');\n        for(let i = 0; i < metas.length; i++){\n            const meta = metas[i];\n            if (meta.index === index) {\n                break;\n            }\n            if (!meta.hidden) {\n                below.unshift(meta.dataset);\n            }\n        }\n        return below;\n    }\n    function addPointsBelow(points, sourcePoint, linesBelow) {\n        const postponed = [];\n        for(let j = 0; j < linesBelow.length; j++){\n            const line = linesBelow[j];\n            const { first , last , point  } = findPoint(line, sourcePoint, 'x');\n            if (!point || first && last) {\n                continue;\n            }\n            if (first) {\n                postponed.unshift(point);\n            } else {\n                points.push(point);\n                if (!last) {\n                    break;\n                }\n            }\n        }\n        points.push(...postponed);\n    }\n    function findPoint(line, sourcePoint, property) {\n        const point = line.interpolate(sourcePoint, property);\n        if (!point) {\n            return {};\n        }\n        const pointValue = point[property];\n        const segments = line.segments;\n        const linePoints = line.points;\n        let first = false;\n        let last = false;\n        for(let i = 0; i < segments.length; i++){\n            const segment = segments[i];\n            const firstValue = linePoints[segment.start][property];\n            const lastValue = linePoints[segment.end][property];\n            if (_isBetween(pointValue, firstValue, lastValue)) {\n                first = pointValue === firstValue;\n                last = pointValue === lastValue;\n                break;\n            }\n        }\n        return {\n            first,\n            last,\n            point\n        };\n    }\n\n    class simpleArc {\n        constructor(opts){\n            this.x = opts.x;\n            this.y = opts.y;\n            this.radius = opts.radius;\n        }\n        pathSegment(ctx, bounds, opts) {\n            const { x , y , radius  } = this;\n            bounds = bounds || {\n                start: 0,\n                end: TAU\n            };\n            ctx.arc(x, y, radius, bounds.end, bounds.start, true);\n            return !opts.bounds;\n        }\n        interpolate(point) {\n            const { x , y , radius  } = this;\n            const angle = point.angle;\n            return {\n                x: x + Math.cos(angle) * radius,\n                y: y + Math.sin(angle) * radius,\n                angle\n            };\n        }\n    }\n\n    function _getTarget(source) {\n        const { chart , fill , line  } = source;\n        if (isNumberFinite(fill)) {\n            return getLineByIndex(chart, fill);\n        }\n        if (fill === 'stack') {\n            return _buildStackLine(source);\n        }\n        if (fill === 'shape') {\n            return true;\n        }\n        const boundary = computeBoundary(source);\n        if (boundary instanceof simpleArc) {\n            return boundary;\n        }\n        return _createBoundaryLine(boundary, line);\n    }\n    function getLineByIndex(chart, index) {\n        const meta = chart.getDatasetMeta(index);\n        const visible = meta && chart.isDatasetVisible(index);\n        return visible ? meta.dataset : null;\n    }\n    function computeBoundary(source) {\n        const scale = source.scale || {};\n        if (scale.getPointPositionForValue) {\n            return computeCircularBoundary(source);\n        }\n        return computeLinearBoundary(source);\n    }\n    function computeLinearBoundary(source) {\n        const { scale ={} , fill  } = source;\n        const pixel = _getTargetPixel(fill, scale);\n        if (isNumberFinite(pixel)) {\n            const horizontal = scale.isHorizontal();\n            return {\n                x: horizontal ? pixel : null,\n                y: horizontal ? null : pixel\n            };\n        }\n        return null;\n    }\n    function computeCircularBoundary(source) {\n        const { scale , fill  } = source;\n        const options = scale.options;\n        const length = scale.getLabels().length;\n        const start = options.reverse ? scale.max : scale.min;\n        const value = _getTargetValue(fill, scale, start);\n        const target = [];\n        if (options.grid.circular) {\n            const center = scale.getPointPositionForValue(0, start);\n            return new simpleArc({\n                x: center.x,\n                y: center.y,\n                radius: scale.getDistanceFromCenterForValue(value)\n            });\n        }\n        for(let i = 0; i < length; ++i){\n            target.push(scale.getPointPositionForValue(i, value));\n        }\n        return target;\n    }\n\n    function _drawfill(ctx, source, area) {\n        const target = _getTarget(source);\n        const { line , scale , axis  } = source;\n        const lineOpts = line.options;\n        const fillOption = lineOpts.fill;\n        const color = lineOpts.backgroundColor;\n        const { above =color , below =color  } = fillOption || {};\n        if (target && line.points.length) {\n            clipArea(ctx, area);\n            doFill(ctx, {\n                line,\n                target,\n                above,\n                below,\n                area,\n                scale,\n                axis\n            });\n            unclipArea(ctx);\n        }\n    }\n    function doFill(ctx, cfg) {\n        const { line , target , above , below , area , scale  } = cfg;\n        const property = line._loop ? 'angle' : cfg.axis;\n        ctx.save();\n        if (property === 'x' && below !== above) {\n            clipVertical(ctx, target, area.top);\n            fill(ctx, {\n                line,\n                target,\n                color: above,\n                scale,\n                property\n            });\n            ctx.restore();\n            ctx.save();\n            clipVertical(ctx, target, area.bottom);\n        }\n        fill(ctx, {\n            line,\n            target,\n            color: below,\n            scale,\n            property\n        });\n        ctx.restore();\n    }\n    function clipVertical(ctx, target, clipY) {\n        const { segments , points  } = target;\n        let first = true;\n        let lineLoop = false;\n        ctx.beginPath();\n        for (const segment of segments){\n            const { start , end  } = segment;\n            const firstPoint = points[start];\n            const lastPoint = points[_findSegmentEnd(start, end, points)];\n            if (first) {\n                ctx.moveTo(firstPoint.x, firstPoint.y);\n                first = false;\n            } else {\n                ctx.lineTo(firstPoint.x, clipY);\n                ctx.lineTo(firstPoint.x, firstPoint.y);\n            }\n            lineLoop = !!target.pathSegment(ctx, segment, {\n                move: lineLoop\n            });\n            if (lineLoop) {\n                ctx.closePath();\n            } else {\n                ctx.lineTo(lastPoint.x, clipY);\n            }\n        }\n        ctx.lineTo(target.first().x, clipY);\n        ctx.closePath();\n        ctx.clip();\n    }\n    function fill(ctx, cfg) {\n        const { line , target , property , color , scale  } = cfg;\n        const segments = _segments(line, target, property);\n        for (const { source: src , target: tgt , start , end  } of segments){\n            const { style: { backgroundColor =color  } = {}  } = src;\n            const notShape = target !== true;\n            ctx.save();\n            ctx.fillStyle = backgroundColor;\n            clipBounds(ctx, scale, notShape && _getBounds(property, start, end));\n            ctx.beginPath();\n            const lineLoop = !!line.pathSegment(ctx, src);\n            let loop;\n            if (notShape) {\n                if (lineLoop) {\n                    ctx.closePath();\n                } else {\n                    interpolatedLineTo(ctx, target, end, property);\n                }\n                const targetLoop = !!target.pathSegment(ctx, tgt, {\n                    move: lineLoop,\n                    reverse: true\n                });\n                loop = lineLoop && targetLoop;\n                if (!loop) {\n                    interpolatedLineTo(ctx, target, start, property);\n                }\n            }\n            ctx.closePath();\n            ctx.fill(loop ? 'evenodd' : 'nonzero');\n            ctx.restore();\n        }\n    }\n    function clipBounds(ctx, scale, bounds) {\n        const { top , bottom  } = scale.chart.chartArea;\n        const { property , start , end  } = bounds || {};\n        if (property === 'x') {\n            ctx.beginPath();\n            ctx.rect(start, top, end - start, bottom - top);\n            ctx.clip();\n        }\n    }\n    function interpolatedLineTo(ctx, target, point, property) {\n        const interpolatedPoint = target.interpolate(point, property);\n        if (interpolatedPoint) {\n            ctx.lineTo(interpolatedPoint.x, interpolatedPoint.y);\n        }\n    }\n\n    var index = {\n        id: 'filler',\n        afterDatasetsUpdate (chart, _args, options) {\n            const count = (chart.data.datasets || []).length;\n            const sources = [];\n            let meta, i, line, source;\n            for(i = 0; i < count; ++i){\n                meta = chart.getDatasetMeta(i);\n                line = meta.dataset;\n                source = null;\n                if (line && line.options && line instanceof LineElement) {\n                    source = {\n                        visible: chart.isDatasetVisible(i),\n                        index: i,\n                        fill: _decodeFill(line, i, count),\n                        chart,\n                        axis: meta.controller.options.indexAxis,\n                        scale: meta.vScale,\n                        line\n                    };\n                }\n                meta.$filler = source;\n                sources.push(source);\n            }\n            for(i = 0; i < count; ++i){\n                source = sources[i];\n                if (!source || source.fill === false) {\n                    continue;\n                }\n                source.fill = _resolveTarget(sources, i, options.propagate);\n            }\n        },\n        beforeDraw (chart, _args, options) {\n            const draw = options.drawTime === 'beforeDraw';\n            const metasets = chart.getSortedVisibleDatasetMetas();\n            const area = chart.chartArea;\n            for(let i = metasets.length - 1; i >= 0; --i){\n                const source = metasets[i].$filler;\n                if (!source) {\n                    continue;\n                }\n                source.line.updateControlPoints(area, source.axis);\n                if (draw && source.fill) {\n                    _drawfill(chart.ctx, source, area);\n                }\n            }\n        },\n        beforeDatasetsDraw (chart, _args, options) {\n            if (options.drawTime !== 'beforeDatasetsDraw') {\n                return;\n            }\n            const metasets = chart.getSortedVisibleDatasetMetas();\n            for(let i = metasets.length - 1; i >= 0; --i){\n                const source = metasets[i].$filler;\n                if (_shouldApplyFill(source)) {\n                    _drawfill(chart.ctx, source, chart.chartArea);\n                }\n            }\n        },\n        beforeDatasetDraw (chart, args, options) {\n            const source = args.meta.$filler;\n            if (!_shouldApplyFill(source) || options.drawTime !== 'beforeDatasetDraw') {\n                return;\n            }\n            _drawfill(chart.ctx, source, chart.chartArea);\n        },\n        defaults: {\n            propagate: true,\n            drawTime: 'beforeDatasetDraw'\n        }\n    };\n\n    const getBoxSize = (labelOpts, fontSize)=>{\n        let { boxHeight =fontSize , boxWidth =fontSize  } = labelOpts;\n        if (labelOpts.usePointStyle) {\n            boxHeight = Math.min(boxHeight, fontSize);\n            boxWidth = labelOpts.pointStyleWidth || Math.min(boxWidth, fontSize);\n        }\n        return {\n            boxWidth,\n            boxHeight,\n            itemHeight: Math.max(fontSize, boxHeight)\n        };\n    };\n    const itemsEqual = (a, b)=>a !== null && b !== null && a.datasetIndex === b.datasetIndex && a.index === b.index;\n    class Legend extends Element {\n        constructor(config){\n            super();\n            this._added = false;\n            this.legendHitBoxes = [];\n            this._hoveredItem = null;\n            this.doughnutMode = false;\n            this.chart = config.chart;\n            this.options = config.options;\n            this.ctx = config.ctx;\n            this.legendItems = undefined;\n            this.columnSizes = undefined;\n            this.lineWidths = undefined;\n            this.maxHeight = undefined;\n            this.maxWidth = undefined;\n            this.top = undefined;\n            this.bottom = undefined;\n            this.left = undefined;\n            this.right = undefined;\n            this.height = undefined;\n            this.width = undefined;\n            this._margins = undefined;\n            this.position = undefined;\n            this.weight = undefined;\n            this.fullSize = undefined;\n        }\n        update(maxWidth, maxHeight, margins) {\n            this.maxWidth = maxWidth;\n            this.maxHeight = maxHeight;\n            this._margins = margins;\n            this.setDimensions();\n            this.buildLabels();\n            this.fit();\n        }\n        setDimensions() {\n            if (this.isHorizontal()) {\n                this.width = this.maxWidth;\n                this.left = this._margins.left;\n                this.right = this.width;\n            } else {\n                this.height = this.maxHeight;\n                this.top = this._margins.top;\n                this.bottom = this.height;\n            }\n        }\n        buildLabels() {\n            const labelOpts = this.options.labels || {};\n            let legendItems = callback(labelOpts.generateLabels, [\n                this.chart\n            ], this) || [];\n            if (labelOpts.filter) {\n                legendItems = legendItems.filter((item)=>labelOpts.filter(item, this.chart.data));\n            }\n            if (labelOpts.sort) {\n                legendItems = legendItems.sort((a, b)=>labelOpts.sort(a, b, this.chart.data));\n            }\n            if (this.options.reverse) {\n                legendItems.reverse();\n            }\n            this.legendItems = legendItems;\n        }\n        fit() {\n            const { options , ctx  } = this;\n            if (!options.display) {\n                this.width = this.height = 0;\n                return;\n            }\n            const labelOpts = options.labels;\n            const labelFont = toFont(labelOpts.font);\n            const fontSize = labelFont.size;\n            const titleHeight = this._computeTitleHeight();\n            const { boxWidth , itemHeight  } = getBoxSize(labelOpts, fontSize);\n            let width, height;\n            ctx.font = labelFont.string;\n            if (this.isHorizontal()) {\n                width = this.maxWidth;\n                height = this._fitRows(titleHeight, fontSize, boxWidth, itemHeight) + 10;\n            } else {\n                height = this.maxHeight;\n                width = this._fitCols(titleHeight, labelFont, boxWidth, itemHeight) + 10;\n            }\n            this.width = Math.min(width, options.maxWidth || this.maxWidth);\n            this.height = Math.min(height, options.maxHeight || this.maxHeight);\n        }\n        _fitRows(titleHeight, fontSize, boxWidth, itemHeight) {\n            const { ctx , maxWidth , options: { labels: { padding  }  }  } = this;\n            const hitboxes = this.legendHitBoxes = [];\n            const lineWidths = this.lineWidths = [\n                0\n            ];\n            const lineHeight = itemHeight + padding;\n            let totalHeight = titleHeight;\n            ctx.textAlign = 'left';\n            ctx.textBaseline = 'middle';\n            let row = -1;\n            let top = -lineHeight;\n            this.legendItems.forEach((legendItem, i)=>{\n                const itemWidth = boxWidth + fontSize / 2 + ctx.measureText(legendItem.text).width;\n                if (i === 0 || lineWidths[lineWidths.length - 1] + itemWidth + 2 * padding > maxWidth) {\n                    totalHeight += lineHeight;\n                    lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = 0;\n                    top += lineHeight;\n                    row++;\n                }\n                hitboxes[i] = {\n                    left: 0,\n                    top,\n                    row,\n                    width: itemWidth,\n                    height: itemHeight\n                };\n                lineWidths[lineWidths.length - 1] += itemWidth + padding;\n            });\n            return totalHeight;\n        }\n        _fitCols(titleHeight, labelFont, boxWidth, _itemHeight) {\n            const { ctx , maxHeight , options: { labels: { padding  }  }  } = this;\n            const hitboxes = this.legendHitBoxes = [];\n            const columnSizes = this.columnSizes = [];\n            const heightLimit = maxHeight - titleHeight;\n            let totalWidth = padding;\n            let currentColWidth = 0;\n            let currentColHeight = 0;\n            let left = 0;\n            let col = 0;\n            this.legendItems.forEach((legendItem, i)=>{\n                const { itemWidth , itemHeight  } = calculateItemSize(boxWidth, labelFont, ctx, legendItem, _itemHeight);\n                if (i > 0 && currentColHeight + itemHeight + 2 * padding > heightLimit) {\n                    totalWidth += currentColWidth + padding;\n                    columnSizes.push({\n                        width: currentColWidth,\n                        height: currentColHeight\n                    });\n                    left += currentColWidth + padding;\n                    col++;\n                    currentColWidth = currentColHeight = 0;\n                }\n                hitboxes[i] = {\n                    left,\n                    top: currentColHeight,\n                    col,\n                    width: itemWidth,\n                    height: itemHeight\n                };\n                currentColWidth = Math.max(currentColWidth, itemWidth);\n                currentColHeight += itemHeight + padding;\n            });\n            totalWidth += currentColWidth;\n            columnSizes.push({\n                width: currentColWidth,\n                height: currentColHeight\n            });\n            return totalWidth;\n        }\n        adjustHitBoxes() {\n            if (!this.options.display) {\n                return;\n            }\n            const titleHeight = this._computeTitleHeight();\n            const { legendHitBoxes: hitboxes , options: { align , labels: { padding  } , rtl  }  } = this;\n            const rtlHelper = getRtlAdapter(rtl, this.left, this.width);\n            if (this.isHorizontal()) {\n                let row = 0;\n                let left = _alignStartEnd(align, this.left + padding, this.right - this.lineWidths[row]);\n                for (const hitbox of hitboxes){\n                    if (row !== hitbox.row) {\n                        row = hitbox.row;\n                        left = _alignStartEnd(align, this.left + padding, this.right - this.lineWidths[row]);\n                    }\n                    hitbox.top += this.top + titleHeight + padding;\n                    hitbox.left = rtlHelper.leftForLtr(rtlHelper.x(left), hitbox.width);\n                    left += hitbox.width + padding;\n                }\n            } else {\n                let col = 0;\n                let top = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - this.columnSizes[col].height);\n                for (const hitbox of hitboxes){\n                    if (hitbox.col !== col) {\n                        col = hitbox.col;\n                        top = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - this.columnSizes[col].height);\n                    }\n                    hitbox.top = top;\n                    hitbox.left += this.left + padding;\n                    hitbox.left = rtlHelper.leftForLtr(rtlHelper.x(hitbox.left), hitbox.width);\n                    top += hitbox.height + padding;\n                }\n            }\n        }\n        isHorizontal() {\n            return this.options.position === 'top' || this.options.position === 'bottom';\n        }\n        draw() {\n            if (this.options.display) {\n                const ctx = this.ctx;\n                clipArea(ctx, this);\n                this._draw();\n                unclipArea(ctx);\n            }\n        }\n        _draw() {\n            const { options: opts , columnSizes , lineWidths , ctx  } = this;\n            const { align , labels: labelOpts  } = opts;\n            const defaultColor = defaults.color;\n            const rtlHelper = getRtlAdapter(opts.rtl, this.left, this.width);\n            const labelFont = toFont(labelOpts.font);\n            const { padding  } = labelOpts;\n            const fontSize = labelFont.size;\n            const halfFontSize = fontSize / 2;\n            let cursor;\n            this.drawTitle();\n            ctx.textAlign = rtlHelper.textAlign('left');\n            ctx.textBaseline = 'middle';\n            ctx.lineWidth = 0.5;\n            ctx.font = labelFont.string;\n            const { boxWidth , boxHeight , itemHeight  } = getBoxSize(labelOpts, fontSize);\n            const drawLegendBox = function(x, y, legendItem) {\n                if (isNaN(boxWidth) || boxWidth <= 0 || isNaN(boxHeight) || boxHeight < 0) {\n                    return;\n                }\n                ctx.save();\n                const lineWidth = valueOrDefault(legendItem.lineWidth, 1);\n                ctx.fillStyle = valueOrDefault(legendItem.fillStyle, defaultColor);\n                ctx.lineCap = valueOrDefault(legendItem.lineCap, 'butt');\n                ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, 0);\n                ctx.lineJoin = valueOrDefault(legendItem.lineJoin, 'miter');\n                ctx.lineWidth = lineWidth;\n                ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, defaultColor);\n                ctx.setLineDash(valueOrDefault(legendItem.lineDash, []));\n                if (labelOpts.usePointStyle) {\n                    const drawOptions = {\n                        radius: boxHeight * Math.SQRT2 / 2,\n                        pointStyle: legendItem.pointStyle,\n                        rotation: legendItem.rotation,\n                        borderWidth: lineWidth\n                    };\n                    const centerX = rtlHelper.xPlus(x, boxWidth / 2);\n                    const centerY = y + halfFontSize;\n                    drawPointLegend(ctx, drawOptions, centerX, centerY, labelOpts.pointStyleWidth && boxWidth);\n                } else {\n                    const yBoxTop = y + Math.max((fontSize - boxHeight) / 2, 0);\n                    const xBoxLeft = rtlHelper.leftForLtr(x, boxWidth);\n                    const borderRadius = toTRBLCorners(legendItem.borderRadius);\n                    ctx.beginPath();\n                    if (Object.values(borderRadius).some((v)=>v !== 0)) {\n                        addRoundedRectPath(ctx, {\n                            x: xBoxLeft,\n                            y: yBoxTop,\n                            w: boxWidth,\n                            h: boxHeight,\n                            radius: borderRadius\n                        });\n                    } else {\n                        ctx.rect(xBoxLeft, yBoxTop, boxWidth, boxHeight);\n                    }\n                    ctx.fill();\n                    if (lineWidth !== 0) {\n                        ctx.stroke();\n                    }\n                }\n                ctx.restore();\n            };\n            const fillText = function(x, y, legendItem) {\n                renderText(ctx, legendItem.text, x, y + itemHeight / 2, labelFont, {\n                    strikethrough: legendItem.hidden,\n                    textAlign: rtlHelper.textAlign(legendItem.textAlign)\n                });\n            };\n            const isHorizontal = this.isHorizontal();\n            const titleHeight = this._computeTitleHeight();\n            if (isHorizontal) {\n                cursor = {\n                    x: _alignStartEnd(align, this.left + padding, this.right - lineWidths[0]),\n                    y: this.top + padding + titleHeight,\n                    line: 0\n                };\n            } else {\n                cursor = {\n                    x: this.left + padding,\n                    y: _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - columnSizes[0].height),\n                    line: 0\n                };\n            }\n            overrideTextDirection(this.ctx, opts.textDirection);\n            const lineHeight = itemHeight + padding;\n            this.legendItems.forEach((legendItem, i)=>{\n                ctx.strokeStyle = legendItem.fontColor;\n                ctx.fillStyle = legendItem.fontColor;\n                const textWidth = ctx.measureText(legendItem.text).width;\n                const textAlign = rtlHelper.textAlign(legendItem.textAlign || (legendItem.textAlign = labelOpts.textAlign));\n                const width = boxWidth + halfFontSize + textWidth;\n                let x = cursor.x;\n                let y = cursor.y;\n                rtlHelper.setWidth(this.width);\n                if (isHorizontal) {\n                    if (i > 0 && x + width + padding > this.right) {\n                        y = cursor.y += lineHeight;\n                        cursor.line++;\n                        x = cursor.x = _alignStartEnd(align, this.left + padding, this.right - lineWidths[cursor.line]);\n                    }\n                } else if (i > 0 && y + lineHeight > this.bottom) {\n                    x = cursor.x = x + columnSizes[cursor.line].width + padding;\n                    cursor.line++;\n                    y = cursor.y = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - columnSizes[cursor.line].height);\n                }\n                const realX = rtlHelper.x(x);\n                drawLegendBox(realX, y, legendItem);\n                x = _textX(textAlign, x + boxWidth + halfFontSize, isHorizontal ? x + width : this.right, opts.rtl);\n                fillText(rtlHelper.x(x), y, legendItem);\n                if (isHorizontal) {\n                    cursor.x += width + padding;\n                } else if (typeof legendItem.text !== 'string') {\n                    const fontLineHeight = labelFont.lineHeight;\n                    cursor.y += calculateLegendItemHeight(legendItem, fontLineHeight) + padding;\n                } else {\n                    cursor.y += lineHeight;\n                }\n            });\n            restoreTextDirection(this.ctx, opts.textDirection);\n        }\n        drawTitle() {\n            const opts = this.options;\n            const titleOpts = opts.title;\n            const titleFont = toFont(titleOpts.font);\n            const titlePadding = toPadding(titleOpts.padding);\n            if (!titleOpts.display) {\n                return;\n            }\n            const rtlHelper = getRtlAdapter(opts.rtl, this.left, this.width);\n            const ctx = this.ctx;\n            const position = titleOpts.position;\n            const halfFontSize = titleFont.size / 2;\n            const topPaddingPlusHalfFontSize = titlePadding.top + halfFontSize;\n            let y;\n            let left = this.left;\n            let maxWidth = this.width;\n            if (this.isHorizontal()) {\n                maxWidth = Math.max(...this.lineWidths);\n                y = this.top + topPaddingPlusHalfFontSize;\n                left = _alignStartEnd(opts.align, left, this.right - maxWidth);\n            } else {\n                const maxHeight = this.columnSizes.reduce((acc, size)=>Math.max(acc, size.height), 0);\n                y = topPaddingPlusHalfFontSize + _alignStartEnd(opts.align, this.top, this.bottom - maxHeight - opts.labels.padding - this._computeTitleHeight());\n            }\n            const x = _alignStartEnd(position, left, left + maxWidth);\n            ctx.textAlign = rtlHelper.textAlign(_toLeftRightCenter(position));\n            ctx.textBaseline = 'middle';\n            ctx.strokeStyle = titleOpts.color;\n            ctx.fillStyle = titleOpts.color;\n            ctx.font = titleFont.string;\n            renderText(ctx, titleOpts.text, x, y, titleFont);\n        }\n        _computeTitleHeight() {\n            const titleOpts = this.options.title;\n            const titleFont = toFont(titleOpts.font);\n            const titlePadding = toPadding(titleOpts.padding);\n            return titleOpts.display ? titleFont.lineHeight + titlePadding.height : 0;\n        }\n        _getLegendItemAt(x, y) {\n            let i, hitBox, lh;\n            if (_isBetween(x, this.left, this.right) && _isBetween(y, this.top, this.bottom)) {\n                lh = this.legendHitBoxes;\n                for(i = 0; i < lh.length; ++i){\n                    hitBox = lh[i];\n                    if (_isBetween(x, hitBox.left, hitBox.left + hitBox.width) && _isBetween(y, hitBox.top, hitBox.top + hitBox.height)) {\n                        return this.legendItems[i];\n                    }\n                }\n            }\n            return null;\n        }\n        handleEvent(e) {\n            const opts = this.options;\n            if (!isListened(e.type, opts)) {\n                return;\n            }\n            const hoveredItem = this._getLegendItemAt(e.x, e.y);\n            if (e.type === 'mousemove' || e.type === 'mouseout') {\n                const previous = this._hoveredItem;\n                const sameItem = itemsEqual(previous, hoveredItem);\n                if (previous && !sameItem) {\n                    callback(opts.onLeave, [\n                        e,\n                        previous,\n                        this\n                    ], this);\n                }\n                this._hoveredItem = hoveredItem;\n                if (hoveredItem && !sameItem) {\n                    callback(opts.onHover, [\n                        e,\n                        hoveredItem,\n                        this\n                    ], this);\n                }\n            } else if (hoveredItem) {\n                callback(opts.onClick, [\n                    e,\n                    hoveredItem,\n                    this\n                ], this);\n            }\n        }\n    }\n    function calculateItemSize(boxWidth, labelFont, ctx, legendItem, _itemHeight) {\n        const itemWidth = calculateItemWidth(legendItem, boxWidth, labelFont, ctx);\n        const itemHeight = calculateItemHeight(_itemHeight, legendItem, labelFont.lineHeight);\n        return {\n            itemWidth,\n            itemHeight\n        };\n    }\n    function calculateItemWidth(legendItem, boxWidth, labelFont, ctx) {\n        let legendItemText = legendItem.text;\n        if (legendItemText && typeof legendItemText !== 'string') {\n            legendItemText = legendItemText.reduce((a, b)=>a.length > b.length ? a : b);\n        }\n        return boxWidth + labelFont.size / 2 + ctx.measureText(legendItemText).width;\n    }\n    function calculateItemHeight(_itemHeight, legendItem, fontLineHeight) {\n        let itemHeight = _itemHeight;\n        if (typeof legendItem.text !== 'string') {\n            itemHeight = calculateLegendItemHeight(legendItem, fontLineHeight);\n        }\n        return itemHeight;\n    }\n    function calculateLegendItemHeight(legendItem, fontLineHeight) {\n        const labelHeight = legendItem.text ? legendItem.text.length : 0;\n        return fontLineHeight * labelHeight;\n    }\n    function isListened(type, opts) {\n        if ((type === 'mousemove' || type === 'mouseout') && (opts.onHover || opts.onLeave)) {\n            return true;\n        }\n        if (opts.onClick && (type === 'click' || type === 'mouseup')) {\n            return true;\n        }\n        return false;\n    }\n    var plugin_legend = {\n        id: 'legend',\n        _element: Legend,\n        start (chart, _args, options) {\n            const legend = chart.legend = new Legend({\n                ctx: chart.ctx,\n                options,\n                chart\n            });\n            layouts.configure(chart, legend, options);\n            layouts.addBox(chart, legend);\n        },\n        stop (chart) {\n            layouts.removeBox(chart, chart.legend);\n            delete chart.legend;\n        },\n        beforeUpdate (chart, _args, options) {\n            const legend = chart.legend;\n            layouts.configure(chart, legend, options);\n            legend.options = options;\n        },\n        afterUpdate (chart) {\n            const legend = chart.legend;\n            legend.buildLabels();\n            legend.adjustHitBoxes();\n        },\n        afterEvent (chart, args) {\n            if (!args.replay) {\n                chart.legend.handleEvent(args.event);\n            }\n        },\n        defaults: {\n            display: true,\n            position: 'top',\n            align: 'center',\n            fullSize: true,\n            reverse: false,\n            weight: 1000,\n            onClick (e, legendItem, legend) {\n                const index = legendItem.datasetIndex;\n                const ci = legend.chart;\n                if (ci.isDatasetVisible(index)) {\n                    ci.hide(index);\n                    legendItem.hidden = true;\n                } else {\n                    ci.show(index);\n                    legendItem.hidden = false;\n                }\n            },\n            onHover: null,\n            onLeave: null,\n            labels: {\n                color: (ctx)=>ctx.chart.options.color,\n                boxWidth: 40,\n                padding: 10,\n                generateLabels (chart) {\n                    const datasets = chart.data.datasets;\n                    const { labels: { usePointStyle , pointStyle , textAlign , color , useBorderRadius , borderRadius  }  } = chart.legend.options;\n                    return chart._getSortedDatasetMetas().map((meta)=>{\n                        const style = meta.controller.getStyle(usePointStyle ? 0 : undefined);\n                        const borderWidth = toPadding(style.borderWidth);\n                        return {\n                            text: datasets[meta.index].label,\n                            fillStyle: style.backgroundColor,\n                            fontColor: color,\n                            hidden: !meta.visible,\n                            lineCap: style.borderCapStyle,\n                            lineDash: style.borderDash,\n                            lineDashOffset: style.borderDashOffset,\n                            lineJoin: style.borderJoinStyle,\n                            lineWidth: (borderWidth.width + borderWidth.height) / 4,\n                            strokeStyle: style.borderColor,\n                            pointStyle: pointStyle || style.pointStyle,\n                            rotation: style.rotation,\n                            textAlign: textAlign || style.textAlign,\n                            borderRadius: useBorderRadius && (borderRadius || style.borderRadius),\n                            datasetIndex: meta.index\n                        };\n                    }, this);\n                }\n            },\n            title: {\n                color: (ctx)=>ctx.chart.options.color,\n                display: false,\n                position: 'center',\n                text: ''\n            }\n        },\n        descriptors: {\n            _scriptable: (name)=>!name.startsWith('on'),\n            labels: {\n                _scriptable: (name)=>![\n                    'generateLabels',\n                    'filter',\n                    'sort'\n                ].includes(name)\n            }\n        }\n    };\n\n    class Title extends Element {\n        constructor(config){\n            super();\n            this.chart = config.chart;\n            this.options = config.options;\n            this.ctx = config.ctx;\n            this._padding = undefined;\n            this.top = undefined;\n            this.bottom = undefined;\n            this.left = undefined;\n            this.right = undefined;\n            this.width = undefined;\n            this.height = undefined;\n            this.position = undefined;\n            this.weight = undefined;\n            this.fullSize = undefined;\n        }\n        update(maxWidth, maxHeight) {\n            const opts = this.options;\n            this.left = 0;\n            this.top = 0;\n            if (!opts.display) {\n                this.width = this.height = this.right = this.bottom = 0;\n                return;\n            }\n            this.width = this.right = maxWidth;\n            this.height = this.bottom = maxHeight;\n            const lineCount = isArray(opts.text) ? opts.text.length : 1;\n            this._padding = toPadding(opts.padding);\n            const textSize = lineCount * toFont(opts.font).lineHeight + this._padding.height;\n            if (this.isHorizontal()) {\n                this.height = textSize;\n            } else {\n                this.width = textSize;\n            }\n        }\n        isHorizontal() {\n            const pos = this.options.position;\n            return pos === 'top' || pos === 'bottom';\n        }\n        _drawArgs(offset) {\n            const { top , left , bottom , right , options  } = this;\n            const align = options.align;\n            let rotation = 0;\n            let maxWidth, titleX, titleY;\n            if (this.isHorizontal()) {\n                titleX = _alignStartEnd(align, left, right);\n                titleY = top + offset;\n                maxWidth = right - left;\n            } else {\n                if (options.position === 'left') {\n                    titleX = left + offset;\n                    titleY = _alignStartEnd(align, bottom, top);\n                    rotation = PI * -0.5;\n                } else {\n                    titleX = right - offset;\n                    titleY = _alignStartEnd(align, top, bottom);\n                    rotation = PI * 0.5;\n                }\n                maxWidth = bottom - top;\n            }\n            return {\n                titleX,\n                titleY,\n                maxWidth,\n                rotation\n            };\n        }\n        draw() {\n            const ctx = this.ctx;\n            const opts = this.options;\n            if (!opts.display) {\n                return;\n            }\n            const fontOpts = toFont(opts.font);\n            const lineHeight = fontOpts.lineHeight;\n            const offset = lineHeight / 2 + this._padding.top;\n            const { titleX , titleY , maxWidth , rotation  } = this._drawArgs(offset);\n            renderText(ctx, opts.text, 0, 0, fontOpts, {\n                color: opts.color,\n                maxWidth,\n                rotation,\n                textAlign: _toLeftRightCenter(opts.align),\n                textBaseline: 'middle',\n                translation: [\n                    titleX,\n                    titleY\n                ]\n            });\n        }\n    }\n    function createTitle(chart, titleOpts) {\n        const title = new Title({\n            ctx: chart.ctx,\n            options: titleOpts,\n            chart\n        });\n        layouts.configure(chart, title, titleOpts);\n        layouts.addBox(chart, title);\n        chart.titleBlock = title;\n    }\n    var plugin_title = {\n        id: 'title',\n        _element: Title,\n        start (chart, _args, options) {\n            createTitle(chart, options);\n        },\n        stop (chart) {\n            const titleBlock = chart.titleBlock;\n            layouts.removeBox(chart, titleBlock);\n            delete chart.titleBlock;\n        },\n        beforeUpdate (chart, _args, options) {\n            const title = chart.titleBlock;\n            layouts.configure(chart, title, options);\n            title.options = options;\n        },\n        defaults: {\n            align: 'center',\n            display: false,\n            font: {\n                weight: 'bold'\n            },\n            fullSize: true,\n            padding: 10,\n            position: 'top',\n            text: '',\n            weight: 2000\n        },\n        defaultRoutes: {\n            color: 'color'\n        },\n        descriptors: {\n            _scriptable: true,\n            _indexable: false\n        }\n    };\n\n    const map = new WeakMap();\n    var plugin_subtitle = {\n        id: 'subtitle',\n        start (chart, _args, options) {\n            const title = new Title({\n                ctx: chart.ctx,\n                options,\n                chart\n            });\n            layouts.configure(chart, title, options);\n            layouts.addBox(chart, title);\n            map.set(chart, title);\n        },\n        stop (chart) {\n            layouts.removeBox(chart, map.get(chart));\n            map.delete(chart);\n        },\n        beforeUpdate (chart, _args, options) {\n            const title = map.get(chart);\n            layouts.configure(chart, title, options);\n            title.options = options;\n        },\n        defaults: {\n            align: 'center',\n            display: false,\n            font: {\n                weight: 'normal'\n            },\n            fullSize: true,\n            padding: 0,\n            position: 'top',\n            text: '',\n            weight: 1500\n        },\n        defaultRoutes: {\n            color: 'color'\n        },\n        descriptors: {\n            _scriptable: true,\n            _indexable: false\n        }\n    };\n\n    const positioners = {\n        average (items) {\n            if (!items.length) {\n                return false;\n            }\n            let i, len;\n            let x = 0;\n            let y = 0;\n            let count = 0;\n            for(i = 0, len = items.length; i < len; ++i){\n                const el = items[i].element;\n                if (el && el.hasValue()) {\n                    const pos = el.tooltipPosition();\n                    x += pos.x;\n                    y += pos.y;\n                    ++count;\n                }\n            }\n            return {\n                x: x / count,\n                y: y / count\n            };\n        },\n        nearest (items, eventPosition) {\n            if (!items.length) {\n                return false;\n            }\n            let x = eventPosition.x;\n            let y = eventPosition.y;\n            let minDistance = Number.POSITIVE_INFINITY;\n            let i, len, nearestElement;\n            for(i = 0, len = items.length; i < len; ++i){\n                const el = items[i].element;\n                if (el && el.hasValue()) {\n                    const center = el.getCenterPoint();\n                    const d = distanceBetweenPoints(eventPosition, center);\n                    if (d < minDistance) {\n                        minDistance = d;\n                        nearestElement = el;\n                    }\n                }\n            }\n            if (nearestElement) {\n                const tp = nearestElement.tooltipPosition();\n                x = tp.x;\n                y = tp.y;\n            }\n            return {\n                x,\n                y\n            };\n        }\n    };\n    function pushOrConcat(base, toPush) {\n        if (toPush) {\n            if (isArray(toPush)) {\n                Array.prototype.push.apply(base, toPush);\n            } else {\n                base.push(toPush);\n            }\n        }\n        return base;\n    }\n    function splitNewlines(str) {\n        if ((typeof str === 'string' || str instanceof String) && str.indexOf('\\n') > -1) {\n            return str.split('\\n');\n        }\n        return str;\n    }\n    function createTooltipItem(chart, item) {\n        const { element , datasetIndex , index  } = item;\n        const controller = chart.getDatasetMeta(datasetIndex).controller;\n        const { label , value  } = controller.getLabelAndValue(index);\n        return {\n            chart,\n            label,\n            parsed: controller.getParsed(index),\n            raw: chart.data.datasets[datasetIndex].data[index],\n            formattedValue: value,\n            dataset: controller.getDataset(),\n            dataIndex: index,\n            datasetIndex,\n            element\n        };\n    }\n    function getTooltipSize(tooltip, options) {\n        const ctx = tooltip.chart.ctx;\n        const { body , footer , title  } = tooltip;\n        const { boxWidth , boxHeight  } = options;\n        const bodyFont = toFont(options.bodyFont);\n        const titleFont = toFont(options.titleFont);\n        const footerFont = toFont(options.footerFont);\n        const titleLineCount = title.length;\n        const footerLineCount = footer.length;\n        const bodyLineItemCount = body.length;\n        const padding = toPadding(options.padding);\n        let height = padding.height;\n        let width = 0;\n        let combinedBodyLength = body.reduce((count, bodyItem)=>count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length, 0);\n        combinedBodyLength += tooltip.beforeBody.length + tooltip.afterBody.length;\n        if (titleLineCount) {\n            height += titleLineCount * titleFont.lineHeight + (titleLineCount - 1) * options.titleSpacing + options.titleMarginBottom;\n        }\n        if (combinedBodyLength) {\n            const bodyLineHeight = options.displayColors ? Math.max(boxHeight, bodyFont.lineHeight) : bodyFont.lineHeight;\n            height += bodyLineItemCount * bodyLineHeight + (combinedBodyLength - bodyLineItemCount) * bodyFont.lineHeight + (combinedBodyLength - 1) * options.bodySpacing;\n        }\n        if (footerLineCount) {\n            height += options.footerMarginTop + footerLineCount * footerFont.lineHeight + (footerLineCount - 1) * options.footerSpacing;\n        }\n        let widthPadding = 0;\n        const maxLineWidth = function(line) {\n            width = Math.max(width, ctx.measureText(line).width + widthPadding);\n        };\n        ctx.save();\n        ctx.font = titleFont.string;\n        each(tooltip.title, maxLineWidth);\n        ctx.font = bodyFont.string;\n        each(tooltip.beforeBody.concat(tooltip.afterBody), maxLineWidth);\n        widthPadding = options.displayColors ? boxWidth + 2 + options.boxPadding : 0;\n        each(body, (bodyItem)=>{\n            each(bodyItem.before, maxLineWidth);\n            each(bodyItem.lines, maxLineWidth);\n            each(bodyItem.after, maxLineWidth);\n        });\n        widthPadding = 0;\n        ctx.font = footerFont.string;\n        each(tooltip.footer, maxLineWidth);\n        ctx.restore();\n        width += padding.width;\n        return {\n            width,\n            height\n        };\n    }\n    function determineYAlign(chart, size) {\n        const { y , height  } = size;\n        if (y < height / 2) {\n            return 'top';\n        } else if (y > chart.height - height / 2) {\n            return 'bottom';\n        }\n        return 'center';\n    }\n    function doesNotFitWithAlign(xAlign, chart, options, size) {\n        const { x , width  } = size;\n        const caret = options.caretSize + options.caretPadding;\n        if (xAlign === 'left' && x + width + caret > chart.width) {\n            return true;\n        }\n        if (xAlign === 'right' && x - width - caret < 0) {\n            return true;\n        }\n    }\n    function determineXAlign(chart, options, size, yAlign) {\n        const { x , width  } = size;\n        const { width: chartWidth , chartArea: { left , right  }  } = chart;\n        let xAlign = 'center';\n        if (yAlign === 'center') {\n            xAlign = x <= (left + right) / 2 ? 'left' : 'right';\n        } else if (x <= width / 2) {\n            xAlign = 'left';\n        } else if (x >= chartWidth - width / 2) {\n            xAlign = 'right';\n        }\n        if (doesNotFitWithAlign(xAlign, chart, options, size)) {\n            xAlign = 'center';\n        }\n        return xAlign;\n    }\n    function determineAlignment(chart, options, size) {\n        const yAlign = size.yAlign || options.yAlign || determineYAlign(chart, size);\n        return {\n            xAlign: size.xAlign || options.xAlign || determineXAlign(chart, options, size, yAlign),\n            yAlign\n        };\n    }\n    function alignX(size, xAlign) {\n        let { x , width  } = size;\n        if (xAlign === 'right') {\n            x -= width;\n        } else if (xAlign === 'center') {\n            x -= width / 2;\n        }\n        return x;\n    }\n    function alignY(size, yAlign, paddingAndSize) {\n        let { y , height  } = size;\n        if (yAlign === 'top') {\n            y += paddingAndSize;\n        } else if (yAlign === 'bottom') {\n            y -= height + paddingAndSize;\n        } else {\n            y -= height / 2;\n        }\n        return y;\n    }\n    function getBackgroundPoint(options, size, alignment, chart) {\n        const { caretSize , caretPadding , cornerRadius  } = options;\n        const { xAlign , yAlign  } = alignment;\n        const paddingAndSize = caretSize + caretPadding;\n        const { topLeft , topRight , bottomLeft , bottomRight  } = toTRBLCorners(cornerRadius);\n        let x = alignX(size, xAlign);\n        const y = alignY(size, yAlign, paddingAndSize);\n        if (yAlign === 'center') {\n            if (xAlign === 'left') {\n                x += paddingAndSize;\n            } else if (xAlign === 'right') {\n                x -= paddingAndSize;\n            }\n        } else if (xAlign === 'left') {\n            x -= Math.max(topLeft, bottomLeft) + caretSize;\n        } else if (xAlign === 'right') {\n            x += Math.max(topRight, bottomRight) + caretSize;\n        }\n        return {\n            x: _limitValue(x, 0, chart.width - size.width),\n            y: _limitValue(y, 0, chart.height - size.height)\n        };\n    }\n    function getAlignedX(tooltip, align, options) {\n        const padding = toPadding(options.padding);\n        return align === 'center' ? tooltip.x + tooltip.width / 2 : align === 'right' ? tooltip.x + tooltip.width - padding.right : tooltip.x + padding.left;\n    }\n    function getBeforeAfterBodyLines(callback) {\n        return pushOrConcat([], splitNewlines(callback));\n    }\n    function createTooltipContext(parent, tooltip, tooltipItems) {\n        return createContext(parent, {\n            tooltip,\n            tooltipItems,\n            type: 'tooltip'\n        });\n    }\n    function overrideCallbacks(callbacks, context) {\n        const override = context && context.dataset && context.dataset.tooltip && context.dataset.tooltip.callbacks;\n        return override ? callbacks.override(override) : callbacks;\n    }\n    const defaultCallbacks = {\n        beforeTitle: noop,\n        title (tooltipItems) {\n            if (tooltipItems.length > 0) {\n                const item = tooltipItems[0];\n                const labels = item.chart.data.labels;\n                const labelCount = labels ? labels.length : 0;\n                if (this && this.options && this.options.mode === 'dataset') {\n                    return item.dataset.label || '';\n                } else if (item.label) {\n                    return item.label;\n                } else if (labelCount > 0 && item.dataIndex < labelCount) {\n                    return labels[item.dataIndex];\n                }\n            }\n            return '';\n        },\n        afterTitle: noop,\n        beforeBody: noop,\n        beforeLabel: noop,\n        label (tooltipItem) {\n            if (this && this.options && this.options.mode === 'dataset') {\n                return tooltipItem.label + ': ' + tooltipItem.formattedValue || tooltipItem.formattedValue;\n            }\n            let label = tooltipItem.dataset.label || '';\n            if (label) {\n                label += ': ';\n            }\n            const value = tooltipItem.formattedValue;\n            if (!isNullOrUndef(value)) {\n                label += value;\n            }\n            return label;\n        },\n        labelColor (tooltipItem) {\n            const meta = tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex);\n            const options = meta.controller.getStyle(tooltipItem.dataIndex);\n            return {\n                borderColor: options.borderColor,\n                backgroundColor: options.backgroundColor,\n                borderWidth: options.borderWidth,\n                borderDash: options.borderDash,\n                borderDashOffset: options.borderDashOffset,\n                borderRadius: 0\n            };\n        },\n        labelTextColor () {\n            return this.options.bodyColor;\n        },\n        labelPointStyle (tooltipItem) {\n            const meta = tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex);\n            const options = meta.controller.getStyle(tooltipItem.dataIndex);\n            return {\n                pointStyle: options.pointStyle,\n                rotation: options.rotation\n            };\n        },\n        afterLabel: noop,\n        afterBody: noop,\n        beforeFooter: noop,\n        footer: noop,\n        afterFooter: noop\n    };\n    function invokeCallbackWithFallback(callbacks, name, ctx, arg) {\n        const result = callbacks[name].call(ctx, arg);\n        if (typeof result === 'undefined') {\n            return defaultCallbacks[name].call(ctx, arg);\n        }\n        return result;\n    }\n    class Tooltip extends Element {\n        static positioners = positioners;\n        constructor(config){\n            super();\n            this.opacity = 0;\n            this._active = [];\n            this._eventPosition = undefined;\n            this._size = undefined;\n            this._cachedAnimations = undefined;\n            this._tooltipItems = [];\n            this.$animations = undefined;\n            this.$context = undefined;\n            this.chart = config.chart;\n            this.options = config.options;\n            this.dataPoints = undefined;\n            this.title = undefined;\n            this.beforeBody = undefined;\n            this.body = undefined;\n            this.afterBody = undefined;\n            this.footer = undefined;\n            this.xAlign = undefined;\n            this.yAlign = undefined;\n            this.x = undefined;\n            this.y = undefined;\n            this.height = undefined;\n            this.width = undefined;\n            this.caretX = undefined;\n            this.caretY = undefined;\n            this.labelColors = undefined;\n            this.labelPointStyles = undefined;\n            this.labelTextColors = undefined;\n        }\n        initialize(options) {\n            this.options = options;\n            this._cachedAnimations = undefined;\n            this.$context = undefined;\n        }\n        _resolveAnimations() {\n            const cached = this._cachedAnimations;\n            if (cached) {\n                return cached;\n            }\n            const chart = this.chart;\n            const options = this.options.setContext(this.getContext());\n            const opts = options.enabled && chart.options.animation && options.animations;\n            const animations = new Animations(this.chart, opts);\n            if (opts._cacheable) {\n                this._cachedAnimations = Object.freeze(animations);\n            }\n            return animations;\n        }\n        getContext() {\n            return this.$context || (this.$context = createTooltipContext(this.chart.getContext(), this, this._tooltipItems));\n        }\n        getTitle(context, options) {\n            const { callbacks  } = options;\n            const beforeTitle = invokeCallbackWithFallback(callbacks, 'beforeTitle', this, context);\n            const title = invokeCallbackWithFallback(callbacks, 'title', this, context);\n            const afterTitle = invokeCallbackWithFallback(callbacks, 'afterTitle', this, context);\n            let lines = [];\n            lines = pushOrConcat(lines, splitNewlines(beforeTitle));\n            lines = pushOrConcat(lines, splitNewlines(title));\n            lines = pushOrConcat(lines, splitNewlines(afterTitle));\n            return lines;\n        }\n        getBeforeBody(tooltipItems, options) {\n            return getBeforeAfterBodyLines(invokeCallbackWithFallback(options.callbacks, 'beforeBody', this, tooltipItems));\n        }\n        getBody(tooltipItems, options) {\n            const { callbacks  } = options;\n            const bodyItems = [];\n            each(tooltipItems, (context)=>{\n                const bodyItem = {\n                    before: [],\n                    lines: [],\n                    after: []\n                };\n                const scoped = overrideCallbacks(callbacks, context);\n                pushOrConcat(bodyItem.before, splitNewlines(invokeCallbackWithFallback(scoped, 'beforeLabel', this, context)));\n                pushOrConcat(bodyItem.lines, invokeCallbackWithFallback(scoped, 'label', this, context));\n                pushOrConcat(bodyItem.after, splitNewlines(invokeCallbackWithFallback(scoped, 'afterLabel', this, context)));\n                bodyItems.push(bodyItem);\n            });\n            return bodyItems;\n        }\n        getAfterBody(tooltipItems, options) {\n            return getBeforeAfterBodyLines(invokeCallbackWithFallback(options.callbacks, 'afterBody', this, tooltipItems));\n        }\n        getFooter(tooltipItems, options) {\n            const { callbacks  } = options;\n            const beforeFooter = invokeCallbackWithFallback(callbacks, 'beforeFooter', this, tooltipItems);\n            const footer = invokeCallbackWithFallback(callbacks, 'footer', this, tooltipItems);\n            const afterFooter = invokeCallbackWithFallback(callbacks, 'afterFooter', this, tooltipItems);\n            let lines = [];\n            lines = pushOrConcat(lines, splitNewlines(beforeFooter));\n            lines = pushOrConcat(lines, splitNewlines(footer));\n            lines = pushOrConcat(lines, splitNewlines(afterFooter));\n            return lines;\n        }\n        _createItems(options) {\n            const active = this._active;\n            const data = this.chart.data;\n            const labelColors = [];\n            const labelPointStyles = [];\n            const labelTextColors = [];\n            let tooltipItems = [];\n            let i, len;\n            for(i = 0, len = active.length; i < len; ++i){\n                tooltipItems.push(createTooltipItem(this.chart, active[i]));\n            }\n            if (options.filter) {\n                tooltipItems = tooltipItems.filter((element, index, array)=>options.filter(element, index, array, data));\n            }\n            if (options.itemSort) {\n                tooltipItems = tooltipItems.sort((a, b)=>options.itemSort(a, b, data));\n            }\n            each(tooltipItems, (context)=>{\n                const scoped = overrideCallbacks(options.callbacks, context);\n                labelColors.push(invokeCallbackWithFallback(scoped, 'labelColor', this, context));\n                labelPointStyles.push(invokeCallbackWithFallback(scoped, 'labelPointStyle', this, context));\n                labelTextColors.push(invokeCallbackWithFallback(scoped, 'labelTextColor', this, context));\n            });\n            this.labelColors = labelColors;\n            this.labelPointStyles = labelPointStyles;\n            this.labelTextColors = labelTextColors;\n            this.dataPoints = tooltipItems;\n            return tooltipItems;\n        }\n        update(changed, replay) {\n            const options = this.options.setContext(this.getContext());\n            const active = this._active;\n            let properties;\n            let tooltipItems = [];\n            if (!active.length) {\n                if (this.opacity !== 0) {\n                    properties = {\n                        opacity: 0\n                    };\n                }\n            } else {\n                const position = positioners[options.position].call(this, active, this._eventPosition);\n                tooltipItems = this._createItems(options);\n                this.title = this.getTitle(tooltipItems, options);\n                this.beforeBody = this.getBeforeBody(tooltipItems, options);\n                this.body = this.getBody(tooltipItems, options);\n                this.afterBody = this.getAfterBody(tooltipItems, options);\n                this.footer = this.getFooter(tooltipItems, options);\n                const size = this._size = getTooltipSize(this, options);\n                const positionAndSize = Object.assign({}, position, size);\n                const alignment = determineAlignment(this.chart, options, positionAndSize);\n                const backgroundPoint = getBackgroundPoint(options, positionAndSize, alignment, this.chart);\n                this.xAlign = alignment.xAlign;\n                this.yAlign = alignment.yAlign;\n                properties = {\n                    opacity: 1,\n                    x: backgroundPoint.x,\n                    y: backgroundPoint.y,\n                    width: size.width,\n                    height: size.height,\n                    caretX: position.x,\n                    caretY: position.y\n                };\n            }\n            this._tooltipItems = tooltipItems;\n            this.$context = undefined;\n            if (properties) {\n                this._resolveAnimations().update(this, properties);\n            }\n            if (changed && options.external) {\n                options.external.call(this, {\n                    chart: this.chart,\n                    tooltip: this,\n                    replay\n                });\n            }\n        }\n        drawCaret(tooltipPoint, ctx, size, options) {\n            const caretPosition = this.getCaretPosition(tooltipPoint, size, options);\n            ctx.lineTo(caretPosition.x1, caretPosition.y1);\n            ctx.lineTo(caretPosition.x2, caretPosition.y2);\n            ctx.lineTo(caretPosition.x3, caretPosition.y3);\n        }\n        getCaretPosition(tooltipPoint, size, options) {\n            const { xAlign , yAlign  } = this;\n            const { caretSize , cornerRadius  } = options;\n            const { topLeft , topRight , bottomLeft , bottomRight  } = toTRBLCorners(cornerRadius);\n            const { x: ptX , y: ptY  } = tooltipPoint;\n            const { width , height  } = size;\n            let x1, x2, x3, y1, y2, y3;\n            if (yAlign === 'center') {\n                y2 = ptY + height / 2;\n                if (xAlign === 'left') {\n                    x1 = ptX;\n                    x2 = x1 - caretSize;\n                    y1 = y2 + caretSize;\n                    y3 = y2 - caretSize;\n                } else {\n                    x1 = ptX + width;\n                    x2 = x1 + caretSize;\n                    y1 = y2 - caretSize;\n                    y3 = y2 + caretSize;\n                }\n                x3 = x1;\n            } else {\n                if (xAlign === 'left') {\n                    x2 = ptX + Math.max(topLeft, bottomLeft) + caretSize;\n                } else if (xAlign === 'right') {\n                    x2 = ptX + width - Math.max(topRight, bottomRight) - caretSize;\n                } else {\n                    x2 = this.caretX;\n                }\n                if (yAlign === 'top') {\n                    y1 = ptY;\n                    y2 = y1 - caretSize;\n                    x1 = x2 - caretSize;\n                    x3 = x2 + caretSize;\n                } else {\n                    y1 = ptY + height;\n                    y2 = y1 + caretSize;\n                    x1 = x2 + caretSize;\n                    x3 = x2 - caretSize;\n                }\n                y3 = y1;\n            }\n            return {\n                x1,\n                x2,\n                x3,\n                y1,\n                y2,\n                y3\n            };\n        }\n        drawTitle(pt, ctx, options) {\n            const title = this.title;\n            const length = title.length;\n            let titleFont, titleSpacing, i;\n            if (length) {\n                const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width);\n                pt.x = getAlignedX(this, options.titleAlign, options);\n                ctx.textAlign = rtlHelper.textAlign(options.titleAlign);\n                ctx.textBaseline = 'middle';\n                titleFont = toFont(options.titleFont);\n                titleSpacing = options.titleSpacing;\n                ctx.fillStyle = options.titleColor;\n                ctx.font = titleFont.string;\n                for(i = 0; i < length; ++i){\n                    ctx.fillText(title[i], rtlHelper.x(pt.x), pt.y + titleFont.lineHeight / 2);\n                    pt.y += titleFont.lineHeight + titleSpacing;\n                    if (i + 1 === length) {\n                        pt.y += options.titleMarginBottom - titleSpacing;\n                    }\n                }\n            }\n        }\n        _drawColorBox(ctx, pt, i, rtlHelper, options) {\n            const labelColor = this.labelColors[i];\n            const labelPointStyle = this.labelPointStyles[i];\n            const { boxHeight , boxWidth  } = options;\n            const bodyFont = toFont(options.bodyFont);\n            const colorX = getAlignedX(this, 'left', options);\n            const rtlColorX = rtlHelper.x(colorX);\n            const yOffSet = boxHeight < bodyFont.lineHeight ? (bodyFont.lineHeight - boxHeight) / 2 : 0;\n            const colorY = pt.y + yOffSet;\n            if (options.usePointStyle) {\n                const drawOptions = {\n                    radius: Math.min(boxWidth, boxHeight) / 2,\n                    pointStyle: labelPointStyle.pointStyle,\n                    rotation: labelPointStyle.rotation,\n                    borderWidth: 1\n                };\n                const centerX = rtlHelper.leftForLtr(rtlColorX, boxWidth) + boxWidth / 2;\n                const centerY = colorY + boxHeight / 2;\n                ctx.strokeStyle = options.multiKeyBackground;\n                ctx.fillStyle = options.multiKeyBackground;\n                drawPoint(ctx, drawOptions, centerX, centerY);\n                ctx.strokeStyle = labelColor.borderColor;\n                ctx.fillStyle = labelColor.backgroundColor;\n                drawPoint(ctx, drawOptions, centerX, centerY);\n            } else {\n                ctx.lineWidth = isObject(labelColor.borderWidth) ? Math.max(...Object.values(labelColor.borderWidth)) : labelColor.borderWidth || 1;\n                ctx.strokeStyle = labelColor.borderColor;\n                ctx.setLineDash(labelColor.borderDash || []);\n                ctx.lineDashOffset = labelColor.borderDashOffset || 0;\n                const outerX = rtlHelper.leftForLtr(rtlColorX, boxWidth);\n                const innerX = rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), boxWidth - 2);\n                const borderRadius = toTRBLCorners(labelColor.borderRadius);\n                if (Object.values(borderRadius).some((v)=>v !== 0)) {\n                    ctx.beginPath();\n                    ctx.fillStyle = options.multiKeyBackground;\n                    addRoundedRectPath(ctx, {\n                        x: outerX,\n                        y: colorY,\n                        w: boxWidth,\n                        h: boxHeight,\n                        radius: borderRadius\n                    });\n                    ctx.fill();\n                    ctx.stroke();\n                    ctx.fillStyle = labelColor.backgroundColor;\n                    ctx.beginPath();\n                    addRoundedRectPath(ctx, {\n                        x: innerX,\n                        y: colorY + 1,\n                        w: boxWidth - 2,\n                        h: boxHeight - 2,\n                        radius: borderRadius\n                    });\n                    ctx.fill();\n                } else {\n                    ctx.fillStyle = options.multiKeyBackground;\n                    ctx.fillRect(outerX, colorY, boxWidth, boxHeight);\n                    ctx.strokeRect(outerX, colorY, boxWidth, boxHeight);\n                    ctx.fillStyle = labelColor.backgroundColor;\n                    ctx.fillRect(innerX, colorY + 1, boxWidth - 2, boxHeight - 2);\n                }\n            }\n            ctx.fillStyle = this.labelTextColors[i];\n        }\n        drawBody(pt, ctx, options) {\n            const { body  } = this;\n            const { bodySpacing , bodyAlign , displayColors , boxHeight , boxWidth , boxPadding  } = options;\n            const bodyFont = toFont(options.bodyFont);\n            let bodyLineHeight = bodyFont.lineHeight;\n            let xLinePadding = 0;\n            const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width);\n            const fillLineOfText = function(line) {\n                ctx.fillText(line, rtlHelper.x(pt.x + xLinePadding), pt.y + bodyLineHeight / 2);\n                pt.y += bodyLineHeight + bodySpacing;\n            };\n            const bodyAlignForCalculation = rtlHelper.textAlign(bodyAlign);\n            let bodyItem, textColor, lines, i, j, ilen, jlen;\n            ctx.textAlign = bodyAlign;\n            ctx.textBaseline = 'middle';\n            ctx.font = bodyFont.string;\n            pt.x = getAlignedX(this, bodyAlignForCalculation, options);\n            ctx.fillStyle = options.bodyColor;\n            each(this.beforeBody, fillLineOfText);\n            xLinePadding = displayColors && bodyAlignForCalculation !== 'right' ? bodyAlign === 'center' ? boxWidth / 2 + boxPadding : boxWidth + 2 + boxPadding : 0;\n            for(i = 0, ilen = body.length; i < ilen; ++i){\n                bodyItem = body[i];\n                textColor = this.labelTextColors[i];\n                ctx.fillStyle = textColor;\n                each(bodyItem.before, fillLineOfText);\n                lines = bodyItem.lines;\n                if (displayColors && lines.length) {\n                    this._drawColorBox(ctx, pt, i, rtlHelper, options);\n                    bodyLineHeight = Math.max(bodyFont.lineHeight, boxHeight);\n                }\n                for(j = 0, jlen = lines.length; j < jlen; ++j){\n                    fillLineOfText(lines[j]);\n                    bodyLineHeight = bodyFont.lineHeight;\n                }\n                each(bodyItem.after, fillLineOfText);\n            }\n            xLinePadding = 0;\n            bodyLineHeight = bodyFont.lineHeight;\n            each(this.afterBody, fillLineOfText);\n            pt.y -= bodySpacing;\n        }\n        drawFooter(pt, ctx, options) {\n            const footer = this.footer;\n            const length = footer.length;\n            let footerFont, i;\n            if (length) {\n                const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width);\n                pt.x = getAlignedX(this, options.footerAlign, options);\n                pt.y += options.footerMarginTop;\n                ctx.textAlign = rtlHelper.textAlign(options.footerAlign);\n                ctx.textBaseline = 'middle';\n                footerFont = toFont(options.footerFont);\n                ctx.fillStyle = options.footerColor;\n                ctx.font = footerFont.string;\n                for(i = 0; i < length; ++i){\n                    ctx.fillText(footer[i], rtlHelper.x(pt.x), pt.y + footerFont.lineHeight / 2);\n                    pt.y += footerFont.lineHeight + options.footerSpacing;\n                }\n            }\n        }\n        drawBackground(pt, ctx, tooltipSize, options) {\n            const { xAlign , yAlign  } = this;\n            const { x , y  } = pt;\n            const { width , height  } = tooltipSize;\n            const { topLeft , topRight , bottomLeft , bottomRight  } = toTRBLCorners(options.cornerRadius);\n            ctx.fillStyle = options.backgroundColor;\n            ctx.strokeStyle = options.borderColor;\n            ctx.lineWidth = options.borderWidth;\n            ctx.beginPath();\n            ctx.moveTo(x + topLeft, y);\n            if (yAlign === 'top') {\n                this.drawCaret(pt, ctx, tooltipSize, options);\n            }\n            ctx.lineTo(x + width - topRight, y);\n            ctx.quadraticCurveTo(x + width, y, x + width, y + topRight);\n            if (yAlign === 'center' && xAlign === 'right') {\n                this.drawCaret(pt, ctx, tooltipSize, options);\n            }\n            ctx.lineTo(x + width, y + height - bottomRight);\n            ctx.quadraticCurveTo(x + width, y + height, x + width - bottomRight, y + height);\n            if (yAlign === 'bottom') {\n                this.drawCaret(pt, ctx, tooltipSize, options);\n            }\n            ctx.lineTo(x + bottomLeft, y + height);\n            ctx.quadraticCurveTo(x, y + height, x, y + height - bottomLeft);\n            if (yAlign === 'center' && xAlign === 'left') {\n                this.drawCaret(pt, ctx, tooltipSize, options);\n            }\n            ctx.lineTo(x, y + topLeft);\n            ctx.quadraticCurveTo(x, y, x + topLeft, y);\n            ctx.closePath();\n            ctx.fill();\n            if (options.borderWidth > 0) {\n                ctx.stroke();\n            }\n        }\n        _updateAnimationTarget(options) {\n            const chart = this.chart;\n            const anims = this.$animations;\n            const animX = anims && anims.x;\n            const animY = anims && anims.y;\n            if (animX || animY) {\n                const position = positioners[options.position].call(this, this._active, this._eventPosition);\n                if (!position) {\n                    return;\n                }\n                const size = this._size = getTooltipSize(this, options);\n                const positionAndSize = Object.assign({}, position, this._size);\n                const alignment = determineAlignment(chart, options, positionAndSize);\n                const point = getBackgroundPoint(options, positionAndSize, alignment, chart);\n                if (animX._to !== point.x || animY._to !== point.y) {\n                    this.xAlign = alignment.xAlign;\n                    this.yAlign = alignment.yAlign;\n                    this.width = size.width;\n                    this.height = size.height;\n                    this.caretX = position.x;\n                    this.caretY = position.y;\n                    this._resolveAnimations().update(this, point);\n                }\n            }\n        }\n        _willRender() {\n            return !!this.opacity;\n        }\n        draw(ctx) {\n            const options = this.options.setContext(this.getContext());\n            let opacity = this.opacity;\n            if (!opacity) {\n                return;\n            }\n            this._updateAnimationTarget(options);\n            const tooltipSize = {\n                width: this.width,\n                height: this.height\n            };\n            const pt = {\n                x: this.x,\n                y: this.y\n            };\n            opacity = Math.abs(opacity) < 1e-3 ? 0 : opacity;\n            const padding = toPadding(options.padding);\n            const hasTooltipContent = this.title.length || this.beforeBody.length || this.body.length || this.afterBody.length || this.footer.length;\n            if (options.enabled && hasTooltipContent) {\n                ctx.save();\n                ctx.globalAlpha = opacity;\n                this.drawBackground(pt, ctx, tooltipSize, options);\n                overrideTextDirection(ctx, options.textDirection);\n                pt.y += padding.top;\n                this.drawTitle(pt, ctx, options);\n                this.drawBody(pt, ctx, options);\n                this.drawFooter(pt, ctx, options);\n                restoreTextDirection(ctx, options.textDirection);\n                ctx.restore();\n            }\n        }\n        getActiveElements() {\n            return this._active || [];\n        }\n        setActiveElements(activeElements, eventPosition) {\n            const lastActive = this._active;\n            const active = activeElements.map(({ datasetIndex , index  })=>{\n                const meta = this.chart.getDatasetMeta(datasetIndex);\n                if (!meta) {\n                    throw new Error('Cannot find a dataset at index ' + datasetIndex);\n                }\n                return {\n                    datasetIndex,\n                    element: meta.data[index],\n                    index\n                };\n            });\n            const changed = !_elementsEqual(lastActive, active);\n            const positionChanged = this._positionChanged(active, eventPosition);\n            if (changed || positionChanged) {\n                this._active = active;\n                this._eventPosition = eventPosition;\n                this._ignoreReplayEvents = true;\n                this.update(true);\n            }\n        }\n        handleEvent(e, replay, inChartArea = true) {\n            if (replay && this._ignoreReplayEvents) {\n                return false;\n            }\n            this._ignoreReplayEvents = false;\n            const options = this.options;\n            const lastActive = this._active || [];\n            const active = this._getActiveElements(e, lastActive, replay, inChartArea);\n            const positionChanged = this._positionChanged(active, e);\n            const changed = replay || !_elementsEqual(active, lastActive) || positionChanged;\n            if (changed) {\n                this._active = active;\n                if (options.enabled || options.external) {\n                    this._eventPosition = {\n                        x: e.x,\n                        y: e.y\n                    };\n                    this.update(true, replay);\n                }\n            }\n            return changed;\n        }\n        _getActiveElements(e, lastActive, replay, inChartArea) {\n            const options = this.options;\n            if (e.type === 'mouseout') {\n                return [];\n            }\n            if (!inChartArea) {\n                return lastActive.filter((i)=>this.chart.data.datasets[i.datasetIndex] && this.chart.getDatasetMeta(i.datasetIndex).controller.getParsed(i.index) !== undefined);\n            }\n            const active = this.chart.getElementsAtEventForMode(e, options.mode, options, replay);\n            if (options.reverse) {\n                active.reverse();\n            }\n            return active;\n        }\n        _positionChanged(active, e) {\n            const { caretX , caretY , options  } = this;\n            const position = positioners[options.position].call(this, active, e);\n            return position !== false && (caretX !== position.x || caretY !== position.y);\n        }\n    }\n    var plugin_tooltip = {\n        id: 'tooltip',\n        _element: Tooltip,\n        positioners,\n        afterInit (chart, _args, options) {\n            if (options) {\n                chart.tooltip = new Tooltip({\n                    chart,\n                    options\n                });\n            }\n        },\n        beforeUpdate (chart, _args, options) {\n            if (chart.tooltip) {\n                chart.tooltip.initialize(options);\n            }\n        },\n        reset (chart, _args, options) {\n            if (chart.tooltip) {\n                chart.tooltip.initialize(options);\n            }\n        },\n        afterDraw (chart) {\n            const tooltip = chart.tooltip;\n            if (tooltip && tooltip._willRender()) {\n                const args = {\n                    tooltip\n                };\n                if (chart.notifyPlugins('beforeTooltipDraw', {\n                    ...args,\n                    cancelable: true\n                }) === false) {\n                    return;\n                }\n                tooltip.draw(chart.ctx);\n                chart.notifyPlugins('afterTooltipDraw', args);\n            }\n        },\n        afterEvent (chart, args) {\n            if (chart.tooltip) {\n                const useFinalPosition = args.replay;\n                if (chart.tooltip.handleEvent(args.event, useFinalPosition, args.inChartArea)) {\n                    args.changed = true;\n                }\n            }\n        },\n        defaults: {\n            enabled: true,\n            external: null,\n            position: 'average',\n            backgroundColor: 'rgba(0,0,0,0.8)',\n            titleColor: '#fff',\n            titleFont: {\n                weight: 'bold'\n            },\n            titleSpacing: 2,\n            titleMarginBottom: 6,\n            titleAlign: 'left',\n            bodyColor: '#fff',\n            bodySpacing: 2,\n            bodyFont: {},\n            bodyAlign: 'left',\n            footerColor: '#fff',\n            footerSpacing: 2,\n            footerMarginTop: 6,\n            footerFont: {\n                weight: 'bold'\n            },\n            footerAlign: 'left',\n            padding: 6,\n            caretPadding: 2,\n            caretSize: 5,\n            cornerRadius: 6,\n            boxHeight: (ctx, opts)=>opts.bodyFont.size,\n            boxWidth: (ctx, opts)=>opts.bodyFont.size,\n            multiKeyBackground: '#fff',\n            displayColors: true,\n            boxPadding: 0,\n            borderColor: 'rgba(0,0,0,0)',\n            borderWidth: 0,\n            animation: {\n                duration: 400,\n                easing: 'easeOutQuart'\n            },\n            animations: {\n                numbers: {\n                    type: 'number',\n                    properties: [\n                        'x',\n                        'y',\n                        'width',\n                        'height',\n                        'caretX',\n                        'caretY'\n                    ]\n                },\n                opacity: {\n                    easing: 'linear',\n                    duration: 200\n                }\n            },\n            callbacks: defaultCallbacks\n        },\n        defaultRoutes: {\n            bodyFont: 'font',\n            footerFont: 'font',\n            titleFont: 'font'\n        },\n        descriptors: {\n            _scriptable: (name)=>name !== 'filter' && name !== 'itemSort' && name !== 'external',\n            _indexable: false,\n            callbacks: {\n                _scriptable: false,\n                _indexable: false\n            },\n            animation: {\n                _fallback: false\n            },\n            animations: {\n                _fallback: 'animation'\n            }\n        },\n        additionalOptionScopes: [\n            'interaction'\n        ]\n    };\n\n    // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n    // Register built-ins\n    Chart.register(controllers, scales, elements, plugins);\n    Chart.helpers = {\n        ...helpers\n    };\n    Chart._adapters = _adapters;\n    Chart.Animation = Animation;\n    Chart.Animations = Animations;\n    Chart.animator = animator;\n    Chart.controllers = registry.controllers.items;\n    Chart.DatasetController = DatasetController;\n    Chart.Element = Element;\n    Chart.elements = elements;\n    Chart.Interaction = Interaction;\n    Chart.layouts = layouts;\n    Chart.platforms = platforms;\n    Chart.Scale = Scale;\n    Chart.Ticks = Ticks;\n    // Compatibility with ESM extensions\n    Object.assign(Chart, controllers, scales, elements, plugins, platforms);\n    Chart.Chart = Chart;\n    if (typeof window !== 'undefined') {\n        window.Chart = Chart;\n    }\n\n    return Chart;\n\n}));\n", "/*!\n * chartjs-adapter-luxon v1.3.1\n * https://www.chartjs.org\n * (c) 2023 chartjs-adapter-luxon Contributors\n * Released under the MIT license\n */\n(function (global, factory) {\n    typeof exports === \"object\" && typeof module !== \"undefined\"\n        ? factory(require(\"chart.js\"), require(\"luxon\"))\n        : typeof define === \"function\" && define.amd\n            ? define([\"chart.js\", \"luxon\"], factory)\n            : ((global =\n                typeof globalThis !== \"undefined\" ? globalThis : global || self),\n                factory(global.Chart, global.luxon));\n})(this, function (chart_js, luxon) {\n    \"use strict\";\n\n    const FORMATS = {\n        datetime: luxon.DateTime.DATETIME_MED_WITH_SECONDS,\n        millisecond: \"h:mm:ss.SSS a\",\n        second: luxon.DateTime.TIME_WITH_SECONDS,\n        minute: luxon.DateTime.TIME_SIMPLE,\n        hour: { hour: \"numeric\" },\n        day: { day: \"numeric\", month: \"short\" },\n        week: \"DD\",\n        month: { month: \"short\", year: \"numeric\" },\n        quarter: \"'Q'q - yyyy\",\n        year: { year: \"numeric\" },\n    };\n\n    chart_js._adapters._date.override({\n        _id: \"luxon\", // DEBUG\n\n        /**\n         * @private\n         */\n        _create: function (time) {\n            return luxon.DateTime.fromMillis(time, this.options);\n        },\n\n        init(chartOptions) {\n            if (!this.options.locale) {\n                this.options.locale = chartOptions.locale;\n            }\n        },\n\n        formats: function () {\n            return FORMATS;\n        },\n\n        parse: function (value, format) {\n            const options = this.options;\n\n            const type = typeof value;\n            if (value === null || type === \"undefined\") {\n                return null;\n            }\n\n            if (type === \"number\") {\n                value = this._create(value);\n            } else if (type === \"string\") {\n                if (typeof format === \"string\") {\n                    value = luxon.DateTime.fromFormat(value, format, options);\n                } else {\n                    value = luxon.DateTime.fromISO(value, options);\n                }\n            } else if (value instanceof Date) {\n                value = luxon.DateTime.fromJSDate(value, options);\n            } else if (type === \"object\" && !(value instanceof luxon.DateTime)) {\n                value = luxon.DateTime.fromObject(value, options);\n            }\n\n            return value.isValid ? value.valueOf() : null;\n        },\n\n        format: function (time, format) {\n            const datetime = this._create(time);\n            return typeof format === \"string\"\n                ? datetime.toFormat(format)\n                : datetime.toLocaleString(format);\n        },\n\n        add: function (time, amount, unit) {\n            const args = {};\n            args[unit] = amount;\n            return this._create(time).plus(args).valueOf();\n        },\n\n        diff: function (max, min, unit) {\n            return this._create(max).diff(this._create(min)).as(unit).valueOf();\n        },\n\n        startOf: function (time, unit, weekday) {\n            if (unit === \"isoWeek\") {\n                weekday = Math.trunc(Math.min(Math.max(0, weekday), 6));\n                const dateTime = this._create(time);\n                return dateTime\n                    .minus({ days: (dateTime.weekday - weekday + 7) % 7 })\n                    .startOf(\"day\")\n                    .valueOf();\n            }\n            return unit ? this._create(time).startOf(unit).valueOf() : time;\n        },\n\n        endOf: function (time, unit) {\n            return this._create(time).endOf(unit).valueOf();\n        },\n    });\n});\n"], "file": "/web/assets/5823771/web.chartjs_lib.js", "sourceRoot": "../../../"}