'
' hypernum.bi
'
' hyper numbers library
'
' June 2024 by 'Berkeley'
'
' provides functions to convert to and from duotrigesimal(32 number base)
' and tetrasexagesimal(64 number base) system numbers
'
' "hyper numbers" are useful to exchange big (integer) numbers on paper like
' IPv6 or RAM addresses - 64 or 128 bit numbers - are
' - they are shorter and less error-prone for transcribing, and it's also
' a kind of encryption - turning "clear" values into cryptical words
'
' they could be used e.g. instead of save files - "level codes" and similar
' most known usage is for "product keys", divided in parts
'
' like for hexadecimal values (16 number base), other characters, especially
' letters, are used to fill the gaps
'
' there is not yet a reliable standard, this one is proprietary, so use
' just this (or a compatible library)
'
' I don't like e.g. RFC 4648 because it has too many letters that can be
' mistaken, although the idea of being more cryptical is good
'
' my paradigmata:
' - "compatible" to decimal and hexadecimal system (and duotrigesimal)
'    this includes to start with 0, and counting up 1, 2, 3...
' - of course typeable with every keyboard / normal, ASCII characters
' - rather familiar, systematic order => A B C
' - avoid confusions
' - try to be good to memorise
' - beeing systematic e.g. the distance of 'A' to 'a' is the same as 'Z' to 'z'
'
' counting list duotrigesimal system:
' 0123456789ABCDEFGHJKLMNRSTUVWXYZ
'
' annotations:
' - 'I' omitted to be not mistaken for '1'
' - omitting deliberately 'O' and 'Q', not to be confused with each other and
'   '0' (zero)
'   'P' is more accidently out to keep the order
' - last/highest digit is 'Z'
' - dislike: '2' and 'Z' can be mistaken
' - dislike: the missing 'I' is not beautiful and might confuse/cause errors
'
' counting list tetrahexagesimal system (space in the middle is not part of it):
' 0123456789ABCDEFGHJKLMNRSTUVWXYZ OIP#$Qoipqabcdefghijklmnrstuvxyz
'
' annotations:
' - there are not enough letters, so you need at least 2 special characters,
'   that shouldn't be mathematical operators / clearly being recognisable as
'   part of the number
' - O, I, P and Q and their minuscules are in disorder
' - the tetrahexagesimal system is good to shorten numbers, but very
'   error-prone for transcription
' - it seems not logical that minuscules come after the majuscules
'
'
' TOBASE32(uint64 value, uint digits)
'   turns a 64 bit integer into base-32 number as a string
'   'digits' digits, filled with zeros, set to 0 to get the pure number
'   you can't get more than 13 digits with a 64 bit base-32 number
'
' TOBASE64(uint64 value, uint digits)
'   turns a 64 bit integer into base-64 number as a string
'   you can't get more than 11 digits with a 64 bit base-64 number
'
' VALBASE32(STRING value)
'   turns a base-32 number string into an integer (ULongInt)
'   it can also deal with negative integers, but "-1" is e.g. counting
'   as &hFFFFFFFFFFFFFFFF, the result is always unsigned
'   just like VAL() only returns as much of a valid number as it is found,
'   this means e.g. "F8Tz1!$A" counts only as "F8T" (15641)
'
' VALBASE64(STRING value)
'   turns a base-64 number string into an integer (ULongInt)
'
' #todo: / future version: support of 128 bit integers
'                          (until supported by FreeBASIC)


DIM SHARED AS STRING * 32 hypernumbase32="0123456789ABCDEFGHJKLMNRSTUVWXYZ"
DIM SHARED AS STRING * 64 hypernumbase64="0123456789ABCDEFGHJKLMNRSTUVWXYZOIP#$Qoipqabcdefghjklmnrstuvwxyz"

FUNCTION TOBASE32(BYVAL value AS ULONGINT, BYVAL digits AS UINTEGER=0) AS STRING
  DIM val_string AS STRING
  DIM c AS ULONGINT

  IF digits>16 THEN digits=16 ' absurd value

  val_string=""
  IF digits THEN
    DO
      c=value AND 31
      val_string=MID(hypernumbase32,c+1,1)+val_string
      value SHR=5
      digits-=1
    LOOP WHILE value
    val_string=STRING(digits,"0")+val_string
  ELSE
    DO
      c=value AND 31
      val_string=MID(hypernumbase32,c+1,1)+val_string
      value SHR=5
    LOOP WHILE value
  ENDIF

  RETURN val_string
END FUNCTION

FUNCTION TOBASE64(BYVAL value AS ULONGINT, BYVAL digits AS UINTEGER=0) AS STRING
  DIM val_string AS STRING
  DIM c AS ULONGINT

  IF digits>12 THEN digits=12 ' absurd value

  val_string=""
  IF digits THEN
    DO
      c=value AND 63
      val_string=MID(hypernumbase64,c+1,1)+val_string
      value SHR=6
      digits-=1
    LOOP WHILE value
    IF digits THEN
        val_string=STRING(digits,"0")+val_string
  ENDIF
  ELSE
    DO
      c=value AND 63
      val_string=MID(hypernumbase64,c+1,1)+val_string
      value SHR=6
    LOOP WHILE value
  ENDIF

  RETURN val_string
END FUNCTION

FUNCTION VALBASE32(BYVAL val_string AS STRING) AS ULONGINT
  DIM value AS ULONGINT
  DIM c AS STRING
  DIM AS INTEGER i, o

  i=1
  value=0
  c=MID(val_string,i,1)
  IF c="-" THEN ' negative value
    i+=1
    c=MID(val_string,i,1)
    DO
      o=INSTR(hypernumbase32, c)
      IF o=0 THEN EXIT DO
      value SHL=5
      o-=1
      value-=o
      i+=1
      c=MID(val_string,i,1)
    LOOP
  ELSE
    DO
      o=INSTR(hypernumbase32, c)
      IF o=0 THEN EXIT DO
      value SHL=5
      o-=1
      value+=o
      i+=1
      c=MID(val_string,i,1)
    LOOP
  ENDIF

  RETURN value
END FUNCTION

FUNCTION VALBASE64(BYVAL val_string AS STRING) AS ULONGINT
  DIM value AS ULONGINT
  DIM c AS STRING
  DIM AS INTEGER i, o

  i=1
  value=0
  c=MID(val_string,i,1)
  IF c="-" THEN ' negative value
    i+=1
    c=MID(val_string,i,1)
    DO
      o=INSTR(hypernumbase64, c)
      IF o=0 THEN EXIT DO
      value SHL=6
      o-=1
      value-=o
      i+=1
      c=MID(val_string,i,1)
    LOOP
  ELSE
    DO
      o=INSTR(hypernumbase64, c)
      IF o=0 THEN EXIT DO
      value SHL=6
      o-=1
      value+=o
      i+=1
      c=MID(val_string,i,1)
    LOOP
  ENDIF

  RETURN value
END FUNCTION
