SCRIPTING:

Kyla Cole
Kyla Cole

BASE CONVERSION & ENUMERATION
Convert form a number expressed in a base (such as binary) into its equivalent into another base (such as decimal). No limit to the type of conversion and bases.
You can convert also providing bases that include alpha numerical items defined by yourself.
You can perform an Enumeration of all the numbers expressed into a base until a given limit.
Ample Test Form at bottom of the file as usual.
August 2002
{ @ }

The model above is Kyla Cole
LOADS OF KYLA COLE ON THE NET


HOW TO USE THE SCRIPT
Basic instructions

The purpose of this script is threefold:
  • Let you perform a base conversion between a number expressed in whatever base (such as, say, 24 in the decimal system) to its equivalent in another base (such as binary, whereas the binary equivalent of 24 is 11000)
  • Let you perform such conversion on bases entirely of your invention, such as, say [3 a4 98ew] or whatever even more irrational group of items that have to be considered as building numbers: in fact what matters when performing a base conversion is not what items are in the base (such as, normally, mere numbers), but the length of the basic items in which the base gets embodied.
  • Most base conversion do not take into account the leading number (normally a zero) when enumerating or converting: for instance you have number such as [0 1 2 3 4 5 6 7 8 9] and the subsequent group start with 1: [10 11 12 13 14...] not with zero: [00, 01, 02, 03, 04...].
    This script let you include such values as well if you prefer; so, to some degree, it is also a script that could be used for performing some kind of mild cryptography.
The script is conceived to be used as a class, hence its length; therefore you have to assign it to a variable possibly by keyword new:
var foo=new baseConverter( startBase, endBase, number, splitter, rejoin, zeroes )
 
REMEMBER: in JavaScript whenever you initialize a variable as a new object, always put the keyword var before it or you might cross into strange behaviours or unsuccessful assignements.
 
This baseConverter requires, as you may have noticed, 3 compulsory argument and an optional fourth one:

Leonardo Da Vinci: tanks
baseConverter ARGUMENTS
startBase
typically a number (or a string or an array: see below: splitter) representing one of the two bases involved in the conversion process: for the decimal system it is 10, for the binary 2, for a base 3 is obviously 3 and so on.
If you mean to pass it as a number be sure it is a number indeed and not a literal version of a number (that is: 2, not "2") or the function would convert the string into a splitted version (see below).
endBase
the second base involved (similar as above).
If you mean to pass it as a number be sure it is a number indeed and not a literal version of a number (that is: 2, not "2") or the function would convert the string into a splitted version (see below).
number
this should be a string possibly.
Anyway what most matters is that such number must be an instance of a number existing within the startBase family. It will be going to be converted into its equivalent in the endBase
splitter
if as startBase or as endBase or both you're to pass strings including a set of elements defined/crafted by yourself (may be your imagination, or a set of typical alpha numerical hexadecimal values as: "0123456789abcdef"), the script will have to turn such a set into an array in order to work with it: therefore the string is to be split.
By default it splits (if it finds a string) item by item, but if you separate your items by a white space or a comma or anything else, well pass such separator as the splitter argument (if it is a full white space: " ", if it is a comma "," and so on...: always in between quotes -set of double or set of single, no matters- for it must be a String data type).
Remember that if you're converting a number which belongs to a base you've instructed to be split by a specific splitter, also such number must separate each item in it by such splitter!
Instance: a fancy base like [aa|ab|ac] separated by the | sign: you mean to convert a number belonging to such fantasy base like ab+ac into, say, its decimal equivalent: well, such number must be passed separating its compsing items by the same splitter: "ab|ac"
var foo=new baseConverter("aa|ab|ac", 10, "ab|ac", "|")
 
Be sure, in order to have a thorough outlook of the proper workings and involvements of using a splitter argument, that you read also the section (further on in this file) on the class method named convert
rejoin
Please for this argument see the section (further on in this file) on the class method named convert
zeroes
Please see below - here: click.

