PROGRAMS = [
    {"n":1,"mod":"Getting started","title":"Hello, World!","what":"The very first Python program — print text to the screen.","learns":["print() function","Running a .py file","Python syntax basics"],"code":'''print("Hello, World!")
print("Welcome to Python 108!")''',"output":"Hello, World!\nWelcome to Python 108!"},
    {"n":2,"mod":"Getting started","title":"Variables & data types","what":"Store different kinds of data in named variables.","learns":["int, float, str, bool","type() function","Variable assignment"],"code":'''name = "Priya"
age = 17
height = 5.4
is_student = True

print(name, age, height, is_student)
print(type(age), type(height))''',"output":"Priya 17 5.4 True\n<class 'int'> <class 'float'>"},
    {"n":3,"mod":"Getting started","title":"String basics","what":"Explore indexing, length, and slicing of strings.","learns":["len()","Indexing s[0]","Slicing s[1:4]"],"code":'''s = "Python"
print(len(s))
print(s[0])
print(s[-1])
print(s[1:4])
print(s[::-1])''',"output":"6\nP\nn\nyt\nnohtyP"},
    {"n":4,"mod":"Getting started","title":"String methods","what":"Built-in tools for transforming and searching strings.","learns":["upper(), lower(), strip()","replace(), split()","in keyword"],"code":'''msg = "  hello world  "
print(msg.strip())
print(msg.strip().upper())
print(msg.strip().replace("world","Python"))
print("hello world".split())
print("Python" in "I love Python")''',"output":"hello world\nHELLO WORLD\nhello Python\n['hello', 'world']\nTrue"},
    {"n":5,"mod":"Getting started","title":"String formatting","what":"Three ways to embed values inside strings cleanly.","learns":["f-strings","str.format()","% formatting"],"code":'''name = "Aasha"
score = 95.5
print(f"Student: {name}, Score: {score:.1f}%")
print("Student: {}, Score: {:.1f}%".format(name, score))
print("Student: %s, Score: %.1f%%" % (name, score))''',"output":"Student: Aasha, Score: 95.5%\nStudent: Aasha, Score: 95.5%\nStudent: Aasha, Score: 95.5%"},
    {"n":6,"mod":"Getting started","title":"User input","what":"Accept data typed by the user and convert its type.","learns":["input()","int(), float() casting","Concatenation with input"],"code":'''# Simulated input for demo
name = "Ravi"
age = int("20")
print(f"Hello {name}! Next year you will be {age + 1}.")''',"output":"Hello Ravi! Next year you will be 21."},
    {"n":7,"mod":"Getting started","title":"Arithmetic operators","what":"All the math operators Python provides.","learns":["+, -, *, /","// floor division","% modulus, ** power"],"code":'''a, b = 17, 5
print(a + b, a - b, a * b)
print(a / b)
print(a // b)
print(a % b)
print(a ** 2)''',"output":"22 12 85\n3.4\n3\n2\n289"},
    {"n":8,"mod":"Getting started","title":"Comparison & logical operators","what":"Make decisions by comparing values.","learns":["==, !=, >, <, >=, <=","and, or, not","Boolean results"],"code":'''x = 10
print(x > 5)
print(x == 10 and x < 20)
print(x == 5 or x == 10)
print(not x == 10)''',"output":"True\nTrue\nTrue\nFalse"},
    {"n":9,"mod":"Getting started","title":"Simple calculator","what":"Combine input, operators, and output into a mini-app.","learns":["Practical operators","int() casting","Formatted output"],"code":'''a, b = 12, 4
print(f"{a} + {b} = {a+b}")
print(f"{a} - {b} = {a-b}")
print(f"{a} * {b} = {a*b}")
print(f"{a} / {b} = {a/b}")
print(f"{a} // {b} = {a//b}")
print(f"{a} % {b} = {a%b}")''',"output":"12 + 4 = 16\n12 - 4 = 8\n12 * 4 = 48\n12 / 4 = 3.0\n12 // 4 = 3\n12 % 4 = 0"},
    {"n":10,"mod":"Control flow","title":"if / elif / else","what":"Branch your program based on conditions.","learns":["if, elif, else","Indentation rules","Boolean conditions"],"code":'''temp = 38
if temp > 37.5:
    print("Fever detected!")
elif temp == 37.5:
    print("Borderline")
else:
    print("Normal temperature")''',"output":"Fever detected!"},
    {"n":11,"mod":"Control flow","title":"Nested if","what":"Put one if-block inside another for layered decisions.","learns":["Nested conditions","Indentation depth","Real-world logic"],"code":'''age = 20
has_id = True
if age >= 18:
    if has_id:
        print("Entry allowed")
    else:
        print("Show your ID")
else:
    print("Too young to enter")''',"output":"Entry allowed"},
    {"n":12,"mod":"Control flow","title":"Grade calculator","what":"Map a numeric score to a letter grade.","learns":["Chained elif","Range logic","Practical conditions"],"code":'''score = 82
if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
elif score >= 70:
    grade = "C"
elif score >= 60:
    grade = "D"
else:
    grade = "F"
print(f"Score: {score} → Grade: {grade}")''',"output":"Score: 82 → Grade: B"},
    {"n":13,"mod":"Control flow","title":"while loop","what":"Repeat a block as long as a condition is true.","learns":["while condition","Loop counter","Infinite-loop prevention"],"code":'''count = 1
while count <= 5:
    print(f"Count: {count}")
    count += 1
print("Done!")''',"output":"Count: 1\nCount: 2\nCount: 3\nCount: 4\nCount: 5\nDone!"},
    {"n":14,"mod":"Control flow","title":"for loop","what":"Iterate over a sequence or a range of numbers.","learns":["range()","for item in sequence","Sum accumulation"],"code":'''total = 0
for i in range(1, 6):
    total += i
    print(f"i={i}, total={total}")
print("Sum 1-5:", total)''',"output":"i=1, total=1\ni=2, total=3\ni=3, total=6\ni=4, total=10\ni=5, total=15\nSum 1-5: 15"},
    {"n":15,"mod":"Control flow","title":"break & continue","what":"Skip iterations or exit loops early.","learns":["break exits the loop","continue skips to next","Practical loop control"],"code":'''for i in range(1, 11):
    if i == 4:
        continue
    if i == 8:
        break
    print(i, end=" ")
print()''',"output":"1 2 3 5 6 7"},
    {"n":16,"mod":"Control flow","title":"Nested loops","what":"A loop inside a loop — builds the multiplication table.","learns":["Nested for loops","end= trick","Grid patterns"],"code":'''for i in range(1, 4):
    for j in range(1, 4):
        print(f"{i*j:3}", end="")
    print()''',"output":"  1  2  3\n  2  4  6\n  3  6  9"},
    {"n":17,"mod":"Control flow","title":"pass statement","what":"A no-op placeholder to keep empty blocks valid.","learns":["pass keyword","When to use placeholders","Stub functions"],"code":'''for i in range(5):
    if i == 2:
        pass  # will handle later
    else:
        print(i)''',"output":"0\n1\n3\n4"},
    {"n":18,"mod":"Control flow","title":"Pattern printing","what":"Use loops to draw star patterns.","learns":["Nested loop logic","String multiplication","Right-aligned patterns"],"code":'''n = 5
for i in range(1, n+1):
    print("*" * i)
print()
for i in range(n, 0, -1):
    print(" " * (n-i) + "*" * i)''',"output":"*\n**\n***\n****\n*****\n\n*****\n ****\n  ***\n   **\n    *"},
    {"n":19,"mod":"Functions","title":"Defining functions","what":"Bundle reusable logic into a named function.","learns":["def keyword","return statement","Calling a function"],"code":'''def greet(name):
    return f"Hello, {name}!"

def square(n):
    return n * n

print(greet("Ananya"))
print(square(7))''',"output":"Hello, Ananya!\n49"},
    {"n":20,"mod":"Functions","title":"Function parameters","what":"Pass data into functions using positional and keyword args.","learns":["Positional arguments","Keyword arguments","Argument order"],"code":'''def introduce(name, city, age):
    print(f"{name} from {city}, age {age}")

introduce("Kiran", "Chennai", 21)
introduce(age=19, name="Meera", city="Mumbai")''',"output":"Kiran from Chennai, age 21\nMeera from Mumbai, age 19"},
    {"n":21,"mod":"Functions","title":"Default arguments","what":"Give parameters fallback values so they become optional.","learns":["Default values","Optional vs required params","Order of defaults"],"code":'''def power(base, exp=2):
    return base ** exp

print(power(3))
print(power(3, 3))
print(power(2, 10))''',"output":"9\n27\n1024"},
    {"n":22,"mod":"Functions","title":"*args & **kwargs","what":"Accept any number of positional or keyword arguments.","learns":["*args tuple","**kwargs dict","Flexible function signatures"],"code":'''def total(*nums):
    return sum(nums)

def profile(**info):
    for k, v in info.items():
        print(f"  {k}: {v}")

print(total(1, 2, 3, 4, 5))
profile(name="Divya", city="Pune", age=22)''',"output":"15\n  name: Divya\n  city: Pune\n  age: 22"},
    {"n":23,"mod":"Functions","title":"Lambda functions","what":"Write tiny anonymous functions in a single line.","learns":["lambda syntax","One-expression functions","Using with sorted/map"],"code":'''square = lambda x: x ** 2
add = lambda a, b: a + b

print(square(5))
print(add(3, 7))

nums = [4, 1, 7, 2, 9]
print(sorted(nums, key=lambda x: -x))''',"output":"25\n10\n[9, 7, 4, 2, 1]"},
    {"n":24,"mod":"Functions","title":"Recursion","what":"A function that calls itself to solve smaller sub-problems.","learns":["Base case","Recursive case","Call stack mental model"],"code":'''def factorial(n):
    if n == 0:
        return 1
    return n * factorial(n - 1)

def fib(n):
    if n <= 1:
        return n
    return fib(n-1) + fib(n-2)

print(factorial(5))
print([fib(i) for i in range(8)])''',"output":"120\n[0, 1, 1, 2, 3, 5, 8, 13]"},
    {"n":25,"mod":"Functions","title":"Scope — local & global","what":"Understand where variables live and how Python finds them.","learns":["LEGB rule","global keyword","Local vs global conflict"],"code":'''x = "global"

def show():
    x = "local"
    print("inside:", x)

def modify():
    global x
    x = "modified"

show()
print("outside:", x)
modify()
print("after modify:", x)''',"output":"inside: local\noutside: global\nafter modify: modified"},
    {"n":26,"mod":"Functions","title":"Closures","what":"An inner function that remembers its enclosing scope.","learns":["Inner functions","Enclosing scope","Factory pattern"],"code":'''def make_multiplier(factor):
    def multiply(n):
        return n * factor
    return multiply

double = make_multiplier(2)
triple = make_multiplier(3)

print(double(5))
print(triple(5))''',"output":"10\n15"},
    {"n":27,"mod":"Functions","title":"Higher-order functions","what":"Functions that take or return other functions.","learns":["map()","filter()","functools.reduce()"],"code":'''from functools import reduce

nums = [1, 2, 3, 4, 5]
squares = list(map(lambda x: x**2, nums))
evens = list(filter(lambda x: x%2==0, nums))
product = reduce(lambda a,b: a*b, nums)

print(squares)
print(evens)
print(product)''',"output":"[1, 4, 9, 16, 25]\n[2, 4]\n120"},
    {"n":28,"mod":"Data structures","title":"Lists","what":"Python's most versatile ordered, mutable collection.","learns":["Creating lists","Indexing & slicing","Mutating elements"],"code":'''fruits = ["apple","banana","cherry"]
print(fruits[0])
print(fruits[-1])
print(fruits[1:])
fruits[1] = "mango"
print(fruits)
print(len(fruits))''',"output":"apple\ncherry\n['banana', 'cherry']\n['apple', 'mango', 'cherry']\n3"},
    {"n":29,"mod":"Data structures","title":"List methods","what":"Built-in list operations for adding, removing, and sorting.","learns":["append, insert, pop","sort, reverse","count, index"],"code":'''nums = [3,1,4,1,5,9,2,6]
nums.append(7)
nums.insert(0, 0)
nums.pop()
nums.sort()
print(nums)
print(nums.count(1))
print(nums.index(5))''',"output":"[0, 1, 1, 2, 3, 4, 5, 9]\n2\n6"},
    {"n":30,"mod":"Data structures","title":"List comprehensions","what":"Create lists in one elegant, readable line.","learns":["[expr for x in iterable]","Conditional filtering","Nested comprehensions"],"code":'''squares = [x**2 for x in range(1,6)]
evens = [x for x in range(20) if x%2==0]
pairs = [(i,j) for i in range(3) for j in range(3) if i!=j]

print(squares)
print(evens)
print(pairs[:4])''',"output":"[1, 4, 9, 16, 25]\n[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]\n[(0, 1), (0, 2), (1, 0), (1, 2)]"},
]

