@classytic/stage 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (458) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +5 -1
  3. package/dist/_virtual/_rolldown/runtime.mjs +18 -0
  4. package/dist/assets/index.mjs +0 -1
  5. package/dist/atomic/aufbau.d.mts +144 -0
  6. package/dist/atomic/aufbau.mjs +384 -0
  7. package/dist/atomic/elements.d.mts +67 -0
  8. package/dist/atomic/elements.mjs +785 -0
  9. package/dist/atomic/emission.d.mts +112 -0
  10. package/dist/atomic/emission.mjs +179 -0
  11. package/dist/atomic/index.d.mts +8 -0
  12. package/dist/atomic/index.mjs +9 -0
  13. package/dist/atomic/periodic.d.mts +82 -0
  14. package/dist/atomic/periodic.mjs +231 -0
  15. package/dist/atomic/properties.d.mts +100 -0
  16. package/dist/atomic/properties.mjs +574 -0
  17. package/dist/atomic/shells.d.mts +137 -0
  18. package/dist/atomic/shells.mjs +170 -0
  19. package/dist/atomic/trends.d.mts +148 -0
  20. package/dist/atomic/trends.mjs +225 -0
  21. package/dist/builder/Palette.mjs +50 -89
  22. package/dist/builder/SceneBuilder.mjs +15 -73
  23. package/dist/chem/chromatography.d.mts +66 -0
  24. package/dist/chem/chromatography.mjs +63 -0
  25. package/dist/chem/complex.d.mts +114 -0
  26. package/dist/chem/complex.mjs +234 -0
  27. package/dist/chem/cycle.d.mts +75 -0
  28. package/dist/chem/cycle.mjs +145 -0
  29. package/dist/chem/equation.d.mts +86 -0
  30. package/dist/chem/equation.mjs +168 -0
  31. package/dist/chem/formula.d.mts +90 -0
  32. package/dist/chem/formula.mjs +201 -0
  33. package/dist/chem/functional-groups.d.mts +31 -0
  34. package/dist/chem/functional-groups.mjs +187 -0
  35. package/dist/chem/hazard.d.mts +94 -0
  36. package/dist/chem/hazard.mjs +329 -0
  37. package/dist/chem/index.d.mts +34 -33
  38. package/dist/chem/index.mjs +34 -28
  39. package/dist/chem/ionic.d.mts +98 -0
  40. package/dist/chem/ionic.mjs +156 -0
  41. package/dist/chem/layout.d.mts +46 -0
  42. package/dist/chem/layout.mjs +342 -0
  43. package/dist/chem/lewis.d.mts +79 -0
  44. package/dist/chem/lewis.mjs +163 -0
  45. package/dist/chem/molecular-orbital.d.mts +114 -0
  46. package/dist/chem/molecular-orbital.mjs +201 -0
  47. package/dist/chem/molecule.d.mts +154 -0
  48. package/dist/chem/molecule.mjs +282 -0
  49. package/dist/chem/nmr.d.mts +153 -0
  50. package/dist/chem/nmr.mjs +165 -0
  51. package/dist/chem/polymer.d.mts +65 -0
  52. package/dist/chem/polymer.mjs +179 -0
  53. package/dist/chem/redox.d.mts +138 -0
  54. package/dist/chem/redox.mjs +328 -0
  55. package/dist/chem/relations.d.mts +183 -0
  56. package/dist/chem/relations.mjs +299 -0
  57. package/dist/chem/skeleton.d.mts +51 -0
  58. package/dist/chem/skeleton.mjs +186 -0
  59. package/dist/chem/solution.d.mts +116 -0
  60. package/dist/chem/solution.mjs +186 -0
  61. package/dist/chem/species.d.mts +47 -0
  62. package/dist/chem/species.mjs +128 -0
  63. package/dist/chem/stereo.d.mts +108 -0
  64. package/dist/chem/stereo.mjs +286 -0
  65. package/dist/chem/stoichiometry.d.mts +95 -0
  66. package/dist/chem/stoichiometry.mjs +145 -0
  67. package/dist/chem/synthesis.d.mts +113 -0
  68. package/dist/chem/synthesis.mjs +281 -0
  69. package/dist/chem/vsepr.d.mts +80 -0
  70. package/dist/chem/vsepr.mjs +247 -0
  71. package/dist/circuit/ac.d.mts +163 -0
  72. package/dist/circuit/ac.mjs +452 -0
  73. package/dist/circuit/dc.d.mts +92 -0
  74. package/dist/circuit/dc.mjs +333 -0
  75. package/dist/circuit/index.d.mts +5 -92
  76. package/dist/circuit/index.mjs +5 -332
  77. package/dist/circuit/network.d.mts +99 -0
  78. package/dist/circuit/network.mjs +440 -0
  79. package/dist/circuit/topology.d.mts +102 -0
  80. package/dist/circuit/topology.mjs +143 -0
  81. package/dist/colour/absorption.d.mts +79 -0
  82. package/dist/colour/absorption.mjs +137 -0
  83. package/dist/colour/index.d.mts +6 -0
  84. package/dist/colour/index.mjs +7 -0
  85. package/dist/colour/name.d.mts +14 -0
  86. package/dist/colour/name.mjs +76 -0
  87. package/dist/colour/observer.d.mts +31 -0
  88. package/dist/colour/observer.mjs +46 -0
  89. package/dist/colour/spectrum.d.mts +61 -0
  90. package/dist/colour/spectrum.mjs +108 -0
  91. package/dist/colour/srgb.d.mts +49 -0
  92. package/dist/colour/srgb.mjs +108 -0
  93. package/dist/core/constants.d.mts +138 -0
  94. package/dist/core/constants.mjs +139 -0
  95. package/dist/core/index.d.mts +4 -1
  96. package/dist/core/index.mjs +4 -1
  97. package/dist/core/math.d.mts +26 -0
  98. package/dist/core/math.mjs +37 -0
  99. package/dist/core/vec3.d.mts +94 -0
  100. package/dist/core/vec3.mjs +123 -0
  101. package/dist/core/worked.d.mts +35 -0
  102. package/dist/core/worked.mjs +44 -0
  103. package/dist/electro/cell.d.mts +102 -0
  104. package/dist/electro/cell.mjs +170 -0
  105. package/dist/electro/conductivity.d.mts +157 -0
  106. package/dist/electro/conductivity.mjs +156 -0
  107. package/dist/electro/couples.d.mts +114 -0
  108. package/dist/electro/couples.mjs +334 -0
  109. package/dist/electro/electrolysis.d.mts +96 -0
  110. package/dist/electro/electrolysis.mjs +274 -0
  111. package/dist/electro/faraday.d.mts +150 -0
  112. package/dist/electro/faraday.mjs +118 -0
  113. package/dist/electro/index.d.mts +6 -0
  114. package/dist/electro/index.mjs +7 -0
  115. package/dist/finance/accounting/adjustments.d.mts +17 -0
  116. package/dist/finance/accounting/adjustments.mjs +202 -0
  117. package/dist/finance/accounting/analysis.d.mts +8 -0
  118. package/dist/finance/accounting/analysis.mjs +205 -0
  119. package/dist/finance/accounting/clubs.d.mts +21 -0
  120. package/dist/finance/accounting/clubs.mjs +217 -0
  121. package/dist/finance/accounting/company-statements.d.mts +7 -0
  122. package/dist/finance/accounting/company-statements.mjs +150 -0
  123. package/dist/finance/accounting/harness.d.mts +7 -0
  124. package/dist/finance/accounting/harness.mjs +45 -0
  125. package/dist/finance/accounting/index.d.mts +18 -0
  126. package/dist/finance/accounting/index.mjs +18 -0
  127. package/dist/finance/accounting/journalize.d.mts +16 -0
  128. package/dist/finance/accounting/journalize.mjs +128 -0
  129. package/dist/finance/accounting/manufacturing.d.mts +8 -0
  130. package/dist/finance/accounting/manufacturing.mjs +236 -0
  131. package/dist/finance/accounting/partnership.d.mts +7 -0
  132. package/dist/finance/accounting/partnership.mjs +152 -0
  133. package/dist/finance/accounting/period-case.d.mts +6 -0
  134. package/dist/finance/accounting/period-case.mjs +71 -0
  135. package/dist/finance/accounting/post.d.mts +9 -0
  136. package/dist/finance/accounting/post.mjs +84 -0
  137. package/dist/finance/accounting/precision.d.mts +9 -0
  138. package/dist/finance/accounting/precision.mjs +15 -0
  139. package/dist/finance/accounting/rules.d.mts +21 -0
  140. package/dist/finance/accounting/rules.mjs +37 -0
  141. package/dist/finance/accounting/solve.d.mts +28 -0
  142. package/dist/finance/accounting/solve.mjs +92 -0
  143. package/dist/finance/accounting/statements.d.mts +7 -0
  144. package/dist/finance/accounting/statements.mjs +142 -0
  145. package/dist/finance/accounting/types.d.mts +649 -0
  146. package/dist/finance/accounting/validate.d.mts +7 -0
  147. package/dist/finance/accounting/validate.mjs +67 -0
  148. package/dist/finance/accounting/verification.d.mts +24 -0
  149. package/dist/finance/accounting/verification.mjs +386 -0
  150. package/dist/finance/bizsim.d.mts +75 -0
  151. package/dist/finance/bizsim.mjs +296 -0
  152. package/dist/finance/index.d.mts +95 -0
  153. package/dist/finance/index.mjs +145 -0
  154. package/dist/finance/investment-math.d.mts +7 -0
  155. package/dist/finance/investment-math.mjs +18 -0
  156. package/dist/finance/management-accounting/budgeting.d.mts +7 -0
  157. package/dist/finance/management-accounting/budgeting.mjs +189 -0
  158. package/dist/finance/management-accounting/cash-budget.d.mts +7 -0
  159. package/dist/finance/management-accounting/cash-budget.mjs +154 -0
  160. package/dist/finance/management-accounting/costing.d.mts +8 -0
  161. package/dist/finance/management-accounting/costing.mjs +175 -0
  162. package/dist/finance/management-accounting/decisions.d.mts +8 -0
  163. package/dist/finance/management-accounting/decisions.mjs +164 -0
  164. package/dist/finance/management-accounting/index.d.mts +13 -0
  165. package/dist/finance/management-accounting/index.mjs +13 -0
  166. package/dist/finance/management-accounting/integrated-budget.d.mts +8 -0
  167. package/dist/finance/management-accounting/integrated-budget.mjs +67 -0
  168. package/dist/finance/management-accounting/inventory-costing.d.mts +7 -0
  169. package/dist/finance/management-accounting/inventory-costing.mjs +173 -0
  170. package/dist/finance/management-accounting/investment-appraisal.d.mts +7 -0
  171. package/dist/finance/management-accounting/investment-appraisal.mjs +148 -0
  172. package/dist/finance/management-accounting/management-case.d.mts +8 -0
  173. package/dist/finance/management-accounting/management-case.mjs +91 -0
  174. package/dist/finance/management-accounting/master-budget.d.mts +7 -0
  175. package/dist/finance/management-accounting/master-budget.mjs +137 -0
  176. package/dist/finance/management-accounting/production-costing.d.mts +8 -0
  177. package/dist/finance/management-accounting/production-costing.mjs +214 -0
  178. package/dist/finance/management-accounting/types.d.mts +644 -0
  179. package/dist/finance/management-accounting/variances.d.mts +7 -0
  180. package/dist/finance/management-accounting/variances.mjs +150 -0
  181. package/dist/index.d.mts +9 -8
  182. package/dist/index.mjs +5 -5
  183. package/dist/interaction/MovableDot.mjs +19 -0
  184. package/dist/interaction/useDraggable.mjs +24 -4
  185. package/dist/math/analysis.d.mts +61 -0
  186. package/dist/math/analysis.mjs +187 -0
  187. package/dist/math/answer-check.d.mts +50 -0
  188. package/dist/math/answer-check.mjs +106 -0
  189. package/dist/math/calculus.d.mts +17 -1
  190. package/dist/math/calculus.mjs +365 -2
  191. package/dist/math/complex.d.mts +93 -0
  192. package/dist/math/complex.mjs +239 -0
  193. package/dist/math/expr.d.mts +23 -0
  194. package/dist/math/expr.mjs +36 -0
  195. package/dist/math/index.d.mts +9 -23
  196. package/dist/math/index.mjs +9 -38
  197. package/dist/math/latex.mjs +10 -1
  198. package/dist/math/parse.mjs +1 -1
  199. package/dist/math/poly.d.mts +55 -0
  200. package/dist/math/poly.mjs +273 -0
  201. package/dist/math/rearrange.d.mts +14 -0
  202. package/dist/math/rearrange.mjs +125 -0
  203. package/dist/math/solve.d.mts +21 -0
  204. package/dist/math/solve.mjs +75 -0
  205. package/dist/networking/addressing.d.mts +26 -0
  206. package/dist/networking/addressing.mjs +33 -0
  207. package/dist/networking/cloud/access.d.mts +36 -0
  208. package/dist/networking/cloud/access.mjs +95 -0
  209. package/dist/networking/cloud/containers.d.mts +43 -0
  210. package/dist/networking/cloud/containers.mjs +54 -0
  211. package/dist/networking/cloud/edge.d.mts +17 -0
  212. package/dist/networking/cloud/edge.mjs +31 -0
  213. package/dist/networking/cloud/failure-domains.d.mts +19 -0
  214. package/dist/networking/cloud/failure-domains.mjs +22 -0
  215. package/dist/networking/cloud/geo-routing.d.mts +22 -0
  216. package/dist/networking/cloud/geo-routing.mjs +64 -0
  217. package/dist/networking/cloud/index.d.mts +18 -0
  218. package/dist/networking/cloud/index.mjs +19 -0
  219. package/dist/networking/cloud/inference-reliability.d.mts +30 -0
  220. package/dist/networking/cloud/inference-reliability.mjs +76 -0
  221. package/dist/networking/cloud/inference.d.mts +118 -0
  222. package/dist/networking/cloud/inference.mjs +179 -0
  223. package/dist/networking/cloud/infrastructure.d.mts +45 -0
  224. package/dist/networking/cloud/infrastructure.mjs +96 -0
  225. package/dist/networking/cloud/observability.d.mts +24 -0
  226. package/dist/networking/cloud/observability.mjs +34 -0
  227. package/dist/networking/cloud/orchestration.d.mts +32 -0
  228. package/dist/networking/cloud/orchestration.mjs +38 -0
  229. package/dist/networking/cloud/recovery.d.mts +21 -0
  230. package/dist/networking/cloud/recovery.mjs +27 -0
  231. package/dist/networking/cloud/releases.d.mts +42 -0
  232. package/dist/networking/cloud/releases.mjs +135 -0
  233. package/dist/networking/cloud/scaling.d.mts +25 -0
  234. package/dist/networking/cloud/scaling.mjs +65 -0
  235. package/dist/networking/cloud/secrets.d.mts +39 -0
  236. package/dist/networking/cloud/secrets.mjs +68 -0
  237. package/dist/networking/cloud/server-deployment.d.mts +29 -0
  238. package/dist/networking/cloud/server-deployment.mjs +72 -0
  239. package/dist/networking/cloud/storage.d.mts +16 -0
  240. package/dist/networking/cloud/storage.mjs +58 -0
  241. package/dist/networking/cloud/vpc.d.mts +18 -0
  242. package/dist/networking/cloud/vpc.mjs +100 -0
  243. package/dist/networking/delivery.d.mts +34 -0
  244. package/dist/networking/delivery.mjs +52 -0
  245. package/dist/networking/index.d.mts +47 -0
  246. package/dist/networking/index.mjs +47 -0
  247. package/dist/networking/ipv6.d.mts +28 -0
  248. package/dist/networking/ipv6.mjs +17 -0
  249. package/dist/networking/journeys.d.mts +83 -0
  250. package/dist/networking/journeys.mjs +161 -0
  251. package/dist/networking/models/airtime.d.mts +87 -0
  252. package/dist/networking/models/airtime.mjs +130 -0
  253. package/dist/networking/models/cellular.d.mts +157 -0
  254. package/dist/networking/models/cellular.mjs +252 -0
  255. package/dist/networking/models/encapsulation.d.mts +98 -0
  256. package/dist/networking/models/encapsulation.mjs +132 -0
  257. package/dist/networking/models/handover.d.mts +116 -0
  258. package/dist/networking/models/handover.mjs +209 -0
  259. package/dist/networking/models/handshake.d.mts +90 -0
  260. package/dist/networking/models/handshake.mjs +122 -0
  261. package/dist/networking/models/index.d.mts +14 -0
  262. package/dist/networking/models/index.mjs +15 -0
  263. package/dist/networking/models/media.d.mts +82 -0
  264. package/dist/networking/models/media.mjs +188 -0
  265. package/dist/networking/models/satellite.d.mts +86 -0
  266. package/dist/networking/models/satellite.mjs +121 -0
  267. package/dist/networking/models/spectrum.d.mts +83 -0
  268. package/dist/networking/models/spectrum.mjs +235 -0
  269. package/dist/networking/models/switch.d.mts +80 -0
  270. package/dist/networking/models/switch.mjs +155 -0
  271. package/dist/networking/models/vlan.d.mts +62 -0
  272. package/dist/networking/models/vlan.mjs +100 -0
  273. package/dist/networking/models/vpn.d.mts +66 -0
  274. package/dist/networking/models/vpn.mjs +125 -0
  275. package/dist/networking/models/wifi-privacy.d.mts +82 -0
  276. package/dist/networking/models/wifi-privacy.mjs +130 -0
  277. package/dist/networking/models/wifi.d.mts +240 -0
  278. package/dist/networking/models/wifi.mjs +562 -0
  279. package/dist/networking/nat-traversal.d.mts +24 -0
  280. package/dist/networking/nat-traversal.mjs +38 -0
  281. package/dist/networking/protocols.d.mts +17 -0
  282. package/dist/networking/protocols.mjs +57 -0
  283. package/dist/networking/queue.d.mts +12 -0
  284. package/dist/networking/queue.mjs +18 -0
  285. package/dist/networking/route-learning.d.mts +23 -0
  286. package/dist/networking/route-learning.mjs +43 -0
  287. package/dist/networking/routing.d.mts +11 -0
  288. package/dist/networking/routing.mjs +25 -0
  289. package/dist/networking/signals.d.mts +26 -0
  290. package/dist/networking/signals.mjs +35 -0
  291. package/dist/networking/subnet.d.mts +49 -0
  292. package/dist/networking/subnet.mjs +89 -0
  293. package/dist/networking/tcp-reliability.d.mts +24 -0
  294. package/dist/networking/tcp-reliability.mjs +38 -0
  295. package/dist/networking/topology/contract.d.mts +123 -0
  296. package/dist/networking/topology/index.d.mts +4 -0
  297. package/dist/networking/topology/index.mjs +4 -0
  298. package/dist/networking/topology/presets.d.mts +7 -0
  299. package/dist/networking/topology/presets.mjs +302 -0
  300. package/dist/networking/topology/simulation.d.mts +8 -0
  301. package/dist/networking/topology/simulation.mjs +179 -0
  302. package/dist/networking/wireless.d.mts +11 -0
  303. package/dist/networking/wireless.mjs +17 -0
  304. package/dist/optics/imaging.d.mts +218 -0
  305. package/dist/optics/imaging.mjs +276 -0
  306. package/dist/optics/index.d.mts +7 -0
  307. package/dist/optics/index.mjs +8 -0
  308. package/dist/optics/laws.d.mts +70 -0
  309. package/dist/optics/laws.mjs +195 -0
  310. package/dist/optics/prism.d.mts +148 -0
  311. package/dist/optics/prism.mjs +197 -0
  312. package/dist/optics/refraction.d.mts +201 -0
  313. package/dist/optics/refraction.mjs +270 -0
  314. package/dist/optics/wave-optics.d.mts +171 -0
  315. package/dist/optics/wave-optics.mjs +259 -0
  316. package/dist/physics/ac/index.d.mts +183 -0
  317. package/dist/physics/ac/index.mjs +203 -0
  318. package/dist/physics/dynamics/forces.d.mts +98 -0
  319. package/dist/physics/dynamics/forces.mjs +153 -0
  320. package/dist/physics/dynamics/friction.d.mts +76 -0
  321. package/dist/physics/dynamics/friction.mjs +119 -0
  322. package/dist/physics/dynamics/system.d.mts +68 -0
  323. package/dist/physics/dynamics/system.mjs +125 -0
  324. package/dist/physics/electrostatics/index.d.mts +169 -0
  325. package/dist/physics/electrostatics/index.mjs +249 -0
  326. package/dist/physics/energy/centre-of-mass.d.mts +115 -0
  327. package/dist/physics/energy/centre-of-mass.mjs +180 -0
  328. package/dist/physics/energy/conservation.d.mts +81 -0
  329. package/dist/physics/energy/conservation.mjs +129 -0
  330. package/dist/physics/energy/power.d.mts +90 -0
  331. package/dist/physics/energy/power.mjs +167 -0
  332. package/dist/physics/energy/spring.d.mts +73 -0
  333. package/dist/physics/energy/spring.mjs +129 -0
  334. package/dist/physics/energy/store.d.mts +63 -0
  335. package/dist/physics/energy/store.mjs +108 -0
  336. package/dist/physics/energy/theorem.d.mts +65 -0
  337. package/dist/physics/energy/theorem.mjs +113 -0
  338. package/dist/physics/energy/work.d.mts +114 -0
  339. package/dist/physics/energy/work.mjs +219 -0
  340. package/dist/physics/gravitation/body.d.mts +85 -0
  341. package/dist/physics/gravitation/body.mjs +168 -0
  342. package/dist/physics/gravitation/kepler.d.mts +127 -0
  343. package/dist/physics/gravitation/kepler.mjs +216 -0
  344. package/dist/physics/gravitation/law.d.mts +117 -0
  345. package/dist/physics/gravitation/law.mjs +175 -0
  346. package/dist/physics/gravitation/orbit.d.mts +119 -0
  347. package/dist/physics/gravitation/orbit.mjs +202 -0
  348. package/dist/physics/gravitation/potential.d.mts +114 -0
  349. package/dist/physics/gravitation/potential.mjs +202 -0
  350. package/dist/physics/gravitation/variation.d.mts +97 -0
  351. package/dist/physics/gravitation/variation.mjs +175 -0
  352. package/dist/physics/index.d.mts +35 -0
  353. package/dist/physics/index.mjs +36 -0
  354. package/dist/physics/integral-sweep.d.mts +63 -0
  355. package/dist/physics/integral-sweep.mjs +126 -0
  356. package/dist/physics/kinematics/equations.d.mts +47 -0
  357. package/dist/physics/kinematics/equations.mjs +126 -0
  358. package/dist/physics/kinematics/free-fall.d.mts +53 -0
  359. package/dist/physics/kinematics/free-fall.mjs +71 -0
  360. package/dist/physics/kinematics/given.d.mts +16 -0
  361. package/dist/physics/kinematics/given.mjs +55 -0
  362. package/dist/physics/kinematics/roots.d.mts +41 -0
  363. package/dist/physics/kinematics/roots.mjs +72 -0
  364. package/dist/physics/kinematics/solve.d.mts +52 -0
  365. package/dist/physics/kinematics/solve.mjs +172 -0
  366. package/dist/physics/magnetism/biot-savart.d.mts +161 -0
  367. package/dist/physics/magnetism/biot-savart.mjs +353 -0
  368. package/dist/physics/magnetism/flux.d.mts +94 -0
  369. package/dist/physics/magnetism/flux.mjs +313 -0
  370. package/dist/physics/magnetism/geomagnetism.d.mts +151 -0
  371. package/dist/physics/magnetism/geomagnetism.mjs +184 -0
  372. package/dist/physics/magnetism/index.d.mts +12 -0
  373. package/dist/physics/magnetism/index.mjs +74 -0
  374. package/dist/physics/magnetism/lorentz.d.mts +28 -0
  375. package/dist/physics/magnetism/lorentz.mjs +88 -0
  376. package/dist/physics/magnetism/magnetic-force.d.mts +102 -0
  377. package/dist/physics/magnetism/magnetic-force.mjs +169 -0
  378. package/dist/physics/momentum/collide.d.mts +77 -0
  379. package/dist/physics/momentum/collide.mjs +115 -0
  380. package/dist/physics/momentum/impulse.d.mts +89 -0
  381. package/dist/physics/momentum/impulse.mjs +159 -0
  382. package/dist/physics/momentum/planar.d.mts +60 -0
  383. package/dist/physics/momentum/planar.mjs +110 -0
  384. package/dist/physics/projectile.d.mts +107 -0
  385. package/dist/physics/projectile.mjs +185 -0
  386. package/dist/physics/rotation/angular.d.mts +85 -0
  387. package/dist/physics/rotation/angular.mjs +130 -0
  388. package/dist/physics/rotation/circular.d.mts +79 -0
  389. package/dist/physics/rotation/circular.mjs +131 -0
  390. package/dist/physics/rotation/inertia.d.mts +164 -0
  391. package/dist/physics/rotation/inertia.mjs +332 -0
  392. package/dist/physics/rotation/torque.d.mts +65 -0
  393. package/dist/physics/rotation/torque.mjs +134 -0
  394. package/dist/physics/vectors.d.mts +64 -0
  395. package/dist/physics/vectors.mjs +114 -0
  396. package/dist/primitives/Dot.d.mts +2 -15
  397. package/dist/primitives/Dot.mjs +6 -4
  398. package/dist/primitives/Grid.d.mts +33 -17
  399. package/dist/primitives/Grid.mjs +89 -17
  400. package/dist/primitives/Label.d.mts +1 -14
  401. package/dist/primitives/Label.mjs +3 -2
  402. package/dist/primitives/Lines.d.mts +4 -32
  403. package/dist/primitives/Lines.mjs +10 -8
  404. package/dist/primitives/Shapes.d.mts +5 -43
  405. package/dist/primitives/Shapes.mjs +12 -10
  406. package/dist/primitives/index.d.mts +2 -2
  407. package/dist/primitives/index.mjs +2 -2
  408. package/dist/primitives/props.mjs +31 -0
  409. package/dist/quantity/index.d.mts +119 -0
  410. package/dist/quantity/index.mjs +417 -0
  411. package/dist/relation/derive.d.mts +74 -0
  412. package/dist/relation/derive.mjs +73 -0
  413. package/dist/relation/index.d.mts +56 -0
  414. package/dist/relation/index.mjs +234 -0
  415. package/dist/scene/Scene.d.mts +6 -1
  416. package/dist/scene/Scene.mjs +15 -40
  417. package/dist/scene/evaluators.mjs +1 -1
  418. package/dist/sim/thermal.mjs +45 -20
  419. package/dist/thermo/calorimetry.d.mts +207 -0
  420. package/dist/thermo/calorimetry.mjs +345 -0
  421. package/dist/thermo/cycles.d.mts +142 -0
  422. package/dist/thermo/cycles.mjs +256 -0
  423. package/dist/thermo/entropy.d.mts +87 -0
  424. package/dist/thermo/entropy.mjs +140 -0
  425. package/dist/thermo/expansion.d.mts +130 -0
  426. package/dist/thermo/expansion.mjs +178 -0
  427. package/dist/thermo/gas.d.mts +197 -0
  428. package/dist/thermo/gas.mjs +279 -0
  429. package/dist/thermo/heat-transfer.d.mts +164 -0
  430. package/dist/thermo/heat-transfer.mjs +217 -0
  431. package/dist/thermo/index.d.mts +9 -82
  432. package/dist/thermo/index.mjs +9 -132
  433. package/dist/thermo/processes.d.mts +95 -0
  434. package/dist/thermo/processes.mjs +154 -0
  435. package/dist/thermo/temperature.d.mts +174 -0
  436. package/dist/thermo/temperature.mjs +236 -0
  437. package/dist/view/Stage.mjs +4 -11
  438. package/dist/waves/damping.d.mts +224 -0
  439. package/dist/waves/damping.mjs +317 -0
  440. package/dist/waves/doppler.d.mts +72 -0
  441. package/dist/waves/doppler.mjs +125 -0
  442. package/dist/waves/index.d.mts +10 -0
  443. package/dist/waves/index.mjs +10 -0
  444. package/dist/waves/pendulum.d.mts +130 -0
  445. package/dist/waves/pendulum.mjs +211 -0
  446. package/dist/waves/pitch.d.mts +125 -0
  447. package/dist/waves/pitch.mjs +158 -0
  448. package/dist/waves/shm.d.mts +170 -0
  449. package/dist/waves/shm.mjs +279 -0
  450. package/dist/waves/springs.d.mts +103 -0
  451. package/dist/waves/springs.mjs +176 -0
  452. package/dist/waves/superposition.d.mts +124 -0
  453. package/dist/waves/superposition.mjs +196 -0
  454. package/dist/waves/wave.d.mts +154 -0
  455. package/dist/waves/wave.mjs +178 -0
  456. package/package.json +103 -23
  457. package/styles.css +125 -8
  458. package/dist/assets/kit/index.mjs +0 -4