A simple invocation:
var foo=new baseConverter(10,2,"228")
Such a move would immediately calculate the binary (2) equivalent to the decimal number 228 (which by the way is 11100100) and you have basically two ways to retrieve such a value:
  1. If you have assigned by keyword new:
    var foo=new baseConverter(10,2,"228")
    now your result is stored in the following property (remember that foo is just a placeholder for whatever variable name you are pleased to initialize):
    foo.result
  2. Actually, you can also initialize without the keyword new:
    var foo=baseConverter(10,2,"228")
    In such case foo is equivalent to the result for the script returns it: only if you initialize your variable as a new instance of the class you can at a second time retrieve also its properties or built in methods we're to describe later on.
  3. My suggestion is to take avail of the keyword new always; also, always pass some arguments upon initializations also if you don't need an immediate conversion: that is, pass at least upon creation of the new instance foo, the arguments:
    • startBase
    • endBase
    Such move is not going to cause any disturb to you: in fact, if you're using a base converter, it is supposed you're doing so for you have... two bases to convert between. Initializing a full object as such would allow you to perform subsequent conversions without having to reinitialize it all, if still in between the same set of two bases: if the bases are different, nothing more logical and practical than initializing a full set of variables each of one meant to work as a permanent specific engines for the conversions.
An hexadecimal conversion might look like:
var foo=new baseConverter(10,"1234567890abcdef","228")
whereas the fourth argument splitter is not necessary for the script fragments item by item a base passed as a string as its default behaviour.
 
I also take this chance to repeat that the third argument is:
  1. A number that must belong to the first base (startBase) lot.
  2. Preferably passed as a string.
