-
Notifications
You must be signed in to change notification settings - Fork 19
/
Chemical Equation Balancer.ahk
executable file
·187 lines (160 loc) · 5.22 KB
/
Chemical Equation Balancer.ahk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#NoEnv
;wip: 2Al + 3H2SO4 -> 3H2 + Al2S3O12
Gui, Font, s18 Bold, Arial
Gui, Add, Text, x2 y0 w470 h40, Enter a chemical equation to balance:
Gui, Font, Norm
Gui, Add, Edit, x2 y40 w470 h40 vEquation gStartBalance, Al + H2SO4 -> H2 + Al2S3O12
Gui, Font, s12
Gui, Add, Edit, x2 y90 w470 h25 vResult ReadOnly
Gui, +ToolWindow +AlwaysOnTop
Gui, Show, w475 h120, Equation Balancer
Gosub, Balance
Return
GuiClose:
ExitApp
StartBalance:
SetTimer, Balance, -500
Return
Balance:
GuiControlGet, Equation,, Equation
If !RegExMatch(Equation,"S)^\s*(\d*[A-Z][\da-zA-Z]*\s*(?:\+\s*\d*[A-Z][\da-zA-Z]*\s*)*)->\s*(\d*[A-Z][\da-zA-Z]*\s*(?:\+\s*\d*[A-Z][\da-zA-Z]*\s*)*)$",Match)
{
GuiControl,, Result, Invalid equation.
Return
}
LeftSide := []
RightSide := []
LeftTerms := []
RightTerms := []
For Index, Entry In [[LeftSide,Match1,LeftTerms],[RightSide,Match2,RightTerms]]
{
Side := Entry[1], Match := Entry[2], Terms := Entry[3]
TermPos := 1
While, TermPos := RegExMatch(Match,"OS)\d*\K[A-Z][\da-zA-Z]*",TermMatch,TermPos)
{
Value := TermMatch.Value(0)
Terms.Insert(Value)
Side[A_Index] := Term := {}
FactorPos := 1
While, FactorPos := RegExMatch(Value,"OS)([A-Z][a-z]*)(\d*)",FactorMatch,FactorPos)
{
Amount := FactorMatch.Value(2)
If !Amount
Amount := 1
Term[FactorMatch.Value(1)] := Amount
FactorPos += FactorMatch.Len(0)
}
TermPos += TermMatch.Len(0)
}
}
Result := BalanceChemicalEquation(LeftSide,RightSide)
MsgBox % ShowObject(Result)
;wip
GuiControl,, Result, %Value%
Return
BalanceChemicalEquation(LeftSide,RightSide)
{
;count elements and assign a row number to each one
Elements := {}
Rows := 0
For Index, Term In LeftSide
{
For Element, Amount In Term
{
If !Elements.HasKey(Element)
{
Rows ++
Elements[Element] := Rows
}
}
}
;initialize the matrix
Columns := LeftSide.MaxIndex() + RightSide.MaxIndex() + 1
Matrix := []
Loop, %Rows%
{
Row := Matrix[A_Index] := []
Loop, %Columns%
Row[A_Index] := 0
}
;fill the matrix with the amount of each element in each term (LeftSide[Element]-RightSide[Element]=0)
For Index, Term In LeftSide
{
For Element, Amount In Term
Matrix[Elements[Element],Index] := Amount
}
Columns := LeftSide.MaxIndex()
For Index, Term In RightSide
{
For Element, Amount In Term
Matrix[Elements[Element],Index + Columns] := -Amount
}
Result := LinearSystemSolve(Matrix)
;convert result entries into integers
GCD := new Fraction(0)
For Index, Entry In Result
GCD.GCD(Entry)
For Index, Entry In Result
Result[Index] := -Result[Index].Divide(GCD).Numerator
Result.Insert(GCD.Denominator)
Return, Result
}
#Include @Completed/Functions and Scriptlets/Fraction/Fraction.ahk
LinearSystemSolve(Matrix)
{
Unknowns := Matrix.MaxIndex()
;convert each number into a fraction
Loop, %Unknowns%
{
Row := Matrix[A_Index]
Loop, % Row.MaxIndex()
Row[A_Index] := new Fraction(Row[A_Index])
}
Column := 0
Loop, % Unknowns - 1
{
Column ++
;find the row with the largest value in the current column
RowMaximum := Column
RowIndex := Column
While, RowIndex < Unknowns ;iterate through each row after the current one
{
RowIndex ++ ;obtain the row index
If Matrix[RowIndex][Column].Clone().Abs().Greater(Matrix[RowMaximum][Column].Clone().Abs()) ;value greater than previous maximum
RowMaximum := RowIndex
}
;swap the maximum row and the current row
Row := Matrix[Column]
Matrix[Column] := Matrix[RowMaximum]
Matrix[RowMaximum] := Row
;check if the matrix has a singular unique solution
If Matrix[Column][Column].Numerator = 0
throw Exception("Could not find singular unique solution.")
;eliminate the unknown in the current column
RowIndex := Column
While, RowIndex < Unknowns ;iterate through each row after the current one
{
RowIndex ++ ;obtain the row index
Index := Unknowns + 1
While, Index >= Column
{
Matrix[RowIndex][Index].Subtract(Matrix[Column][Index].Clone().Multiply(Matrix[RowIndex][Column]).Divide(Matrix[Column][Column]))
Index --
}
}
}
;substitute known coefficients for unknowns
Result := []
RowIndex := Unknowns
While, RowIndex > 0 ;loop through each unknown backwards
{
Row := Matrix[RowIndex]
Value := new Fraction(0)
Index := Unknowns
While, Index > RowIndex
Value.Add(Result[Index].Clone().Multiply(Row[Index])), Index --
Result[RowIndex] := Row[Unknowns + 1].Clone().Subtract(Value).Divide(Row[RowIndex])
RowIndex --
}
Return, Result
}