///|
fn latin_fold_upper(code : UInt16) -> String {
  match code {
    0x00C0
    | 0x00C1
    | 0x00C2
    | 0x00C3
    | 0x00C4
    | 0x00C5
    | 0x00E0
    | 0x00E1
    | 0x00E2
    | 0x00E3
    | 0x00E4
    | 0x00E5
    | 0x0100
    | 0x0101
    | 0x0102
    | 0x0103
    | 0x0104
    | 0x0105
    | 0x01CD
    | 0x01CE
    | 0x01DE
    | 0x01DF
    | 0x01E0
    | 0x01E1
    | 0x01FA
    | 0x01FB => "A"
    0x00C6 | 0x00E6 | 0x01FC | 0x01FD => "AE"
    0x00C7
    | 0x00E7
    | 0x0106
    | 0x0107
    | 0x0108
    | 0x0109
    | 0x010A
    | 0x010B
    | 0x010C
    | 0x010D => "C"
    0x00D0 | 0x00F0 | 0x010E | 0x010F | 0x0110 | 0x0111 => "D"
    0x00C8
    | 0x00C9
    | 0x00CA
    | 0x00CB
    | 0x00E8
    | 0x00E9
    | 0x00EA
    | 0x00EB
    | 0x0112
    | 0x0113
    | 0x0114
    | 0x0115
    | 0x0116
    | 0x0117
    | 0x0118
    | 0x0119
    | 0x011A
    | 0x011B => "E"
    0x011C | 0x011D | 0x011E | 0x011F | 0x0120 | 0x0121 | 0x0122 | 0x0123 => "G"
    0x0124 | 0x0125 | 0x0126 | 0x0127 => "H"
    0x00CC
    | 0x00CD
    | 0x00CE
    | 0x00CF
    | 0x00EC
    | 0x00ED
    | 0x00EE
    | 0x00EF
    | 0x0128
    | 0x0129
    | 0x012A
    | 0x012B
    | 0x012C
    | 0x012D
    | 0x012E
    | 0x012F
    | 0x0130
    | 0x0131 => "I"
    0x0134 | 0x0135 => "J"
    0x0136 | 0x0137 | 0x0138 => "K"
    0x0139
    | 0x013A
    | 0x013B
    | 0x013C
    | 0x013D
    | 0x013E
    | 0x013F
    | 0x0140
    | 0x0141
    | 0x0142 => "L"
    0x00D1
    | 0x00F1
    | 0x0143
    | 0x0144
    | 0x0145
    | 0x0146
    | 0x0147
    | 0x0148
    | 0x0149
    | 0x014A
    | 0x014B => "N"
    0x00D2
    | 0x00D3
    | 0x00D4
    | 0x00D5
    | 0x00D6
    | 0x00D8
    | 0x00F2
    | 0x00F3
    | 0x00F4
    | 0x00F5
    | 0x00F6
    | 0x00F8
    | 0x014C
    | 0x014D
    | 0x014E
    | 0x014F
    | 0x0150
    | 0x0151
    | 0x01A0
    | 0x01A1
    | 0x01D1
    | 0x01D2
    | 0x01EA
    | 0x01EB
    | 0x01EC
    | 0x01ED => "O"
    0x0152 | 0x0153 => "OE"
    0x0154 | 0x0155 | 0x0156 | 0x0157 | 0x0158 | 0x0159 => "R"
    0x015A | 0x015B | 0x015C | 0x015D | 0x015E | 0x015F | 0x0160 | 0x0161 => "S"
    0x00DF | 0x1E9E => "SS"
    0x0162 | 0x0163 | 0x0164 | 0x0165 | 0x0166 | 0x0167 => "T"
    0x00DE | 0x00FE => "TH"
    0x00D9
    | 0x00DA
    | 0x00DB
    | 0x00DC
    | 0x00F9
    | 0x00FA
    | 0x00FB
    | 0x00FC
    | 0x0168
    | 0x0169
    | 0x016A
    | 0x016B
    | 0x016C
    | 0x016D
    | 0x016E
    | 0x016F
    | 0x0170
    | 0x0171
    | 0x0172
    | 0x0173
    | 0x01AF
    | 0x01B0
    | 0x01D3
    | 0x01D4
    | 0x01D5
    | 0x01D6
    | 0x01D7
    | 0x01D8
    | 0x01D9
    | 0x01DA
    | 0x01DB
    | 0x01DC => "U"
    0x0174 | 0x0175 => "W"
    0x00DD | 0x00FD | 0x00FF | 0x0176 | 0x0177 => "Y"
    0x0179 | 0x017A | 0x017B | 0x017C | 0x017D | 0x017E => "Z"
    _ => ""
  }
}