Also, keep in mind that the function returns the result as follows:
  1. If any fillers were into place (such as leading zeros just to fill in a given length), all those would be stripped off (that is: things like, say, 00100 would get turned into just 100). By filler the script assumes the first char in the startBase grouping (it becomes an array anyway in the inner workings of the script, even if you passed it as a mere number. On the other hand if you yourself passed a string to be split into an array or downright an array, the filler is assumed as being the first entry in such array).
  2. The script returns as the result a STRINGed version of the number, so always consider you may want to parseFloat it before using the result for further calculations out of the script. [the reason with this is that if you used personally defined alphanumeric items, parse-floating the result by default, would have caused potential errors like stripping away trailing alphabetical values!].
  3. Such returned stringed version always separates each item of the given base by the argument passed by splitter: if no such argument is passed, all the items in the sgring would be close to each other (instance: they would be "1293" and not, say, "1-2-9-3" which would be the shape they'd have if you would have passed as a splitter argument the following: "-").
    To overcome this namely to strip from the result the splitter, pass the argument named rejoin both upon construction and each time you may be invoking the foo.convert(number, rejoin) built in method discussed further on.
Known Issue: the script is crafted to work only with bases starting from 2: a base zero would be meaningless, and a base 1 would simply mean an object repeated as many times as necessary to fill in a decimal amount (and in order to convert from bases higher than 1 into decimal, you can definitely use the script).


OVERVIEW OF THE INNER METHODS
What the inner method and propertie of the class are and how they can work

This baseConverter class can either perform one conversion as soon as it is called if 3 arguments are passed, or it can be initialized in order to use its built in methods either at a second time or repeatedly.
Thus an overview of the methods may be useful: i will also give many details on how they work, so if you deem these informations are too technical for you, you can surely skip and bo at bottom to the test form.
All the examples suppose you've initialized a foo variable like follows:
var foo=new baseConverter(10,3)
which is like assigning to foo all the built in methods of the baseConverter to perform calculations from decimal (10) base to base 3 (instead than the usual binary, that is...).
 
Actually this class is so flòexible you could even initalize as follows:
var foo=new baseConverter()
That is, with no arguments! Now foo would be a base converter that upon invoking its built in method convert is fit to convert whatever number from whatever base into whatever base if the convert is fed with the right arguments (see also further on):
foo.convert(10,2,"140")

baseConverter INNER METHODS
baseToDecimal
foo.baseToDecimal( base, num, includeZeros )

 
 
 
 
 
Leonardo Da Vinci: perspective

The trick used throughout the script is to transform each number of a given base into its equivalent decimal, and then such decimal into the equivalent number of the end base.
The two methods named after baseToDecimal are meant to derive the decimal equivalent of the given number from the begin with base, namely the startBase argument.
 
The first of the two methods [baseToDecimal, baseToDecimal2] is meant to calculate how many items, expressed in decimals in that given start base, are found before the lot the passed number to be converted belongs to. In other words:
each base is composed of lots: like the decimal system is composed of the tents, then of the hudreds, then of the thousands: this first method finds the amount of entries that are in all the lots before the lot the given number belongs to in its base.
The fomula I found to perform such calculation is:
(base^mask)-[(base^mask)/base]
whereas base is always the startBase and by mask I mean the amount of numbers included in the currently explored lots (for instance: the mask of the tents is 2, for each item includes two digits; the mask of the thousands is 4, in fct each item includes 4 digits, and so on...)
Please not that the mask may appear being longer than the given digits if the item you're using (such as customed bases items) are longer than one digit: say a mask 3 on a fancy base that looks like [aa, ab, ac, bb. bc] may look like: aa|ab|ab=aaabab which seems 6, but actually it is 3 items!
 
You can verify the correctness of the above (^ means pow) by considering a case within our habitual decimal base:
10^3-[(10^3)/10]=900
which means that in the group before the lot of the thousands (3 digits) there are 900 numbers (in fact from 100 to 1000: 900).
If you recursively inspect all the lots beforee the last one the number partakes to, the eventual sum of the found values for each mask (the script derives the mask by checking how many digits are in the num argument; if mask is, say, 5 it recurs 4 times: until the grouping before mask=5, that is) would tell you how many numbers (in decimal expression) are before the given number in the given base before the last lot:

foo.baseToDecimal(base, num, zeroes)
If you pass also the third argument zeroes as something (say number 1) it will calculate even the zeros as legitimate to lead sets of numbers (I assume you do not want this, for decimals like 0049 do not exist!); zeroes, or whichever item you may have instructed a possible completely invented base as being the first within such base.

Although the calculation is obvious when it comes to our usual decimal base, it is quite moot when it comes to other basis, therefore the method is necessary and calculates all the lots recursively until it finds it has reached the lot the number belongs to, and returns a number which represents the amount of items (in decimals!) in the given startBase before the last lot the number belongs to.
 
IMPORTANT: actually such number is the decimal less one: in other words, it is an index for the arrays of the start and end bases. Note this is exactly what you need, for even the decimal system really starts with zero not with 1: otherwise no number would ever include the... zeros!
baseToDecimal2
foo.baseToDecimal2( base, num, includeZeros )

 
 
Leonardo Da Vinci: dents

The arguments exposed above apply here as well, with the difference this second and last method for the base into decimal conversions would perform the same as above but in within the last lot the number belongs to within the startBase family (see above: I mean the same: the tents, the hundreds, the thousands... and all their equivalent in all the other possible bases).
 
The applied formula I devised is anyway a bit different:
num*(base^looplevel_of_num)
That is: the number is split inot each of its digits, and each of these digits is checked from left to right: in such line each step towards the left means there are more numbers belonging to such subgroup: decimal example:
125:
  • 5: one digit subsector: there are at most 10 values [0-9]. Loop level=0. Therefore
    5*(10^0)=5
  • 2: tents subsector: there are at least 100 values [10-99]. Loop level=1. Therefore
    2*(10^1)=20
  • 1: hundreds subsector: there are at least 1000 values [100-999]. Loop level=2. Therefore
    1*(10^2)=100
As you see, the formula is a perfectly fit way to scan the amounts in the subsectors of the last lot a number belongs to, and in the same way it works within decimals, it works within any base (in fact: base^loopLevel).
 
The result yielded by baseToDecimal added to the result yielded by this latest baseToDecimal2, gives the eventual value: the decimal equivalent of a given number belonging to the startBase, for it sums all the decimals found in the earlier lots with the decimals found in the subsectors of the last lot.
 
You may have noticed the possible speed: a number as long as 20 digits (too many for a computer I think, but that's the same for our example) would be calculated with a mere recursion of 20 steps nearly only performing the formula above each step: that's good.
decimalToBase
foo.decimalToBase( base, decimalNum, includeZeros )
I assume you've read something in the methods above: so this second lot performs the opposite: given a number (arguably non decimal - although the script can perform meaningless operations like conversion from decimal into... decimal! You're safe), once found the decimal equivalent of a number expressed in some non decimal base, let's find the opposite: given the decimal, what is its equivalent expressed in another base? You can now guess how comes the baseConverter can convert, say, a number from base 16 to base... 5!
 
With a formula likewise baseToDecimal, it calculates all the groupings until the grouping in within which the number equivalent to the given decimal position is located, and returns the length of the mask corresponding to such grouping, which is going to be a data used in the next method.
 
This method once run produces an important adjunctive property: sums (such as: foo.sums) which stores how many decimals have been counted in the process, before the last lot whose corresponding mask length is returned.
decimalToBase2
foo.decimalToBase2( base, decimalNum, mask, includeZeros )
 

Leonardo Da Vinci: spine

Leonardo Da Vinci: standing man

Accomplishes the last step to convert the given decimal into the reassembled equivalewnt number in the endBase by knowing: the (end) base, the decimal number, the length of the mask. The fourth argument is optional and most of the times should never be set: it would calculate numbers as if numbers starting with zero (the first available number in the end base, we argue) might have existed.
First it calculates in this last lot how many decimals are to be scanned before finding the right number in the lot (tents, hundreds, thousands and the alike in other basis, remember?): to work that out, it reckons the passed decimals subtracting to it the property sums (foo.sums, say) that the previously run decimalToBase generated.
 
Then: why is the length of the mask useful?
Consider the following decimal:
725: its mask is 3: (three digits, that is).
Proceed as follows, remembering that the property sums=100 in such case (in fact before the group of the hundreds which a number like 725 belongs to, are indeed 100 numbers: 0-99!):
  • 7: Initialize a number like: position=0.
    Now calculate: base^(mask-1)
    In this case yields: 10^(3-1)=100
    Is this number higher than 625 (namely decimal-sums: 725-100=625 for before the hundreds we have 0-99 namely one hundred numbers, and we're dealing with subsectors likewise we did in baseToDecimal2: the property named sums, remember, stores 100 as well in this case)? You find this number 10^(3-1)=(100) is not higher than 625.
    So, position++, and repeat.
    Sum to the previously generated 100: you yield 200.
    Is this number higher than 625? No.
    So position++, and repeat.
    Sum to the previously recorded 200 this latest 100: you yield 300.
    Go on until you find 700, which is higher than 625: you've found the position index (in fact position was updated by one at each unsuccess) of the given startBase array from which you must pick the number to fill this slot: in our case the 8th index for counting started with 0, and the 8th index (position==8) is exactly... 7: 0,1,2,3,4,5,6,7.
    Save the earlier power still lower: 600. You save it in a variable named, in the method, toCurrent.
    Each recursion takes only at most startBase.length steps (binary? At most 2 steps! consider that looping even 50,000 entries is a matter of nothing, and I do not think you're going to use a base... 50000!).
  • Reset position to zero, and repeat for each number: 2 and 5 as well, that is: each time you tackle a new digit you decrease mask by 1 too: --mask, because you're inspecting groupings that are step by step lower than the hundreds (in our case)!
    Therefore the power you pow at decreases with the mask, but if you inspect the code of the method there is a variable named toCurrent which keeps track of the previous level: therefore
    at the beginning you compare:
    0, 100, 200, 300, 400, 500, 600, 700 against 625
     
    at the second round you compare (600 preserved):
    600, 610, 620, 630 against 625
     
    and eventually you compare (620 preserved):
    620, 621, 622, 623. 625. 626 against 625.
    Got it? I hope so: bit convoluted an explanation, I agree! I'm more interested that you get the idea than the details.
calculate
convert
foo.calculate( number, rejoin, withZeros )
 
foo.convert( number, rejoin, withZeros )

 
 

Leonardo Da Vinci: allegory of the boat wolf and eagle, particular
This is a method that you can invoke either as calculate or as convert: passing as arguments the the number belonging to the startBase, and an optional and likely useless withZeros in case you want to take into account numbers starting with... zero (the first item in the arrays generated out of the passed bases).
By simply calling it you can convert thorugh our placeholder foo whatever number from the start base into the end base (but you cannot do the opposite -from end to start base- and you cannot introduce bases different from the original ones provided upon construction); instance:
var foo=new baseConverter(2,10)
now foo is set as a baseConverter instance meant to convert from base 2 to base 10.
So:
foo.convert("10101")
converts binary 10101 into its decimal, returning (and also assigning to foo.result): 21.
If you want to convert with other bases or the inverse from decimal to binary, you have to initialize some other foo variable with the base/end bases couple swapped.
Alos, rember that if you use convert from an initialized baseConverter instance whose starting base is a set of arbitrary items split by a given splitter argument, the number you pass to convert must be expressed separating each item that consitutes it by the original splitter (see above the splitter argument if you're not sure what I mean or you've missed it).

As for the rejoin argument, if it is passed as number 1 it does the following: it makes sure the returned string holding the converted number doesn't include the optional splitter you might have passed upon constructing the new instance of a baseConverter.
In fact, if you want to convert between fantasy bases like say ["aa,ab,ac"] and ["xx,yyyy"] that would be like converting between base 3 (three fancy entries) and base 2 (two fancy entries) whereas the only difference is that instead of numbers you have instructed the baseConverter to handle fantasy items.
Therefore you may have to pass the splitter argument in order to be sure that the baseConverter would not transform ["aa,ab,ac"] into something like [a,a,a,b,a,c], for by default the baseConverter splits any given base item by item, no matter whether such base is passed either as a string or a single number representing the base (in the latter case, the baseConverter will produce out of a number, like say 10, a list of 10 items from 0 to 9!), .
So in such case above you may pass as a splitter a comma: ","
Upon doing such move (that is, upon passing a splitter) the results are likely to include the splitter as well: in order to instruct the conversion not to include the splitter at all (in case you don't want it any longer) just pass rejoin as 1. Got it?
enumerate
foo.enumerate( maxMask, withZeros )
This method would simply enumerate all the number of the startBase provided upon initialization of a new instance of the baseConverter (var foo=new baseConverter(2,10) in such case the startBase is 2),and would go on until the mask of the generated numbers doesn't exceed the required maxMask (the mask is the amount of the digits in the number, thus a decimal base instructed to enumerate until maxMask=5 would enumerate all the decimals until... 99,999).
This method can be slow, anyway, with high masks, for it uses many built in javaScript methods like slice and concat that actually aren't but further loops. The baseConverter is meant to perform very good mostly on conversions rather than on enumeration. Feel free to use the Test Form


CODE AND TEST FORM
The codex, the enumeration form and the test form

THE CODEX
Leonardo Da Vinci: self protrait
[ Explorer only: with comments ]
ENUMERATOR
[the higher the base the slower]
From Base: »
or insert white space separated cutomized entries below:
Max Mask:         [ include zeros? » ]

Kyla Cole
The model above is Kyla Cole
LOADS OF KYLA COLE ON THE NET
CONVERSION
Leonardo Da Vinci: Lady with an Ermine
From Base: »
or insert white space separated cutomized entries below:

to Base: »
or insert white space separated cutomized entries below:

INSERT NUMBER
Rejoin? »  
zeroes? »