# Programs 31-108: condensed with key data
EXTRA_PROGRAMS = [
    {"n":31,"mod":"Data structures","title":"Tuples","what":"Immutable sequences — fixed data that shouldn't change.","learns":["Tuple creation","Packing & unpacking","When to use tuples"],"code":'''point = (3, 7)
x, y = point
print(f"x={x}, y={y}")

rgb = (255, 128, 0)
r, g, b = rgb
print(f"Red={r} Green={g} Blue={b}")''',"output":"x=3, y=7\nRed=255 Green=128 Blue=0"},
    {"n":32,"mod":"Data structures","title":"Sets","what":"Unordered collections of unique elements.","learns":["set()","union, intersection, difference","Removing duplicates"],"code":'''a = {1,2,3,4,5}
b = {3,4,5,6,7}
print(a | b)
print(a & b)
print(a - b)''',"output":"{1, 2, 3, 4, 5, 6, 7}\n{3, 4, 5}\n{1, 2}"},
    {"n":33,"mod":"Data structures","title":"Dictionaries","what":"Map keys to values — Python's most used data structure.","learns":["dict creation","get, update, delete","Key existence check"],"code":'''student = {"name":"Arjun","age":18,"grade":"A"}
print(student["name"])
print(student.get("score","N/A"))
student["score"] = 95
del student["grade"]
print(student)''',"output":"Arjun\nN/A\n{'name': 'Arjun', 'age': 18, 'score': 95}"},
    {"n":34,"mod":"Data structures","title":"Dict methods","what":"Iterate and inspect dictionaries with built-in methods.","learns":["keys(), values(), items()","Looping over dicts","dict.update()"],"code":'''d = {"a":1,"b":2,"c":3}
print(list(d.keys()))
print(list(d.values()))
for k, v in d.items():
    print(f"  {k} => {v}")''',"output":"['a', 'b', 'c']\n[1, 2, 3]\n  a => 1\n  b => 2\n  c => 3"},
    {"n":35,"mod":"Data structures","title":"Dict comprehensions","what":"Build dictionaries with the same elegance as list comps.","learns":["Dict comp syntax","Transforming keys/values","Filtering in dict comps"],"code":'''squares = {x: x**2 for x in range(1,6)}
print(squares)
words = ["apple","banana","kiwi","fig"]
lengths = {w: len(w) for w in words if len(w) > 3}
print(lengths)''',"output":"{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}\n{'apple': 5, 'banana': 6, 'kiwi': 4}"},
    {"n":36,"mod":"Data structures","title":"Nested data structures","what":"Lists inside dicts, dicts inside lists — real-world shapes.","learns":["Accessing nested data","Looping nested structures","Building records"],"code":'''students = [
    {"name":"Lakshmi","scores":[85,90,78]},
    {"name":"Rohan","scores":[92,88,95]},
]
for s in students:
    avg = sum(s["scores"]) / len(s["scores"])
    print(f"{s['name']}: avg={avg:.1f}")''',"output":"Lakshmi: avg=84.3\nRohan: avg=91.7"},
    {"n":37,"mod":"Files & exceptions","title":"Reading files","what":"Open and read text files using Python's built-in open().","learns":["open(), read()","readlines() for list","Closing files"],"code":'''lines = ["Line 1: Hello","Line 2: World","Line 3: Python"]
for i, line in enumerate(lines, 1):
    print(f"{i}: {line}")''',"output":"1: Line 1: Hello\n2: Line 2: World\n3: Line 3: Python"},
    {"n":38,"mod":"Files & exceptions","title":"Writing files","what":"Create and write content to files on disk.","learns":["'w' write mode","'a' append mode","writelines()"],"code":'''import io
buf = io.StringIO()
buf.write("First line\n")
buf.write("Second line\n")
print(buf.getvalue())''',"output":"First line\nSecond line\n"},
    {"n":39,"mod":"Files & exceptions","title":"with statement","what":"Automatically close resources using context managers.","learns":["with open() as f","Context manager protocol","Why with is safer"],"code":'''import io
data = "Name,Age\nAlice,30\nBob,25\n"
with io.StringIO(data) as f:
    for line in f:
        print(line.strip())
print("File closed automatically")''',"output":"Name,Age\nAlice,30\nBob,25\nFile closed automatically"},
    {"n":40,"mod":"Files & exceptions","title":"Working with CSV","what":"Read and write comma-separated data using the csv module.","learns":["csv.reader","csv.writer","DictReader for headers"],"code":'''import csv, io
data = "name,age\nAlice,30\nBob,25\n"
reader = csv.DictReader(io.StringIO(data))
for row in reader:
    print(f"{row['name']} is {row['age']}")''',"output":"Alice is 30\nBob is 25"},
    {"n":41,"mod":"Files & exceptions","title":"Working with JSON","what":"Serialize Python objects to JSON and parse JSON back.","learns":["json.dumps()","json.loads()","indent for pretty print"],"code":'''import json
student = {"name":"Priya","grades":[90,85,92],"active":True}
json_str = json.dumps(student, indent=2)
print(json_str)''',"output":'{\n  "name": "Priya",\n  "grades": [90, 85, 92],\n  "active": true\n}'},
    {"n":42,"mod":"Files & exceptions","title":"try / except","what":"Catch errors gracefully instead of crashing.","learns":["try...except block","Catching specific errors","Error messages"],"code":'''def safe_divide(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return "Cannot divide by zero"

print(safe_divide(10, 2))
print(safe_divide(10, 0))''',"output":"5.0\nCannot divide by zero"},
    {"n":43,"mod":"Files & exceptions","title":"Exception types","what":"Python's built-in exception hierarchy.","learns":["Common exceptions","KeyError, IndexError","TypeError, AttributeError"],"code":'''tests = [
    lambda: {}["missing"],
    lambda: [1,2,3][9],
    lambda: int("xyz"),
]
for t in tests:
    try:
        t()
    except Exception as e:
        print(f"{type(e).__name__}: {e}")''',"output":"KeyError: 'missing'\nIndexError: list index out of range\nValueError: invalid literal for int() with base 10: 'xyz'"},
    {"n":44,"mod":"Files & exceptions","title":"finally & else","what":"Run cleanup code or happy-path code alongside try/except.","learns":["finally always runs","else runs if no exception","Cleanup pattern"],"code":'''def read_number(s):
    try:
        n = int(s)
    except ValueError:
        print(f"  '{s}' is not a number")
    else:
        print(f"  Got number: {n}")
    finally:
        print("  --- done ---")

read_number("42")
read_number("oops")''',"output":"  Got number: 42\n  --- done ---\n  'oops' is not a number\n  --- done ---"},
    {"n":45,"mod":"Files & exceptions","title":"Custom exceptions","what":"Define your own exception classes for domain errors.","learns":["class MyError(Exception)","raise keyword","Custom messages"],"code":'''class AgeError(Exception):
    pass

def check_age(age):
    if age < 0:
        raise AgeError("Age cannot be negative")
    return f"Valid age: {age}"

for a in [25, -1]:
    try:
        print(check_age(a))
    except AgeError as e:
        print(f"AgeError: {e}")''',"output":"Valid age: 25\nAgeError: Age cannot be negative"},
    {"n":46,"mod":"OOP basics","title":"Classes & objects","what":"Create blueprints (classes) and instances (objects).","learns":["class keyword","__init__ constructor","self parameter"],"code":'''class Dog:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed

    def bark(self):
        return f"{self.name} says: Woof!"

d1 = Dog("Bruno", "Labrador")
print(d1.bark())
print(d1.breed)''',"output":"Bruno says: Woof!\nLabrador"},
    {"n":47,"mod":"OOP basics","title":"Instance vs class attributes","what":"Attributes shared across all instances vs per-object.","learns":["Class attributes","Instance attributes","Accessing via class vs self"],"code":'''class Circle:
    pi = 3.14159
    def __init__(self, radius):
        self.radius = radius
    def area(self):
        return Circle.pi * self.radius ** 2

c1 = Circle(5)
c2 = Circle(10)
print(f"c1 area: {c1.area():.2f}")
print(f"c2 area: {c2.area():.2f}")''',"output":"c1 area: 78.54\nc2 area: 314.16"},
    {"n":48,"mod":"OOP basics","title":"Methods","what":"Instance, class, and static methods — three kinds.","learns":["@classmethod","@staticmethod","When to use each"],"code":'''class Temperature:
    def __init__(self, celsius):
        self.celsius = celsius
    def to_fahrenheit(self):
        return self.celsius * 9/5 + 32
    @staticmethod
    def is_hot(c):
        return c > 30

t = Temperature(100)
print(t.to_fahrenheit())
print(Temperature.is_hot(35))''',"output":"212.0\nTrue"},
    {"n":49,"mod":"OOP basics","title":"Inheritance","what":"A child class inherits and extends a parent class.","learns":["class Child(Parent)","super()","Method override"],"code":'''class Animal:
    def __init__(self, name):
        self.name = name
    def speak(self):
        return "..."

class Cat(Animal):
    def speak(self):
        return f"{self.name} says: Meow!"

class Dog(Animal):
    def speak(self):
        return f"{self.name} says: Woof!"

for a in [Cat("Whiskers"), Dog("Rex")]:
    print(a.speak())''',"output":"Whiskers says: Meow!\nRex says: Woof!"},
    {"n":50,"mod":"OOP basics","title":"Multiple inheritance","what":"A class can inherit from more than one parent.","learns":["class C(A, B)","MRO — method resolution order","super() with MRO"],"code":'''class Flyable:
    def fly(self): return "I can fly!"

class Swimmable:
    def swim(self): return "I can swim!"

class Duck(Flyable, Swimmable):
    def quack(self): return "Quack!"

d = Duck()
print(d.fly())
print(d.swim())
print(d.quack())''',"output":"I can fly!\nI can swim!\nQuack!"},
    {"n":51,"mod":"OOP basics","title":"Encapsulation","what":"Hide internal data using private attributes.","learns":["_ convention","__ name mangling","Getter/setter pattern"],"code":'''class BankAccount:
    def __init__(self, balance):
        self.__balance = balance
    def deposit(self, amt):
        if amt > 0:
            self.__balance += amt
    def get_balance(self):
        return self.__balance

acc = BankAccount(1000)
acc.deposit(500)
print(acc.get_balance())''',"output":"1500"},
    {"n":52,"mod":"OOP basics","title":"Polymorphism","what":"Different objects responding to the same method call.","learns":["Duck typing","Method overriding","isinstance()"],"code":'''class Rectangle:
    def __init__(self,w,h): self.w,self.h=w,h
    def area(self): return self.w*self.h

class Circle:
    def __init__(self,r): self.r=r
    def area(self): return 3.14159*self.r**2

shapes = [Rectangle(4,5), Circle(3)]
for s in shapes:
    print(f"{type(s).__name__}: area={s.area():.2f}")''',"output":"Rectangle: area=20.00\nCircle: area=28.27"},
    {"n":53,"mod":"OOP basics","title":"Magic methods","what":"Special __ methods that give classes Pythonic behaviour.","learns":["__str__, __repr__","__len__, __eq__","__add__ operator overloading"],"code":'''class Vector:
    def __init__(self,x,y): self.x,self.y=x,y
    def __repr__(self): return f"Vector({self.x},{self.y})"
    def __add__(self,other): return Vector(self.x+other.x, self.y+other.y)
    def __len__(self): return int((self.x**2+self.y**2)**0.5)

v1=Vector(3,4); v2=Vector(1,2)
print(v1)
print(v1+v2)
print(len(v1))''',"output":"Vector(3,4)\nVector(4,6)\n5"},
    {"n":54,"mod":"OOP basics","title":"Dataclasses","what":"Auto-generate boilerplate OOP code with @dataclass.","learns":["@dataclass decorator","field() defaults","Auto __init__, __repr__, __eq__"],"code":'''from dataclasses import dataclass, field

@dataclass
class Student:
    name: str
    age: int
    grades: list = field(default_factory=list)
    def average(self):
        return sum(self.grades)/len(self.grades) if self.grades else 0

s = Student("Nila", 19, [88,92,76])
print(s)
print(f"Average: {s.average():.1f}")''',"output":"Student(name='Nila', age=19, grades=[88, 92, 76])\nAverage: 85.3"},
    {"n":55,"mod":"Modules & packages","title":"Importing modules","what":"Load functionality from other files and the standard library.","learns":["import module","from module import name","import as alias"],"code":'''import math
from math import pi, sqrt

print(math.ceil(4.2))
print(pi)
print(sqrt(144))''',"output":"5\n3.141592653589793\n12.0"},
    {"n":56,"mod":"Modules & packages","title":"Standard library tour","what":"A whirlwind tour of Python's most useful built-in modules.","learns":["os, sys, math","random, datetime","collections, itertools"],"code":'''import sys, math, random
print("Platform:", sys.platform)
print("Pi:", math.pi)
print("Random:", random.randint(1,10))''',"output":"Platform: linux\nPi: 3.141592653589793\nRandom: 7"},
    {"n":57,"mod":"Modules & packages","title":"os module","what":"Interact with the operating system — paths, dirs, env.","learns":["os.getcwd()","os.path.join()","os.environ"],"code":'''import os
print(os.getcwd())
print(os.path.join("home","user","file.txt"))
print(os.path.splitext("report.pdf"))''',"output":"/home/user\nhome/user/file.txt\n('report', '.pdf')"},
    {"n":58,"mod":"Modules & packages","title":"datetime module","what":"Work with dates, times, and time differences.","learns":["date, datetime, timedelta","Formatting with strftime","Arithmetic on dates"],"code":'''from datetime import date, timedelta
today = date.today()
print("Today:", today)
delta = timedelta(days=30)
print("30 days later:", today + delta)''',"output":"Today: 2026-05-02\n30 days later: 2026-06-01"},
    {"n":59,"mod":"Modules & packages","title":"random module","what":"Generate random numbers, pick items, shuffle sequences.","learns":["randint, random()","choice, sample","shuffle, seed"],"code":'''import random
random.seed(42)
print(random.randint(1,100))
fruits = ["apple","banana","cherry","date"]
print(random.choice(fruits))
random.shuffle(fruits)
print(fruits)''',"output":"52\ndate\n['date', 'apple', 'cherry', 'banana']"},
    {"n":60,"mod":"Modules & packages","title":"math & statistics","what":"Mathematical functions and descriptive statistics.","learns":["math.sqrt, log, sin","statistics.mean, median","statistics.stdev"],"code":'''import math, statistics
data = [12, 17, 9, 21, 15, 8, 19]
print("Mean:", statistics.mean(data))
print("Median:", statistics.median(data))
print("sqrt(256):", math.sqrt(256))''',"output":"Mean: 14.428571428571429\nMedian: 15\nsqrt(256): 16.0"},
]

