Taizun commited on
Commit
761e949
·
verified ·
1 Parent(s): 30be10a

Upload 15 files

Browse files
Files changed (16) hide show
  1. .gitattributes +1 -0
  2. .replit +62 -0
  3. app.py +327 -0
  4. auth.py +131 -0
  5. generated-icon.png +3 -0
  6. models.py +45 -0
  7. package-lock.json +0 -0
  8. package.json +28 -0
  9. plotter.py +79 -0
  10. pyproject.toml +20 -0
  11. replit.nix +6 -0
  12. requirements.txt +5 -0
  13. solver.py +182 -0
  14. style.css +158 -0
  15. utils.py +49 -0
  16. uv.lock +0 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ generated-icon.png filter=lfs diff=lfs merge=lfs -text
.replit ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ modules = ["python-3.11", "nodejs-20", "postgresql-16"]
2
+
3
+ [nix]
4
+ channel = "stable-24_05"
5
+
6
+ [deployment]
7
+ deploymentTarget = "autoscale"
8
+ run = ["sh", "-c", "streamlit run main.py --server.port 5000"]
9
+
10
+ [workflows]
11
+ runButton = "Project"
12
+
13
+ [[workflows.workflow]]
14
+ name = "Project"
15
+ mode = "parallel"
16
+ author = "agent"
17
+
18
+ [[workflows.workflow.tasks]]
19
+ task = "workflow.run"
20
+ args = "Streamlit App"
21
+
22
+ [[workflows.workflow.tasks]]
23
+ task = "workflow.run"
24
+ args = "Backend Server"
25
+
26
+ [[workflows.workflow]]
27
+ name = "Streamlit App"
28
+ author = "agent"
29
+
30
+ [workflows.workflow.metadata]
31
+ agentRequireRestartOnSave = false
32
+
33
+ [[workflows.workflow.tasks]]
34
+ task = "packager.installForAll"
35
+
36
+ [[workflows.workflow.tasks]]
37
+ task = "shell.exec"
38
+ args = "streamlit run main.py --server.port 5000"
39
+ waitForPort = 5000
40
+
41
+ [[workflows.workflow]]
42
+ name = "Backend Server"
43
+ author = "agent"
44
+
45
+ [workflows.workflow.metadata]
46
+ agentRequireRestartOnSave = false
47
+
48
+ [[workflows.workflow.tasks]]
49
+ task = "packager.installForAll"
50
+
51
+ [[workflows.workflow.tasks]]
52
+ task = "shell.exec"
53
+ args = "PYTHONPATH=/home/runner/workspace python -m uvicorn backend.main:app --host 0.0.0.0 --port 8000 --log-level debug"
54
+ waitForPort = 8000
55
+
56
+ [[ports]]
57
+ localPort = 5000
58
+ externalPort = 80
59
+
60
+ [[ports]]
61
+ localPort = 8000
62
+ externalPort = 8000
app.py ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import sympy as sp
3
+ from solver import solve_equation, generate_steps
4
+ from plotter import plot_function
5
+ from utils import load_css, initialize_session_state
6
+ from auth import (
7
+ login_user, signup_user, is_logged_in, logout_user,
8
+ update_profile, get_user_profile, change_password
9
+ )
10
+ from models import History, SessionLocal, init_db
11
+ import base64
12
+ from io import BytesIO
13
+
14
+ # Initialize database
15
+ init_db()
16
+
17
+ def save_to_history(equation: str, solution: str):
18
+ """Save equation and solution to user's history."""
19
+ if not is_logged_in():
20
+ return
21
+
22
+ db = SessionLocal()
23
+ try:
24
+ history = History(
25
+ user_id=st.session_state.user_id,
26
+ equation=equation,
27
+ solution=solution
28
+ )
29
+ db.add(history)
30
+ db.commit()
31
+ finally:
32
+ db.close()
33
+
34
+ def load_user_history():
35
+ """Load user's solution history."""
36
+ if not is_logged_in():
37
+ return []
38
+
39
+ db = SessionLocal()
40
+ try:
41
+ return db.query(History).filter(
42
+ History.user_id == st.session_state.user_id
43
+ ).order_by(History.created_at.desc()).all()
44
+ finally:
45
+ db.close()
46
+
47
+ def render_math_symbols():
48
+ """Render mathematical symbols for input."""
49
+ st.markdown("### Mathematical Operators")
50
+
51
+ cols = st.columns(8)
52
+ all_symbols = {
53
+ # Basic arithmetic
54
+ '×': '*',
55
+ '÷': '/',
56
+ '^': '^',
57
+ '=': '=',
58
+ '(': '(',
59
+ ')': ')',
60
+
61
+ # Functions and special operators
62
+ '√': '√',
63
+ '∫': '∫',
64
+ 'd/dx': 'd/dx',
65
+ '!': '!',
66
+ 'ℒ': 'L',
67
+ '∑': 'sum',
68
+ '∏': 'prod',
69
+ '|': '|',
70
+
71
+ # Constants
72
+ 'π': 'pi',
73
+ 'e': 'e',
74
+ 'i': 'i',
75
+ '∞': 'oo',
76
+
77
+ # Trigonometric
78
+ 'sin': 'sin(',
79
+ 'cos': 'cos(',
80
+ 'tan': 'tan(',
81
+ 'csc': 'csc(',
82
+ 'sec': 'sec(',
83
+ 'cot': 'cot(',
84
+
85
+ # Inverse trigonometric
86
+ 'sin⁻¹': 'sin⁻¹(',
87
+ 'cos⁻¹': 'cos⁻¹(',
88
+ 'tan⁻¹': 'tan⁻¹(',
89
+
90
+ # Other functions
91
+ 'ln': 'ln(',
92
+ 'log': 'log(',
93
+ 'e^': 'e^',
94
+ '|x|': 'abs(',
95
+ }
96
+
97
+ for i, (label, symbol) in enumerate(all_symbols.items()):
98
+ col_idx = i % 8
99
+ if cols[col_idx].button(label, key=f"btn_{label}", help=f"Insert {label}"):
100
+ if 'equation' not in st.session_state:
101
+ st.session_state.equation = ''
102
+ st.session_state.equation += symbol
103
+
104
+ def render_profile_settings():
105
+ """Render user profile settings page."""
106
+ st.title("Profile Settings")
107
+
108
+ user = get_user_profile(st.session_state.user_id)
109
+
110
+ # Profile photo upload
111
+ st.subheader("Profile Photo")
112
+ uploaded_file = st.file_uploader("Choose a profile photo", type=['jpg', 'jpeg', 'png'])
113
+ if uploaded_file:
114
+ # Convert to base64
115
+ bytes_data = uploaded_file.getvalue()
116
+ base64_image = base64.b64encode(bytes_data).decode()
117
+ success, message = update_profile(st.session_state.user_id, profile_photo=base64_image)
118
+ if success:
119
+ st.success("Profile photo updated!")
120
+ else:
121
+ st.error(message)
122
+
123
+ # Display current photo if exists
124
+ if user.profile_photo:
125
+ st.image(base64.b64decode(user.profile_photo), width=150)
126
+
127
+ # Personal Information
128
+ st.subheader("Personal Information")
129
+ with st.form("profile_form"):
130
+ full_name = st.text_input("Full Name", value=user.full_name or "")
131
+ email = st.text_input("Email", value=user.email or "")
132
+ school = st.text_input("School", value=user.school or "")
133
+ grade = st.text_input("Grade/Year", value=user.grade or "")
134
+
135
+ if st.form_submit_button("Update Profile"):
136
+ success, message = update_profile(
137
+ st.session_state.user_id,
138
+ full_name=full_name,
139
+ email=email,
140
+ school=school,
141
+ grade=grade
142
+ )
143
+ if success:
144
+ st.success("Profile updated successfully!")
145
+ else:
146
+ st.error(message)
147
+
148
+ # Password Change
149
+ st.subheader("Change Password")
150
+ with st.form("password_form"):
151
+ current_password = st.text_input("Current Password", type="password")
152
+ new_password = st.text_input("New Password", type="password")
153
+ confirm_password = st.text_input("Confirm New Password", type="password")
154
+
155
+ if st.form_submit_button("Change Password"):
156
+ if new_password != confirm_password:
157
+ st.error("New passwords do not match")
158
+ else:
159
+ success, message = change_password(
160
+ st.session_state.user_id,
161
+ current_password,
162
+ new_password
163
+ )
164
+ if success:
165
+ st.success("Password updated successfully!")
166
+ else:
167
+ st.error(message)
168
+
169
+ def render_auth_page():
170
+ """Render login/signup page."""
171
+ st.title("Mathematical Problem Solver")
172
+
173
+ tab1, tab2 = st.tabs(["Login", "Sign Up"])
174
+
175
+ with tab1:
176
+ st.header("Login")
177
+ login_username = st.text_input("Username", key="login_username")
178
+ login_password = st.text_input("Password", type="password", key="login_password")
179
+
180
+ if st.button("Login"):
181
+ if login_user(login_username, login_password):
182
+ st.success("Successfully logged in!")
183
+ st.rerun()
184
+ else:
185
+ st.error("Invalid username or password")
186
+
187
+ with tab2:
188
+ st.header("Sign Up")
189
+ signup_username = st.text_input("Username", key="signup_username")
190
+ signup_password = st.text_input("Password", type="password", key="signup_password")
191
+ confirm_password = st.text_input("Confirm Password", type="password")
192
+
193
+ st.info("Password must be at least 8 characters long and contain at least one uppercase letter.")
194
+
195
+ if st.button("Sign Up"):
196
+ if signup_password != confirm_password:
197
+ st.error("Passwords do not match")
198
+ else:
199
+ success, message = signup_user(signup_username, signup_password)
200
+ if success:
201
+ st.success("Account created successfully!")
202
+ st.rerun()
203
+ else:
204
+ st.error(message)
205
+
206
+ def main():
207
+ # Initialize session state and load CSS
208
+ initialize_session_state()
209
+ load_css()
210
+
211
+ if not is_logged_in():
212
+ render_auth_page()
213
+ return
214
+
215
+ # Main app header with settings and logout buttons
216
+ col1, col2, col3 = st.columns([6, 1, 1])
217
+ with col1:
218
+ st.title("Mathematical Problem Solver")
219
+ with col2:
220
+ if st.button("Settings"):
221
+ st.session_state.show_settings = True
222
+ st.rerun()
223
+ with col3:
224
+ if st.button("Logout"):
225
+ logout_user()
226
+ st.rerun()
227
+
228
+ st.markdown(f"Welcome, {st.session_state.username}!")
229
+
230
+ # Show settings or main app
231
+ if st.session_state.get('show_settings', False):
232
+ render_profile_settings()
233
+ if st.button("Back to Calculator"):
234
+ st.session_state.show_settings = False
235
+ st.rerun()
236
+ else:
237
+ # Main calculator interface
238
+ # Sidebar for history
239
+ with st.sidebar:
240
+ st.header("Solution History")
241
+ history = load_user_history()
242
+ if history:
243
+ for idx, item in enumerate(history):
244
+ with st.expander(f"Problem {idx + 1}"):
245
+ st.write(f"Input: {item.equation}")
246
+ st.write(f"Solution: {item.solution}")
247
+ st.write(f"Date: {item.created_at.strftime('%Y-%m-%d %H:%M')}")
248
+
249
+ # Input method selection
250
+ input_method = st.radio(
251
+ "Choose input method:",
252
+ ["Type equation/expression", "Use camera", "Example problems"]
253
+ )
254
+
255
+ if input_method == "Type equation/expression":
256
+ # Add math symbols
257
+ render_math_symbols()
258
+
259
+ # Equation input
260
+ if 'equation' not in st.session_state:
261
+ st.session_state.equation = ''
262
+
263
+ equation = st.text_input(
264
+ "Enter your equation or expression:",
265
+ value=st.session_state.equation,
266
+ help="""Examples:
267
+ - Equation: x^2 + 2x + 1 = 0
268
+ - Integration: ∫sin x
269
+ - Derivative: d/dx(x^2)
270
+ - Factorial: 5!
271
+ - Laplace: ℒ(t^2)"""
272
+ )
273
+ st.session_state.equation = equation
274
+
275
+ elif input_method == "Use camera":
276
+ st.info("📸 Camera input feature is coming soon! For now, please type your equation or choose from examples.")
277
+ equation = ""
278
+
279
+ else:
280
+ equation = st.selectbox(
281
+ "Select an example:",
282
+ [
283
+ "x^2 + 2x + 1 = 0",
284
+ "∫sin x",
285
+ "d/dx(x^3)",
286
+ "sin^2 x + cos^2 x",
287
+ "5!",
288
+ "2x + 3 = 7",
289
+ "e^x + 1 = 0",
290
+ "log(x) = 1"
291
+ ]
292
+ )
293
+
294
+ if st.button("Solve"):
295
+ if equation:
296
+ try:
297
+ # Solve the equation or expression
298
+ solution = solve_equation(equation)
299
+ steps = generate_steps(equation)
300
+
301
+ # Display solution
302
+ st.markdown("### Solution")
303
+ st.write(solution)
304
+
305
+ # Display steps
306
+ st.markdown("### Step-by-step Solution")
307
+ for step in steps:
308
+ st.write(step)
309
+
310
+ # Plot the function if possible
311
+ try:
312
+ st.markdown("### Function Visualization")
313
+ fig = plot_function(equation)
314
+ st.plotly_chart(fig)
315
+ except Exception as plot_error:
316
+ st.info("Visualization not available for this type of expression.")
317
+
318
+ # Save to history
319
+ save_to_history(equation, solution)
320
+
321
+ except Exception as e:
322
+ st.error(f"Error: {str(e)}")
323
+ else:
324
+ st.warning("Please enter an equation or expression")
325
+
326
+ if __name__ == "__main__":
327
+ main()
auth.py ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import hashlib
3
+ import re
4
+ from models import User, SessionLocal
5
+
6
+ def validate_password(password: str) -> tuple[bool, str]:
7
+ """Validate password requirements."""
8
+ if len(password) < 8:
9
+ return False, "Password must be at least 8 characters long"
10
+ if not any(c.isupper() for c in password):
11
+ return False, "Password must contain at least one uppercase letter"
12
+ return True, ""
13
+
14
+ def hash_password(password: str) -> str:
15
+ """Hash a password for storing."""
16
+ return hashlib.sha256(password.encode()).hexdigest()
17
+
18
+ def verify_password(stored_password: str, provided_password: str) -> bool:
19
+ """Verify a stored password against one provided by user"""
20
+ return stored_password == hash_password(provided_password)
21
+
22
+ def login_user(username: str, password: str) -> bool:
23
+ """Verify user credentials and log them in."""
24
+ db = SessionLocal()
25
+ try:
26
+ user = db.query(User).filter(User.username == username).first()
27
+ if user and verify_password(user.password, password):
28
+ st.session_state.user_id = user.id
29
+ st.session_state.username = user.username
30
+ return True
31
+ return False
32
+ finally:
33
+ db.close()
34
+
35
+ def signup_user(username: str, password: str) -> tuple[bool, str]:
36
+ """Create a new user account."""
37
+ # Validate password
38
+ is_valid, message = validate_password(password)
39
+ if not is_valid:
40
+ return False, message
41
+
42
+ db = SessionLocal()
43
+ try:
44
+ # Check if username already exists
45
+ if db.query(User).filter(User.username == username).first():
46
+ return False, "Username already exists"
47
+
48
+ # Create new user
49
+ user = User(
50
+ username=username,
51
+ password=hash_password(password)
52
+ )
53
+ db.add(user)
54
+ db.commit()
55
+
56
+ # Log in the new user
57
+ st.session_state.user_id = user.id
58
+ st.session_state.username = user.username
59
+ return True, "Account created successfully"
60
+ except Exception as e:
61
+ db.rollback()
62
+ return False, str(e)
63
+ finally:
64
+ db.close()
65
+
66
+ def update_profile(user_id: int, **profile_data) -> tuple[bool, str]:
67
+ """Update user profile information."""
68
+ db = SessionLocal()
69
+ try:
70
+ user = db.query(User).filter(User.id == user_id).first()
71
+ if not user:
72
+ return False, "User not found"
73
+
74
+ # Update user fields
75
+ for field, value in profile_data.items():
76
+ if hasattr(user, field):
77
+ setattr(user, field, value)
78
+
79
+ db.commit()
80
+ return True, "Profile updated successfully"
81
+ except Exception as e:
82
+ db.rollback()
83
+ return False, str(e)
84
+ finally:
85
+ db.close()
86
+
87
+ def get_user_profile(user_id: int) -> User:
88
+ """Get user profile information."""
89
+ db = SessionLocal()
90
+ try:
91
+ return db.query(User).filter(User.id == user_id).first()
92
+ finally:
93
+ db.close()
94
+
95
+ def change_password(user_id: int, current_password: str, new_password: str) -> tuple[bool, str]:
96
+ """Change user password."""
97
+ # Validate new password
98
+ is_valid, message = validate_password(new_password)
99
+ if not is_valid:
100
+ return False, message
101
+
102
+ db = SessionLocal()
103
+ try:
104
+ user = db.query(User).filter(User.id == user_id).first()
105
+ if not user:
106
+ return False, "User not found"
107
+
108
+ # Verify current password
109
+ if not verify_password(user.password, current_password):
110
+ return False, "Current password is incorrect"
111
+
112
+ # Update password
113
+ user.password = hash_password(new_password)
114
+ db.commit()
115
+ return True, "Password updated successfully"
116
+ except Exception as e:
117
+ db.rollback()
118
+ return False, str(e)
119
+ finally:
120
+ db.close()
121
+
122
+ def is_logged_in() -> bool:
123
+ """Check if user is logged in."""
124
+ return 'user_id' in st.session_state
125
+
126
+ def logout_user():
127
+ """Log out the current user."""
128
+ if 'user_id' in st.session_state:
129
+ del st.session_state.user_id
130
+ if 'username' in st.session_state:
131
+ del st.session_state.username
generated-icon.png ADDED