@@ -0,0 +1,234 @@
1
+ //#region src/chem/complex.ts
2
+ /**
3
+ * complex, the geometry and colour of a transition-metal complex ion.
4
+ *
5
+ * Three questions the syllabus asks about every complex, and they are all answered from the same
6
+ * declaration: what shape is it, how many isomers has it, and why is it that colour.
7
+ *
8
+ * ## Coordination number is not the number of ligands
9
+ *
10
+ * It is the number of COORDINATE BONDS, so a bidentate ligand counts twice. That single sentence
11
+ * settles a question learners routinely get wrong: [Cu(en)₂]²⁺ has two ligands and a coordination
12
+ * number of four, and [Cr(C₂O₄)₃]³⁻ has three ligands and a coordination number of six.
13
+ *
14
+ * ## Why shape follows from the number and not from the metal, except when it does not
15
+ *
16
+ * Six is octahedral and two is linear, with no exceptions worth teaching. Four is the interesting
17
+ * one: usually tetrahedral, but square planar for platinum and palladium. That is not a detail. A
18
+ * square-planar complex shows cis-trans isomerism and a tetrahedral one CANNOT, because in a
19
+ * tetrahedron every position is adjacent to every other, so there is no "opposite" to be trans to.
20
+ *
21
+ * Cisplatin is the case that matters: it is the cis isomer of a square-planar platinum complex and
22
+ * it is a cancer drug, while the trans isomer is not. Had the geometry been tetrahedral there would
23
+ * have been no two isomers to tell apart.
24
+ *
25
+ * ## Colour is what is NOT absorbed
26
+ *
27
+ * A d-block ion's d orbitals split, and the gap ΔE corresponds to a wavelength in the visible
28
+ * range. The ion absorbs that wavelength and what reaches the eye is the rest, which is why copper
29
+ * sulfate is blue BECAUSE it absorbs orange. Stating it the other way round, that it is blue
30
+ * because it absorbs blue, is the commonest sentence in a wrong answer.
31
+ *
32
+ * Pure: no React, no pixels.
33
+ */
34
+ /** Planck constant, J s. */
35
+ const PLANCK = 6626e-37;
36
+ /** Speed of light, m/s. */
37
+ const LIGHT_SPEED = 2998e5;
38
+ /** Avogadro constant, per mole. */
39
+ const AVOGADRO = 6022e20;
40
+ const ligand = (name, formula, charge, denticity = 1) => ({
41
+ name,
42
+ formula,
43
+ charge,
44
+ denticity
45
+ });
46
+ /** The ligands the syllabus actually uses. */
47
+ const LIGANDS = {
48
+ water: ligand("water", "H2O", 0),
49
+ ammonia: ligand("ammonia", "NH3", 0),
50
+ chloride: ligand("chloride", "Cl", -1),
51
+ cyanide: ligand("cyanide", "CN", -1),
52
+ hydroxide: ligand("hydroxide", "OH", -1),
53
+ /** 1,2-diaminoethane, the standard neutral bidentate ligand. */
54
+ en: ligand("1,2-diaminoethane", "en", 0, 2),
55
+ /** Ethanedioate, the standard charged bidentate ligand. */
56
+ ethanedioate: ligand("ethanedioate", "C2O4", -2, 2)
57
+ };
58
+ /** Metals that are square planar at coordination number four. */
59
+ const SQUARE_PLANAR = new Set(["Pt", "Pd"]);
60
+ /** Coordinate bonds, NOT the number of ligands. A bidentate ligand counts twice. */
61
+ const coordinationNumber = (spec) => spec.ligands.reduce((n, l) => n + l.count * l.ligand.denticity, 0);
62
+ /** Overall charge: the metal's oxidation number plus every ligand's own charge. */
63
+ const complexCharge = (spec) => spec.ligands.reduce((q, l) => q + l.count * l.ligand.charge, spec.oxidation);
64
+ function geometryOf(spec) {
65
+ if (spec.geometry) return spec.geometry;
66
+ const n = coordinationNumber(spec);
67
+ if (n === 6) return "octahedral";
68
+ if (n === 2) return "linear";
69
+ if (n === 4) return SQUARE_PLANAR.has(spec.metal) ? "square-planar" : "tetrahedral";
70
+ throw new Error(`Coordination number ${n} for ${spec.metal} has no shape in this kernel. The syllabus uses 2, 4 and 6.`);
71
+ }
72
+ /** The bond angle a shape gives, in degrees. */
73
+ function bondAngle(geometry) {
74
+ switch (geometry) {
75
+ case "linear": return 180;
76
+ case "tetrahedral": return 109.5;
77
+ case "square-planar": return 90;
78
+ case "octahedral": return 90;
79
+ }
80
+ }
81
+ /** `[Cu(H2O)6]2+`, with the charge written the way a data book writes it. */
82
+ function complexFormula(spec) {
83
+ const inner = spec.ligands.map((l) => l.count === 1 ? `(${l.ligand.formula})` : `(${l.ligand.formula})${l.count}`).join("");
84
+ const q = complexCharge(spec);
85
+ const sign = q === 0 ? "" : q > 0 ? `${Math.abs(q) === 1 ? "" : Math.abs(q)}+` : `${Math.abs(q) === 1 ? "" : Math.abs(q)}-`;
86
+ return `[${spec.metal}${inner}]${sign}`;
87
+ }
88
+ /**
89
+ * How many stereoisomers a complex has, and what kind.
90
+ *
91
+ * Covers the shapes the syllabus sets and refuses to guess beyond them. The important NEGATIVE
92
+ * result is the tetrahedral one: no cis-trans isomerism at any composition, because a tetrahedron
93
+ * has no two positions opposite each other.
94
+ */
95
+ function stereoisomers(spec) {
96
+ const geometry = geometryOf(spec);
97
+ const mono = spec.ligands.filter((l) => l.ligand.denticity === 1);
98
+ const bi = spec.ligands.filter((l) => l.ligand.denticity === 2);
99
+ const counts = mono.map((l) => l.count).sort((a, b) => b - a);
100
+ const biTotal = bi.reduce((n, l) => n + l.count, 0);
101
+ if (geometry === "linear") return {
102
+ count: 1,
103
+ kinds: [],
104
+ reason: "Two ligands on a straight line have only one arrangement."
105
+ };
106
+ if (geometry === "tetrahedral") return {
107
+ count: 1,
108
+ kinds: [],
109
+ reason: "A tetrahedron has no two positions opposite each other, so there is nothing for a ligand to be trans to. Tetrahedral complexes show no cis-trans isomerism at all."
110
+ };
111
+ if (geometry === "square-planar") {
112
+ if (counts.length === 2 && counts[0] === 2 && counts[1] === 2) return {
113
+ count: 2,
114
+ kinds: ["cis", "trans"],
115
+ reason: "Two pairs in a square: the identical ligands are either adjacent (cis) or opposite (trans). This is the cisplatin case, where only the cis isomer is the drug."
116
+ };
117
+ return {
118
+ count: 1,
119
+ kinds: [],
120
+ reason: "Only one arrangement of these ligands in a square."
121
+ };
122
+ }
123
+ if (biTotal === 3) return {
124
+ count: 2,
125
+ kinds: ["optical pair"],
126
+ reason: "Three bidentate ligands around an octahedron make a propeller, and a propeller has a handedness. The two forms are non-superimposable mirror images."
127
+ };
128
+ if (biTotal === 2 && counts.length === 1 && counts[0] === 2) return {
129
+ count: 3,
130
+ kinds: ["cis (optical pair)", "trans"],
131
+ reason: "The two single ligands are adjacent or opposite. The trans form has a mirror plane; the cis form does not, so it is itself a pair of optical isomers. Two plus one is three."
132
+ };
133
+ if (counts.length === 2 && counts[0] === 4 && counts[1] === 2) return {
134
+ count: 2,
135
+ kinds: ["cis", "trans"],
136
+ reason: "The two odd ligands sit next to each other (cis) or on opposite corners (trans)."
137
+ };
138
+ if (counts.length === 2 && counts[0] === 3 && counts[1] === 3) return {
139
+ count: 2,
140
+ kinds: ["fac", "mer"],
141
+ reason: "Three and three: the matching set occupies one triangular face (fac) or a meridian running around the ion (mer)."
142
+ };
143
+ if (counts.length === 1) return {
144
+ count: 1,
145
+ kinds: [],
146
+ reason: "Six identical ligands have one arrangement."
147
+ };
148
+ return {
149
+ count: 1,
150
+ kinds: [],
151
+ reason: "No stereoisomerism for this combination in the cases the syllabus sets."
152
+ };
153
+ }
154
+ /** The splitting energy ΔE for light of this wavelength, in joules per photon. */
155
+ const splittingEnergy = (wavelengthNm) => PLANCK * LIGHT_SPEED / (wavelengthNm * 1e-9);
156
+ /** The same energy per mole, in kJ/mol, which is the form a question asks for. */
157
+ const splittingEnergyPerMole = (wavelengthNm) => splittingEnergy(wavelengthNm) * AVOGADRO / 1e3;
158
+ /** The wavelength whose photon energy is ΔE joules. */
159
+ const wavelengthFor = (joules) => PLANCK * LIGHT_SPEED / joules / 1e-9;
160
+ const WHEEL = [
161
+ {
162
+ from: 400,
163
+ to: 435,
164
+ absorbed: "violet",
165
+ seen: "yellow-green"
166
+ },
167
+ {
168
+ from: 435,
169
+ to: 480,
170
+ absorbed: "blue",
171
+ seen: "yellow"
172
+ },
173
+ {
174
+ from: 480,
175
+ to: 490,
176
+ absorbed: "green-blue",
177
+ seen: "orange"
178
+ },
179
+ {
180
+ from: 490,
181
+ to: 500,
182
+ absorbed: "blue-green",
183
+ seen: "red"
184
+ },
185
+ {
186
+ from: 500,
187
+ to: 560,
188
+ absorbed: "green",
189
+ seen: "purple"
190
+ },
191
+ {
192
+ from: 560,
193
+ to: 580,
194
+ absorbed: "yellow-green",
195
+ seen: "violet"
196
+ },
197
+ {
198
+ from: 580,
199
+ to: 595,
200
+ absorbed: "yellow",
201
+ seen: "blue"
202
+ },
203
+ {
204
+ from: 595,
205
+ to: 650,
206
+ absorbed: "orange",
207
+ seen: "blue-green"
208
+ },
209
+ {
210
+ from: 650,
211
+ to: 750,
212
+ absorbed: "red",
213
+ seen: "green-blue"
214
+ }
215
+ ];
216
+ /**
217
+ * What colour a solution looks, given the wavelength it absorbs.
218
+ *
219
+ * The answer is the COMPLEMENTARY colour, never the absorbed one. Copper sulfate absorbs orange
220
+ * light at about 600 nm and therefore looks blue, and a learner who says it looks blue because it
221
+ * absorbs blue has the physics exactly backwards.
222
+ */
223
+ function observedColour(wavelengthNm) {
224
+ const band = WHEEL.find((b) => wavelengthNm >= b.from && wavelengthNm < b.to);
225
+ if (!band) throw new Error(`${wavelengthNm} nm is outside the visible range (400-750 nm), so the complex would look colourless.`);
226
+ return {
227
+ absorbed: band.absorbed,
228
+ seen: band.seen,
229
+ wavelengthNm
230
+ };
231
+ }
232
+
233
+ //#endregion
234
+ export { AVOGADRO, LIGANDS, LIGHT_SPEED, PLANCK, bondAngle, complexCharge, complexFormula, coordinationNumber, geometryOf, observedColour, splittingEnergy, splittingEnergyPerMole, stereoisomers, wavelengthFor };
@@ -0,0 +1,75 @@
1
+ import { Quantity } from "../quantity/index.mjs";
2
+ import { Worked } from "../core/worked.mjs";
3
+
4
+ //#region src/chem/cycle.d.ts
5
+ /** kJ mol⁻¹ is what every data book and every mark scheme uses. */
6
+ declare const DEFAULT_CYCLE_UNIT = "kJ/mol";
7
+ interface CycleTerm {
8
+ /** "first ionisation energy of Na". Shown in the working. */
9
+ readonly label: string;
10
+ /** The enthalpy change for this step as written. Omit for the unknown. */
11
+ readonly value?: number | Quantity;
12
+ /** Stoichiometric multiplier, e.g. 2 chlorine atomisations in MgCl₂. Default 1. */
13
+ readonly per?: number;
14
+ /** The datum is quoted for the REVERSE of the step as written, so negate it. */
15
+ readonly reversed?: boolean;
16
+ }
17
+ interface CycleSpec {
18
+ readonly title?: string;
19
+ /** Unit for bare numbers. Default kJ/mol. */
20
+ readonly unit?: string;
21
+ /** The route: the steps that must sum to the total. */
22
+ readonly route: readonly CycleTerm[];
23
+ /** The overall change the route must add up to. */
24
+ readonly total: CycleTerm;
25
+ }
26
+ interface CycleSolution {
27
+ readonly label: string;
28
+ readonly value: number;
29
+ readonly unit: string;
30
+ readonly quantity: Quantity;
31
+ /** Negative is exothermic, by the convention above. */
32
+ readonly exothermic: boolean;
33
+ /** True when the solved term was the overall change rather than a step. */
34
+ readonly isTotal: boolean;
35
+ }
36
+ declare class Cycle {
37
+ readonly title: string;
38
+ readonly unit: string;
39
+ readonly route: readonly CycleTerm[];
40
+ readonly total: CycleTerm;
41
+ constructor(spec: CycleSpec);
42
+ /** The signed contribution of a term, in `this.unit`, with `per` and `reversed` applied. */
43
+ contribution(term: CycleTerm): number;
44
+ /** Sum of the steps that have values. */
45
+ private sumKnownSteps;
46
+ /**
47
+ * The one unknown term.
48
+ *
49
+ * If a step is unknown: step = total − (the other steps).
50
+ * If the total is unknown: total = the sum of the steps.
51
+ */
52
+ solve(): CycleSolution;
53
+ private report;
54
+ /**
55
+ * With every term known, how far the route misses the total by.
56
+ *
57
+ * Zero is a cycle that is self-consistent. This is the check worth running over an authored
58
+ * lesson: five data-book enthalpies and a quoted formation enthalpy that do NOT close to within a
59
+ * kilojoule mean one of the six numbers is wrong, and the lesson cannot say which.
60
+ */
61
+ residual(): number;
62
+ /** True when the cycle closes to within `tol` (default 1 kJ/mol, the data-book rounding). */
63
+ closes(tol?: number): boolean;
64
+ /**
65
+ * The working: every step with its sign, then the rearrangement and the answer.
66
+ *
67
+ * A `per` factor is shown as `2 × 122`, not as `244`, because the per-mole datum is the thing the
68
+ * mark scheme wants to see quoted.
69
+ */
70
+ steps(sf?: number): Worked;
71
+ }
72
+ /** `cycle({ route: [...], total: {...} })`. */
73
+ declare const cycle: (spec: CycleSpec) => Cycle;
74
+ //#endregion
75
+ export { Cycle, CycleSolution, CycleSpec, CycleTerm, DEFAULT_CYCLE_UNIT, cycle };
@@ -0,0 +1,145 @@
1
+ import { calc } from "../core/worked.mjs";
2
+ import { Quantity, qty } from "../quantity/index.mjs";
3
+
4
+ //#region src/chem/cycle.ts
5
+ /**
6
+ * cycle, an energy cycle: a list of steps whose enthalpies must sum to a known total.
7
+ *
8
+ * This is Hess's law as a data structure. The law says the enthalpy change between two states does
9
+ * not depend on the route, so any two routes between the same states sum to the same number:
10
+ *
11
+ * ΔH(step 1) + ΔH(step 2) + … + ΔH(step n) = ΔH(overall)
12
+ *
13
+ * Exactly one term may be unknown, anywhere in the cycle INCLUDING the total, and the kernel
14
+ * returns it. That single rule covers three different-looking exam questions:
15
+ *
16
+ * • Born-Haber — every step known except the lattice energy.
17
+ * • Hess's law — the route is known and the overall change is wanted.
18
+ * • enthalpy of formation from combustion data — the total is known and one step is not.
19
+ *
20
+ * WHY A CYCLE IS NOT A `relation`. A relation has a fixed set of named variables. A cycle has an
21
+ * arbitrary number of steps whose only constraint is that they sum, so expressing it as a relation
22
+ * would mean one relation per cycle shape: one for NaCl, another for MgCl₂, another for MgO.
23
+ *
24
+ * SIGN CONVENTION, which is where the marks are lost. Every value is the enthalpy change for the
25
+ * step AS WRITTEN, left to right, with exothermic negative. So the electron affinity of chlorine is
26
+ * −349 kJ mol⁻¹ and the lattice energy of NaCl is −787 kJ mol⁻¹, both being formation processes.
27
+ * A step written in the reverse direction takes the opposite sign, and `reversed` on a term says so
28
+ * explicitly rather than leaving the author to flip it by hand and hope.
29
+ *
30
+ * STOICHIOMETRY. `per` multiplies a step, because a Born-Haber cycle for MgCl₂ needs two chlorine
31
+ * atomisations and two electron affinities. Writing `per: 2` keeps the per-mole datum visible in the
32
+ * working, which is what the mark scheme wants to see; writing 244 instead of 2 × 122 hides it.
33
+ *
34
+ * Pure: no React, no pixels.
35
+ */
36
+ /** kJ mol⁻¹ is what every data book and every mark scheme uses. */
37
+ const DEFAULT_CYCLE_UNIT = "kJ/mol";
38
+ const known = (t) => t.value !== void 0;
39
+ var Cycle = class {
40
+ title;
41
+ unit;
42
+ route;
43
+ total;
44
+ constructor(spec) {
45
+ this.unit = spec.unit ?? "kJ/mol";
46
+ this.title = spec.title ?? "energy cycle";
47
+ this.route = spec.route;
48
+ this.total = spec.total;
49
+ if (!spec.route.length) throw new Error(`${this.title}: a cycle needs at least one step.`);
50
+ const unknowns = [...spec.route, spec.total].filter((t) => !known(t));
51
+ if (unknowns.length > 1) throw new Error(`${this.title}: ${unknowns.length} unknown terms (${unknowns.map((t) => t.label).join(", ")}). A cycle determines exactly one.`);
52
+ for (const t of spec.route) if (t.per !== void 0 && (!Number.isFinite(t.per) || t.per === 0)) throw new Error(`${this.title}: "${t.label}" has per = ${t.per}, which is not a count of steps.`);
53
+ }
54
+ /** The signed contribution of a term, in `this.unit`, with `per` and `reversed` applied. */
55
+ contribution(term) {
56
+ if (term.value === void 0) throw new Error(`${this.title}: "${term.label}" has no value.`);
57
+ return (term.value instanceof Quantity ? term.value.in(this.unit) : term.value) * (term.per ?? 1) * (term.reversed ? -1 : 1);
58
+ }
59
+ /** Sum of the steps that have values. */
60
+ sumKnownSteps() {
61
+ return this.route.filter(known).reduce((acc, t) => acc + this.contribution(t), 0);
62
+ }
63
+ /**
64
+ * The one unknown term.
65
+ *
66
+ * If a step is unknown: step = total − (the other steps).
67
+ * If the total is unknown: total = the sum of the steps.
68
+ */
69
+ solve() {
70
+ const missingStep = this.route.find((t) => !known(t));
71
+ const totalKnown = known(this.total);
72
+ if (!missingStep && totalKnown) throw new Error(`${this.title}: every term is known, so there is nothing to solve. Use residual() to check consistency.`);
73
+ if (!totalKnown) {
74
+ const value = this.sumKnownSteps();
75
+ return this.report(this.total, value, true);
76
+ }
77
+ const per = missingStep.per ?? 1;
78
+ const sign = missingStep.reversed ? -1 : 1;
79
+ const value = (this.contribution(this.total) - this.sumKnownSteps()) / per * sign;
80
+ return this.report(missingStep, value, false);
81
+ }
82
+ report(term, value, isTotal) {
83
+ return {
84
+ label: term.label,
85
+ value,
86
+ unit: this.unit,
87
+ quantity: qty(value, this.unit),
88
+ exothermic: value < 0,
89
+ isTotal
90
+ };
91
+ }
92
+ /**
93
+ * With every term known, how far the route misses the total by.
94
+ *
95
+ * Zero is a cycle that is self-consistent. This is the check worth running over an authored
96
+ * lesson: five data-book enthalpies and a quoted formation enthalpy that do NOT close to within a
97
+ * kilojoule mean one of the six numbers is wrong, and the lesson cannot say which.
98
+ */
99
+ residual() {
100
+ if (!known(this.total)) throw new Error(`${this.title}: the total is unknown, so use solve().`);
101
+ const missing = this.route.find((t) => !known(t));
102
+ if (missing) throw new Error(`${this.title}: "${missing.label}" is unknown, so use solve().`);
103
+ return this.sumKnownSteps() - this.contribution(this.total);
104
+ }
105
+ /** True when the cycle closes to within `tol` (default 1 kJ/mol, the data-book rounding). */
106
+ closes(tol = 1) {
107
+ return Math.abs(this.residual()) <= tol;
108
+ }
109
+ /**
110
+ * The working: every step with its sign, then the rearrangement and the answer.
111
+ *
112
+ * A `per` factor is shown as `2 × 122`, not as `244`, because the per-mole datum is the thing the
113
+ * mark scheme wants to see quoted.
114
+ */
115
+ steps(sf = 4) {
116
+ const solved = this.solve();
117
+ const w = calc().step(`\\sum \\Delta H(\\text{route}) = \\Delta H(\\text{overall})`, `${this.title}: Hess's law, so the route and the direct change agree`);
118
+ for (const t of this.route) {
119
+ const per = t.per ?? 1;
120
+ if (!known(t)) {
121
+ w.step(`${tex(t.label)} = ?`, "the unknown");
122
+ continue;
123
+ }
124
+ const base = t.value instanceof Quantity ? t.value.in(this.unit) : t.value;
125
+ const shown = per === 1 ? fmt(base, sf) : `${per} \\times ${fmt(base, sf)}`;
126
+ w.step(`${tex(t.label)} = ${shown}\\ \\mathrm{${this.unit}}`, t.reversed ? "quoted for the reverse step, so the sign flips" : void 0);
127
+ }
128
+ if (known(this.total)) {
129
+ w.step(`${tex(this.total.label)} = ${fmt(this.contribution(this.total), sf)}\\ \\mathrm{${this.unit}}`, "the overall change");
130
+ w.step(`${tex(solved.label)} = ${fmt(this.contribution(this.total), sf)} - (${fmt(this.sumKnownSteps(), sf)})`, "the unknown step is the overall change minus the rest of the route");
131
+ }
132
+ w.step(`${tex(solved.label)} = ${fmt(solved.value, sf)}\\ \\mathrm{${this.unit}}`, solved.exothermic ? "negative, so the step is exothermic" : "positive, so the step is endothermic");
133
+ return w.done(solved.value);
134
+ }
135
+ };
136
+ const tex = (label) => `\\Delta H(\\text{${label}})`;
137
+ function fmt(value, sf) {
138
+ if (!Number.isFinite(value)) return "\\text{?}";
139
+ return String(Number(value.toPrecision(sf)));
140
+ }
141
+ /** `cycle({ route: [...], total: {...} })`. */
142
+ const cycle = (spec) => new Cycle(spec);
143
+
144
+ //#endregion
145
+ export { Cycle, DEFAULT_CYCLE_UNIT, cycle };
@@ -0,0 +1,86 @@
1
+ //#region src/chem/equation.d.ts
2
+ /**
3
+ * Balancing a chemical equation, by solving it rather than by trial and error.
4
+ *
5
+ * ## Balancing is linear algebra, and saying so is the lesson
6
+ *
7
+ * Balancing is taught as inspection: try a number, see what breaks, adjust. That works for simple
8
+ * equations, fails on awkward ones, and teaches nothing about why an equation can be balanced at
9
+ * all. It is actually a small linear system. Write one row per element and one column per species,
10
+ * with reactants positive and products negative, and a balanced equation is any vector of
11
+ * coefficients the matrix sends to zero:
12
+ *
13
+ * Fe 3a - c = 0
14
+ * O 4a - 2d = 0 for a Fe3O4 + b CO -> c Fe + d CO2
15
+ * C b - d = 0
16
+ *
17
+ * So the coefficients are the null space of the element matrix, and the smallest whole-number
18
+ * answer is that vector cleared of fractions. Conservation of atoms is not a rule applied to the
19
+ * arithmetic, it IS the arithmetic.
20
+ *
21
+ * It also explains the cases inspection cannot. A system with no non-zero solution is an equation
22
+ * that cannot be balanced, which is a real answer and usually means a species is missing. A system
23
+ * with more than one independent solution is an equation that can be balanced in more than one way,
24
+ * which is also real and means the reaction as written is really two reactions.
25
+ *
26
+ * ## Exact arithmetic, because floating point cannot be trusted with this
27
+ *
28
+ * The elimination runs on fractions held as a numerator and a denominator, not on doubles. A
29
+ * coefficient is a whole number or the equation is wrong, and a solver that produced 2.9999999998
30
+ * would either have to round, which hides a genuine failure, or refuse a correct equation. Integers
31
+ * throughout means the answer is exactly right or honestly rejected.
32
+ *
33
+ * Pure: no React, no pixels.
34
+ */
35
+ /** One species in an equation, with the coefficient balancing gave it. */
36
+ interface Term {
37
+ readonly formula: string;
38
+ readonly coefficient: number;
39
+ }
40
+ interface BalancedEquation {
41
+ readonly reactants: readonly Term[];
42
+ readonly products: readonly Term[];
43
+ /** The elements the balance was solved over. */
44
+ readonly elements: readonly string[];
45
+ }
46
+ /** Split "a + b -> c + d" into its two sides, accepting the arrows people actually type. */
47
+ declare function splitEquation(input: string): {
48
+ left: string[];
49
+ right: string[];
50
+ };
51
+ /**
52
+ * Balance an equation, returning the smallest whole-number coefficients.
53
+ *
54
+ * `balance('Fe3O4 + CO -> Fe + CO2')`, `balance('C3H8 + O2 -> CO2 + H2O')`.
55
+ *
56
+ * Coefficients already written into the input are ignored: the whole point is to compute them, and
57
+ * accepting them would let a wrong one through unchallenged.
58
+ */
59
+ declare function balance(input: string): BalancedEquation;
60
+ /** Write a balanced equation out, leaving a coefficient of one unwritten. */
61
+ declare function formatEquation(equation: BalancedEquation, arrow?: string): string;
62
+ /**
63
+ * How many atoms of each element sit on each side.
64
+ *
65
+ * For showing the check rather than doing it: the balance is exact by construction, and a learner
66
+ * still has to see both columns match before they believe it.
67
+ */
68
+ declare function atomTally(equation: BalancedEquation): {
69
+ element: string;
70
+ left: number;
71
+ right: number;
72
+ }[];
73
+ /**
74
+ * Total charge on each side.
75
+ *
76
+ * Separate from the atom tally because it is a different conservation law, and because showing it
77
+ * beside the atoms is what makes an ionic equation make sense: the two sides of a precipitation
78
+ * carry the same charge as well as the same atoms, and a learner who only counts atoms will balance
79
+ * it wrongly and never see why.
80
+ */
81
+ declare function chargeTally(equation: BalancedEquation): {
82
+ left: number;
83
+ right: number;
84
+ };
85
+ //#endregion
86
+ export { BalancedEquation, Term, atomTally, balance, chargeTally, formatEquation, splitEquation };