# Programs 61-108
FINAL_PROGRAMS = []
titles_61_108 = [
    (61,"Modules & packages","Creating your own module","Write reusable Python files and import them."),
    (62,"Modules & packages","Package structure","Organise multiple modules into a package folder."),
    (63,"Modules & packages","pip & virtual envs","Install third-party packages and isolate environments."),
    (64,"Iterators & generators","Iterators","Understand how Python's for loops work under the hood."),
    (65,"Iterators & generators","Generator functions","Use yield to produce values lazily."),
    (66,"Iterators & generators","Generator expressions","Like list comprehensions but lazy."),
    (67,"Iterators & generators","itertools module","Powerful combinatoric and chaining tools."),
    (68,"Iterators & generators","enumerate & zip","Iterate with index or walk two sequences in parallel."),
    (69,"Iterators & generators","Sorting with key","Sort any sequence by a custom criterion."),
    (70,"Iterators & generators","Unpacking & starred","Destructure sequences flexibly with * and **."),
    (71,"Iterators & generators","Walrus operator :=","Assign and test a value in a single expression."),
    (72,"Iterators & generators","Named tuples","Tuples with field names — readable, lightweight records."),
    (73,"Decorators & advanced functions","Decorators basics","Wrap a function to add behaviour without changing its code."),
    (74,"Decorators & advanced functions","Decorators with args","Create decorators that themselves accept parameters."),
    (75,"Decorators & advanced functions","functools.wraps","Preserve the original function's name and docstring."),
    (76,"Decorators & advanced functions","Property decorator","Control attribute access with getter and setter methods."),
    (77,"Decorators & advanced functions","classmethod & staticmethod","Alternative constructors and utility methods on classes."),
    (78,"Decorators & advanced functions","Memoisation","Cache results of expensive function calls automatically."),
    (79,"Decorators & advanced functions","Context managers (custom)","Build your own with-statement compatible objects."),
    (80,"Decorators & advanced functions","Partial functions","Pre-fill some arguments of a function for reuse."),
    (81,"Decorators & advanced functions","Type hints & annotations","Document expected types to catch bugs early."),
    (82,"Working with data","Regular expressions","Match and extract patterns from text using the re module."),
    (83,"Working with data","String parsing project","Parse a real-looking log file to extract useful data."),
    (84,"Working with data","Collections module","Specialised container types beyond list/dict/set."),
    (85,"Working with data","Working with numbers","Exact decimal arithmetic, fractions, and complex numbers."),
    (86,"Working with data","Comprehension deep-dive","Advanced comprehension patterns and combinations."),
    (87,"Working with data","SQLite basics","Store and query data using Python's built-in SQLite."),
    (88,"Working with data","Web requests","Fetch data from the internet using urllib."),
    (89,"Working with data","Data project — CSV analyser","Read, compute stats, write a summary report."),
    (90,"Working with data","Intro to NumPy","Fast numerical computing with multi-dimensional arrays."),
    (91,"Concurrency & testing","Multithreading","Run multiple tasks concurrently using threads."),
    (92,"Concurrency & testing","Multiprocessing","Use multiple CPU cores for true parallelism."),
    (93,"Concurrency & testing","asyncio basics","Write asynchronous code with async/await."),
    (94,"Concurrency & testing","async HTTP requests","Fetch multiple URLs concurrently with aiohttp."),
    (95,"Concurrency & testing","Unit testing with unittest","Write automated tests to verify your functions work."),
    (96,"Concurrency & testing","pytest fundamentals","The most popular Python testing framework."),
    (97,"Concurrency & testing","Mocking","Replace real objects with controllable fakes in tests."),
    (98,"Concurrency & testing","Logging module","Record events at different severity levels."),
    (99,"Concurrency & testing","Profiling & optimisation","Measure where your code spends its time."),
    (100,"Capstone projects","CLI to-do app","A full command-line to-do list with file persistence."),
    (101,"Capstone projects","Contact book","OOP contact manager with search and JSON storage."),
    (102,"Capstone projects","Number guessing game","A complete game with hints, limited tries, and scoring."),
    (103,"Capstone projects","Text adventure game","A room-based adventure with inventory and classes."),
    (104,"Capstone projects","Student grade manager","Full grade management system with stats and file output."),
    (105,"Capstone projects","Weather CLI app","Fetch and display weather from a JSON API."),
    (106,"Capstone projects","Simple web scraper","Extract data from HTML using BeautifulSoup."),
    (107,"Capstone projects","Mini bank system","OOP, inheritance, exceptions, and tests combined."),
    (108,"Capstone projects","Python showcase","Combine all concepts and present your best work."),
]

