package registrar;

import tester.Tester;

class Examples {

    ILo<Integer> testList = new Cons<Integer>(
        1,
        new Cons<Integer>(2, new Cons<Integer>(3, new Mt<Integer>()))
    );

    void testILoUniqAppend(Tester t) {
        t.checkExpect(testList.uniqAppend(2), testList);
        t.checkExpect(
            testList.uniqAppend(4),
            new Cons<Integer>(
                1,
                new Cons<Integer>(
                    2,
                    new Cons<Integer>(
                        3,
                        new Cons<Integer>(4, new Mt<Integer>())
                    )
                )
            )
        );
    }
}

class School {

    static int cid = 0;

    // Returns the current ID, then increments it for the next time.
    static int getThenInc() {
        return School.cid++;
    }
}

class Course {

    String name;
    Teacher teacher;
    ILo<Student> students;

    Course(String name, Teacher teacher, ILo<Student> students) {
        this.name = name;
        this.teacher = teacher;
        this.teacher.assignCourse(this);
        this.students = students;
    }

    // Convenience constructor for initial course construction.
    Course(String name, Teacher teacher) {
        this(name, teacher, new Mt<Student>());
    }
}

class Teacher {

    String name;
    ILo<Course> courses;

    Teacher(String name, ILo<Course> courses) {
        this.name = name;
        this.courses = courses;
    }

    // Convenience constructor for initial teacher construction.
    Teacher(String name) {
        this(name, new Mt<Course>());
    }

    // Effect: Assigns the given Course to this Teacher.
    void assignCourse(Course course) {
        this.courses.uniqAppend(course);
    }

    // Effect: Enrolls the given Student in this Course.
    void enroll(Student student) {}
}

class Student {

    String name;
    int id;
    ILo<Course> courses;

    Student(String name, int id, ILo<Course> courses) {
        this.name = name;
        this.id = id;
        this.courses = courses;
    }

    // Convenience constructor for initial student construction.
    Student(String name) {
        this(name, School.cid, new Mt<Course>());
    }

    // Effect: Enrolls this student in the given course.
    void enroll(Course course) {
        course.enroll(this);
        this.courses.uniqAppend(course);
    }
}

interface ILo<A> {
    // Uniquely append an element (i.e., leaves list unchanged if element is already present.)
    ILo<A> uniqAppend(A a);
}

class Cons<A> implements ILo<A> {

    A first;
    ILo<A> rest;

    Cons(A first, ILo<A> rest) {
        this.first = first;
        this.rest = rest;
    }

    public ILo<A> uniqAppend(A a) {
        if (this.first.equals(a)) return this;
        else return new Cons<A>(this.first, this.rest.uniqAppend(a));
    }
}

class Mt<A> implements ILo<A> {

    public ILo<A> uniqAppend(A a) {
        return new Cons<A>(a, this);
    }
}
