student(name(john,smith), [mat101, csi108, csi148, csi270]).
student(name(jim,roy), []).
student(name(jane,brown), [mat101, csi108]).
student(name(emily,white), [mat101, csi108, mat246]).
student(name(emma,smith), [mat101, csi108, csi148, mat246]).

prereq(csi108, []).
prereq(csi148, [csi108, mat101]).
prereq(csi238, [csi148]).
prereq(csi260, [csi108]).
prereq(csi270, [csi148]).
prereq(csi310, [[csi260, csi270], [sta107, sta205, sta255]]).
prereq(csi324, [[csi148], [csi238, mat246]]).
 

%1. took: student name and course name. 
%?-took(emma, mat101).
%yes

took(X,C):- student(name(X,_),L), member(C,L).

%2 is_prereq: has two course names as arguments and verifies if the second 
%one is a prerequisite for the first one. Assume that the two arguments are instantiated. 
%?- is_prereq(csi310, sta205).
%Yes

is_prereq(X,Y):- prereq(X, L), helper_prereq(Y,L).
helper_prereq(H,[H|_]). 
helper_prereq(X,[Y|_]):- is_list(Y),member(X,Y). 
helper_prereq(X,[Y|T]):- X\=Y, helper_prereq(X,T). 


%3.  can-take has two arguments, a student name and a course, and succeds 
%if the student has all the prerequisites to take the course. 
%If there are alternative prerequisites, the student is required to have at 
%least one of them. A student cannot take a course if she/he already took it. 
%Example:
%can_take(emma, csi324).
%yes

can_take(X,Y):- not(took(X,Y)), has_all_prereq(X,Y).

has_all_prereq(X,Y):-prereq(Y,L), helper_can_take(X,L).

helper_can_take(_,[]).
helper_can_take(X,[H|T]):- taken_one(X,H), helper_can_take(X,T).

taken_one(X,C):- not(is_list(C)), took(X,C).
taken_one(X,[H|T]):- took(X,H); taken_one(X,T).


%4. can-take-list has two arguments, a student and a list of courses, 
%and succeeds if the student can take all the courses in the list.  
%Example:
%:-can_take_list(emma, [csi324, csi270]).
%yes

can_take_list(_,[]).
can_take_list(X,[H|T]):- can_take(X,H), can_take_list(X,T).


%5. 
%all-students has one argument: a list with the first names of all students 
%enumerated in the database. 
%Example:

%?- all_students(X).
% X = [john, jim, jane, emily, emma]

all_students(L):- findall(X, student(name(X,_),_),L).


%6.  all_courses_taken  has one uninstatiated argument:  
%a list with all the courses taken by at least one student. No duplicates.
%Example:
%?-all_courses_taken(X).
%X = [mat246, mat101, csi108, csi148, csi270]
%Note: the courses in the list can be in any order

all_courses_taken(Result):- findall(L, student(name(_,_),L), [H|T]), 
                            get_elements(T,H,Result).
get_elements([],A,A).
get_elements([H|T],Acc, R):- get_elem(H,Acc,R1), get_elements(T,R1,R).
get_elem([],A,A).
get_elem([H|T], Acc, R):- member(H,Acc), get_elem(T, Acc, R).
get_elem([H|T], Acc, R):- not(member(H,Acc)),get_elem(T,[H|Acc],R).
