/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-â
âvi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8                                :viâ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¡
â Copyright 2021 Justine Alexandra Roberts Tunney                              â
â                                                                              â
â Permission to use, copy, modify, and/or distribute this software for         â
â any purpose with or without fee is hereby granted, provided that the         â
â above copyright notice and this permission notice appear in all copies.      â
â                                                                              â
â THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL                â
â WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED                â
â WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE             â
â AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL         â
â DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR        â
â PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER               â
â TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR             â
â PERFORMANCE OF THIS SOFTWARE.                                                â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ*/
#include "libc/dce.h"
#include "libc/macros.internal.h"
#include "libc/str/str.h"
/* clang-format off */

static const struct {
  unsigned short x;
  unsigned short y;
  short d;
} kLower[] = {
    {0x00c0, 0x00d6, +32},    /* 23x Ã ..Ã  â Ã  ..Ã¶  Watin */
    {0x00d8, 0x00de, +32},    /*  7x Ã ..Ã  â Ã¸ ..Ã¾  Watin */
    {0x0178, 0x0178, -121},   /*  1x Å¸ ..Å¸  â Ã¿ ..Ã¿  Watin-A */
    {0x0179, 0x0179, +1},     /*  1x Å¹ ..Å¹  â Åº ..Åº  Watin-A */
    {0x017b, 0x017b, +1},     /*  1x Å» ..Å»  â Å¼ ..Å¼  Watin-A */
    {0x017d, 0x017d, +1},     /*  1x Å½ ..Å½  â Å¾ ..Å¾  Watin-A */
    {0x0181, 0x0181, +210},   /*  1x Æ ..Æ  â É ..É  Watin-B */
    {0x0182, 0x0182, +1},     /*  1x Æ ..Æ  â Æ ..Æ  Watin-B */
    {0x0184, 0x0184, +1},     /*  1x Æ ..Æ  â Æ ..Æ  Watin-B */
    {0x0186, 0x0186, +206},   /*  1x Æ ..Æ  â É ..É  Watin-B */
    {0x0187, 0x0187, +1},     /*  1x Æ ..Æ  â Æ ..Æ  Watin-B */
    {0x0189, 0x018a, +205},   /*  2x Æ ..Æ  â É ..É  Watin-B */
    {0x018b, 0x018b, +1},     /*  1x Æ ..Æ  â Æ ..Æ  Watin-B */
    {0x018e, 0x018e, +79},    /*  1x Æ ..Æ  â Ç ..Ç  Watin-B */
    {0x018f, 0x018f, +202},   /*  1x Æ ..Æ  â É ..É  Watin-B */
    {0x0190, 0x0190, +203},   /*  1x Æ ..Æ  â É ..É  Watin-B */
    {0x0191, 0x0191, +1},     /*  1x Æ ..Æ  â Æ ..Æ  Watin-B */
    {0x0193, 0x0193, +205},   /*  1x Æ ..Æ  â É  ..É   Watin-B */
    {0x0194, 0x0194, +207},   /*  1x Æ ..Æ  â É£ ..É£  Watin-B */
    {0x0196, 0x0196, +211},   /*  1x Æ ..Æ  â É© ..É©  Watin-B */
    {0x0197, 0x0197, +209},   /*  1x Æ ..Æ  â É¨ ..É¨  Watin-B */
    {0x0198, 0x0198, +1},     /*  1x Æ ..Æ  â Æ ..Æ  Watin-B */
    {0x019c, 0x019c, +211},   /*  1x Æ ..Æ  â É¯ ..É¯  Watin-B */
    {0x019d, 0x019d, +213},   /*  1x Æ ..Æ  â É² ..É²  Watin-B */
    {0x019f, 0x019f, +214},   /*  1x Æ ..Æ  â Éµ ..Éµ  Watin-B */
    {0x01a0, 0x01a0, +1},     /*  1x Æ  ..Æ   â Æ¡ ..Æ¡  Watin-B */
    {0x01a2, 0x01a2, +1},     /*  1x Æ¢ ..Æ¢  â Æ£ ..Æ£  Watin-B */
    {0x01a4, 0x01a4, +1},     /*  1x Æ¤ ..Æ¤  â Æ¥ ..Æ¥  Watin-B */
    {0x01a6, 0x01a6, +218},   /*  1x Æ¦ ..Æ¦  â Ê ..Ê  Watin-B */
    {0x01a7, 0x01a7, +1},     /*  1x Æ§ ..Æ§  â Æ¨ ..Æ¨  Watin-B */
    {0x01a9, 0x01a9, +218},   /*  1x Æ© ..Æ©  â Ê ..Ê  Watin-B */
    {0x01ac, 0x01ac, +1},     /*  1x Æ¬ ..Æ¬  â Æ­ ..Æ­  Watin-B */
    {0x01ae, 0x01ae, +218},   /*  1x Æ® ..Æ®  â Ê ..Ê  Watin-B */
    {0x01af, 0x01af, +1},     /*  1x Æ¯ ..Æ¯  â Æ° ..Æ°  Watin-B */
    {0x01b1, 0x01b2, +217},   /*  2x Æ± ..Æ²  â Ê ..Ê  Watin-B */
    {0x01b3, 0x01b3, +1},     /*  1x Æ³ ..Æ³  â Æ´ ..Æ´  Watin-B */
    {0x01b5, 0x01b5, +1},     /*  1x Æµ ..Æµ  â Æ¶ ..Æ¶  Watin-B */
    {0x01b7, 0x01b7, +219},   /*  1x Æ· ..Æ·  â Ê ..Ê  Watin-B */
    {0x01b8, 0x01b8, +1},     /*  1x Æ¸ ..Æ¸  â Æ¹ ..Æ¹  Watin-B */
    {0x01bc, 0x01bc, +1},     /*  1x Æ¼ ..Æ¼  â Æ½ ..Æ½  Watin-B */
    {0x01c4, 0x01c4, +2},     /*  1x Ç ..Ç  â Ç ..Ç  Watin-B */
    {0x01c5, 0x01c5, +1},     /*  1x Ç ..Ç  â Ç ..Ç  Watin-B */
    {0x01c7, 0x01c7, +2},     /*  1x Ç ..Ç  â Ç ..Ç  Watin-B */
    {0x01c8, 0x01c8, +1},     /*  1x Ç ..Ç  â Ç ..Ç  Watin-B */
    {0x01ca, 0x01ca, +2},     /*  1x Ç ..Ç  â Ç ..Ç  Watin-B */
    {0x01cb, 0x01cb, +1},     /*  1x Ç ..Ç  â Ç ..Ç  Watin-B */
    {0x01cd, 0x01cd, +1},     /*  1x Ç ..Ç  â Ç ..Ç  Watin-B */
    {0x01f1, 0x01f1, +2},     /*  1x Ç± ..Ç±  â Ç³ ..Ç³  Watin-B */
    {0x01f2, 0x01f2, +1},     /*  1x Ç² ..Ç²  â Ç³ ..Ç³  Watin-B */
    {0x01f4, 0x01f4, +1},     /*  1x Ç´ ..Ç´  â Çµ ..Çµ  Watin-B */
    {0x01f6, 0x01f6, -97},    /*  1x Ç¶ ..Ç¶  â Æ ..Æ  Watin-B */
    {0x01f7, 0x01f7, -56},    /*  1x Ç· ..Ç·  â Æ¿ ..Æ¿  Watin-B */
    {0x0220, 0x0220, -130},   /*  1x È  ..È   â Æ ..Æ  Watin-B */
    {0x023b, 0x023b, +1},     /*  1x È» ..È»  â È¼ ..È¼  Watin-B */
    {0x023d, 0x023d, -163},   /*  1x È½ ..È½  â Æ ..Æ  Watin-B */
    {0x0241, 0x0241, +1},     /*  1x É ..É  â É ..É  Watin-B */
    {0x0243, 0x0243, -195},   /*  1x É ..É  â Æ ..Æ  Watin-B */
    {0x0244, 0x0244, +69},    /*  1x É ..É  â Ê ..Ê  Watin-B */
    {0x0245, 0x0245, +71},    /*  1x É ..É  â Ê ..Ê  Watin-B */
    {0x0246, 0x0246, +1},     /*  1x É ..É  â É ..É  Watin-B */
    {0x0248, 0x0248, +1},     /*  1x É ..É  â É ..É  Watin-B */
    {0x024a, 0x024a, +1},     /*  1x É ..É  â É ..É  Watin-B */
    {0x024c, 0x024c, +1},     /*  1x É ..É  â É ..É  Watin-B */
    {0x024e, 0x024e, +1},     /*  1x É ..É  â É ..É  Watin-B */
    {0x0386, 0x0386, +38},    /*  1x Î ..Î  â Î¬ ..Î¬  Greek */
    {0x0388, 0x038a, +37},    /*  3x Î ..Î  â Î­ ..Î¯  Greek */
    {0x038c, 0x038c, +64},    /*  1x Î ..Î  â Ï ..Ï  Greek */
    {0x038e, 0x038f, +63},    /*  2x Î ..Î  â Ï ..Ï  Greek */
    {0x0391, 0x03a1, +32},    /* 17x Î ..Î¡  â Î± ..Ï  Greek */
    {0x03a3, 0x03ab, +32},    /*  9x Î£ ..Î«  â Ï ..Ï  Greek */
    {0x03dc, 0x03dc, +1},     /*  1x Ï ..Ï  â Ï ..Ï  Greek */
    {0x03f4, 0x03f4, -60},    /*  1x Ï´ ..Ï´  â Î¸ ..Î¸  Greek */
    {0x0400, 0x040f, +80},    /* 16x Ð ..Ð  â Ñ ..Ñ  Cyrillic */
    {0x0410, 0x042f, +32},    /* 32x Ð ..Ð¯  â Ð° ..Ñ  Cyrillic */
    {0x0460, 0x0460, +1},     /*  1x Ñ  ..Ñ   â Ñ¡ ..Ñ¡  Cyrillic */
    {0x0462, 0x0462, +1},     /*  1x Ñ¢ ..Ñ¢  â Ñ£ ..Ñ£  Cyrillic */
    {0x0464, 0x0464, +1},     /*  1x Ñ¤ ..Ñ¤  â Ñ¥ ..Ñ¥  Cyrillic */
    {0x0472, 0x0472, +1},     /*  1x Ñ² ..Ñ²  â Ñ³ ..Ñ³  Cyrillic */
    {0x0490, 0x0490, +1},     /*  1x Ò ..Ò  â Ò ..Ò  Cyrillic */
    {0x0498, 0x0498, +1},     /*  1x Ò ..Ò  â Ò ..Ò  Cyrillic */
    {0x049a, 0x049a, +1},     /*  1x Ò ..Ò  â Ò ..Ò  Cyrillic */
    {0x0531, 0x0556, +48},    /* 38x Ô± ..Õ  â Õ¡ ..Ö  Armenian */
    {0x10a0, 0x10c5, +7264},  /* 38x á  ..á  â â´ ..â´¥  Georgian */
    {0x10c7, 0x10c7, +7264},  /*  1x á ..á  â â´§ ..â´§  Georgian */
    {0x10cd, 0x10cd, +7264},  /*  1x á ..á  â â´­ ..â´­  Georgian */
    {0x13f0, 0x13f5, +8},     /*  6x á° ..áµ  â á¸ ..á½  Cherokee */
    {0x1c90, 0x1cba, -3008}, /* 43x á² ..á²º  â á ..áº  Georgian2 */
    {0x1cbd, 0x1cbf, -3008}, /*  3x á²½ ..á²¿  â á½ ..á¿  Georgian2 */
    {0x1f08, 0x1f0f, -8},    /*  8x á¼ ..á¼  â á¼ ..á¼  Greek2 */
    {0x1f18, 0x1f1d, -8},    /*  6x á¼ ..á¼  â á¼ ..á¼  Greek2 */
    {0x1f28, 0x1f2f, -8},    /*  8x á¼¨ ..á¼¯  â á¼  ..á¼§  Greek2 */
    {0x1f38, 0x1f3f, -8},    /*  8x á¼¸ ..á¼¿  â á¼° ..á¼·  Greek2 */
    {0x1f48, 0x1f4d, -8},    /*  6x á½ ..á½  â á½ ..á½  Greek2 */
    {0x1f59, 0x1f59, -8},    /*  1x á½ ..á½  â á½ ..á½  Greek2 */
    {0x1f5b, 0x1f5b, -8},    /*  1x á½ ..á½  â á½ ..á½  Greek2 */
    {0x1f5d, 0x1f5d, -8},    /*  1x á½ ..á½  â á½ ..á½  Greek2 */
    {0x1f5f, 0x1f5f, -8},    /*  1x á½ ..á½  â á½ ..á½  Greek2 */
    {0x1f68, 0x1f6f, -8},    /*  8x á½¨ ..á½¯  â á½  ..á½§  Greek2 */
    {0x1f88, 0x1f8f, -8},    /*  8x á¾ ..á¾  â á¾ ..á¾  Greek2 */
    {0x1f98, 0x1f9f, -8},    /*  8x á¾ ..á¾  â á¾ ..á¾  Greek2 */
    {0x1fa8, 0x1faf, -8},    /*  8x á¾¨ ..á¾¯  â á¾  ..á¾§  Greek2 */
    {0x1fb8, 0x1fb9, -8},    /*  2x á¾¸ ..á¾¹  â á¾° ..á¾±  Greek2 */
    {0x1fba, 0x1fbb, -74},   /*  2x á¾º ..á¾»  â á½° ..á½±  Greek2 */
    {0x1fbc, 0x1fbc, -9},    /*  1x á¾¼ ..á¾¼  â á¾³ ..á¾³  Greek2 */
    {0x1fc8, 0x1fcb, -86},   /*  4x á¿ ..á¿  â á½² ..á½µ  Greek2 */
    {0x1fcc, 0x1fcc, -9},    /*  1x á¿ ..á¿  â á¿ ..á¿  Greek2 */
    {0x1fd8, 0x1fd9, -8},    /*  2x á¿ ..á¿  â á¿ ..á¿  Greek2 */
    {0x1fda, 0x1fdb, -100},  /*  2x á¿ ..á¿  â á½¶ ..á½·  Greek2 */
    {0x1fe8, 0x1fe9, -8},    /*  2x á¿¨ ..á¿©  â á¿  ..á¿¡  Greek2 */
    {0x1fea, 0x1feb, -112},  /*  2x á¿ª ..á¿«  â á½º ..á½»  Greek2 */
    {0x1fec, 0x1fec, -7},    /*  1x á¿¬ ..á¿¬  â á¿¥ ..á¿¥  Greek2 */
    {0x1ff8, 0x1ff9, -128},  /*  2x á¿¸ ..á¿¹  â á½¸ ..á½¹  Greek2 */
    {0x1ffa, 0x1ffb, -126},  /*  2x á¿º ..á¿»  â á½¼ ..á½½  Greek2 */
    {0x1ffc, 0x1ffc, -9},    /*  1x á¿¼ ..á¿¼  â á¿³ ..á¿³  Greek2 */
    {0x2126, 0x2126, -7517}, /*  1x â¦ ..â¦  â Ï ..Ï  Letterlike */
    {0x212a, 0x212a, -8383}, /*  1x âª ..âª  â k ..k  Letterlike */
    {0x212b, 0x212b, -8262}, /*  1x â« ..â«  â Ã¥ ..Ã¥  Letterlike */
    {0x2132, 0x2132, +28}, /*  1x â² ..â²  â â ..â  Letterlike */
    {0x2160, 0x216f, +16}, /* 16x â  ..â¯  â â° ..â¿  Numbery */
    {0x2183, 0x2183, +1},  /*  1x â ..â  â â ..â  Numbery */
    {0x24b6, 0x24cf, +26}, /* 26x â¶ ..â  â â ..â©  Enclosed */
    {0x2c00, 0x2c2e, +48}, /* 47x â° ..â°®  â â°° ..â±  Glagolitic */
    {0xff21, 0xff3a, +32}, /* 26x ï¼¡..ï¼º â ï½..ï½ Dubs */
};

