Browse Source

first commit

master
Juni 1 year ago
commit
0f22cd789d
  1. 160
      .dockerignore
  2. 161
      .gitignore
  3. 11
      Dockerfile
  4. 32
      app.py
  5. 19
      requirements.txt
  6. 240
      solver.py
  7. 70
      templates/page.html

160
.dockerignore

@ -0,0 +1,160 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

161
.gitignore

@ -0,0 +1,161 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
.DS_Store
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

11
Dockerfile

@ -0,0 +1,11 @@
FROM python:latest
WORKDIR /app
COPY ./requirements.txt /app/
EXPOSE 8000
RUN pip install -r requirements.txt
COPY . /app
CMD ["gunicorn", "app:app", "-w", "3", "-b", "0.0.0.0:8000"]

32
app.py

@ -0,0 +1,32 @@
from flask import Flask, render_template, request
import solver
app = Flask(__name__)
@app.route("/", methods=["GET", "POST"])
def index():
if request.method == "GET":
return render_template("page.html")
else:
try:
M = int(request.form["M"])
N = int(request.form["N"])
except ValueError:
return render_template("page.html", invalid=True)
if not (1 <= M <= 40 and 1 <= N <= 40):
return render_template("page.html", invalid=True)
g = solver.construct(M, N)
if g is None:
return render_template("page.html", nosol=True, M=M, N=N)
else:
image, works = g.plot()
return render_template("page.html", image=image, works=works, M=M, N=N)
if __name__ == "__main__":
app.run(
host="0.0.0.0",
port=8000
)

19
requirements.txt

@ -0,0 +1,19 @@
blinker==1.7.0
click==8.1.7
contourpy==1.2.0
cycler==0.12.1
Flask==3.0.0
fonttools==4.45.1
gunicorn==21.2.0
itsdangerous==2.1.2
Jinja2==3.1.2
kiwisolver==1.4.5
MarkupSafe==2.1.3
matplotlib==3.8.2
numpy==1.26.2
packaging==23.2
Pillow==10.1.0
pyparsing==3.1.1
python-dateutil==2.8.2
six==1.16.0
Werkzeug==3.0.1

240
solver.py

