function registerAction(gb: Gradebook, gb': Gradebook)->boolean =
	(gb.history.next = gb') and (gb'.history.prev = gb);
	
function itemToString(i: Item)->string;

operation Undo
    inputs: gb:Gradebook;
    outputs: gb':Gradebook;
    precondition: gb.history.prev != nil;
    postcondition: gb' = gb.history.prev;
    description: (*
        If one or more operation has been preformed on the Gradebook, remove the most recent change. Otherwise, change nothing.
    *);
end Undo;

operation Redo
    inputs: gb:Gradebook;
    outputs: gb':Gradebook;
    precondition: gb.history.next != nil;
    postcondition: gb' = gb.history.next;
    description: (*
        If one or more Undo has been preformed on the Gradebook, re-apply the most recently un-done change. Otherwise, change nothing.
    *);
end Redo;

operation Cut
    inputs: i:Item,clipboard:string;
    outputs: clipboard':string;
    precondition: ;
    postcondition: clipboard' = itemToString(i);
    description: (*
        Places the contents of the Item on the operating system's clipboard.
    *);
end Cut;

operation Copy
    inputs: i:Item,clipboard:string;
    outputs: clipboard': string;
    precondition: ;
    postcondition: clipboard' = itemToString(i);
    description: (*
        Places the contents of the Item on the operating system's clipboard.
    *);
end Copy;

operation Paste
    inputs: i:Item, clipboard:string;
    outputs: i':Item;
    precondition: ;
    postcondition: i = i' or itemToString(i') = clipboard;
    description: (*
        Places the contents of the clipboard in the specified Item. If the clipboard contents were placed there by cut, the source Item is Cleared.
    *);
end Paste;

operation Clear
    inputs: Item;
    outputs: i':Item;
    precondition: ;
    postcondition: i' = nil;
    description: (*
        Replaces the contens of the inputted Item with a blank Item.
    *);
end Clear;

operation SelectAll
    inputs: integer, g:Gradebook;
    outputs: ;
    precondition: ;
    postcondition: 
    ((forall (sections in g.sections) 
    		forall (student in sections.students) student.selected = true)
	    and
    (forall (sections in g.sections)
    	forall (groups in sections.groups)
    	 forall (student in groups.students) student.selected = true))
    or
    ((forall (item in g.items) item.selected = true)
   	 and
    (g.FinalGrade.selected = true));
    description: (*
        Sets the "selected" variable of all Items within the specified Gradebook which match the type specified by the int to "true."
    *);
end SelectAll;

operation Find
    inputs: g:Gradebook, s:string, e:EasyGrader;
    outputs: i':Item, e':EasyGrader;
    precondition: ;
    postcondition: ((s = itemToString(i') and i' in g.items) or i' = nil) and e'.findString = s;
    description: (*
        Searches the Items within the Gradebook for an Item whose contents match the provided string. Returns the first full match. Remember string.
    *);
end Find;

operation FindNext
    inputs: g:Gradebook, e:EasyGrader;
    outputs: i':Item;
    precondition: e.findString != nil;
    postcondition: (e.findString = itemToString(i')) and (i' in g.items);
    description: (*
        Searches the Items within the Gradebook for an Item whose contents match the string provided when find was called. Returns the N+1'th match, where N = number of times
        findNext has been called since the find string was last set. If find string not set, does nothing.
    *);
end FindNext;

operation Replace
    inputs: g:Gradebook, s1:string, s2:string, i:Item;
    outputs: i':Item;
    precondition: ;
    postcondition: itemToString(i) = s1 and itemToString(i') = s2;
    description: (*
        Searches the Items within the Gradebook for an Item whose contents match the first string provided. When found, the second strings' contents are written over the first.
    *);
end Replace;