Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
121 commits
Select commit Hold shift + click to select a range
148164e
Create tables
APRILjcc Jun 23, 2026
28b7038
Delete data/tables
APRILjcc Jun 23, 2026
644a199
Add initial IPI mockup figures and tables
APRILjcc Jun 23, 2026
e727e5c
Create mockup.html
aruship4 Jun 24, 2026
c0fcc32
mockup
aruship4 Jun 26, 2026
5478bf7
fix: resolve path before relative_to in extract-prices
haojian Jun 27, 2026
6406878
feat: trailing-12-month recent-window IPI pipeline
haojian Jun 27, 2026
e87f50e
data: recent trailing-12-month IPI outputs
haojian Jun 27, 2026
c847a8c
docs: log trailing-12mo IPI completion; unblock website plan
haojian Jun 27, 2026
f563a29
feat: CSRankings-style IPI website (monthly, client-side recompute)
haojian Jun 27, 2026
4ee6ada
chore: add deploy-site.sh helper; log gh-pages publish
haojian Jun 27, 2026
173f3fe
revert: take down IPI website frontend + gh-pages deploy
haojian Jun 27, 2026
a066f02
docs: document data.json contract for the IPI website data layer
haojian Jun 27, 2026
b818beb
docs: add step-by-step build guide for plain-JS interactive IPI site
haojian Jun 27, 2026
1f3a0f3
feat: commit self-contained IPI frontend (no external libs)
haojian Jun 30, 2026
72a75fc
docs: update todo — frontend committed, website item narrowed to deploy
haojian Jun 30, 2026
07954af
ci: add GitHub Pages workflow to deploy site/ from mockup
haojian Jun 30, 2026
c7a6597
docs: update todo — Pages workflow added, pending one-time Source set…
haojian Jun 30, 2026
34d9fdd
deploy: move site/ -> docs/ for GitHub Pages branch deploy
haojian Jun 30, 2026
be7d56e
feat: add FAQ & methodology page to the IPI site
haojian Jun 30, 2026
bf5dc2c
style: refresh IPI site aesthetics (cards, type, accent, logo)
haojian Jun 30, 2026
e002c91
feat(site): define IPI under title, side-by-side chart + category sel…
haojian Jun 30, 2026
778ccb7
feat(site): highlight significant month-over-month composite moves
haojian Jun 30, 2026
fb1f806
docs(faq): draft FAQ section with existing + new questions
haojian Jun 30, 2026
d980fff
feat(site): bigger chart, move descriptions, and main+subcategory index
haojian Jun 30, 2026
830adb9
Expand FAQ section with new questions and updates
APRILjcc Jun 30, 2026
a275909
feat(site): full-history quarterly IPI (2020→2026) + freelancer rankings
haojian Jul 3, 2026
7ff3058
feat(site): freelancer explorer — 25/category + per-gig price-over-ti…
haojian Jul 7, 2026
012b7ea
fix(index): gap-tolerant panel chaining + graph quarter-inspector
haojian Jul 7, 2026
bc72b15
feat(site): distinct CVD-safe per-category color palette
haojian Jul 7, 2026
c9584b7
style(site): smaller page gutters + mobile-scrollable FAQ tables
haojian Jul 7, 2026
818d63d
docs: log site gutter/margin change in progress.md
haojian Jul 7, 2026
4847c4c
style(site): center header title/subtitle + drop rank (#) column
haojian Jul 7, 2026
078d0cd
docs(faq): sync draft to 2020Q1→2026Q1 quarterly chart + 6 new questions
haojian Jul 7, 2026
4151a24
feat(site): apply all 21 FAQ questions to live docs/faq.html
haojian Jul 7, 2026
ba8e3dd
style(faq): widen content column 900→1600px to shrink side margins
haojian Jul 7, 2026
7137877
style(faq): center header title and subtitle
haojian Jul 7, 2026
4c20173
feat(site): default to single-category view; add axis units; drop com…
haojian Jul 11, 2026
95a5935
pilot(index): unbalanced-panel methods reveal chain drift in headline…
haojian Jul 11, 2026
a61a91f
feat(site): label IPI units on axis, caption, tooltip & inspector
haojian Jul 11, 2026
1379eb0
feat(site): title the line chart & enlarge category dropdown caret
haojian Jul 12, 2026
fd38079
docs(faq): rewrite answers, merge 21→15 questions, fix stale framing
haojian Jul 12, 2026
7a765bb
docs(faq): sync drafts/sections/faq.md to shipped 15-question page
haojian Jul 12, 2026
f594f03
Update FAQ on gig pricing and data sources
APRILjcc Jul 12, 2026
f13f63d
Update FAQ with category qualification details
APRILjcc Jul 12, 2026
f5dc29a
docs(faq): restore Q6 tables + write out Q8 equations in draft
haojian Jul 12, 2026
d19216a
docs(faq): sync draft Q5/Q7/Q8 into live faq.html
haojian Jul 13, 2026
9a663e9
docs(faq): clarify Q3 above/below-100 reading against the actual chart
haojian Jul 13, 2026
35c3760
Update FAQ on index reading and price changes
APRILjcc Jul 13, 2026
132e9f9
docs(site): add index-point units wherever an IPI level number renders
haojian Jul 13, 2026
fccefeb
docs(site): show "pts" unit on every y-axis gridline, not just the to…
haojian Jul 13, 2026
f096693
docs(progress): log per-tick y-axis units + push
haojian Jul 13, 2026
43005b1
docs(site): sync new FAQ Q3 into live page; default category list to A→Z
haojian Jul 13, 2026
4f9f376
feat(index): add time-dummy (TPD) index; redesign per-quarter inspector
haojian Jul 13, 2026
9fd8337
feat(site): show corrected time-dummy index as a second chart under t…
haojian Jul 13, 2026
705c3ae
docs(site): label corrected-index chart "fixed-effects" (presentation…
haojian Jul 13, 2026
286d9d7
docs(site): separate the fixed-effects chart into its own card; clean…
haojian Jul 13, 2026
f35d0f1
fix(site): restore main-chart content; make fixed-effects chart same …
haojian Jul 13, 2026
348a77d
revert(site): restore original quarter-click percentage labels (QoQ/Y…
haojian Jul 13, 2026
65db931
feat(site): quarter readout shows composite then all 7 categories (A-Z)
haojian Jul 14, 2026
4870b1a
site: composite+7-category quarter readout; units explainer; TOP_N=10…
haojian Jul 14, 2026
a0a5a4e
docs(progress): log mission statement added to site header
haojian Jul 14, 2026
c7b005e
site: condense header into two side-by-side boxes
haojian Jul 14, 2026
ed1071c
validate: price-extraction spot-check + fixed-effects 95% confidence …
haojian Jul 14, 2026
92f60f8
docs(site): surface data-validation result on the fixed-effects chart
haojian Jul 14, 2026
b876ea0
feat(site): fixed-effects chart gets its own quarter dropdown + readout
haojian Jul 14, 2026
7b82973
docs(site): style the fixed-effects quarter dropdown; move validated …
haojian Jul 14, 2026
c93d7cf
docs(site): fixed-effects card as bullets; fold validation into a bullet
haojian Jul 14, 2026
8026970
feat(site): add per-category Gallery page (index trend + featured gig)
haojian Jul 14, 2026
adfdb6c
docs: add professor-facing website + fixed-effects update summary
haojian Jul 14, 2026
2a70cad
docs(site): FAQ Step 5 — plain-language fixed-effects example
haojian Jul 14, 2026
b66c587
feat: replace fixed effects with GEKS-Jevons across site and paper
haojian Jul 23, 2026
e0b8357
fix(site): drop the naive chained index — GEKS-Jevons is the only ser…
haojian Jul 27, 2026
baf94b2
feat(site): publish the index in real terms, CPI-U-deflated by default
haojian Jul 30, 2026
49b47e1
docs(faq): audit the whole FAQ, not just the inflation answer
haojian Jul 30, 2026
e954d76
fix(data): exclude non-gig landing pages from the panel
haojian Aug 5, 2026
a19a14b
feat(site): publish precision, and retract the category ranking
haojian Aug 5, 2026
e4e10e3
feat(analysis): hedonic regression and MIN_MATCH sensitivity sweep
haojian Aug 6, 2026
802eb3e
docs(plans): publication plan, readiness audit, and the MIN_MATCH dec…
haojian Aug 6, 2026
2632c0f
chore(drafts): track the 2026-07-14 render
haojian Aug 6, 2026
833b782
feat(analysis): reputation-adjusted band, and a pooled beta forced by…
haojian Aug 6, 2026
c801954
feat(analysis): window choice stays 2020Q1 — and per-category growth …
haojian Aug 6, 2026
fe4c437
feat(analysis): cut the elasticity table as spurious; retire TD1 by d…
haojian Aug 6, 2026
8d0be82
docs(paper): freeze the numbers and rewrite every prose section off them
haojian Aug 6, 2026
1c58da7
docs(paper): figures, bibliography, and the check that closes R10
haojian Aug 6, 2026
8284e32
docs(paper): publish the hedonic coefficients, not just its conclusions
haojian Aug 6, 2026
a5e3915
docs(method): the collection's revision history, and two claims the l…
haojian Aug 10, 2026
ffd600d
docs(templates): the two halves of the collection template — run it, …
haojian Aug 10, 2026
cdaf965
docs(data): the collection written up in full, and both enlarged craw…
haojian Aug 10, 2026
c77cc67
chore(git): keep the enlarged collections' bulk outputs out of a publ…
haojian Aug 12, 2026
5e92b98
feat(collection): the two enlarged crawls, and the census work that s…
haojian Aug 12, 2026
446d96b
docs(method): the collection told twice — once plainly, once auditably
haojian Aug 12, 2026
0512254
docs(method): stop using "the index" for two different objects
haojian Aug 12, 2026
fb0867d
docs(method): say plainly that 2.5M was a projection and 1.78M supers…
haojian Aug 12, 2026
6e723ca
docs(method): the same section, told once
haojian Aug 13, 2026
740d515
feat(results): the enlarged collections, finally measured
haojian Aug 13, 2026
415575b
docs(results): move results.md into drafts/sections
haojian Aug 13, 2026
e9be5f9
docs(method): say what order the collection happened in
haojian Aug 13, 2026
9663bca
docs(results): say it in words a reader already has
haojian Aug 13, 2026
f72b732
docs(method): pair each limitation with what we did about it
haojian Aug 13, 2026
853c4c9
docs(method): tell the collection in the order it happened
haojian Aug 13, 2026
ba194d2
docs(method): cut section 3 to 3,996 words, add Appendix A
haojian Aug 13, 2026
2b6f8a0
docs(results): say what the final model is
haojian Aug 13, 2026
35f4d47
docs: add a plain-language methods-and-results walkthrough
haojian Aug 13, 2026
52c012a
prereg: lock the exposure ranking before any outcome is estimated
haojian Aug 17, 2026
2b8cf3f
phase 0: step 24's nulls were underpowering; the AI attribution is not
haojian Aug 17, 2026
2d29e5c
external: Fiverr Inc.'s own books say the transaction decline is real
haojian Aug 17, 2026
81e82bd
category: identification abandoned on evidence, not effort
haojian Aug 17, 2026
02a55f6
structure: four of five commoditisation predictions fail in sign, the…
haojian Aug 18, 2026
ce918dd
designs 6-8: the analysis scripts behind the committed progress entries
haojian Aug 19, 2026
7432d4f
collection: the route audit, and the first sitemap snapshot
haojian Aug 19, 2026
a431d80
diffusion: AI is measured inside the market, and the null survives a …
haojian Aug 19, 2026
62cc5a7
plan: bring the market-structure plan current through step 57
haojian Aug 19, 2026
d6058d4
design 10: twenty named launches, monthly, and the placebo that halve…
haojian Aug 20, 2026
3dce6eb
orders: what buyers paid was in the archive the whole time
haojian Aug 20, 2026
b128cb9
summary: the whole project in plain language, and the "during" correc…
haojian Aug 20, 2026
a1dc11f
record: three progress entries, two user requirements, three open items
haojian Aug 20, 2026
1f64a1a
summary: gloss the finance vocabulary, which the readability pass had…
haojian Aug 20, 2026
fdfd35b
prereg: design 11, staggered niche-level AI arrival, before vs after
haojian Aug 20, 2026
ab8c9f1
site: the transaction count, and the seven categories that turn befor…
haojian Aug 20, 2026
9536fe8
site: a volume card that never touches a price index
haojian Aug 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,38 @@ data/cdx-index/*.tmp

# Raw scraped HTML pages
data/pilot/html/
data/pilot/html-recent/
data/pilot/html-recent-test/

# Transient pipeline stdout logs
data/pilot/*.out
runs/**/*.out

# Derived title-flag panel (33 MB) — regenerated by code/57-ai-diffusion-titles.py
data/pilot/ai-title-flags.csv

# Large pilot manifests (small derived CSVs/summaries are kept)
data/pilot/*-manifest*.tsv

# Scratch / working artifacts (not deliverables)
scratchpad/

# === Python / OS cruft ===
__pycache__/
*.pyc
*.pyo
.DS_Store
.ipynb_checkpoints/
data/pilot/html-balanced/
data/pilot/html-expanded/

# === Enlarged-collection outputs (2026-08) ===
# Regenerable from the manifests + code/38, code/41 and the pipeline drivers.
# 85 MB / 25 MB price files and their logs would exceed GitHub's limits on a
# public repo; the small pilot-scale equivalents above stay tracked.
data/pilot/balanced-prices.csv
data/pilot/expanded-prices.csv
data/pilot/gig-status-ledger.tsv
data/pilot/expanded-gig-status.tsv
data/pilot/*-download-log.tsv
data/pilot/*-download-checkpoint.txt
78 changes: 60 additions & 18 deletions code/08-download-html.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import argparse
import asyncio
import aiohttp
import gzip
import time
import csv
import os
Expand All @@ -41,6 +42,15 @@ def parse_args():
p.add_argument("--max-rate", type=float, default=12.0,
help="Max requests per second")
p.add_argument("--max-retries", type=int, default=3)
p.add_argument("--gzip", action="store_true",
help="Store pages gzipped as <name>.html.gz. Measured 5.0x on "
"this corpus (22 GB -> ~4.4 GB per 15k pages); "
"09-extract-prices.py reads both forms transparently.")
p.add_argument("--chunk", type=int, default=5000,
help="Schedule this many downloads at a time instead of "
"creating one coroutine per manifest row up front.")
p.add_argument("--limit", type=int, default=0,
help="Stop after N new downloads (0 = no limit). For pilots.")
return p.parse_args()


Expand All @@ -56,12 +66,26 @@ def extract_seller_slug(url):
return "unknown", "unknown"


def build_output_path(html_dir, seller, slug, timestamp):
"""Build output path: html/<seller>/<YYYYMMDD>_<slug>.html"""
def build_output_path(html_dir, seller, slug, timestamp, use_gzip=False):
"""Build output path: html/<seller>/<YYYYMMDD>_<slug>.html[.gz]

Does NOT create the directory — with ~90k gigs this is called once per
manifest row during the existence check, and mkdir on every row is a
measurable cost. The writer creates the parent when it actually writes.
"""
date = timestamp[:8]
out_dir = html_dir / seller
out_dir.mkdir(parents=True, exist_ok=True)
return out_dir / f"{date}_{slug}.html"
return html_dir / seller / f"{date}_{slug}.html{'.gz' if use_gzip else ''}"


def existing_path(html_dir, seller, slug, timestamp):
"""Return an already-downloaded page for this snapshot in either storage
form, or None. Lets a gzipped run reuse the plain files from earlier runs
instead of re-fetching 15,150 pages that are already on disk."""
for gz in (False, True):
p = build_output_path(html_dir, seller, slug, timestamp, gz)
if p.exists() and p.stat().st_size > 0:
return p
return None


def load_manifest(manifest_path):
Expand Down Expand Up @@ -109,16 +133,18 @@ async def acquire(self):


async def download_one(session, timestamp, url, html_dir, rate_limiter,
max_retries, log_file, checkpoint_file, stats):
max_retries, log_file, checkpoint_file, stats,
use_gzip=False):
"""Download a single Wayback snapshot."""
seller, slug = extract_seller_slug(url)
out_path = build_output_path(html_dir, seller, slug, timestamp)

# Skip if already exists on disk
if out_path.exists() and out_path.stat().st_size > 0:
# Skip if already on disk in either storage form
if existing_path(html_dir, seller, slug, timestamp) is not None:
stats["skipped"] += 1
return

out_path = build_output_path(html_dir, seller, slug, timestamp, use_gzip)

wayback_url = WAYBACK_TPL.format(timestamp=timestamp, url=url)

for attempt in range(max_retries):
Expand All @@ -128,8 +154,12 @@ async def download_one(session, timestamp, url, html_dir, rate_limiter,
if resp.status == 200:
content = await resp.read()
out_path.parent.mkdir(parents=True, exist_ok=True)
with open(out_path, "wb") as f:
f.write(content)
if use_gzip:
with gzip.open(out_path, "wb", compresslevel=6) as f:
f.write(content)
else:
with open(out_path, "wb") as f:
f.write(content)

stats["ok"] += 1
size = len(content)
Expand Down Expand Up @@ -171,10 +201,15 @@ async def main_async(args):

# Filter out already done
todo = [(ts, url) for ts, url in entries if f"{ts}\t{url}" not in done]
n_pending = len(todo)
if args.limit:
todo = todo[:args.limit]

print(f"Manifest: {len(entries):,} snapshots")
print(f"Already done:{len(entries) - len(todo):,}")
print(f"To download: {len(todo):,}")
print(f"Already done:{len(entries) - n_pending:,}")
print(f"To download: {len(todo):,}" +
(f" (--limit of {n_pending:,} pending)" if args.limit else ""))
print(f"Storage: {'gzip' if args.gzip else 'plain'}")
print(f"Concurrency: {args.concurrency}")
print(f"Rate limit: {args.max_rate} req/s")
print()
Expand Down Expand Up @@ -205,7 +240,8 @@ async def bounded_download(ts, url):
async with sem:
await download_one(session, ts, url, args.html_dir,
rate_limiter, args.max_retries,
log_file, checkpoint_file, stats)
log_file, checkpoint_file, stats,
use_gzip=args.gzip)

# Progress update
total_done = stats["ok"] + stats["failed"] + stats["skipped"]
Expand All @@ -218,8 +254,12 @@ async def bounded_download(ts, url):
f"skip={stats['skipped']:,} "
f"| {rate:.1f}/s | {elapsed:.0f}s")

tasks = [bounded_download(ts, url) for ts, url in todo]
await asyncio.gather(*tasks)
# Schedule in chunks. One coroutine per manifest row up front is
# fine at 15k rows and not at 100k on a 5 GB box.
for i in range(0, len(todo), args.chunk):
batch = todo[i:i + args.chunk]
await asyncio.gather(*(bounded_download(ts, url)
for ts, url in batch))

elapsed = time.time() - start_time
print()
Expand All @@ -230,9 +270,11 @@ async def bounded_download(ts, url):
print(f" Skipped: {stats['skipped']:,}")
print(f" Retries: {stats['retries']:,}")

# Check disk usage
total_size = sum(f.stat().st_size for f in args.html_dir.rglob("*.html"))
# Check disk usage (both storage forms)
total_size = sum(f.stat().st_size for f in args.html_dir.rglob("*.html*"))
print(f" Disk: {total_size / 1024 / 1024 / 1024:.2f} GB")
if stats["ok"]:
print(f" Rate: {stats['ok'] / elapsed:.2f} pages/s sustained")


def main():
Expand Down
31 changes: 26 additions & 5 deletions code/09-extract-prices.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"""

import csv
import gzip
import json
import os
import re
Expand Down Expand Up @@ -244,8 +245,13 @@ def extract_rating(html):

def process_file(filepath):
"""Extract all data from a single HTML file."""
# Parse filename: <YYYYMMDD>_<slug>.html
fname = filepath.stem # e.g., "20230801_write-content-for-your-website"
# Parse filename: <YYYYMMDD>_<slug>.html or <YYYYMMDD>_<slug>.html.gz
# (.stem on a .html.gz leaves the ".html", which would land in the slug)
fname = filepath.name
for suffix in (".html.gz", ".html"):
if fname.endswith(suffix):
fname = fname[:-len(suffix)]
break
seller = filepath.parent.name

parts = fname.split("_", 1)
Expand All @@ -255,8 +261,9 @@ def process_file(filepath):
year = date[:4] if len(date) >= 4 else ""
month = date[4:6] if len(date) >= 6 else ""

opener = gzip.open if filepath.name.endswith(".gz") else open
try:
with open(filepath, "r", encoding="utf-8", errors="replace") as f:
with opener(filepath, "rt", encoding="utf-8", errors="replace") as f:
html = f.read()
except Exception:
return None, "read_error"
Expand Down Expand Up @@ -293,7 +300,10 @@ def process_file(filepath):
result["year"] = year
result["month"] = month
result["title"] = extract_title(html)
result["file_path"] = str(filepath.relative_to(BASE_DIR))
try:
result["file_path"] = str(Path(filepath).resolve().relative_to(BASE_DIR))
except ValueError:
result["file_path"] = str(filepath)

rating, review_count = extract_rating(html)
result["rating"] = rating if rating is not None else ""
Expand All @@ -303,9 +313,20 @@ def process_file(filepath):


def main():
import argparse
global HTML_DIR, OUTPUT, ERRORS_LOG
ap = argparse.ArgumentParser()
ap.add_argument("--html-dir", type=Path, default=HTML_DIR)
ap.add_argument("--output", type=Path, default=OUTPUT)
ap.add_argument("--errors-log", type=Path, default=ERRORS_LOG)
a = ap.parse_args()
HTML_DIR, OUTPUT, ERRORS_LOG = a.html_dir, a.output, a.errors_log

print("Extracting prices from downloaded HTML...")
print(f" Input: {HTML_DIR}")

html_files = sorted(HTML_DIR.rglob("*.html"))
html_files = sorted(list(HTML_DIR.rglob("*.html")) +
list(HTML_DIR.rglob("*.html.gz")))
print(f" Found {len(html_files):,} HTML files")

success = 0
Expand Down
23 changes: 18 additions & 5 deletions code/12-panel-ipi.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
import numpy as np
from scipy import stats as sp_stats

sys.path.insert(0, str(Path(__file__).resolve().parent))
from gigfilter import is_gig

BASE_DIR = Path(__file__).resolve().parent.parent
PRICES_FILE = BASE_DIR / "data" / "pilot" / "pilot-prices.csv"
ITEMS_FILE = BASE_DIR / "data" / "pilot" / "gig-items.csv"
Expand Down Expand Up @@ -111,6 +114,8 @@ def main():

with open(PRICES_FILE) as f:
for row in csv.DictReader(f):
if not is_gig(row["seller"]): # /hire/, /agencies/ landing pages
continue
key = (row["seller"], row["slug"])
item = item_map.get(key)
if not item:
Expand Down Expand Up @@ -196,17 +201,25 @@ def main():
# Initialize at base quarter = 100
index = {base_q: 100.0}

# Chain forward from base
# Chain forward from base. Gap-tolerant: the matched-model relative at a
# quarter is p_t / p_{gig's previous observed quarter}, which already spans
# any gap, so we chain from the most recent quarter already in the index
# rather than requiring the immediately-preceding grid quarter. This keeps a
# category alive across coverage gaps (e.g. translation is empty in 2019Q2–Q4
# right after the base but has 18 solid transitions over 2020Q1–2024Q3; the
# old "prev grid quarter must be in index" rule silently discarded all of it).
forward_qs = [q for q in all_quarters if q >= base_q]
last_q = base_q
for i in range(1, len(forward_qs)):
q = forward_qs[i]
if q in quarter_relatives and len(quarter_relatives[q]) >= 3:
geo_mean = np.exp(np.mean(np.log(quarter_relatives[q])))
prev_q = forward_qs[i-1]
if prev_q in index:
index[q] = index[prev_q] * geo_mean
index[q] = index[last_q] * geo_mean
last_q = q

# Chain backward from base
# Chain backward from base. Kept strict (immediately-preceding grid quarter
# must already be in the index) so pre-base historical levels — which feed
# the paper's peak/start figures — are unchanged by this fix.
backward_qs = [q for q in reversed(all_quarters) if q <= base_q]
for i in range(1, len(backward_qs)):
q = backward_qs[i]
Expand Down
Loading
Loading