@ -0,0 +1,240 @@
#!/usr/bin/env python3
import io
import base64
from matplotlib import pyplot as plt
import numpy
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
class Grid:
def __init__(self, M, N):
self.N = N
self.M = M
self.grid = [[0 for _ in range(M+N-1)] for _ in range(M+N-1)]
def check_bounds(self, x, y):
return 1 <= x <= self.M+self.N-1 and\
1 <= y <= self.M+self.N-1 and\
self.M+1 <= x+y <= 2*self.M + self.N - 1
def get(self, x, y):
if not self.check_bounds(x, y):
return 0
return self.grid[x-1][y-1]
def select(self, x, y):
assert self.check_bounds(x, y)
self.grid[x-1][y-1] = 1
def grid_parity(self):
for x in range(1, self.M+self.N):
nums = [self.get(x, y) for y in range(1, self.N + self.M)]
if sum(nums) % 2 != 1:
print(f"Found a contradiction at x={x}! {nums}")
return False
for y in range(1, self.M+self.N):
nums = [self.get(x, y) for x in range(1, self.N + self.M)]
if sum(nums) % 2 != 1:
print(f"Found a contradiction at y={y}! {nums}")
return False
for s in range(self.M+1, 2*self.M+self.N):
nums = [self.get(x, s-x) for x in range(1, s)]
if sum(nums) % 2 != 1:
print(f"Found a contradiction at x+y={s}! {nums}")
return False
return True
def reflect(self):
res = [[0 for _ in range(len(self.grid[i]))]
for i in range(len(self.grid))]
for x in range(self.M+self.N-1):
# reflect across x + y = m + n - 2
for y in range(self.M+self.N-1):
# sm = 2 * (self.M+self.N-2) - (x+y)
# diff = x - y
res[self.M+self.N-2-x][self.M+self.N-2-y] = self.grid[x][y]
self.grid = res
tmp = self.M
self.M = self.N
self.N = tmp
def plot(self) -> tuple[str, bool]:
fig = Figure()
ax = fig.add_subplot(1, 1, 1)
ax.set_xticks(numpy.arange(1, self.M+self.N, 1))
ax.set_yticks(numpy.arange(1, self.M+self.N, 1))
ax.set_aspect("equal")
ax.set_xbound(0, self.M+self.N)
ax.set_ybound(0, self.M+self.N)
ax.autoscale(enable=False)
x = []
y = []
for i in range(1, self.M+self.N):
for j in range(1, self.M+self.N):
if self.get(i, j):
x.append(i)
y.append(j)
ax.scatter(x, y, color='b')
# plt.title(f"Construction for M={self.M}, N={self.N}")
ax.grid()
ax.plot([1, self.M], [self.M, 1], color='r')
ax.plot([self.M, self.N+self.M-1],
[self.N+self.M-1, self.M], color='r')
ax.plot([1, 1, self.M], [self.M, self.M +
self.N-1, self.M+self.N-1], color='r')
ax.plot([self.M, self.M+self.N-1, self.M+self.N-1],
[1, 1, self.M], color='r')
pngImage = io.BytesIO()
FigureCanvas(fig).print_png(pngImage)
pngImageB64String = "data:image/png;base64,"
pngImageB64String += base64.b64encode(
pngImage.getvalue()).decode('utf8')
return pngImageB64String, self.grid_parity()
def one(M, N) -> Grid:
assert (N-M) % 4 == 0
assert M == 1 or N == 1
reflect = M == 1
if reflect:
tmp = M
M = N
N = tmp
g = Grid(M, N)
for k in range(1, M+1):
g.select(k, M+1-k)
for k in range(1, M//2+1):
g.select((M+1)//2, (M+1)//2+k)
g.select(M, (M+1)//2+k)
if reflect:
g.reflect()
return g
def cong_mod4(M, N) -> Grid:
assert (N-M) % 4 == 0 and N != 1 and M != 1
reflect = M > N
if reflect:
tmp = M
M = N
N = tmp
g = Grid(M, N)
if M % 2 == 1:
g.select(M, M)
# Main Axis points
for k in range(1, N//2+1):
g.select(M, M+2*k-1)
for k in range(1, (M-1)//2+1):
g.select(M, M-2*k)
for k in range(1, (N-1)//2+1):
g.select(M+2*k, M)
for k in range(1, M//2+1):
g.select(M-2*k+1, M)
# Points on the diagonal
for k in range(1, M//2 + 1):
g.select(M+2*k-1, M-2*k+1) # going down
for k in range(1, (M-1)//2+1):
g.select(M-2*k, M+2*k) # going up
for k in range(1, (N-M)//2+1):
g.select(
M+2*(M//2) - 1 + 2*k,
M-2*(M//2) + 2,
)
g.select(
M - 2*((M-1)//2) + 1,
M + 2*((M-1)//2) + 2*k,
)
if reflect:
g.reflect()
return g
def mod_0_1(M, N):
assert (M+N) % 4 == 1 and M % 4 in [0, 1]
reflect = (M % 4) == 1
if reflect:
tmp = M
M = N
N = tmp
g = Grid(M, N)
g.select(M, M)
# Main Axis Points
for k in range(1, N//2+1):
g.select(M, M+2*k-1)
for k in range(1, (M-1)//2+1):
g.select(M, M-2*k)
for k in range(1, (N-1)//2+1):
g.select(M+2*k, M)
for k in range(1, M//2+1):
g.select(M-2*k+1, M)
# Step 3
for k in range(0, M//2):
g.select(M-2*k, 1+2*k)
# Tail
for k in range(1, (N-1)//2+1):
g.select(2, M+2*k)
g.select(M+2*k-1, 3)
if reflect:
g.reflect()
return g
def mod_2_3(M, N):
assert (M+N) % 4 == 1 and M % 4 in [2, 3]
reflect = (M % 4) == 2
if reflect:
tmp = M
M = N
N = tmp
g = Grid(M, N)
for k in range(1, M+1):
g.select(k, M+1-k)
for k in range((M+1)//2+1, M+1):
g.select((M+1)//2, k)
g.select(M+1, k)
g.select((M+1)//2, M+1)
for k in range(1, (N-2)//2 + 1):
g.select(M+1, M+2*k)
g.select(M+2*k, M)
g.select(1, M+1+2*k)
g.select(M+1+2*k, 1)
if reflect:
g.reflect()
return g
def construct(M, N) -> 'Grid | None':
if (M-N) % 4 == 0:
if M != 1 and N != 1:
return cong_mod4(M, N)
else:
return one(M, N)
elif (M+N) % 4 == 1:
if (M % 4) in [0, 1]:
return mod_0_1(M, N)
else:
return mod_2_3(M, N)
else:
assert ((N+M-1)*(N-M)) % 4 == 2
return None

70
templates/page.html

@ -0,0 +1,70 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>USAMTS 2/5/35 Solution</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@1/css/pico.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css" integrity="sha384-n8MVd4RsNIU0tAv4ct0nTaAbDJwPJzDEaqSD1odI+WdtXRGWt2kTvGFasHpSy3SV" crossorigin="anonymous">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js" integrity="sha384-XjKyOOlGwcjNTAIQHIpgOno0Hl1YQqzUOEleOLALmuqehneUG+vnGctmUb0ZY0l8" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"
onload="renderMathInElement(document.body);"></script>
</head>
<body>
<main class="container">
<strong><h3>Problem</h3></strong>
<p>
Let \(m\) and \(n\) be positive integers. Let \(S\) be the set of all points
\((x, y)\) with integer coordinates such that \(1 \leq x, y \leq m + n − 1\)
and \(m + 1 \leq x + y \leq 2m + n − 1\). Let L be the set of the \(3m + 3n −
3\) lines parallel to one of \(x = 0\), \(y = 0\), or \(x + y = 0\) and passing
through at least one point in \(S\). For which pairs \((m, n)\) does there
exist a subset \(T\) of \(S\) such that every line in \(L\) intersects an odd
number of elements of \(T\)?
</p>
<strong><h3>Proof</h3></strong>
<p>
The answer is just all \((m,n)\) such that \( (m+n-1)(m-n) \) is divisible
by 4. The sum of all the x and y coordinates should clearly be even, but
the sum of all \(x+y \mod 2\) is \( \frac{(m+n-1)(3m+n)}{2} \). For a
construction to even be viable, this quantity clearly needs to be even.
</p>
<strong><h3>Constructor</h3></strong>
{% if invalid %}
<p> Invalid Input! </p>
{% endif %}
{% if nosol %}
<p> No Valid Solution is Possible! </p>
{% endif %}
{% if image %}
<img src="{{ image }}" alt="Solution Image" />
{% endif %}
{% if works == False %}
<p> Claimed Construction Fails!!!! </p>
{% endif %}
<form method="POST" action="{{url_for ('index')}}">
<table role="grid">
<tbody>
<tr>
<td>
<label for="M">Value for M:</label>
</td>
<td>
<input type="number" name="M" min="1" max="40" value="{{M}}" required />
</td>
</tr>
<tr>
<td>
<label for="N">Value for N:</label>
</td>
<td>
<input type="number" name="N" min="1" max="40" value="{{N}}" required />
</td>
</tr>
</tbody>
</table>
<input type="submit" value="Construct">
</form>
</main>
</body>
</html>
Loading…
Cancel
Save