So far we have covered only simple types like Integers and Reals. Enumerations and sets are two examples of complex types. Complex types have to be declared (just like we declare variables) and can subsequently be used to declare variables and constants. The type reserved word is used to declare a type. By convention in Object Pascal, any types that are declared (i.e., not built in) have identifiers that start with the letter "T" so that someone reading the code can easily recognize them as types.

An enumerated type is the simplest of the complex types. Enumerations simply assign names to ordinal values. Given that enumerations name ordinal values, enumerated types are themselves ordinal types. By convention in Object Pascal, the named values in enumerations are always prefixed with 2 to 3 lowercase letters. The reason for this is to prevent naming conflicts with other identifiers in the program. Copy/paste the following code into Workspace and run it.
{$APPTYPE CONSOLE}
program Workspace;

type
  TNumber = (numZero,numOne,numTwo,numThree,numFour,numFive,
    numSix,numSeven,numEight,numNine);
  TAlignType = (atLeft,atCenter,atRight,atJustify);

var
  Num : TNumber;
  Align : TAlignType;
begin
  Writeln(Ord(numOne)); // Output: 1
  Writeln(numOne > numTwo); // Output: FALSE
  Num := numSeven;
  case Num of
    numThree: Writeln('3 is a lucky number.');
    numSeven: Writeln('7 is a lucky number.');
  end;
  Writeln(Ord(atCenter)); // Output: 1
  Num := atLeft; // This generates a compiler error (comment out to run)
  Readln;
end.
Let's analyze the different parts of this code. Indices of names in enumerated types always start with zero in Object Pascal. This example illustrates a number of important points. First, you can see that we can use the Ord function to determine the ordinal value of an enumerated name because enumerations are ordinal types. 
Writeln(Ord(atCenter)); // Output: 1
Second, numOne > numTwo returns False because numTwo is greater than numOne based on the order we specified when declaring TNumber.
Writeln(numOne > numTwo); // Output: FALSE
Third, enumerated types can be used in case statements since case statements expect ordinal values. 
case Num of
  numThree: Writeln('3 is a lucky number.');
  numSeven: Writeln('7 is a lucky number.');
end;
Finally, even though enumerated types simply assign names to numbered values, incompatible enumerated names cannot be assigned to variables of different types. The reason for this is simply to reduce mistakes made by a programmer. Chances are we don't really need to assign atLeft to Num.
Num := atLeft; // This generates a compiler error
Excercise 3-6.
Write code for the following problems.
  1. Create an enumeration that lists the 4 seasons (Spring, Summer, Fall, Winter).
  2. Write a function that takes a single enumerated type of TSeasons and returns a string with that season's name.
  3. Write a graphical program that draws a dotted and a dashed line using the SetPen(Color,PenWidth,PenStyle) procedure from the FTGraphics. TPenStyle is an enumeration in the Graphics unit. This means you will need to include Graphics and FTGraphics. The possible line styles are psSolid, psDash, psDot, psDashDot, psDashDotDot. The template for a graphical app is given below:
    program Workspace;
    uses Graphics, FTGraphics;

    procedure Initialize;
    begin
    end;

    procedure OnEnterFrame;
    begin
    end;

    begin
      RunGraphicalApp(@Initialize,@OnEnterFrame);
    end.
Sets are collections of values of the same ordinal type. Order does not matter in a set and no value can be included twice in a set. In other words, if you are familiar with the mathematical concept of a set, an Object Pascal set is almost identical. Enumerated values are often used as the elements of sets as in the example below:
type
  TFontStyle = (fsBold,fsUnderline,fsItalic);
  TFontStyles = set of TFontStyle;
var
  Styles1 : TFontStyles;
  Styles2 : TFontStyles;
  Styles3 : TFontStyles;
begin
  Styles1 := [fsBold,fsUnderline];
  Styles2 := [];
  Styles3 := [fsUnderline,fsBold];
end.
You can see from this example that sets are often useful to describe the state of an object such as the style of font when multiple descriptions apply (here Styles says that the text is both bold and underlined but not italicized). This example also demonstrates that sets are initialized with square brackets. Sets can be initialized to contain elements (as in Styles1) or as empty sets (as in Styles2). Order does not matter in a set. Thus, Styles1 and Styles3 are identical sets. Elements can also be included or excluded from a set after initialization:
begin
  Styles1 := [fsBold,fsUnderline];
  Styles2 := [];
  Exclude(Styles1,fsBold);
  Include(Styles2,fsItalic);
end.
Using Exclude on an element not already in the set does nothing. Likewise, using Include for an element already in the set also does nothing since sets cannot contain duplicate elements.
begin
  Exclude(Styles1,fsItalic); // Meaningless
  Include(Styles1,fsUnderline); // Meaningless
end.
The in operator can be used to determine whether or not an element is in a set:
begin
  Styles1 := [fsBold,fsUnderline];
  Writeln(fsBold in Styles1); // Returns True
  Writeln(fsItalic in Styles1); // Returns False
end.
In addition to enumerations, there are times when we want to use actual numbers as elements in sets. Object Pascal provides an additional type called a subrange which can be used for this purpose (among others as we will see in Section 3.5).

Subranges represent a subset of the values from another ordinal type. Once a subrange is defined, it becomes an ordinal type itself. The following are examples of subrange declarations and uses:
{$APPTYPE CONSOLE}
program Example;

type
  TSubrange = 1..5;

  TFontStyle = (fsBold,fsUnderline,fsItalic);
  TFontStyleSubrange = fsBold..fsUnderline;

  TSubrangeSet = set of TSubrange;
var
  S : TSubrange;
  FontSubrange : TFontStyleSubrange;
  SubSet : TSubrangeSet;
begin
  S := 4;
  S := 6; // Invalid (violates subrange bounds)
  FontSubrange := fsItalic; // Invalid (violates subrange bounds)
  SubSet := [1,3,5];
  Writeln(5 in SubSet); // Returns True
  Writeln(2 in SubSet); // Returns False
  Readln;
end.
Excercise 3-7.
Write code for the following problems.
  1. Use SetFont(Name,Color,Size,Style) to write red bold, italicized text to the screen in Arial font with any size you like.
  2. Modify the code from the previous problem so that the font is normal (style of []) when the program starts and becomes bold and italicized only when the left mouse button is held. Hint: The left mouse button's state is in Mouse.Left.Down.
  3. Now modify the code so that the text stays bold and italic once the mouse is clicked and then switches back to normal after it is clicked again. Hint: You'll need to declare a variable called CurrentStyle that is of type TFontStyles in order to keep track of the style across frames. The elements that are currently in CurrentStyle can be determined using the in operator. Mouse.Left.Click will be useful also.
In mathematics, sets are often operated on by the union () and intersection () operators. The union operation on two sets such as results in a new set that contains all of the elements in both and . The intersection operator on two sets such as results in a new set that contains only elements common to both and . Object Pascal uses + for union and * for intersection since and are not symbols on the keyboard. 
Excercise 3-8.
Consider the following code for the following problems:
type
  TDigitSet = set of 0..9;
var
  S1,S2 : TDigitSet;
begin
  S1 := [0,4,3,9];
  S2 := [9,0,1,3];
end.
  1. What are the elements in the set S1+S2?
  2. What are the elements in the set S1*S2?