static const int kAstralLower[][3] = {
    {0x10400, 0x10427, +40}, /* 40x ð ..ð§  â ð¨ ..ð  Deseret */
    {0x104b0, 0x104d3, +40}, /* 36x ð° ..ð  â ð ..ð»  Osage */
    {0x1d400, 0x1d419, +26}, /* 26x ð ..ð  â ð ..ð³  Math */
    {0x1d43c, 0x1d44d, +26}, /* 18x ð¼ ..ð  â ð ..ð§  Math */
    {0x1d468, 0x1d481, +26}, /* 26x ð¨ ..ð  â ð ..ð  Math */
    {0x1d4ae, 0x1d4b5, +26}, /*  8x ð® ..ðµ  â ð ..ð  Math */
    {0x1d4d0, 0x1d4e9, +26}, /* 26x ð ..ð©  â ðª ..ð  Math */
    {0x1d50d, 0x1d514, +26}, /*  8x ð ..ð  â ð§ ..ð®  Math */
    {0x1d56c, 0x1d585, +26}, /* 26x ð¬ ..ð  â ð ..ð  Math */
    {0x1d5a0, 0x1d5b9, +26}, /* 26x ð  ..ð¹  â ðº ..ð  Math */
    {0x1d5d4, 0x1d5ed, +26}, /* 26x ð ..ð­  â ð® ..ð  Math */
    {0x1d608, 0x1d621, +26}, /* 26x ð ..ð¡  â ð¢ ..ð»  Math */
    {0x1d63c, 0x1d655, -442}, /* 26x ð¼ ..ð  â ð ..ð  Math */
    {0x1d670, 0x1d689, +26},  /* 26x ð° ..ð  â ð ..ð£  Math */
    {0x1d6a8, 0x1d6b8, +26},  /* 17x ð¨ ..ð¸  â ð ..ð  Math */
    {0x1d6e2, 0x1d6f2, +26},  /* 17x ð¢ ..ð²  â ð¼ ..ð  Math */
    {0x1d71c, 0x1d72c, +26},  /* 17x ð ..ð¬  â ð¶ ..ð  Math */
    {0x1d756, 0x1d766, +26},  /* 17x ð ..ð¦  â ð° ..ð  Math */
    {0x1d790, 0x1d7a0, -90},  /* 17x ð ..ð   â ð¶ ..ð  Math */
};

