module Enums { 
 
  // ENUM BASICS 
 
  // enums define a new type and associated constant values of that type 
  enum Component { 
    RED     = 1, 
    GREEN   = 2, 
    BLUE    = 3 
  } 
 
  // enums constants are accessed using the enum name 
  fn test1() { 
    let x : Component = Component.A; 
    let y = Component.B; 
  } 
 
  // CONTROLLING ENUM TYPE 
 
  // by default, enums are based on the int type.  Use : to specify other types. 
  // for example, the following enum is represented using an unsigned 16-bit value (u16) 
  enum ByteOrderMark : u16 { 
    BIG_ENDIAN      = 0xFEFF, 
    LITTLE_ENDIAN   = 0xFFFE 
  } 
 
  // AUTO INCREMENTING ENUMS 
 
  // if the constant value is omitted, it's value is one more than the previous value.  
  // if not specified, the first value is 0. 
  enum TokenKind { 
    SPACE,      // 0 
    DIGIT,      // 1 
    PLUS,       // 2 
    MINUS = 10, // 10 
    TIMES,      // 11 
    DIVIDE      // 12 
  } 
 
  // REFERRING TO ENUM CONSTANTS 
 
  // enum constant expressions can refer to other constants in the enum without qualification 
  enum ReadWrite { 
    READ        = 0x1, 
    WRITE       = 0x2, 
    READ_WRITE  = READ | WRITE 
  } 
 
  // NON NUMERIC ENUMS 
 
  // enums can be based any type.  if a non-integer type is used, then the constant  
  // values must be specified (there is no auto incrementing). 
  enum ColorName : string { 
    RED     = "#ff0000", 
    GREEN   = "#00ff00", 
    BLUE    = "#0000ff" 
  } 
 
  // EXTENSIBLE VS NON-EXTENSIBLE ENUMS 
 
  // enums automatically coerce to and from their base type.  when coercing/casting 
  // from the base type to the enum type, the cast may fail, however, due to the value 
  // being provided not being one of the specified constant values. 
 
  enum NonExtensible { 
    A = 1, 
    B = 2 
  } 
 
  fn test2() { 
    let x : int = NonExtensible.A;   // ok 
    let y : NonExtensible = 1;       // ok, since 1 is NonExtensible.A 
    let z : NonExtensible = 100;     // throws, since 100 is not a valid value in NonExtensible 
  } 
 
  // enums can alternatively be defined as extensible, meaning additional values are  
  // allowed, within the constraint of the base type.  use ... at the end  
  // to indicate that the enum is extensible. 
  enum Extensible { 
    A = 1, 
    B = 2, 
    ...     // enum is extensible, meaning it allows values other than those listed 
  } 
 
  fn test3() { 
    let x : int = Extensible.A;   // ok, since Extensible enum is extensible 
    let y : Extensible = 1;       // ok, since Extensible enum is extensible 
    let z : Extensible = 100;     // ok, since Extensible enum is extensible 
    let q : Extensible = "hi";    // error, since "hi" is not an int 
  } 
 
  // PROVIDING ADDITIONAL CONSTANTS FOR EXTENSIBLE ENUMS 
 
  // extensible enums can be extended with additional constants separately from their definition 
  // the following URI scheme enum defines a few values but allows others 
  enum UriScheme : string { 
    HTTP    = "http", 
    HTTPS   = "https", 
    FTP     = "ftp", 
    MAILTO  = "mailto", 
    ... 
  } 
 
  // add a constant to the UriScheme enum 
  enum ...UriScheme { 
    MONGODB = "mongodb", 
  } 
 
  // add a constant to the UriScheme enum 
  enum ...UriScheme { 
    CHROME = "chrome", 
  } 
 
  fn test4() { 
    let x : UriScheme; 
    x = UriScheme.HTTP; 
    x = UriScheme.CHROME; 
    x = UriScheme.MONGODB; 
  } 
 
  // AUTO NUMBERING AND EXTENSIBILITY 
 
  // Normally, if the value of an integer enum constant is not provided, 
  // then it is one greater than the previous value (or 0 if the first 
  // constant in the enum). 
 
  // For an enum extension, if a value is not provided, it will be the next available 
  // ascending value.  A value is available if there is not another constant 
  // defined with that value.  This allows extensions to have unique values from 
  // the base and from each other. 
 
  // The compiler is free to choose the ordering of the extensions when assigning 
  // the values, as illustrated in the following example: the compiler may choose  
  // the values from Order1 or Order2. 
 
  enum AutoNumber { 
    A,        // 0 
    B,        // 1 
    C = 4,    // 4 
    D,        // 5 
    ... 
  } 
 
  enum ...AutoNumber { 
    E,        // Order1: 2, Order2: 3 
    F = 4,    // 4 
  } 
 
  enum ...AutoNumber { 
    G,        // Order1: 3, Order2: 2 
    H         // Order1: 6, Order2: 6 
  } 
}