for n, mod, title, what in titles_61_108:
    FINAL_PROGRAMS.append({
        "n": n, "mod": mod, "title": title, "what": what,
        "learns": ["See lesson plan for full details"],
        "code": f'# Program {n}: {title}\n# Write your solution here\nprint("Program {n} — {title}")',
        "output": f"Program {n} — {title}"
    })

ALL_PROGRAMS = PROGRAMS + EXTRA_PROGRAMS + FINAL_PROGRAMS

EXERCISES = [
    {"n":1,"diff":"Easy","q":"Write a program that prints your full name, your city, and today's date — each on a separate line. Then print all three on one line separated by a dash.","hint":"Use print() three times, then use print() once with an f-string to combine them.","answer":'''name = "Priya Sharma"
city = "Chennai"
date = "02-May-2026"
print(name)
print(city)
print(date)
print(f"{name} - {city} - {date}")'''},
    {"n":2,"diff":"Easy","q":"Create variables for: product name (str), price (float), quantity (int), in-stock (bool). Print each with its type. Then print: '3 x Widget @ ₹49.99 = ₹149.97'","hint":"Use type() to display type. Multiply price × quantity for total.","answer":'''name = "Widget"
price = 49.99
qty = 3
in_stock = True
print(name, type(name))
print(price, type(price))
total = price * qty
print(f"{qty} x {name} @ ₹{price} = ₹{total:.2f}")'''},
    {"n":3,"diff":"Easy","q":"Given 'Python Programming', extract and print: the first word, the last word, every second character, and the string reversed.","hint":"Use slicing: s[:6] for first word, s[7:] for second, s[::2] for every other, s[::-1] for reversed.","answer":'''s = "Python Programming"
print(s[:6])
print(s[7:])
print(s[::2])
print(s[::-1])'''},
    {"n":4,"diff":"Easy","q":"Take '   hello WORLD   ' and: strip it, title-case it, replace 'WORLD' with 'Python', split into a word list.","hint":"Chain .strip(), .title(), .replace() — each returns a new string.","answer":'''s = "   hello WORLD   "
clean = s.strip()
print(clean.title())
print(clean.replace("WORLD","Python"))
print(clean.split())'''},
    {"n":5,"diff":"Easy","q":"A student scored 87.333. Print a report card using all three formatting styles showing: 'Name: Aasha | Score: 87.3 | Grade: B'","hint":"Use :.1f for one decimal place in all three styles.","answer":'''name, score, grade = "Aasha", 87.333, "B"
print(f"Name: {name} | Score: {score:.1f} | Grade: {grade}")
print("Name: {} | Score: {:.1f} | Grade: {}".format(name,score,grade))'''},
    {"n":6,"diff":"Easy","q":"Ask for a birth year and calculate the user's age this year (2026). Also print how many days they have approximately lived.","hint":"Subtract birth year from 2026. Multiply years by 365 for approximate days.","answer":'''birth_year = int(input("Enter your birth year: "))
age = 2026 - birth_year
days = age * 365
print(f"You are {age} years old.")
print(f"That is approximately {days:,} days!")'''},
    {"n":7,"diff":"Easy","q":"Write a temperature converter: convert 100°C to Fahrenheit, Kelvin, and Rankine. Print all results with 2 decimal places.","hint":"F = C*9/5+32, K = C+273.15, R = (C+273.15)*9/5","answer":'''c = 100
f = c * 9/5 + 32
k = c + 273.15
r = (c + 273.15) * 9/5
print(f"{c}°C = {f:.2f}°F")
print(f"{c}°C = {k:.2f} K")'''},
    {"n":8,"diff":"Easy","q":"Cinema discount: age < 12 OR age >= 60 → discount, 12–17 → full price, else → normal. Given ages [8, 15, 25, 65], print who pays what.","hint":"Use or and and with comparison operators inside if/elif/else.","answer":'''ages = [8, 15, 25, 65]
for age in ages:
    if age < 12 or age >= 60:
        print(f"Age {age}: Discount")
    elif 12 <= age <= 17:
        print(f"Age {age}: Full price")
    else:
        print(f"Age {age}: Normal price")'''},
    {"n":9,"diff":"Easy","q":"Build a calculator that handles all 6 operators (+, -, *, /, //, %) and checks for division by zero, displaying a friendly error message.","hint":"Use if/elif for each operator. Check if b==0 before dividing.","answer":'''a, op, b = 10, "/", 0
if op == "/" and b == 0:
    print("Error: cannot divide by zero")
elif op == "+": print(a + b)
elif op == "-": print(a - b)
elif op == "*": print(a * b)
elif op == "/": print(a / b)'''},
    {"n":10,"diff":"Easy","q":"BMI classifier: given weight (kg) and height (m), compute BMI = weight/height². Print the value and category: Underweight/Normal/Overweight/Obese.","hint":"BMI = weight / (height ** 2). Use elif chains for categories.","answer":'''weight, height = 70, 1.75
bmi = weight / height**2
print(f"BMI: {bmi:.1f}")
if bmi < 18.5: print("Underweight")
elif bmi < 25: print("Normal")
elif bmi < 30: print("Overweight")
else: print("Obese")'''},
]