/**
 * Converts wide character to lower case.
 */
wint_t towlower(wint_t c) {
  int m, l, r, n;
  if (c < 0200) {
    if ('A' <= c && c <= 'Z') {
      return c + 32;
    } else {
      return c;
    }
  } else if (c <= 0xffff) {
    if ((0x0100 <= c && c <= 0x0176) || /* 60x Ä..Ä â Ä..Åµ Watin-A */
        (0x01de <= c && c <= 0x01ee) || /*  9x Ç..Ç® â Ç..Ç¯ Watin-B */
        (0x01f8 <= c && c <= 0x021e) || /* 20x Ç¸..È â Ç¹..È Watin-B */
        (0x0222 <= c && c <= 0x0232) || /*  9x È¢..È² â È£..È³ Watin-B */
        (0x1e00 <= c && c <= 0x1eff)) { /*256x á¸..á»¾ â á¸..á»¿ Watin-C */
      if (c == 0x0130) return c - 199;
      if (c == 0x1e9e) return c;
      return c + (~c & 1);
    } else if (0x01cf <= c && c <= 0x01db) {
      return c + (c & 1); /* 7x Ç..Ç â Ç..Ç Watin-B */
    } else if (0x13a0 <= c && c <= 0x13ef) {
      return c + 38864; /* 80x á  ..á¯  â ê­° ..ê®¿  Cherokee */
    } else {
      l = 0;
      r = n = sizeof(kLower) / sizeof(kLower[0]);
      while (l < r) {
        m = (l + r) >> 1;
        if (kLower[m].y < c) {
          l = m + 1;
        } else {
          r = m;
        }
      }
      if (l < n && kLower[l].x <= c && c <= kLower[l].y) {
        return c + kLower[l].d;
      } else {
        return c;
      }
    }
  } else {
    l = 0;
    r = n = sizeof(kAstralLower) / sizeof(kAstralLower[0]);
    while (l < r) {
      m = (l + r) >> 1;
      if (kAstralLower[m][1] < c) {
        l = m + 1;
      } else {
        r = m;
      }
    }
    if (l < n && kAstralLower[l][0] <= c && c <= kAstralLower[l][1]) {
      return c + kAstralLower[l][2];
    } else {
      return c;
    }
  }
}