///|
fn ascii_lower_text(input : String) -> String {
  let mut output = ""
  for i = 0; i < input.length(); i = i + 1 {
    let code = input[i]
    if code >= 65 && code <= 90 {
      output = output + (code + 32).unsafe_to_char().to_string()
    } else {
      output = output + code.unsafe_to_char().to_string()
    }
  }
  output
}

///|
fn latin_source_is_lower(code : UInt16) -> Bool {
  (code >= 0x00DF && code <= 0x00FF && code != 0x00F7) ||
  code == 0x0101 ||
  code == 0x0103 ||
  code == 0x0105 ||
  code == 0x0107 ||
  code == 0x0109 ||
  code == 0x010B ||
  code == 0x010D ||
  code == 0x010F ||
  code == 0x0111 ||
  code == 0x0113 ||
  code == 0x0115 ||
  code == 0x0117 ||
  code == 0x0119 ||
  code == 0x011B ||
  code == 0x011D ||
  code == 0x011F ||
  code == 0x0121 ||
  code == 0x0123 ||
  code == 0x0125 ||
  code == 0x0127 ||
  code == 0x0129 ||
  code == 0x012B ||
  code == 0x012D ||
  code == 0x012F ||
  code == 0x0131 ||
  code == 0x0135 ||
  code == 0x0137 ||
  code == 0x0138 ||
  code == 0x013A ||
  code == 0x013C ||
  code == 0x013E ||
  code == 0x0140 ||
  code == 0x0142 ||
  code == 0x0144 ||
  code == 0x0146 ||
  code == 0x0148 ||
  code == 0x0149 ||
  code == 0x014B ||
  code == 0x014D ||
  code == 0x014F ||
  code == 0x0151 ||
  code == 0x0153 ||
  code == 0x0155 ||
  code == 0x0157 ||
  code == 0x0159 ||
  code == 0x015B ||
  code == 0x015D ||
  code == 0x015F ||
  code == 0x0161 ||
  code == 0x0163 ||
  code == 0x0165 ||
  code == 0x0167 ||
  code == 0x0169 ||
  code == 0x016B ||
  code == 0x016D ||
  code == 0x016F ||
  code == 0x0171 ||
  code == 0x0173 ||
  code == 0x0175 ||
  code == 0x0177 ||
  code == 0x017A ||
  code == 0x017C ||
  code == 0x017E ||
  code == 0x01A1 ||
  code == 0x01B0 ||
  code == 0x01CE ||
  code == 0x01D2 ||
  code == 0x01D4 ||
  code == 0x01D6 ||
  code == 0x01D8 ||
  code == 0x01DA ||
  code == 0x01DC ||
  code == 0x01DF ||
  code == 0x01E1 ||
  code == 0x01EB ||
  code == 0x01ED ||
  code == 0x01FB ||
  code == 0x01FD
}

///|
fn folded_latin_for_case(code : UInt16, policy : CasePolicy) -> String {
  let folded = latin_fold_upper(code)
  match policy {
    Upper => folded
    Lower => ascii_lower_text(folded)
    Preserve =>
      if latin_source_is_lower(code) {
        ascii_lower_text(folded)
      } else {
        folded
      }
  }
}