# Extend exercises for programs 11-108 with placeholder data
EXERCISE_TITLES = {
    11: ("Easy","Loan approval: approve if income > 30000 AND credit score >= 700. Print the decision with a reason.","Check income first (outer if), then score inside (inner if)."),
    12: ("Easy","For 6 students and scores, print a formatted table showing name, score, grade, and pass/fail status.","Use a list of tuples. Loop and apply grade logic."),
    13: ("Easy","Simulate a guessing game: secret=42, guesses=[10,40,30,42]. Print Too low/Too high/Correct with attempt count.","Keep a counter. Increment it each loop. Break when correct."),
    14: ("Easy","Print the first 10 Fibonacci numbers using a for loop. Also compute their sum.","Keep two variables a,b. Each step: a,b = b, a+b."),
    15: ("Easy","Loop through 1–50. Skip multiples of 3. Stop when you find the first multiple of 17.","Use continue for multiples of 3, break for multiples of 17."),
    16: ("Medium","Print a hollow rectangle of stars (5×10) — only the border has stars, inside is spaces.","For first/last rows print all stars. Otherwise print star, spaces, star."),
    17: ("Easy","Sketch a school management system using only class/function definitions with pass. Include Student, Teacher, Classroom with 4 methods each.","Use pass inside every def and class body."),
    18: ("Medium","Print a diamond pattern of stars with n=5. Top half grows, bottom half shrinks. Centre-align each row.","Top half: range(1,n+1,2). Bottom half: range(n-2,0,-2). Use str.center()."),
    19: ("Easy","Write three functions: is_even(n), is_prime(n), and digit_sum(n). Test each with [0,1,7,12,97,100].","is_prime: loop from 2 to sqrt(n). digit_sum: convert to str and sum digits."),
    20: ("Easy","Write build_email(name, domain, tld='com') that generates email addresses. Test with different combinations.","Convert name to lowercase, replace spaces with dots, combine with domain and tld."),
}

for n in range(11, 109):
    if n not in [e["n"] for e in EXERCISES]:
        if n in EXERCISE_TITLES:
            diff, q, hint = EXERCISE_TITLES[n]
        else:
            prog = next((p for p in ALL_PROGRAMS if p["n"] == n), None)
            title = prog["title"] if prog else f"Program {n}"
            diff = "Medium"
            q = f"Write a complete Python program demonstrating '{title}'. Include at least 3 different examples and edge cases."
            hint = f"Review Program {n} code and extend it with your own examples. Focus on the core concept."
        EXERCISES.append({
            "n": n, "diff": diff, "q": q,
            "hint": hint,
            "answer": f"# Your solution for Exercise {n}\n# Study the program first, then write your own version\nprint('Exercise {n} complete!')"
        })

MODULES = list(dict.fromkeys(p["mod"] for p in ALL_PROGRAMS))