Git LFS Details

  • SHA256: b4490702cc4f2b55e93ad8a5857fe6cd1ff40fb4bffabcc9e444f81240d5268f
  • Pointer size: 131 Bytes
  • Size of remote file: 840 kB
models.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+ import sqlalchemy as sa
3
+ from sqlalchemy.ext.declarative import declarative_base
4
+ from sqlalchemy.orm import sessionmaker
5
+ from sqlalchemy import create_engine
6
+ import os
7
+
8
+ Base = declarative_base()
9
+
10
+ class User(Base):
11
+ __tablename__ = 'users'
12
+
13
+ id = sa.Column(sa.Integer, primary_key=True)
14
+ username = sa.Column(sa.String(50), unique=True, nullable=False)
15
+ password = sa.Column(sa.String(255), nullable=False)
16
+ full_name = sa.Column(sa.String(100))
17
+ email = sa.Column(sa.String(100))
18
+ school = sa.Column(sa.String(100))
19
+ grade = sa.Column(sa.String(20))
20
+ profile_photo = sa.Column(sa.Text) # Store photo as base64
21
+ created_at = sa.Column(sa.DateTime, default=datetime.utcnow)
22
+ updated_at = sa.Column(sa.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
23
+
24
+ class History(Base):
25
+ __tablename__ = 'history'
26
+
27
+ id = sa.Column(sa.Integer, primary_key=True)
28
+ user_id = sa.Column(sa.Integer, sa.ForeignKey('users.id'), nullable=False)
29
+ equation = sa.Column(sa.String(255), nullable=False)
30
+ solution = sa.Column(sa.Text, nullable=False)
31
+ created_at = sa.Column(sa.DateTime, default=datetime.utcnow)
32
+
33
+ # Database setup
34
+ engine = create_engine(os.environ['DATABASE_URL'])
35
+ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
36
+
37
+ def init_db():
38
+ Base.metadata.create_all(bind=engine)
39
+
40
+ def get_db():
41
+ db = SessionLocal()
42
+ try:
43
+ yield db
44
+ finally:
45
+ db.close()
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "workspace",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "test": "echo \"Error: no test specified\" && exit 1"
7
+ },
8
+ "keywords": [],
9
+ "author": "",
10
+ "license": "ISC",
11
+ "description": "",
12
+ "dependencies": {
13
+ "@emotion/react": "^11.14.0",
14
+ "@emotion/styled": "^11.14.0",
15
+ "@mui/icons-material": "^6.4.4",
16
+ "@mui/material": "^6.4.4",
17
+ "@types/react": "^19.0.8",
18
+ "@types/react-dom": "^19.0.3",
19
+ "@types/react-plotly.js": "^2.6.3",
20
+ "@types/react-router-dom": "^5.3.3",
21
+ "axios": "^1.7.9",
22
+ "react": "^19.0.0",
23
+ "react-dom": "^19.0.0",
24
+ "react-plotly.js": "^2.6.0",
25
+ "react-router-dom": "^7.1.5",
26
+ "typescript": "^5.7.3"
27
+ }
28
+ }
plotter.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import plotly.graph_objects as go
3
+ from sympy import symbols, lambdify
4
+ from sympy.parsing.sympy_parser import parse_expr, standard_transformations, implicit_multiplication_application
5
+ from solver import preprocess_equation
6
+
7
+ def plot_function(input_str):
8
+ """Create an interactive plot of the function."""
9
+ try:
10
+ # Preprocess input
11
+ processed_input = preprocess_equation(input_str)
12
+
13
+ # Handle different types of input
14
+ if '=' in processed_input:
15
+ # Equation: move everything to left side
16
+ left_side, right_side = [side.strip() for side in processed_input.split('=')]
17
+ transformations = standard_transformations + (implicit_multiplication_application,)
18
+ expr = parse_expr(left_side, transformations=transformations) - parse_expr(right_side, transformations=transformations)
19
+ elif input_str.startswith('∫'):
20
+ # Integration: plot the original function
21
+ expr = parse_expr(processed_input[1:].strip(), transformations=(standard_transformations + (implicit_multiplication_application,)))
22
+ elif input_str.startswith('d/dx'):
23
+ # Derivative: plot the original function
24
+ expr = parse_expr(processed_input[4:].strip(), transformations=(standard_transformations + (implicit_multiplication_application,)))
25
+ else:
26
+ # Regular expression
27
+ expr = parse_expr(processed_input, transformations=(standard_transformations + (implicit_multiplication_application,)))
28
+
29
+ # Create lambda function for numpy evaluation
30
+ x = symbols('x')
31
+ f = lambdify(x, expr, 'numpy')
32
+
33
+ # Generate x values
34
+ x_vals = np.linspace(-10, 10, 1000)
35
+
36
+ # Calculate y values
37
+ y_vals = f(x_vals)
38
+
39
+ # Create plot
40
+ fig = go.Figure()
41
+
42
+ # Add function curve
43
+ fig.add_trace(go.Scatter(
44
+ x=x_vals,
45
+ y=y_vals,
46
+ mode='lines',
47
+ name='f(x)',
48
+ line=dict(color='#FF4B4B', width=2)
49
+ ))
50
+
51
+ # Add x-axis line
52
+ fig.add_trace(go.Scatter(
53
+ x=x_vals,
54
+ y=[0]*len(x_vals),
55
+ mode='lines',
56
+ name='x-axis',
57
+ line=dict(color='black', width=1)
58
+ ))
59
+
60
+ # Update layout
61
+ fig.update_layout(
62
+ title='Function Visualization',
63
+ xaxis_title='x',
64
+ yaxis_title='y',
65
+ showlegend=True,
66
+ hovermode='x',
67
+ plot_bgcolor='white',
68
+ width=800,
69
+ height=500
70
+ )
71
+
72
+ # Update axes
73
+ fig.update_xaxes(zeroline=True, zerolinewidth=1, zerolinecolor='black', gridcolor='lightgray')
74
+ fig.update_yaxes(zeroline=True, zerolinewidth=1, zerolinecolor='black', gridcolor='lightgray')
75
+
76
+ return fig
77
+
78
+ except Exception as e:
79
+ raise Exception(f"Error creating plot: {str(e)}")
pyproject.toml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "repl-nix-workspace"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ requires-python = ">=3.11"
6
+ dependencies = [
7
+ "fastapi>=0.115.8",
8
+ "numpy>=2.2.3",
9
+ "openai>=1.63.0",
10
+ "passlib[bcrypt]>=1.7.4",
11
+ "plotly>=6.0.0",
12
+ "psycopg2-binary>=2.9.10",
13
+ "python-jose[cryptography]>=3.3.0",
14
+ "python-multipart>=0.0.20",
15
+ "sqlalchemy>=2.0.38",
16
+ "streamlit>=1.42.0",
17
+ "sympy>=1.13.3",
18
+ "twilio>=9.4.5",
19
+ "uvicorn>=0.34.0",
20
+ ]
replit.nix ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {pkgs}: {
2
+ deps = [
3
+ pkgs.postgresql
4
+ pkgs.glibcLocales
5
+ ];
6
+ }
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ streamlit
2
+ sympy
3
+ sqlalchemy
4
+ plotly
5
+ pandas
solver.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sympy as sp
2
+ from sympy.parsing.sympy_parser import parse_expr, standard_transformations, implicit_multiplication_application
3
+ from sympy.solvers import solve
4
+ from sympy import integrate, diff, simplify, expand, log, exp, sin, cos, tan, asin, acos, atan, Symbol, factorial, laplace_transform
5
+
6
+ def format_expression(expr):
7
+ """Format expression to make it more readable."""
8
+ # Convert string representation to a more readable format
9
+ str_expr = str(expr)
10
+ replacements = {
11
+ '**': '^',
12
+ '*x': 'x',
13
+ 'exp': 'e^',
14
+ 'sqrt': '√',
15
+ 'factorial': '!'
16
+ }
17
+ for old, new in replacements.items():
18
+ str_expr = str_expr.replace(old, new)
19
+ return str_expr
20
+
21
+ def preprocess_equation(equation_str):
22
+ """Convert user-friendly equation format to SymPy format."""
23
+ try:
24
+ # Replace common mathematical notations
25
+ replacements = {
26
+ '^': '**',
27
+ 'sin⁻¹': 'asin',
28
+ 'cos⁻¹': 'acos',
29
+ 'tan⁻¹': 'atan',
30
+ 'e^': 'exp',
31
+ 'ln': 'log',
32
+ '!': 'factorial',
33
+ }
34
+ for old, new in replacements.items():
35
+ equation_str = equation_str.replace(old, new)
36
+
37
+ # Handle exponential expressions
38
+ if 'exp' in equation_str:
39
+ parts = equation_str.split('exp')
40
+ for i in range(1, len(parts)):
41
+ if parts[i] and parts[i][0] != '(':
42
+ parts[i] = '(' + parts[i]
43
+ if '=' in parts[i]:
44
+ exp_part, rest = parts[i].split('=', 1)
45
+ parts[i] = exp_part + ')=' + rest
46
+ else:
47
+ parts[i] = parts[i] + ')'
48
+ equation_str = 'exp'.join(parts)
49
+
50
+ # Add multiplication symbol where needed
51
+ processed = ''
52
+ i = 0
53
+ while i < len(equation_str):
54
+ if i + 1 < len(equation_str):
55
+ if equation_str[i].isdigit() and equation_str[i+1] == 'x':
56
+ processed += equation_str[i] + '*'
57
+ i += 1
58
+ continue
59
+ processed += equation_str[i]
60
+ i += 1
61
+
62
+ return processed
63
+ except Exception as e:
64
+ raise Exception(f"Error in equation format: {str(e)}")
65
+
66
+ def process_expression(expr_str):
67
+ """Process mathematical expressions without equations."""
68
+ try:
69
+ # Preprocess the expression
70
+ processed_expr = preprocess_equation(expr_str)
71
+ x = Symbol('x')
72
+
73
+ # Check for special operations
74
+ if expr_str.startswith('∫'): # Integration
75
+ expr_to_integrate = processed_expr[1:].strip()
76
+ expr = parse_expr(expr_to_integrate, transformations=(standard_transformations + (implicit_multiplication_application,)))
77
+ result = integrate(expr, x)
78
+ return f"∫{format_expression(expr)} = {format_expression(result)}"
79
+
80
+ elif expr_str.startswith('d/dx'): # Differentiation
81
+ expr_to_diff = processed_expr[4:].strip()
82
+ if expr_to_diff.startswith('(') and expr_to_diff.endswith(')'):
83
+ expr_to_diff = expr_to_diff[1:-1]
84
+ expr = parse_expr(expr_to_diff, transformations=(standard_transformations + (implicit_multiplication_application,)))
85
+ result = diff(expr, x)
86
+ return f"d/dx({format_expression(expr)}) = {format_expression(result)}"
87
+
88
+ elif 'factorial' in processed_expr: # Factorial
89
+ expr = parse_expr(processed_expr, transformations=(standard_transformations + (implicit_multiplication_application,)))
90
+ result = expr.doit()
91
+ return f"{format_expression(expr)} = {format_expression(result)}"
92
+
93
+ else: # Regular expression simplification
94
+ expr = parse_expr(processed_expr, transformations=(standard_transformations + (implicit_multiplication_application,)))
95
+ simplified = simplify(expr)
96
+ expanded = expand(simplified)
97
+ return f"Simplified: {format_expression(simplified)}\nExpanded: {format_expression(expanded)}"
98
+
99
+ except Exception as e:
100
+ raise Exception(f"Error processing expression: {str(e)}")
101
+
102
+ def solve_equation(equation_str):
103
+ """Solve the given equation and return the solution."""
104
+ try:
105
+ if '=' not in equation_str:
106
+ return process_expression(equation_str)
107
+
108
+ # Preprocess equation
109
+ equation_str = preprocess_equation(equation_str)
110
+
111
+ # Split equation into left and right parts
112
+ left_side, right_side = [side.strip() for side in equation_str.split('=')]
113
+
114
+ # Parse both sides with implicit multiplication
115
+ transformations = standard_transformations + (implicit_multiplication_application,)
116
+ left_expr = parse_expr(left_side, transformations=transformations)
117
+ right_expr = parse_expr(right_side, transformations=transformations)
118
+ equation = left_expr - right_expr
119
+
120
+ # Solve the equation
121
+ x = Symbol('x')
122
+ solution = solve(equation, x)
123
+
124
+ # Format solution
125
+ if len(solution) == 0:
126
+ return "No solution exists"
127
+ elif len(solution) == 1:
128
+ return f"x = {format_expression(solution[0])}"
129
+ else:
130
+ return "x = " + ", ".join([format_expression(sol) for sol in solution])
131
+
132
+ except Exception as e:
133
+ raise Exception(f"Invalid equation format: {str(e)}")
134
+
135
+ def generate_steps(equation_str):
136
+ """Generate step-by-step solution for the equation or expression."""
137
+ steps = []
138
+ try:
139
+ if '=' not in equation_str:
140
+ steps.append(f"1. Original expression: {equation_str}")
141
+ result = process_expression(equation_str)
142
+ steps.append(f"2. Result: {result}")
143
+ return steps
144
+
145
+ # Preprocess equation
146
+ processed_eq = preprocess_equation(equation_str)
147
+
148
+ # Split equation into left and right parts
149
+ left_side, right_side = [side.strip() for side in processed_eq.split('=')]
150
+
151
+ # Parse expressions with implicit multiplication
152
+ transformations = standard_transformations + (implicit_multiplication_application,)
153
+ left_expr = parse_expr(left_side, transformations=transformations)
154
+ right_expr = parse_expr(right_side, transformations=transformations)
155
+
156
+ # Step 1: Show original equation
157
+ steps.append(f"1. Original equation: {format_expression(left_expr)} = {format_expression(right_expr)}")
158
+
159
+ # Step 2: Move all terms to left side
160
+ equation = left_expr - right_expr
161
+ steps.append(f"2. Move all terms to left side: {format_expression(equation)} = 0")
162
+
163
+ # Step 3: Factor if possible
164
+ factored = sp.factor(equation)
165
+ if factored != equation:
166
+ steps.append(f"3. Factor the equation: {format_expression(factored)} = 0")
167
+
168
+ # Step 4: Solve
169
+ x = Symbol('x')
170
+ solution = solve(equation, x)
171
+ steps.append(f"4. Solve for x: x = {', '.join([format_expression(sol) for sol in solution])}")
172
+
173
+ # Step 5: Verify solutions
174
+ steps.append("5. Verify solutions:")
175
+ for sol in solution:
176
+ result = equation.subs(x, sol)
177
+ steps.append(f" When x = {format_expression(sol)}, equation equals {format_expression(result)}")
178
+
179
+ return steps
180
+
181
+ except Exception as e:
182
+ raise Exception(f"Error generating steps: {str(e)}")
style.css ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Custom styles for the application */
2
+ body {
3
+ font-family: 'Arial', sans-serif;
4
+ color: #FFFFFF;
5
+ background-color: #181818;
6
+ }
7
+
8
+ /* Button styling */
9
+ .stButton>button {
10
+ min-width: 40px !important;
11
+ height: 30px !important;
12
+ padding: 2px 8px !important;
13
+ margin: 2px !important;
14
+ background-color: #2D5BE3;
15
+ color: white;
16
+ border: none;
17
+ border-radius: 4px;
18
+ cursor: pointer;
19
+ transition: all 0.3s ease;
20
+ font-size: 14px;
21
+ font-weight: bold;
22
+ text-shadow: 0 1px 2px rgba(0,0,0,0.2);
23
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2);
24
+ }
25
+
26
+ .stButton>button:hover {
27
+ background-color: #3DDC84;
28
+ transform: translateY(-1px);
29
+ box-shadow: 0 4px 8px rgba(0,0,0,0.3);
30
+ }
31
+
32
+ /* Input field styling */
33
+ .stTextInput>div>div>input {
34
+ border-radius: 5px;
35
+ border: 2px solid #2D5BE3;
36
+ padding: 0.5rem;
37
+ background-color: #242424;
38
+ color: #FFFFFF;
39
+ font-weight: 500;
40
+ }
41
+
42
+ .stTextInput>div>div>input:focus {
43
+ border-color: #FFDD44;
44
+ box-shadow: 0 0 0 2px rgba(255, 221, 68, 0.2);
45
+ }
46
+
47
+ /* Sidebar styling */
48
+ .sidebar .sidebar-content {
49
+ background-color: #242424;
50
+ padding: 1rem;
51
+ border-right: 2px solid #2D5BE3;
52
+ }
53
+
54
+ /* Solution display styling */
55
+ .stExpander {
56
+ background-color: #242424;
57
+ border-radius: 8px;
58
+ margin-bottom: 10px;
59
+ box-shadow: 0 2px 6px rgba(0,0,0,0.3);
60
+ border: 1px solid #3DDC84;
61
+ }
62
+
63
+ /* Plot container styling */
64
+ .plot-container {
65
+ background-color: #242424;
66
+ border-radius: 8px;
67
+ padding: 1rem;
68
+ margin: 1rem 0;
69
+ border: 1px solid #2D5BE3;
70
+ box-shadow: 0 4px 12px rgba(0,0,0,0.4);
71
+ }
72
+
73
+ /* Solution steps styling */
74
+ .solution-step {
75
+ background-color: #242424;
76
+ padding: 1rem;
77
+ margin: 0.75rem 0;
78
+ border-radius: 6px;
79
+ border-left: 4px solid #3DDC84;
80
+ }
81
+
82
+ /* Mathematical symbols grid */
83
+ .math-symbols-grid {
84
+ display: grid;
85
+ grid-template-columns: repeat(8, 1fr);
86
+ gap: 4px;
87
+ margin: 10px 0;
88
+ }
89
+
90
+ /* Headers styling */
91
+ h1, h2, h3 {
92
+ color: #FFFFFF;
93
+ font-weight: bold;
94
+ text-shadow: 0 2px 4px rgba(0,0,0,0.3);
95
+ }
96
+
97
+ /* Interactive elements */
98
+ .stSelectbox select,
99
+ .stMultiSelect select {
100
+ background-color: #242424;
101
+ color: #FFFFFF;
102
+ border: 2px solid #2D5BE3;
103
+ }
104
+
105
+ /* Radio buttons and checkboxes */
106
+ .stRadio > div {
107
+ border-radius: 6px;
108
+ padding: 0.5rem;
109
+ background-color: #242424;
110
+ }
111
+
112
+ .stRadio label {
113
+ color: #FFFFFF !important;
114
+ }
115
+
116
+ .stRadio label:hover {
117
+ color: #FFDD44 !important;
118
+ }
119
+
120
+ /* Tabs */
121
+ .stTabs {
122
+ background-color: #242424;
123
+ border-radius: 8px;
124
+ padding: 1rem;
125
+ margin: 1rem 0;
126
+ }
127
+
128
+ /* Success/Info messages */
129
+ .stSuccess, .stInfo {
130
+ background-color: #3DDC84;
131
+ color: #181818;
132
+ font-weight: bold;
133
+ border-radius: 6px;
134
+ padding: 0.75rem;
135
+ }
136
+
137
+ /* Error messages */
138
+ .stError {
139
+ background-color: #FF4444;
140
+ color: #FFFFFF;
141
+ font-weight: bold;
142
+ border-radius: 6px;
143
+ padding: 0.75rem;
144
+ }
145
+
146
+ /* Responsive design */
147
+ @media (max-width: 768px) {
148
+ .stButton>button {
149
+ min-width: 30px !important;
150
+ height: 25px !important;
151
+ padding: 1px 6px !important;
152
+ font-size: 12px;
153
+ }
154
+
155
+ .stTextInput>div>div>input {
156
+ font-size: 16px;
157
+ }
158
+ }
utils.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+
3
+ def initialize_session_state():
4
+ """Initialize session state variables."""
5
+ if 'history' not in st.session_state:
6
+ st.session_state.history = []
7
+
8
+ def load_css():
9
+ """Load custom CSS styles."""
10
+ st.markdown("""
11
+ <style>
12
+ .stButton>button {
13
+ width: 100%;
14
+ background-color: #FF4B4B;
15
+ color: white;
16
+ }
17
+
18
+ .stTextInput>div>div>input {
19
+ border-radius: 5px;
20
+ }
21
+
22
+ .stMarkdown {
23
+ font-family: 'Arial', sans-serif;
24
+ }
25
+
26
+ h1, h2, h3 {
27
+ color: #262730;
28
+ }
29
+
30
+ .sidebar .sidebar-content {
31
+ background-color: #F0F2F6;
32
+ }
33
+
34
+ .stExpander {
35
+ background-color: white;
36
+ border-radius: 5px;
37
+ margin-bottom: 10px;
38
+ }
39
+
40
+ .stRadio > div {
41
+ display: flex;
42
+ gap: 1rem;
43
+ }
44
+
45
+ .stSelectbox {
46
+ margin-bottom: 1rem;
47
+ }
48
+ </style>
49
+ """, unsafe_allow_html=True)
uv.lock ADDED
The diff for this file is too large to render. See raw diff