@classytic/stage 0.3.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.
- package/dist/_virtual/_rolldown/runtime.mjs +18 -0
- package/dist/atomic/aufbau.d.mts +144 -0
- package/dist/atomic/aufbau.mjs +384 -0
- package/dist/atomic/elements.d.mts +67 -0
- package/dist/atomic/elements.mjs +785 -0
- package/dist/atomic/emission.d.mts +112 -0
- package/dist/atomic/emission.mjs +179 -0
- package/dist/atomic/index.d.mts +8 -0
- package/dist/atomic/index.mjs +9 -0
- package/dist/atomic/periodic.d.mts +82 -0
- package/dist/atomic/periodic.mjs +231 -0
- package/dist/atomic/properties.d.mts +100 -0
- package/dist/atomic/properties.mjs +574 -0
- package/dist/atomic/shells.d.mts +137 -0
- package/dist/atomic/shells.mjs +170 -0
- package/dist/atomic/trends.d.mts +148 -0
- package/dist/atomic/trends.mjs +225 -0
- package/dist/chem/chromatography.d.mts +66 -0
- package/dist/chem/chromatography.mjs +63 -0
- package/dist/chem/complex.d.mts +114 -0
- package/dist/chem/complex.mjs +234 -0
- package/dist/chem/cycle.d.mts +75 -0
- package/dist/chem/cycle.mjs +145 -0
- package/dist/chem/equation.d.mts +86 -0
- package/dist/chem/equation.mjs +168 -0
- package/dist/chem/formula.d.mts +90 -0
- package/dist/chem/formula.mjs +201 -0
- package/dist/chem/functional-groups.d.mts +31 -0
- package/dist/chem/functional-groups.mjs +187 -0
- package/dist/chem/hazard.d.mts +94 -0
- package/dist/chem/hazard.mjs +329 -0
- package/dist/chem/index.d.mts +34 -33
- package/dist/chem/index.mjs +34 -28
- package/dist/chem/ionic.d.mts +98 -0
- package/dist/chem/ionic.mjs +156 -0
- package/dist/chem/layout.d.mts +46 -0
- package/dist/chem/layout.mjs +342 -0
- package/dist/chem/lewis.d.mts +79 -0
- package/dist/chem/lewis.mjs +163 -0
- package/dist/chem/molecular-orbital.d.mts +114 -0
- package/dist/chem/molecular-orbital.mjs +201 -0
- package/dist/chem/molecule.d.mts +154 -0
- package/dist/chem/molecule.mjs +282 -0
- package/dist/chem/nmr.d.mts +153 -0
- package/dist/chem/nmr.mjs +165 -0
- package/dist/chem/polymer.d.mts +65 -0
- package/dist/chem/polymer.mjs +179 -0
- package/dist/chem/redox.d.mts +138 -0
- package/dist/chem/redox.mjs +328 -0
- package/dist/chem/relations.d.mts +183 -0
- package/dist/chem/relations.mjs +299 -0
- package/dist/chem/skeleton.d.mts +51 -0
- package/dist/chem/skeleton.mjs +186 -0
- package/dist/chem/solution.d.mts +116 -0
- package/dist/chem/solution.mjs +186 -0
- package/dist/chem/species.d.mts +47 -0
- package/dist/chem/species.mjs +128 -0
- package/dist/chem/stereo.d.mts +108 -0
- package/dist/chem/stereo.mjs +286 -0
- package/dist/chem/stoichiometry.d.mts +95 -0
- package/dist/chem/stoichiometry.mjs +145 -0
- package/dist/chem/synthesis.d.mts +113 -0
- package/dist/chem/synthesis.mjs +281 -0
- package/dist/chem/vsepr.d.mts +80 -0
- package/dist/chem/vsepr.mjs +247 -0
- package/dist/circuit/ac.d.mts +163 -0
- package/dist/circuit/ac.mjs +452 -0
- package/dist/circuit/dc.d.mts +92 -0
- package/dist/circuit/dc.mjs +333 -0
- package/dist/circuit/index.d.mts +5 -92
- package/dist/circuit/index.mjs +5 -332
- package/dist/circuit/network.d.mts +99 -0
- package/dist/circuit/network.mjs +440 -0
- package/dist/circuit/topology.d.mts +102 -0
- package/dist/circuit/topology.mjs +143 -0
- package/dist/colour/absorption.d.mts +79 -0
- package/dist/colour/absorption.mjs +137 -0
- package/dist/colour/index.d.mts +6 -0
- package/dist/colour/index.mjs +7 -0
- package/dist/colour/name.d.mts +14 -0
- package/dist/colour/name.mjs +76 -0
- package/dist/colour/observer.d.mts +31 -0
- package/dist/colour/observer.mjs +46 -0
- package/dist/colour/spectrum.d.mts +61 -0
- package/dist/colour/spectrum.mjs +108 -0
- package/dist/colour/srgb.d.mts +49 -0
- package/dist/colour/srgb.mjs +108 -0
- package/dist/core/constants.d.mts +138 -0
- package/dist/core/constants.mjs +139 -0
- package/dist/core/index.d.mts +3 -1
- package/dist/core/index.mjs +3 -1
- package/dist/core/vec3.d.mts +94 -0
- package/dist/core/vec3.mjs +123 -0
- package/dist/core/worked.d.mts +35 -0
- package/dist/core/worked.mjs +44 -0
- package/dist/electro/cell.d.mts +102 -0
- package/dist/electro/cell.mjs +170 -0
- package/dist/electro/conductivity.d.mts +157 -0
- package/dist/electro/conductivity.mjs +156 -0
- package/dist/electro/couples.d.mts +114 -0
- package/dist/electro/couples.mjs +334 -0
- package/dist/electro/electrolysis.d.mts +96 -0
- package/dist/electro/electrolysis.mjs +274 -0
- package/dist/electro/faraday.d.mts +150 -0
- package/dist/electro/faraday.mjs +118 -0
- package/dist/electro/index.d.mts +6 -0
- package/dist/electro/index.mjs +7 -0
- package/dist/finance/accounting/adjustments.d.mts +17 -0
- package/dist/finance/accounting/adjustments.mjs +202 -0
- package/dist/finance/accounting/analysis.d.mts +8 -0
- package/dist/finance/accounting/analysis.mjs +205 -0
- package/dist/finance/accounting/clubs.d.mts +21 -0
- package/dist/finance/accounting/clubs.mjs +217 -0
- package/dist/finance/accounting/company-statements.d.mts +7 -0
- package/dist/finance/accounting/company-statements.mjs +150 -0
- package/dist/finance/accounting/harness.d.mts +7 -0
- package/dist/finance/accounting/harness.mjs +45 -0
- package/dist/finance/accounting/index.d.mts +18 -0
- package/dist/finance/accounting/index.mjs +18 -0
- package/dist/finance/accounting/journalize.d.mts +16 -0
- package/dist/finance/accounting/journalize.mjs +128 -0
- package/dist/finance/accounting/manufacturing.d.mts +8 -0
- package/dist/finance/accounting/manufacturing.mjs +236 -0
- package/dist/finance/accounting/partnership.d.mts +7 -0
- package/dist/finance/accounting/partnership.mjs +152 -0
- package/dist/finance/accounting/period-case.d.mts +6 -0
- package/dist/finance/accounting/period-case.mjs +71 -0
- package/dist/finance/accounting/post.d.mts +9 -0
- package/dist/finance/accounting/post.mjs +84 -0
- package/dist/finance/accounting/precision.d.mts +9 -0
- package/dist/finance/accounting/precision.mjs +15 -0
- package/dist/finance/accounting/rules.d.mts +21 -0
- package/dist/finance/accounting/rules.mjs +37 -0
- package/dist/finance/accounting/solve.d.mts +28 -0
- package/dist/finance/accounting/solve.mjs +92 -0
- package/dist/finance/accounting/statements.d.mts +7 -0
- package/dist/finance/accounting/statements.mjs +142 -0
- package/dist/finance/accounting/types.d.mts +649 -0
- package/dist/finance/accounting/validate.d.mts +7 -0
- package/dist/finance/accounting/validate.mjs +67 -0
- package/dist/finance/accounting/verification.d.mts +24 -0
- package/dist/finance/accounting/verification.mjs +386 -0
- package/dist/finance/bizsim.d.mts +21 -39
- package/dist/finance/bizsim.mjs +276 -97
- package/dist/finance/index.d.mts +20 -43
- package/dist/finance/index.mjs +19 -77
- package/dist/finance/investment-math.d.mts +7 -0
- package/dist/finance/investment-math.mjs +18 -0
- package/dist/finance/management-accounting/budgeting.d.mts +7 -0
- package/dist/finance/management-accounting/budgeting.mjs +189 -0
- package/dist/finance/management-accounting/cash-budget.d.mts +7 -0
- package/dist/finance/management-accounting/cash-budget.mjs +154 -0
- package/dist/finance/management-accounting/costing.d.mts +8 -0
- package/dist/finance/management-accounting/costing.mjs +175 -0
- package/dist/finance/management-accounting/decisions.d.mts +8 -0
- package/dist/finance/management-accounting/decisions.mjs +164 -0
- package/dist/finance/management-accounting/index.d.mts +13 -0
- package/dist/finance/management-accounting/index.mjs +13 -0
- package/dist/finance/management-accounting/integrated-budget.d.mts +8 -0
- package/dist/finance/management-accounting/integrated-budget.mjs +67 -0
- package/dist/finance/management-accounting/inventory-costing.d.mts +7 -0
- package/dist/finance/management-accounting/inventory-costing.mjs +173 -0
- package/dist/finance/management-accounting/investment-appraisal.d.mts +7 -0
- package/dist/finance/management-accounting/investment-appraisal.mjs +148 -0
- package/dist/finance/management-accounting/management-case.d.mts +8 -0
- package/dist/finance/management-accounting/management-case.mjs +91 -0
- package/dist/finance/management-accounting/master-budget.d.mts +7 -0
- package/dist/finance/management-accounting/master-budget.mjs +137 -0
- package/dist/finance/management-accounting/production-costing.d.mts +8 -0
- package/dist/finance/management-accounting/production-costing.mjs +214 -0
- package/dist/finance/management-accounting/types.d.mts +644 -0
- package/dist/finance/management-accounting/variances.d.mts +7 -0
- package/dist/finance/management-accounting/variances.mjs +150 -0
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +1 -1
- package/dist/math/analysis.d.mts +61 -0
- package/dist/math/analysis.mjs +187 -0
- package/dist/math/answer-check.d.mts +50 -0
- package/dist/math/answer-check.mjs +106 -0
- package/dist/math/calculus.d.mts +4 -9
- package/dist/math/calculus.mjs +216 -7
- package/dist/math/complex.d.mts +93 -0
- package/dist/math/complex.mjs +239 -0
- package/dist/math/expr.d.mts +23 -0
- package/dist/math/expr.mjs +36 -0
- package/dist/math/index.d.mts +8 -22
- package/dist/math/index.mjs +8 -37
- package/dist/math/latex.mjs +2 -1
- package/dist/math/parse.mjs +1 -1
- package/dist/math/poly.d.mts +55 -0
- package/dist/math/poly.mjs +273 -0
- package/dist/math/rearrange.d.mts +14 -0
- package/dist/math/rearrange.mjs +125 -0
- package/dist/math/solve.d.mts +21 -0
- package/dist/math/solve.mjs +75 -0
- package/dist/networking/addressing.d.mts +26 -0
- package/dist/networking/addressing.mjs +33 -0
- package/dist/networking/cloud/access.d.mts +36 -0
- package/dist/networking/cloud/access.mjs +95 -0
- package/dist/networking/cloud/containers.d.mts +43 -0
- package/dist/networking/cloud/containers.mjs +54 -0
- package/dist/networking/cloud/edge.d.mts +17 -0
- package/dist/networking/cloud/edge.mjs +31 -0
- package/dist/networking/cloud/failure-domains.d.mts +19 -0
- package/dist/networking/cloud/failure-domains.mjs +22 -0
- package/dist/networking/cloud/geo-routing.d.mts +22 -0
- package/dist/networking/cloud/geo-routing.mjs +64 -0
- package/dist/networking/cloud/index.d.mts +18 -0
- package/dist/networking/cloud/index.mjs +19 -0
- package/dist/networking/cloud/inference-reliability.d.mts +30 -0
- package/dist/networking/cloud/inference-reliability.mjs +76 -0
- package/dist/networking/cloud/inference.d.mts +118 -0
- package/dist/networking/cloud/inference.mjs +179 -0
- package/dist/networking/cloud/infrastructure.d.mts +45 -0
- package/dist/networking/cloud/infrastructure.mjs +96 -0
- package/dist/networking/cloud/observability.d.mts +24 -0
- package/dist/networking/cloud/observability.mjs +34 -0
- package/dist/networking/cloud/orchestration.d.mts +32 -0
- package/dist/networking/cloud/orchestration.mjs +38 -0
- package/dist/networking/cloud/recovery.d.mts +21 -0
- package/dist/networking/cloud/recovery.mjs +27 -0
- package/dist/networking/cloud/releases.d.mts +42 -0
- package/dist/networking/cloud/releases.mjs +135 -0
- package/dist/networking/cloud/scaling.d.mts +25 -0
- package/dist/networking/cloud/scaling.mjs +65 -0
- package/dist/networking/cloud/secrets.d.mts +39 -0
- package/dist/networking/cloud/secrets.mjs +68 -0
- package/dist/networking/cloud/server-deployment.d.mts +29 -0
- package/dist/networking/cloud/server-deployment.mjs +72 -0
- package/dist/networking/cloud/storage.d.mts +16 -0
- package/dist/networking/cloud/storage.mjs +58 -0
- package/dist/networking/cloud/vpc.d.mts +18 -0
- package/dist/networking/cloud/vpc.mjs +100 -0
- package/dist/networking/delivery.d.mts +34 -0
- package/dist/networking/delivery.mjs +52 -0
- package/dist/networking/index.d.mts +47 -0
- package/dist/networking/index.mjs +47 -0
- package/dist/networking/ipv6.d.mts +28 -0
- package/dist/networking/ipv6.mjs +17 -0
- package/dist/networking/journeys.d.mts +83 -0
- package/dist/networking/journeys.mjs +161 -0
- package/dist/networking/models/airtime.d.mts +87 -0
- package/dist/networking/models/airtime.mjs +130 -0
- package/dist/networking/models/cellular.d.mts +157 -0
- package/dist/networking/models/cellular.mjs +252 -0
- package/dist/networking/models/encapsulation.d.mts +98 -0
- package/dist/networking/models/encapsulation.mjs +132 -0
- package/dist/networking/models/handover.d.mts +116 -0
- package/dist/networking/models/handover.mjs +209 -0
- package/dist/networking/models/handshake.d.mts +90 -0
- package/dist/networking/models/handshake.mjs +122 -0
- package/dist/networking/models/index.d.mts +14 -0
- package/dist/networking/models/index.mjs +15 -0
- package/dist/networking/models/media.d.mts +82 -0
- package/dist/networking/models/media.mjs +188 -0
- package/dist/networking/models/satellite.d.mts +86 -0
- package/dist/networking/models/satellite.mjs +121 -0
- package/dist/networking/models/spectrum.d.mts +83 -0
- package/dist/networking/models/spectrum.mjs +235 -0
- package/dist/networking/models/switch.d.mts +80 -0
- package/dist/networking/models/switch.mjs +155 -0
- package/dist/networking/models/vlan.d.mts +62 -0
- package/dist/networking/models/vlan.mjs +100 -0
- package/dist/networking/models/vpn.d.mts +66 -0
- package/dist/networking/models/vpn.mjs +125 -0
- package/dist/networking/models/wifi-privacy.d.mts +82 -0
- package/dist/networking/models/wifi-privacy.mjs +130 -0
- package/dist/networking/models/wifi.d.mts +240 -0
- package/dist/networking/models/wifi.mjs +562 -0
- package/dist/networking/nat-traversal.d.mts +24 -0
- package/dist/networking/nat-traversal.mjs +38 -0
- package/dist/networking/protocols.d.mts +17 -0
- package/dist/networking/protocols.mjs +57 -0
- package/dist/networking/queue.d.mts +12 -0
- package/dist/networking/queue.mjs +18 -0
- package/dist/networking/route-learning.d.mts +23 -0
- package/dist/networking/route-learning.mjs +43 -0
- package/dist/networking/routing.d.mts +11 -0
- package/dist/networking/routing.mjs +25 -0
- package/dist/networking/signals.d.mts +26 -0
- package/dist/networking/signals.mjs +35 -0
- package/dist/networking/subnet.d.mts +49 -0
- package/dist/networking/subnet.mjs +89 -0
- package/dist/networking/tcp-reliability.d.mts +24 -0
- package/dist/networking/tcp-reliability.mjs +38 -0
- package/dist/networking/topology/contract.d.mts +123 -0
- package/dist/networking/topology/index.d.mts +4 -0
- package/dist/networking/topology/index.mjs +4 -0
- package/dist/networking/topology/presets.d.mts +7 -0
- package/dist/networking/topology/presets.mjs +302 -0
- package/dist/networking/topology/simulation.d.mts +8 -0
- package/dist/networking/topology/simulation.mjs +179 -0
- package/dist/networking/wireless.d.mts +11 -0
- package/dist/networking/wireless.mjs +17 -0
- package/dist/optics/imaging.d.mts +218 -0
- package/dist/optics/imaging.mjs +276 -0
- package/dist/optics/index.d.mts +7 -0
- package/dist/optics/index.mjs +8 -0
- package/dist/optics/laws.d.mts +70 -0
- package/dist/optics/laws.mjs +195 -0
- package/dist/optics/prism.d.mts +148 -0
- package/dist/optics/prism.mjs +197 -0
- package/dist/optics/refraction.d.mts +201 -0
- package/dist/optics/refraction.mjs +270 -0
- package/dist/optics/wave-optics.d.mts +171 -0
- package/dist/optics/wave-optics.mjs +259 -0
- package/dist/physics/ac/index.d.mts +183 -0
- package/dist/physics/ac/index.mjs +203 -0
- package/dist/physics/dynamics/forces.d.mts +98 -0
- package/dist/physics/dynamics/forces.mjs +153 -0
- package/dist/physics/dynamics/friction.d.mts +76 -0
- package/dist/physics/dynamics/friction.mjs +119 -0
- package/dist/physics/dynamics/system.d.mts +68 -0
- package/dist/physics/dynamics/system.mjs +125 -0
- package/dist/physics/electrostatics/index.d.mts +169 -0
- package/dist/physics/electrostatics/index.mjs +249 -0
- package/dist/physics/energy/centre-of-mass.d.mts +115 -0
- package/dist/physics/energy/centre-of-mass.mjs +180 -0
- package/dist/physics/energy/conservation.d.mts +81 -0
- package/dist/physics/energy/conservation.mjs +129 -0
- package/dist/physics/energy/power.d.mts +90 -0
- package/dist/physics/energy/power.mjs +167 -0
- package/dist/physics/energy/spring.d.mts +73 -0
- package/dist/physics/energy/spring.mjs +129 -0
- package/dist/physics/energy/store.d.mts +63 -0
- package/dist/physics/energy/store.mjs +108 -0
- package/dist/physics/energy/theorem.d.mts +65 -0
- package/dist/physics/energy/theorem.mjs +113 -0
- package/dist/physics/energy/work.d.mts +114 -0
- package/dist/physics/energy/work.mjs +219 -0
- package/dist/physics/gravitation/body.d.mts +85 -0
- package/dist/physics/gravitation/body.mjs +168 -0
- package/dist/physics/gravitation/kepler.d.mts +127 -0
- package/dist/physics/gravitation/kepler.mjs +216 -0
- package/dist/physics/gravitation/law.d.mts +117 -0
- package/dist/physics/gravitation/law.mjs +175 -0
- package/dist/physics/gravitation/orbit.d.mts +119 -0
- package/dist/physics/gravitation/orbit.mjs +202 -0
- package/dist/physics/gravitation/potential.d.mts +114 -0
- package/dist/physics/gravitation/potential.mjs +202 -0
- package/dist/physics/gravitation/variation.d.mts +97 -0
- package/dist/physics/gravitation/variation.mjs +175 -0
- package/dist/physics/index.d.mts +35 -0
- package/dist/physics/index.mjs +36 -0
- package/dist/physics/integral-sweep.d.mts +63 -0
- package/dist/physics/integral-sweep.mjs +126 -0
- package/dist/physics/kinematics/equations.d.mts +47 -0
- package/dist/physics/kinematics/equations.mjs +126 -0
- package/dist/physics/kinematics/free-fall.d.mts +53 -0
- package/dist/physics/kinematics/free-fall.mjs +71 -0
- package/dist/physics/kinematics/given.d.mts +16 -0
- package/dist/physics/kinematics/given.mjs +55 -0
- package/dist/physics/kinematics/roots.d.mts +41 -0
- package/dist/physics/kinematics/roots.mjs +72 -0
- package/dist/physics/kinematics/solve.d.mts +52 -0
- package/dist/physics/kinematics/solve.mjs +172 -0
- package/dist/physics/magnetism/biot-savart.d.mts +161 -0
- package/dist/physics/magnetism/biot-savart.mjs +353 -0
- package/dist/physics/magnetism/flux.d.mts +94 -0
- package/dist/physics/magnetism/flux.mjs +313 -0
- package/dist/physics/magnetism/geomagnetism.d.mts +151 -0
- package/dist/physics/magnetism/geomagnetism.mjs +184 -0
- package/dist/physics/magnetism/index.d.mts +12 -0
- package/dist/physics/magnetism/index.mjs +74 -0
- package/dist/physics/magnetism/lorentz.d.mts +28 -0
- package/dist/physics/magnetism/lorentz.mjs +88 -0
- package/dist/physics/magnetism/magnetic-force.d.mts +102 -0
- package/dist/physics/magnetism/magnetic-force.mjs +169 -0
- package/dist/physics/momentum/collide.d.mts +77 -0
- package/dist/physics/momentum/collide.mjs +115 -0
- package/dist/physics/momentum/impulse.d.mts +89 -0
- package/dist/physics/momentum/impulse.mjs +159 -0
- package/dist/physics/momentum/planar.d.mts +60 -0
- package/dist/physics/momentum/planar.mjs +110 -0
- package/dist/physics/projectile.d.mts +107 -0
- package/dist/physics/projectile.mjs +185 -0
- package/dist/physics/rotation/angular.d.mts +85 -0
- package/dist/physics/rotation/angular.mjs +130 -0
- package/dist/physics/rotation/circular.d.mts +79 -0
- package/dist/physics/rotation/circular.mjs +131 -0
- package/dist/physics/rotation/inertia.d.mts +164 -0
- package/dist/physics/rotation/inertia.mjs +332 -0
- package/dist/physics/rotation/torque.d.mts +65 -0
- package/dist/physics/rotation/torque.mjs +134 -0
- package/dist/physics/vectors.d.mts +64 -0
- package/dist/physics/vectors.mjs +114 -0
- package/dist/quantity/index.d.mts +119 -0
- package/dist/quantity/index.mjs +417 -0
- package/dist/relation/derive.d.mts +74 -0
- package/dist/relation/derive.mjs +73 -0
- package/dist/relation/index.d.mts +56 -0
- package/dist/relation/index.mjs +234 -0
- package/dist/scene/evaluators.mjs +1 -1
- package/dist/sim/thermal.mjs +45 -20
- package/dist/thermo/calorimetry.d.mts +207 -0
- package/dist/thermo/calorimetry.mjs +345 -0
- package/dist/thermo/cycles.d.mts +142 -0
- package/dist/thermo/cycles.mjs +256 -0
- package/dist/thermo/entropy.d.mts +87 -0
- package/dist/thermo/entropy.mjs +140 -0
- package/dist/thermo/expansion.d.mts +130 -0
- package/dist/thermo/expansion.mjs +178 -0
- package/dist/thermo/gas.d.mts +197 -0
- package/dist/thermo/gas.mjs +279 -0
- package/dist/thermo/heat-transfer.d.mts +164 -0
- package/dist/thermo/heat-transfer.mjs +217 -0
- package/dist/thermo/index.d.mts +9 -82
- package/dist/thermo/index.mjs +9 -132
- package/dist/thermo/processes.d.mts +95 -0
- package/dist/thermo/processes.mjs +154 -0
- package/dist/thermo/temperature.d.mts +174 -0
- package/dist/thermo/temperature.mjs +236 -0
- package/dist/waves/damping.d.mts +224 -0
- package/dist/waves/damping.mjs +317 -0
- package/dist/waves/doppler.d.mts +72 -0
- package/dist/waves/doppler.mjs +125 -0
- package/dist/waves/index.d.mts +10 -0
- package/dist/waves/index.mjs +10 -0
- package/dist/waves/pendulum.d.mts +130 -0
- package/dist/waves/pendulum.mjs +211 -0
- package/dist/waves/pitch.d.mts +125 -0
- package/dist/waves/pitch.mjs +158 -0
- package/dist/waves/shm.d.mts +170 -0
- package/dist/waves/shm.mjs +279 -0
- package/dist/waves/springs.d.mts +103 -0
- package/dist/waves/springs.mjs +176 -0
- package/dist/waves/superposition.d.mts +124 -0
- package/dist/waves/superposition.mjs +196 -0
- package/dist/waves/wave.d.mts +154 -0
- package/dist/waves/wave.mjs +178 -0
- package/package.json +74 -2
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Structure } from "./molecule.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/chem/stereo.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Compare two branches by CIP priority.
|
|
6
|
+
*
|
|
7
|
+
* Returns a negative number when the first outranks the second, positive when the second does, and
|
|
8
|
+
* `undefined` when they cannot be separated within the spheres explored, which for a real molecule
|
|
9
|
+
* means they are the same branch.
|
|
10
|
+
*
|
|
11
|
+
* The comparison walks outward one sphere at a time. At each sphere both branches offer a list of
|
|
12
|
+
* atoms sorted heaviest first; the lists are compared position by position, and the first
|
|
13
|
+
* difference decides it. Only if they are identical does the walk go further out.
|
|
14
|
+
*/
|
|
15
|
+
declare function comparePriority(structure: Structure, centre: number, firstAtom: number, secondAtom: number, maxSpheres?: number): number | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* The same comparison, for two branches hanging off DIFFERENT atoms.
|
|
18
|
+
*
|
|
19
|
+
* Needed by the meso check, where the two branches being weighed against each other belong to the
|
|
20
|
+
* two ends of the molecule rather than to one centre. Passing both to {@link comparePriority} with
|
|
21
|
+
* a single centre let each walk escape backwards through the other's parent, so two branches that
|
|
22
|
+
* are plainly the same came out different and no meso form was ever found.
|
|
23
|
+
*/
|
|
24
|
+
declare function compareBranches(structure: Structure, firstAtom: number, firstFrom: number, secondAtom: number, secondFrom: number, maxSpheres?: number): number | undefined;
|
|
25
|
+
interface RankedSubstituent {
|
|
26
|
+
readonly atom: number;
|
|
27
|
+
readonly element: string;
|
|
28
|
+
/** 1 is highest priority. */
|
|
29
|
+
readonly rank: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The substituents of an atom in priority order, highest first.
|
|
33
|
+
*
|
|
34
|
+
* Returns undefined when two of them cannot be separated, which is the honest answer: a carbon with
|
|
35
|
+
* two identical groups on it has no handedness, and saying so is more useful than an arbitrary
|
|
36
|
+
* ordering that would then produce a confident R or S for a molecule that is neither.
|
|
37
|
+
*/
|
|
38
|
+
declare function rankSubstituents(structure: Structure, centre: number): RankedSubstituent[] | undefined;
|
|
39
|
+
interface Stereocentre {
|
|
40
|
+
readonly atom: number;
|
|
41
|
+
readonly substituents: readonly RankedSubstituent[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Every carbon with four different groups on it.
|
|
45
|
+
*
|
|
46
|
+
* The definition, applied rather than paraphrased. A carbon qualifies when the four things attached
|
|
47
|
+
* to it can all be told apart by the priority rules, which is a stronger statement than "four
|
|
48
|
+
* different atoms" and is why 3-methylhexane has a stereocentre and 3-methylpentane does not: in
|
|
49
|
+
* the second, two of the branches are both ethyl.
|
|
50
|
+
*/
|
|
51
|
+
declare function stereocentres(structure: Structure): Stereocentre[];
|
|
52
|
+
/**
|
|
53
|
+
* How many stereoisomers a molecule has, from its stereocentre count.
|
|
54
|
+
*
|
|
55
|
+
* Two arrangements at each centre, independently, so 2^n. This is the upper bound: see
|
|
56
|
+
* {@link stereoisomerCount} for the number a molecule with an internal mirror plane actually has.
|
|
57
|
+
*/
|
|
58
|
+
declare const maximumStereoisomers: (centres: number) => number;
|
|
59
|
+
interface StereoisomerCount {
|
|
60
|
+
readonly centres: number;
|
|
61
|
+
/** Two arrangements at each centre, independently. */
|
|
62
|
+
readonly maximum: number;
|
|
63
|
+
/** What the molecule actually has. */
|
|
64
|
+
readonly actual: number;
|
|
65
|
+
/** True when one arrangement is its own mirror image and so is not a separate substance. */
|
|
66
|
+
readonly hasMeso: boolean;
|
|
67
|
+
readonly because: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* How many stereoisomers a molecule really has.
|
|
71
|
+
*
|
|
72
|
+
* The rule of 2^n over-counts whenever the molecule has an internal mirror plane. Tartaric acid has
|
|
73
|
+
* two stereocentres and three isomers, not four: the arrangement with one centre left-handed and
|
|
74
|
+
* the other right-handed is superimposable on its own reflection, so the two that 2^n counts
|
|
75
|
+
* separately are one substance, and that substance is not optically active at all.
|
|
76
|
+
*
|
|
77
|
+
* Detected by asking whether the centres are mirror images of one another, which is checkable with
|
|
78
|
+
* the machinery already here: the branches leading away from each centre have to be
|
|
79
|
+
* indistinguishable, and indistinguishable is exactly what {@link comparePriority} reports when it
|
|
80
|
+
* cannot separate two branches.
|
|
81
|
+
*
|
|
82
|
+
* Restricted to the two-centre case, which is the one syllabuses set. A molecule with more centres
|
|
83
|
+
* and partial symmetry needs a full search over configurations, and this reports the maximum for
|
|
84
|
+
* those rather than guessing.
|
|
85
|
+
*/
|
|
86
|
+
declare function stereoisomerCount(structure: Structure): StereoisomerCount;
|
|
87
|
+
type DoubleBondShape = 'E' | 'Z';
|
|
88
|
+
interface DoubleBondStereo {
|
|
89
|
+
readonly a: number;
|
|
90
|
+
readonly b: number;
|
|
91
|
+
/** The higher-priority substituent on each carbon. */
|
|
92
|
+
readonly higherOnA: number;
|
|
93
|
+
readonly higherOnB: number;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Double bonds that can show E/Z, with the higher-priority group on each end identified.
|
|
97
|
+
*
|
|
98
|
+
* A double bond has a shape worth naming only when BOTH its carbons carry two different groups. If
|
|
99
|
+
* either end has two of the same thing, swapping them changes nothing and there is only one
|
|
100
|
+
* compound, which is why 1,1-dichloroethene has no isomers and 1,2-dichloroethene has two.
|
|
101
|
+
*
|
|
102
|
+
* Which arrangement is E and which is Z cannot be read off the connection table: it depends on
|
|
103
|
+
* where the groups actually are in space, which a graph does not record. So this reports WHICH
|
|
104
|
+
* group wins on each end, and the geometry has to say the rest.
|
|
105
|
+
*/
|
|
106
|
+
declare function stereogenicDoubleBonds(structure: Structure): DoubleBondStereo[];
|
|
107
|
+
//#endregion
|
|
108
|
+
export { DoubleBondShape, DoubleBondStereo, RankedSubstituent, Stereocentre, StereoisomerCount, compareBranches, comparePriority, maximumStereoisomers, rankSubstituents, stereocentres, stereogenicDoubleBonds, stereoisomerCount };
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { elementBySymbol } from "../atomic/elements.mjs";
|
|
2
|
+
import { bondsAt, implicitHydrogens, otherEnd } from "./molecule.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/chem/stereo.ts
|
|
5
|
+
/**
|
|
6
|
+
* Stereochemistry: which carbons have a handedness, and which way round each one is.
|
|
7
|
+
*
|
|
8
|
+
* ## The question, and why ranking is the whole of it
|
|
9
|
+
*
|
|
10
|
+
* Two molecules can have the same atoms joined in the same order and still be different substances,
|
|
11
|
+
* because four different groups around one carbon can be arranged two ways that are mirror images
|
|
12
|
+
* and cannot be superimposed. Telling those two apart needs a rule for which group outranks which,
|
|
13
|
+
* and that rule is the Cahn-Ingold-Prelog priority.
|
|
14
|
+
*
|
|
15
|
+
* Almost every treatment gives the rule as "highest atomic number first, and if there is a tie,
|
|
16
|
+
* look further out". The second half is where all the difficulty lives and it is where textbooks
|
|
17
|
+
* stop. Looking further out means comparing two whole branches, and the comparison has a definite
|
|
18
|
+
* shape: gather the atoms at distance one from each branch, sort them heaviest first, compare the
|
|
19
|
+
* two lists; if they match, gather distance two, and so on. It is a breadth-first comparison of
|
|
20
|
+
* trees, and written that way it is neither vague nor hard.
|
|
21
|
+
*
|
|
22
|
+
* {@link comparePriority} does exactly that, so a learner can be shown the sphere at which two
|
|
23
|
+
* branches finally differed rather than being told the answer.
|
|
24
|
+
*
|
|
25
|
+
* ## Double bonds are duplicated, which is the part that looks like a trick
|
|
26
|
+
*
|
|
27
|
+
* A doubly bonded oxygen counts twice. That is not a convention invented to break ties: the rule is
|
|
28
|
+
* that an atom joined by a double bond is treated as joined to two of that atom, because a double
|
|
29
|
+
* bond is two connections. So the carbon of an aldehyde carries (O, O, H) and the carbon of an
|
|
30
|
+
* alcohol carries (O, H, H), and the aldehyde outranks it for a reason rather than by decree.
|
|
31
|
+
*
|
|
32
|
+
* ## What this covers, and what it refuses
|
|
33
|
+
*
|
|
34
|
+
* Tetrahedral carbon centres, and double bonds for E/Z. The priority comparison explores outward
|
|
35
|
+
* through the whole molecule, so it settles the cases that matter at this level.
|
|
36
|
+
*
|
|
37
|
+
* It does NOT implement the auxiliary descriptors CIP needs for rings that are symmetric until
|
|
38
|
+
* several spheres out, or for deciding between two branches that are identical in constitution and
|
|
39
|
+
* differ only in their own stereochemistry. Those exist, they are genuinely intricate, and a
|
|
40
|
+
* kernel that guessed at them would be wrong quietly. {@link comparePriority} returns undefined
|
|
41
|
+
* where it cannot separate two branches, and everything above it reports that rather than picking.
|
|
42
|
+
*
|
|
43
|
+
* Pure: no React, no pixels.
|
|
44
|
+
*/
|
|
45
|
+
/** Proton number, which is what "higher atomic number wins" is comparing. */
|
|
46
|
+
const protons = (element) => elementBySymbol(element)?.Z ?? 0;
|
|
47
|
+
/**
|
|
48
|
+
* The atoms one step out from `atom`, coming from `cameFrom`, heaviest first.
|
|
49
|
+
*
|
|
50
|
+
* Doubly and triply bonded neighbours appear as many times as the bond order, which is the
|
|
51
|
+
* duplication rule. Implicit hydrogens are included, because a hydrogen is a substituent and
|
|
52
|
+
* leaving it out would make a CH2 look like a CH.
|
|
53
|
+
*/
|
|
54
|
+
function neighbours(structure, atom, cameFrom) {
|
|
55
|
+
const out = [];
|
|
56
|
+
for (const bond of bondsAt(structure, atom)) {
|
|
57
|
+
const other = otherEnd(bond, atom);
|
|
58
|
+
const element = structure.atoms[other].element;
|
|
59
|
+
const order = bond.aromatic ? 2 : bond.order;
|
|
60
|
+
if (other !== cameFrom) out.push({
|
|
61
|
+
element,
|
|
62
|
+
id: other
|
|
63
|
+
});
|
|
64
|
+
for (let k = 1; k < order; k += 1) out.push({
|
|
65
|
+
element,
|
|
66
|
+
id: void 0
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
for (let k = 0; k < implicitHydrogens(structure, atom); k += 1) out.push({
|
|
70
|
+
element: "H",
|
|
71
|
+
id: void 0
|
|
72
|
+
});
|
|
73
|
+
return out.sort((a, b) => protons(b.element) - protons(a.element));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Compare two branches by CIP priority.
|
|
77
|
+
*
|
|
78
|
+
* Returns a negative number when the first outranks the second, positive when the second does, and
|
|
79
|
+
* `undefined` when they cannot be separated within the spheres explored, which for a real molecule
|
|
80
|
+
* means they are the same branch.
|
|
81
|
+
*
|
|
82
|
+
* The comparison walks outward one sphere at a time. At each sphere both branches offer a list of
|
|
83
|
+
* atoms sorted heaviest first; the lists are compared position by position, and the first
|
|
84
|
+
* difference decides it. Only if they are identical does the walk go further out.
|
|
85
|
+
*/
|
|
86
|
+
function comparePriority(structure, centre, firstAtom, secondAtom, maxSpheres = 12) {
|
|
87
|
+
return compareBranches(structure, firstAtom, centre, secondAtom, centre, maxSpheres);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The same comparison, for two branches hanging off DIFFERENT atoms.
|
|
91
|
+
*
|
|
92
|
+
* Needed by the meso check, where the two branches being weighed against each other belong to the
|
|
93
|
+
* two ends of the molecule rather than to one centre. Passing both to {@link comparePriority} with
|
|
94
|
+
* a single centre let each walk escape backwards through the other's parent, so two branches that
|
|
95
|
+
* are plainly the same came out different and no meso form was ever found.
|
|
96
|
+
*/
|
|
97
|
+
function compareBranches(structure, firstAtom, firstFrom, secondAtom, secondFrom, maxSpheres = 12) {
|
|
98
|
+
const first = protons(structure.atoms[firstAtom].element);
|
|
99
|
+
const second = protons(structure.atoms[secondAtom].element);
|
|
100
|
+
if (first !== second) return second - first;
|
|
101
|
+
let frontA = [{
|
|
102
|
+
atom: firstAtom,
|
|
103
|
+
cameFrom: firstFrom
|
|
104
|
+
}];
|
|
105
|
+
let frontB = [{
|
|
106
|
+
atom: secondAtom,
|
|
107
|
+
cameFrom: secondFrom
|
|
108
|
+
}];
|
|
109
|
+
for (let sphere = 0; sphere < maxSpheres; sphere += 1) {
|
|
110
|
+
if (frontA.length === 0 && frontB.length === 0) return void 0;
|
|
111
|
+
const expand = (front) => {
|
|
112
|
+
const list = [];
|
|
113
|
+
const next = [];
|
|
114
|
+
for (const node of front) for (const n of neighbours(structure, node.atom, node.cameFrom)) {
|
|
115
|
+
list.push(n.element);
|
|
116
|
+
if (n.id !== void 0) next.push({
|
|
117
|
+
atom: n.id,
|
|
118
|
+
cameFrom: node.atom
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
list,
|
|
123
|
+
next
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
const a = expand(frontA);
|
|
127
|
+
const b = expand(frontB);
|
|
128
|
+
const sortedA = [...a.list].sort((x, y) => protons(y) - protons(x));
|
|
129
|
+
const sortedB = [...b.list].sort((x, y) => protons(y) - protons(x));
|
|
130
|
+
const length = Math.max(sortedA.length, sortedB.length);
|
|
131
|
+
for (let k = 0; k < length; k += 1) {
|
|
132
|
+
const pa = protons(sortedA[k] ?? "");
|
|
133
|
+
const pb = protons(sortedB[k] ?? "");
|
|
134
|
+
if (pa !== pb) return pb - pa;
|
|
135
|
+
}
|
|
136
|
+
frontA = a.next;
|
|
137
|
+
frontB = b.next;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The substituents of an atom in priority order, highest first.
|
|
142
|
+
*
|
|
143
|
+
* Returns undefined when two of them cannot be separated, which is the honest answer: a carbon with
|
|
144
|
+
* two identical groups on it has no handedness, and saying so is more useful than an arbitrary
|
|
145
|
+
* ordering that would then produce a confident R or S for a molecule that is neither.
|
|
146
|
+
*/
|
|
147
|
+
function rankSubstituents(structure, centre) {
|
|
148
|
+
const attached = bondsAt(structure, centre).map((bond) => otherEnd(bond, centre));
|
|
149
|
+
const hydrogens = implicitHydrogens(structure, centre);
|
|
150
|
+
if (attached.length + hydrogens !== 4) return void 0;
|
|
151
|
+
const sorted = [...attached];
|
|
152
|
+
for (let i = 0; i < sorted.length; i += 1) for (let j = i + 1; j < sorted.length; j += 1) {
|
|
153
|
+
const order = comparePriority(structure, centre, sorted[i], sorted[j]);
|
|
154
|
+
if (order === void 0) return void 0;
|
|
155
|
+
if (order > 0) [sorted[i], sorted[j]] = [sorted[j], sorted[i]];
|
|
156
|
+
}
|
|
157
|
+
const ranked = sorted.map((atom, index) => ({
|
|
158
|
+
atom,
|
|
159
|
+
element: structure.atoms[atom].element,
|
|
160
|
+
rank: index + 1
|
|
161
|
+
}));
|
|
162
|
+
if (hydrogens === 1) return ranked;
|
|
163
|
+
if (hydrogens === 0) return ranked;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Every carbon with four different groups on it.
|
|
167
|
+
*
|
|
168
|
+
* The definition, applied rather than paraphrased. A carbon qualifies when the four things attached
|
|
169
|
+
* to it can all be told apart by the priority rules, which is a stronger statement than "four
|
|
170
|
+
* different atoms" and is why 3-methylhexane has a stereocentre and 3-methylpentane does not: in
|
|
171
|
+
* the second, two of the branches are both ethyl.
|
|
172
|
+
*/
|
|
173
|
+
function stereocentres(structure) {
|
|
174
|
+
const found = [];
|
|
175
|
+
for (const atom of structure.atoms) {
|
|
176
|
+
if (atom.element !== "C" || atom.aromatic) continue;
|
|
177
|
+
const substituents = rankSubstituents(structure, atom.id);
|
|
178
|
+
if (substituents) found.push({
|
|
179
|
+
atom: atom.id,
|
|
180
|
+
substituents
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
return found;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* How many stereoisomers a molecule has, from its stereocentre count.
|
|
187
|
+
*
|
|
188
|
+
* Two arrangements at each centre, independently, so 2^n. This is the upper bound: see
|
|
189
|
+
* {@link stereoisomerCount} for the number a molecule with an internal mirror plane actually has.
|
|
190
|
+
*/
|
|
191
|
+
const maximumStereoisomers = (centres) => 2 ** centres;
|
|
192
|
+
/**
|
|
193
|
+
* Whether two stereocentres are mirror images of each other through the middle of the molecule.
|
|
194
|
+
*
|
|
195
|
+
* True when the branches leading away from each centre are the same on both sides, which is what
|
|
196
|
+
* makes the molecule superimposable on its own reflection.
|
|
197
|
+
*/
|
|
198
|
+
function centresArePaired(structure, first, second) {
|
|
199
|
+
const branchesOf = (centre, away) => bondsAt(structure, centre).map((bond) => otherEnd(bond, centre)).filter((id) => id !== away);
|
|
200
|
+
const a = branchesOf(first, second);
|
|
201
|
+
const b = branchesOf(second, first);
|
|
202
|
+
if (a.length !== b.length) return false;
|
|
203
|
+
const spare = [...b];
|
|
204
|
+
for (const branch of a) {
|
|
205
|
+
const match = spare.findIndex((other) => compareBranches(structure, branch, first, other, second) === void 0);
|
|
206
|
+
if (match === -1) return false;
|
|
207
|
+
spare.splice(match, 1);
|
|
208
|
+
}
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* How many stereoisomers a molecule really has.
|
|
213
|
+
*
|
|
214
|
+
* The rule of 2^n over-counts whenever the molecule has an internal mirror plane. Tartaric acid has
|
|
215
|
+
* two stereocentres and three isomers, not four: the arrangement with one centre left-handed and
|
|
216
|
+
* the other right-handed is superimposable on its own reflection, so the two that 2^n counts
|
|
217
|
+
* separately are one substance, and that substance is not optically active at all.
|
|
218
|
+
*
|
|
219
|
+
* Detected by asking whether the centres are mirror images of one another, which is checkable with
|
|
220
|
+
* the machinery already here: the branches leading away from each centre have to be
|
|
221
|
+
* indistinguishable, and indistinguishable is exactly what {@link comparePriority} reports when it
|
|
222
|
+
* cannot separate two branches.
|
|
223
|
+
*
|
|
224
|
+
* Restricted to the two-centre case, which is the one syllabuses set. A molecule with more centres
|
|
225
|
+
* and partial symmetry needs a full search over configurations, and this reports the maximum for
|
|
226
|
+
* those rather than guessing.
|
|
227
|
+
*/
|
|
228
|
+
function stereoisomerCount(structure) {
|
|
229
|
+
const centres = stereocentres(structure);
|
|
230
|
+
const maximum = maximumStereoisomers(centres.length);
|
|
231
|
+
if (centres.length !== 2) return {
|
|
232
|
+
centres: centres.length,
|
|
233
|
+
maximum,
|
|
234
|
+
actual: maximum,
|
|
235
|
+
hasMeso: false,
|
|
236
|
+
because: centres.length < 2 ? "with fewer than two stereocentres there is no internal mirror plane to lose an isomer to" : "more than two centres are not searched for internal symmetry here, so this is the upper bound"
|
|
237
|
+
};
|
|
238
|
+
const paired = centresArePaired(structure, centres[0].atom, centres[1].atom);
|
|
239
|
+
return {
|
|
240
|
+
centres: 2,
|
|
241
|
+
maximum,
|
|
242
|
+
actual: paired ? maximum - 1 : maximum,
|
|
243
|
+
hasMeso: paired,
|
|
244
|
+
because: paired ? "the two centres carry the same groups, so the arrangement with one of each handedness is superimposable on its own mirror image: that meso form is one substance rather than two, and it is not optically active" : "the two centres carry different groups, so every one of the four arrangements is a distinct substance"
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Double bonds that can show E/Z, with the higher-priority group on each end identified.
|
|
249
|
+
*
|
|
250
|
+
* A double bond has a shape worth naming only when BOTH its carbons carry two different groups. If
|
|
251
|
+
* either end has two of the same thing, swapping them changes nothing and there is only one
|
|
252
|
+
* compound, which is why 1,1-dichloroethene has no isomers and 1,2-dichloroethene has two.
|
|
253
|
+
*
|
|
254
|
+
* Which arrangement is E and which is Z cannot be read off the connection table: it depends on
|
|
255
|
+
* where the groups actually are in space, which a graph does not record. So this reports WHICH
|
|
256
|
+
* group wins on each end, and the geometry has to say the rest.
|
|
257
|
+
*/
|
|
258
|
+
function stereogenicDoubleBonds(structure) {
|
|
259
|
+
const found = [];
|
|
260
|
+
for (const bond of structure.bonds) {
|
|
261
|
+
if (bond.order !== 2 || bond.aromatic) continue;
|
|
262
|
+
const higher = (end, far) => {
|
|
263
|
+
const groups = bondsAt(structure, end).map((b) => otherEnd(b, end)).filter((id) => id !== far);
|
|
264
|
+
const hydrogens = implicitHydrogens(structure, end);
|
|
265
|
+
if (groups.length + hydrogens !== 2) return void 0;
|
|
266
|
+
if (groups.length === 1 && hydrogens === 1) return groups[0];
|
|
267
|
+
if (groups.length !== 2) return void 0;
|
|
268
|
+
const order = comparePriority(structure, end, groups[0], groups[1]);
|
|
269
|
+
if (order === void 0) return void 0;
|
|
270
|
+
return order < 0 ? groups[0] : groups[1];
|
|
271
|
+
};
|
|
272
|
+
const higherOnA = higher(bond.a, bond.b);
|
|
273
|
+
const higherOnB = higher(bond.b, bond.a);
|
|
274
|
+
if (higherOnA === void 0 || higherOnB === void 0) continue;
|
|
275
|
+
found.push({
|
|
276
|
+
a: bond.a,
|
|
277
|
+
b: bond.b,
|
|
278
|
+
higherOnA,
|
|
279
|
+
higherOnB
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
return found;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
//#endregion
|
|
286
|
+
export { compareBranches, comparePriority, maximumStereoisomers, rankSubstituents, stereocentres, stereogenicDoubleBonds, stereoisomerCount };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { BalancedEquation } from "./equation.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/chem/stoichiometry.d.ts
|
|
4
|
+
/** How much of a substance is present, in whichever way it was measured. */
|
|
5
|
+
interface Amount {
|
|
6
|
+
/** Grams. */
|
|
7
|
+
readonly mass?: number;
|
|
8
|
+
/** Moles, if they are known directly. */
|
|
9
|
+
readonly moles?: number;
|
|
10
|
+
/** Volume in cubic decimetres, for something in solution. */
|
|
11
|
+
readonly volumeDm3?: number;
|
|
12
|
+
/** Concentration in mol per cubic decimetre, used with the volume. */
|
|
13
|
+
readonly concentration?: number;
|
|
14
|
+
}
|
|
15
|
+
/** Moles of a substance from however it was given. */
|
|
16
|
+
declare function molesOf(formula: string, amount: Amount): number;
|
|
17
|
+
interface ReactantState {
|
|
18
|
+
readonly formula: string;
|
|
19
|
+
readonly coefficient: number;
|
|
20
|
+
readonly moles: number;
|
|
21
|
+
/**
|
|
22
|
+
* Moles available divided by the coefficient: how many times the equation can run on this one.
|
|
23
|
+
*
|
|
24
|
+
* The number the whole calculation turns on, and the one a mass comparison never reaches.
|
|
25
|
+
*/
|
|
26
|
+
readonly equationRuns: number;
|
|
27
|
+
readonly limiting: boolean;
|
|
28
|
+
/** Moles of this reactant left when the limiting one is spent. */
|
|
29
|
+
readonly leftOver: number;
|
|
30
|
+
}
|
|
31
|
+
interface ProductState {
|
|
32
|
+
readonly formula: string;
|
|
33
|
+
readonly coefficient: number;
|
|
34
|
+
readonly moles: number;
|
|
35
|
+
readonly mass: number;
|
|
36
|
+
}
|
|
37
|
+
interface Reaction {
|
|
38
|
+
readonly equation: BalancedEquation;
|
|
39
|
+
/** How many times the equation as written can proceed. */
|
|
40
|
+
readonly runs: number;
|
|
41
|
+
readonly reactants: readonly ReactantState[];
|
|
42
|
+
readonly products: readonly ProductState[];
|
|
43
|
+
readonly limiting: ReactantState;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Work a reaction out from the amounts given.
|
|
47
|
+
*
|
|
48
|
+
* The equation is balanced here rather than taken on trust, because every mole ratio below depends
|
|
49
|
+
* on the coefficients and an unbalanced equation would make all of them confidently wrong.
|
|
50
|
+
*/
|
|
51
|
+
declare function react(equation: string, amounts: Readonly<Record<string, Amount>>): Reaction;
|
|
52
|
+
/** The most of a product the equation allows, in grams. */
|
|
53
|
+
declare function theoreticalYield(reaction: Reaction, product: string): number;
|
|
54
|
+
interface YieldReport {
|
|
55
|
+
/** Measured over theoretical, as a percentage. Reported even when it is impossible. */
|
|
56
|
+
readonly percent: number;
|
|
57
|
+
/** Why this figure cannot be a yield, when it cannot be. */
|
|
58
|
+
readonly impossible?: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The measured mass as a percentage of the theoretical one, whatever it comes to.
|
|
62
|
+
*
|
|
63
|
+
* Reports rather than refuses, because a figure above a hundred per cent is a real thing that comes
|
|
64
|
+
* out of a real balance and is the single most useful measurement in the topic: it is the one that
|
|
65
|
+
* tells you something is wrong with the sample. A lab teaching that has to be able to SHOW 112 per
|
|
66
|
+
* cent before asking what it means.
|
|
67
|
+
*
|
|
68
|
+
* What it must not do is call that a yield. So the number comes back with the reason it is not one
|
|
69
|
+
* attached, and {@link percentageYield} is the strict form built on this for the places where the
|
|
70
|
+
* value has to be legitimate.
|
|
71
|
+
*/
|
|
72
|
+
declare function apparentYield(theoretical: number, measured: number): YieldReport;
|
|
73
|
+
/**
|
|
74
|
+
* The fraction of the theoretical yield actually obtained, as a percentage.
|
|
75
|
+
*
|
|
76
|
+
* Refuses anything above a hundred. A flask cannot produce more than the equation allows, so a
|
|
77
|
+
* figure above it is evidence about the sample rather than about the reaction, and reporting it as
|
|
78
|
+
* a yield hides the one piece of information the number actually carries. Use {@link apparentYield}
|
|
79
|
+
* where that figure needs to be shown rather than accepted.
|
|
80
|
+
*/
|
|
81
|
+
declare function percentageYield(theoretical: number, measured: number): number;
|
|
82
|
+
/**
|
|
83
|
+
* The mass of one reactant needed to use up a given amount of another exactly.
|
|
84
|
+
*
|
|
85
|
+
* What a preparation asks: how much of this do I weigh out so nothing is wasted.
|
|
86
|
+
*/
|
|
87
|
+
declare function amountNeeded(equation: string, have: {
|
|
88
|
+
formula: string;
|
|
89
|
+
amount: Amount;
|
|
90
|
+
}, want: string): {
|
|
91
|
+
moles: number;
|
|
92
|
+
mass: number;
|
|
93
|
+
};
|
|
94
|
+
//#endregion
|
|
95
|
+
export { Amount, ProductState, ReactantState, Reaction, YieldReport, amountNeeded, apparentYield, molesOf, percentageYield, react, theoreticalYield };
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { molarMass } from "./formula.mjs";
|
|
2
|
+
import { balance } from "./equation.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/chem/stoichiometry.ts
|
|
5
|
+
/**
|
|
6
|
+
* How much of what: limiting reagent, theoretical yield, and what is left over.
|
|
7
|
+
*
|
|
8
|
+
* ## The whole subject is one division
|
|
9
|
+
*
|
|
10
|
+
* Every quantitative question reduces to the same move. Convert what you have into moles, divide
|
|
11
|
+
* each by its coefficient, and whichever comes out smallest runs out first. That ratio, the moles
|
|
12
|
+
* available per unit of equation, is the only number that matters: it says how many times the
|
|
13
|
+
* reaction as written can proceed, and everything else is that number multiplied by a coefficient.
|
|
14
|
+
*
|
|
15
|
+
* So {@link react} computes it once and reads every answer off it. There is no separate method for
|
|
16
|
+
* limiting reagent, for yield, for excess: they are three questions about one quantity.
|
|
17
|
+
*
|
|
18
|
+
* ## The limiting reagent is not the one there is least of
|
|
19
|
+
*
|
|
20
|
+
* The most common mistake in the topic, and it is a mistake about what "least" means. Ten grams of
|
|
21
|
+
* hydrogen and twenty-five of oxygen: there is less hydrogen by mass and far more by moles, and the
|
|
22
|
+
* oxygen runs out first because the equation needs two hydrogens for every oxygen. Comparing masses
|
|
23
|
+
* answers a question nobody asked, and comparing moles without dividing by the coefficients answers
|
|
24
|
+
* a different wrong one.
|
|
25
|
+
*
|
|
26
|
+
* ## Yield is measured, and the theoretical figure is not a prediction of it
|
|
27
|
+
*
|
|
28
|
+
* The theoretical yield is what the equation permits. What comes out of a flask is always less:
|
|
29
|
+
* some product stays in solution, some is lost in transfer, the reaction may not finish. So
|
|
30
|
+
* {@link percentageYield} takes a measured mass and compares, and a yield above a hundred per cent
|
|
31
|
+
* is refused rather than reported, because it means the product is wet or impure and the useful
|
|
32
|
+
* answer is to say so.
|
|
33
|
+
*
|
|
34
|
+
* Pure: no React, no pixels.
|
|
35
|
+
*/
|
|
36
|
+
/** Moles of a substance from however it was given. */
|
|
37
|
+
function molesOf(formula, amount) {
|
|
38
|
+
if (amount.moles !== void 0) return amount.moles;
|
|
39
|
+
if (amount.mass !== void 0) return amount.mass / molarMass(formula);
|
|
40
|
+
if (amount.volumeDm3 !== void 0 && amount.concentration !== void 0) return amount.volumeDm3 * amount.concentration;
|
|
41
|
+
throw new Error(`not enough to work out the moles of ${formula}: give a mass, a number of moles, or a volume together with a concentration`);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Work a reaction out from the amounts given.
|
|
45
|
+
*
|
|
46
|
+
* The equation is balanced here rather than taken on trust, because every mole ratio below depends
|
|
47
|
+
* on the coefficients and an unbalanced equation would make all of them confidently wrong.
|
|
48
|
+
*/
|
|
49
|
+
function react(equation, amounts) {
|
|
50
|
+
const balanced = balance(equation);
|
|
51
|
+
const named = new Set(Object.keys(amounts));
|
|
52
|
+
const missing = balanced.reactants.filter((r) => !named.has(r.formula));
|
|
53
|
+
if (missing.length > 0) throw new Error(`no amount given for ${missing.map((r) => r.formula).join(" or ")}: every reactant needs one, since the limiting one cannot be found without them all`);
|
|
54
|
+
const withRuns = balanced.reactants.map((term) => {
|
|
55
|
+
const moles = molesOf(term.formula, amounts[term.formula]);
|
|
56
|
+
return {
|
|
57
|
+
term,
|
|
58
|
+
moles,
|
|
59
|
+
equationRuns: moles / term.coefficient
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
const runs = Math.min(...withRuns.map((r) => r.equationRuns));
|
|
63
|
+
const limitingRuns = runs;
|
|
64
|
+
const reactants = withRuns.map(({ term, moles, equationRuns }) => ({
|
|
65
|
+
formula: term.formula,
|
|
66
|
+
coefficient: term.coefficient,
|
|
67
|
+
moles,
|
|
68
|
+
equationRuns,
|
|
69
|
+
limiting: equationRuns === limitingRuns,
|
|
70
|
+
leftOver: moles - limitingRuns * term.coefficient
|
|
71
|
+
}));
|
|
72
|
+
return {
|
|
73
|
+
equation: balanced,
|
|
74
|
+
runs,
|
|
75
|
+
reactants,
|
|
76
|
+
products: balanced.products.map((term) => ({
|
|
77
|
+
formula: term.formula,
|
|
78
|
+
coefficient: term.coefficient,
|
|
79
|
+
moles: runs * term.coefficient,
|
|
80
|
+
mass: runs * term.coefficient * molarMass(term.formula)
|
|
81
|
+
})),
|
|
82
|
+
limiting: reactants.find((r) => r.limiting)
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/** The most of a product the equation allows, in grams. */
|
|
86
|
+
function theoreticalYield(reaction, product) {
|
|
87
|
+
const found = reaction.products.find((p) => p.formula === product);
|
|
88
|
+
if (!found) throw new Error(`${product} is not a product of this reaction: it makes ${reaction.products.map((p) => p.formula).join(", ")}`);
|
|
89
|
+
return found.mass;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The measured mass as a percentage of the theoretical one, whatever it comes to.
|
|
93
|
+
*
|
|
94
|
+
* Reports rather than refuses, because a figure above a hundred per cent is a real thing that comes
|
|
95
|
+
* out of a real balance and is the single most useful measurement in the topic: it is the one that
|
|
96
|
+
* tells you something is wrong with the sample. A lab teaching that has to be able to SHOW 112 per
|
|
97
|
+
* cent before asking what it means.
|
|
98
|
+
*
|
|
99
|
+
* What it must not do is call that a yield. So the number comes back with the reason it is not one
|
|
100
|
+
* attached, and {@link percentageYield} is the strict form built on this for the places where the
|
|
101
|
+
* value has to be legitimate.
|
|
102
|
+
*/
|
|
103
|
+
function apparentYield(theoretical, measured) {
|
|
104
|
+
if (theoretical <= 0) throw new Error("a theoretical yield of zero has no percentage");
|
|
105
|
+
if (measured < 0) throw new Error("a measured mass cannot be negative");
|
|
106
|
+
const percent = measured / theoretical * 100;
|
|
107
|
+
if (percent > 100.0001) return {
|
|
108
|
+
percent,
|
|
109
|
+
impossible: `${measured} g is ${percent.toFixed(1)} per cent of the ${theoretical.toFixed(3)} g the equation allows. A reaction cannot make more than its theoretical maximum, so the product is wet or impure, or the balance is wrong.`
|
|
110
|
+
};
|
|
111
|
+
return { percent };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The fraction of the theoretical yield actually obtained, as a percentage.
|
|
115
|
+
*
|
|
116
|
+
* Refuses anything above a hundred. A flask cannot produce more than the equation allows, so a
|
|
117
|
+
* figure above it is evidence about the sample rather than about the reaction, and reporting it as
|
|
118
|
+
* a yield hides the one piece of information the number actually carries. Use {@link apparentYield}
|
|
119
|
+
* where that figure needs to be shown rather than accepted.
|
|
120
|
+
*/
|
|
121
|
+
function percentageYield(theoretical, measured) {
|
|
122
|
+
const report = apparentYield(theoretical, measured);
|
|
123
|
+
if (report.impossible) throw new Error(report.impossible);
|
|
124
|
+
return report.percent;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* The mass of one reactant needed to use up a given amount of another exactly.
|
|
128
|
+
*
|
|
129
|
+
* What a preparation asks: how much of this do I weigh out so nothing is wasted.
|
|
130
|
+
*/
|
|
131
|
+
function amountNeeded(equation, have, want) {
|
|
132
|
+
const balanced = balance(equation);
|
|
133
|
+
const from = balanced.reactants.find((r) => r.formula === have.formula);
|
|
134
|
+
const to = balanced.reactants.find((r) => r.formula === want);
|
|
135
|
+
if (!from) throw new Error(`${have.formula} is not a reactant in this equation`);
|
|
136
|
+
if (!to) throw new Error(`${want} is not a reactant in this equation`);
|
|
137
|
+
const moles = molesOf(have.formula, have.amount) / from.coefficient * to.coefficient;
|
|
138
|
+
return {
|
|
139
|
+
moles,
|
|
140
|
+
mass: moles * molarMass(want)
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
//#endregion
|
|
145
|
+
export { amountNeeded, apparentYield, molesOf, percentageYield, react, theoreticalYield };
|