Contents
Articles
Behaviors
Books
Director News
Director Web Sites
FAQ
Games
Mailing Lists
News Groups
Project Examples
Reviews
Software
Tools
Useful Web Sites
Utilities
Xtras

Don't miss these
Direct Media Video Slider
Opening Local HTML pages
Type Writer Fonts
XML Text Renderer v1.1
Build to order Project
Copy Progress handler for Buddy API
Open Windows Explorer
Accept Only Alpha-Numeric Characters
Library for work with formatted text documents
DirectTransition
 

 

 

Behavior Large Integer and Base Conversion Integer Parent Script

Added on 4/2/2004

 

Compatibilities:
D8_5

This item has not yet been rated

Author: Daniel Nelson

The lack of an unsigned integer type in Director creates difficulty in working with 32-bit pixels, as well as other tasks. This parent script uses list elements as integer places, so you can create a base 256 integer and access each 8-bit portion by accessing the list elements. Basic arithmetic and comparison operators are included. It also accomplishes base conversion. Execution is slow, but because it relies on lists, rather than strings, I expect it to be faster than string based methods. The slowness does make it impractical to use for converting images to a text friendly representation such as ASCII 85 encoding.

--Allows for arbitrarily large integer math (limited functions, feel free to add more) in Lingo
--Feel free to modify and use.
--Contact d.nelson@bluejade.com with any comments or corrections

--a base integer is defined as a list with at least one positive integer element.
--when displayed, each element of the list takes on a "decimal" place, but instead of base 10, we use
--the base defined in new().
--So if the base is 256, then [1, 3] = 256^1 * 1 + 256^0 * 3 = 256 + 3 = 259.
--A first element of -1 indicates that the integer is negative.
--This might make color math easy since when you need the different components, simply access
--the elements in the list.
--Note: all arithmetic concludes with simplifying the integer so that no zeros precede the first
--non-zero number on the left and zero is always positive.
--
--Base conversion is also available throught the convertBase(, ) method.

property p_base

on new me, anInteger
  --copyright 2004 Daniel W. Nelson
  --anInteger is the base, which must be less than 2^31 - 1
  --for arbitrarily long integers, using a base of 256 is desirable.  it is especially good for 32-bit
  --colors, since the color components can be grabbed by accessing that element of the list.
  if voidP(anInteger) then anInteger = 256
  p_base = anInteger
  return me
end new

on getBase me
  --copyright 2004 Daniel W. Nelson
  return p_base
end getBase


on convertBase me, baseInteger, newBase
  --copyright 2004 Daniel W. Nelson
  
  resultingBaseInteger = []
  if not(baseInteger[1]) then --the integer is zero
    return [0]
  else if baseInteger[1] < 0 then
    resultIsNegative = TRUE
    baseInteger.deleteAt(1)
  else
    resultIsNegative = FALSE
  end if
  
  newBase = me.makeIntegerIntoBaseNumber(newBase) --convert newBase to a base integer in the current base so that it
  --can be run through the arithmetic operators
  
  --compute the smallest power that, dividing anInteger by the p_base raised to that power gives zero with
  --integer division
  divisor = newBase
  power = 1
  repeat while me.divide(baseInteger, divisor)[1] <> 0
    divisor = newBase
    power = power + 1
    repeat with j = power - 1 down to 1
      divisor = me.multiply(divisor, newBase)
    end repeat
  end repeat
  --end: compute the smallest power that...
  
  repeat with i = power down to 1
    divisor = [1]
    repeat with j = i - 1 down to 1
      divisor = me.multiply(divisor, newBase)
    end repeat
    thisPlace = me.divide(baseInteger, divisor)
    if thisPlace[1] <> 0 or resultingBaseInteger.count then --add places if the place is significant or if higher order places
      --exist
      resultingBaseInteger.add(0)
      resultingBaseInteger = me.add(resultingBaseInteger, thisPlace)
      baseInteger = me.subtract(baseInteger, me.multiply(thisPlace, divisor))
    end if
  end repeat
  
  
  if resultIsNegative then resultingBaseInteger.addAt(1, -1)
  return resultingBaseInteger
end convertBase

on absoluteValue me, baseInteger
  --copyright 2004 Daniel W. Nelson
  if me.isNegative(baseInteger) then return me.negate(baseInteger)
  return baseInteger
end absoluteValue

on isNegative me, baseInteger
  --copyright 2004 Daniel W. Nelson
  return baseInteger[1] < 0
end isNegative

on negate me, baseInteger
  --copyright 2004 Daniel W. Nelson
  baseInteger = baseInteger.duplicate()
  if baseInteger[1] < 0 then
    baseInteger.deleteAt(1)
  else
    baseInteger.addAt(1, -1)
  end if
  return baseInteger
end negate

on lessThanOrEqual me, baseInteger, anotherBaseInteger
  --copyright 2004 Daniel W. Nelson
  return not(me.greaterThan(baseInteger, anotherBaseInteger))
end lessThanOrEqual

on greaterThanOrEqual me, baseInteger, anotherBaseInteger
  --copyright 2004 Daniel W. Nelson
  return not(me.lessThan(baseInteger, anotherBaseInteger))
end greaterThanOrEqual

on lessThan me, baseInteger, anotherBaseInteger
  --copyright 2004 Daniel W. Nelson
  return me.greaterThan(anotherBaseInteger, baseInteger)
end lessThan

on equals me, baseInteger, anotherBaseInteger
  --copyright 2004 Daniel W. Nelson
  return baseInteger = anotherBaseInteger
end equals

on greaterThan me, baseInteger, anotherBaseInteger
  --copyright 2004 Daniel W. Nelson
  if baseInteger[1] < 0 and anotherBaseInteger[1] < 0 then
    return me.greaterThan(me.negate(anotherBaseInteger), me.negate(baseInteger))
  else if baseInteger[1] < 0 then
    return FALSE --the first parameter is less than zero and the second is not less than zero
  else if anotherBaseInteger[1] < 0 then
    return TRUE --the second parameter is less than zero and the first is not less than zero
    
    
  else if baseInteger.count <> anotherBaseInteger.count then --has more orders (analogous to one having
    --a hundredths place while the other only has a tens place)
    return baseInteger.count > anotherBaseInteger.count
    
  else --compare each order, starting with the most significant.  if they differ, return TRUE iff
    --the first parameter is larger than the second parameter
    repeat with i = 1 to baseInteger.count
      if baseInteger[i] <> anotherBaseInteger[i] then return baseInteger[i] > anotherBaseInteger[i]
    end repeat
  end if
  
  return FALSE
end greaterThan

on add me, baseInteger, anotherBaseInteger
  --copyright 2004 Daniel W. Nelson
  --returns a base integer that is equal to baseInteger + anotherBaseInteger
  
  if baseInteger[1] < 0 and anotherBaseInteger[1] < 0 then
    baseInteger = baseInteger.duplicate()
    anotherBaseInteger = anotherBaseInteger.duplicate()
    baseInteger.deleteAt(1)
    anotherBaseInteger.deleteAt(1)
    resultIsNegative = TRUE
    
  else if baseInteger[1] < 0 then
    return me.subtract(anotherBaseInteger, me.negate(baseInteger))
    
  else if anotherBaseInteger[1] < 0 then
    return me.subtract(baseInteger, me.negate(anotherBaseInteger))
    
  else
    resultIsNegative = FALSE
    
  end if
  
  
  resultingBaseInteger = []
  carry = FALSE
  cnt = max(baseInteger.count, anotherBaseInteger.count) - 1
  repeat with i = 0 to cnt
    if i >= baseInteger.count then
      anInteger = anotherBaseInteger[anotherBaseInteger.count - i]
    else if i >= anotherBaseInteger.count then
      anInteger = baseInteger[baseInteger.count - i]
    else
      anInteger = baseInteger[baseInteger.count - i] + anotherBaseInteger[anotherBaseInteger.count - i]
    end if
    if carry then
      anInteger = anInteger + 1
      carry = FALSE
    end if
    if anInteger >= p_base then
      carry = TRUE
      anInteger = anInteger - p_base
    end if
    resultingBaseInteger.addAt(1, anInteger)
  end repeat
  
  if carry then resultingBaseInteger.addAt(1, 1)
  
  if resultIsNegative then resultingBaseInteger.addAt(1, -1)
  return me._simplifybaseInteger(resultingBaseInteger)
end add

on subtract me, baseInteger, anotherBaseInteger
  --copyright 2004 Daniel W. Nelson
  --returns a base integer that is equal to baseInteger - anotherBaseInteger
  
  if baseInteger[1] < 0 and anotherBaseInteger[1] < 0 then --a negative minus a negative =>
    --a negative plus a positive. switch it around to get a positive minus a positive
    baseInteger = baseInteger.duplicate()
    anotherBaseInteger = anotherBaseInteger.duplicate()
    baseInteger.deleteAt(1)
    anotherBaseInteger.deleteAt(1)
    temp = baseInteger
    baseInteger = anotherBaseInteger
    anotherBaseInteger = temp
    
  else if baseInteger[1] < 0 then --negative minus a positive. simply add the two negatives
    return me.add(baseInteger, me.negate(anotherBaseInteger))
    
  else if anotherBaseInteger[1] < 0 then --positive minus a negative.  simply add the two positives
    return me.add(baseInteger, me.negate(anotherBaseInteger))
  end if
  --we now have a positive minus a positive
  if me.lessThan(baseInteger, anotherBaseInteger) then --the first integer is smaller than the second
    return me.negate(me.subtract(anotherBaseInteger, baseInteger))
  end if
  
  resultingBaseInteger = []
  inverseCarry = FALSE
  cnt = max(baseInteger.count, anotherBaseInteger.count) - 1
  repeat with i = 0 to cnt
    if i >= anotherBaseInteger.count then --we know that largerInteger has equal or more places than
      --anotherBaseInteger from the above lessThan comparison
      anInteger = baseInteger[baseInteger.count - i]
    else
      anInteger = baseInteger[baseInteger.count - i] - anotherBaseInteger[anotherBaseInteger.count - i]
    end if
    if inverseCarry then
      anInteger = anInteger - 1
      inverseCarry = FALSE
    end if
    if anInteger < 0 then
      inverseCarry = TRUE
      anInteger = anInteger + p_base
    end if
    resultingBaseInteger.addAt(1, anInteger)
  end repeat
  --no inverseCarry after this since we checked above that we were subtracting the
  --smaller from the larger (or equal from equal)
  
  return me._simplifybaseInteger(resultingBaseInteger)
end subtract

on multiply me, baseInteger, anotherBaseInteger
  --copyright 2004 Daniel W. Nelson
  --baseInteger and anotherBaseInteger are base integers (defined in the makeIntegerIntoBaseNumber handler)
  --returns a base integer that is equal to baseInteger * anotherBaseInteger
  
  if baseInteger[1] < 0 and anotherBaseInteger[1] < 0 then
    baseInteger = baseInteger.duplicate()
    anotherBaseInteger = anotherBaseInteger.duplicate()
    baseInteger.deleteAt(1)
    anotherBaseInteger.deleteAt(1)
    resultIsNegative = FALSE
    
  else if baseInteger[1] < 0 then
    baseInteger = baseInteger.duplicate()
    baseInteger.deleteAt(1)
    resultIsNegative = TRUE
    
  else if anotherBaseInteger[1] < 0 then
    anotherBaseInteger = anotherBaseInteger.duplicate()
    anotherBaseInteger.deleteAt(1)
    resultIsNegative = TRUE
    
  else
    resultIsNegative = FALSE
    
  end if
  
  resultingBaseInteger = [0]
  carry = 0
  repeat with i = baseInteger.count down to 1
    tempBaseInteger = []
    repeat with k = 1 to baseInteger.count - i
      tempBaseInteger.add(0) --think of this as standard multiplication: add zeros to move the number
      --one place to the left for each time iterating through the bottom number:
      --  baseInteger
      --* anotherBaseInteger
      -- _____________________
      -- resultingBaseInteger
    end repeat
    repeat with j = anotherBaseInteger.count down to 1
      anInteger = anotherBaseInteger[j] * baseInteger[i]
      
      if carry then
        anInteger = anInteger + carry
        carry = 0
      end if
      
      if anInteger >= p_base then
        carry = anInteger / p_base
        anInteger = anInteger - carry * p_base
      end if
      tempBaseInteger.addAt(1, anInteger)
    end repeat
    
    
    if carry then
      tempBaseInteger.addAt(1, carry) --tempBaseInteger is already properly computed
      carry = 0
    end if
    resultingBaseInteger = me.add(resultingBaseInteger, tempBaseInteger)
  end repeat
  
  if carry then
    tempBaseInteger = []
    repeat with k = 1 to baseInteger.count - i
      tempBaseInteger.add(0) --think of this as standard multiplication: add zeros to move the number
      --one place to the left for each time iterating through the bottom number:
      --  baseInteger
      --* anotherBaseInteger
      -- _____________________
      -- resultingBaseInteger
    end repeat
    tempBaseInteger.addAt(1, carry)
    resultingBaseInteger = me.add(resultingBaseInteger, tempBaseInteger)
  end if
  if resultIsNegative then resultingBaseInteger.addAt(1, -1)
  return me._simplifybaseInteger(resultingBaseInteger)
end multiply

on divide me, numerator, denominator
  --copyright 2004 Daniel W. Nelson
  --numerator and denominator are base integers (defined in the makeIntegerIntoBaseNumber handler)
  --returns a base integer that is equal to numerator / denominator
  numerator = numerator.duplicate() --this process is destructive to the numerator
  
  if numerator[1] < 0 and denominator[1] < 0 then
    denominator = denominator.duplicate()
    numerator.deleteAt(1)
    denominator.deleteAt(1)
    resultIsNegative = FALSE
    
  else if numerator[1] < 0 then
    numerator.deleteAt(1)
    resultIsNegative = TRUE
    
  else if denominator[1] < 0 then
    denominator = denominator.duplicate()
    denominator.deleteAt(1)
    resultIsNegative = TRUE
    
  else
    resultIsNegative = FALSE
    
  end if
  
  resultingBaseInteger = []
  remainder = 0
  repeat while numerator.count
    leftPartOfNumerator = []
    leftPartOfNumerator.add(numerator[1] + remainder)
    remainder = 0
    numerator.deleteAt(1)
    repeat while me.lessThan(leftPartOfNumerator, denominator) and numerator.count
      --find the fewest digits off the left side of the numerator that are greater than or equal to the denominator
      leftPartOfNumerator.add(numerator[1])
      numerator.deleteAt(1)
    end repeat
    
    if resultingBaseInteger.count then
      repeat with k = leftPartOfNumerator.count - 1 down to 1 --need to add a zero for every place we skip
        resultingBaseInteger.add(0)
      end repeat
    end if
    
    if me.lessThan(leftPartOfNumerator, denominator) then
      if leftPartOfNumerator.count then resultingBaseInteger.add(0) --since we aren't adding any current value below, we
      --need to add the one zero we omitted in the preceding repeat loop
      exit repeat --finished dividing
    end if
    
    minInt = 1
    maxInt = p_base
    repeat while TRUE --multiply by an increasing amount until the amount is greater than the fewest
      --digits off the left side of the numerator.  the place will be one less than that
      currentValue = (minInt + maxInt) / 2
      multipliedResult = me.multiply(denominator, [currentValue])
      if me.greaterThan(multipliedResult, leftPartOfNumerator) then
        if maxInt = minInt + 1 then --the max and min integers bracket the float that would be the real answer
          currentValue = currentValue - 1
          exit repeat
        end if
        
        maxInt = currentValue
      else if me.lessThan(multipliedResult, leftPartOfNumerator) then
        if maxInt = minInt + 1 then
          exit repeat
        end if
        minInt = currentValue
      else --equals
        exit repeat
      end if
    end repeat
    remainder =  me.subtract(leftPartOfNumerator, me.multiply(denominator, [currentValue]))[1]
    resultingBaseInteger.add(currentValue)
  end repeat
  
  if not(resultingBaseInteger.count) then return [0]
  if resultIsNegative then resultingBaseInteger.addAt(1, -1)
  return resultingBaseInteger
end divide


on makeIntegerIntoBaseNumber me, anInteger
  --copyright 2004 Daniel W. Nelson
  --if positive, anInteger must be less than or equal to 2^31 - 1 (2147483647)
  --if negative, anInteger must be greater than -(2^31 - 1)
  --a base integer integer is guaranteed to have at least one list element
  baseInteger = []
  if not(anInteger) then --the integer is zero
    return [0]
  else if anInteger < 0 then
    resultIsNegative = TRUE
    anInteger = - anInteger
  else
    resultIsNegative = FALSE
  end if
  
  --compute the smallest power that, dividing anInteger by the p_base raised to that power gives zero with
  --integer division
  divisor = p_base
  power = 1
  repeat while anInteger / divisor <> 0
    divisor = p_base
    power = power + 1
    repeat with j = power - 1 down to 1
      divisor = divisor * p_base
    end repeat
    if divisor <= 0 then exit repeat --we have exceded the integer math for this number
  end repeat
  --end: compute the smallest power that...
  
  repeat with i = power down to 1
    divisor = 1
    repeat with j = i - 1 down to 1
      divisor = divisor * p_base
    end repeat
    thisPlace = anInteger / divisor
    if thisPlace or baseInteger.count then --add places if the place is significant or if higher order places
      --exist
      baseInteger.add(thisPlace)
      anInteger = anInteger - thisPlace * divisor
    end if
  end repeat
  
  if resultIsNegative then baseInteger.addAt(1, -1)
  return baseInteger
end makeIntegerIntoBaseNumber

on baseIntegerToInteger me, baseInteger
  --copyright 2004 Daniel W. Nelson
  --will only work for positive integers <= 2^31 - 1 and negative integers > -(2^31 - 1)
  anInteger = 0
  if baseInteger[1] < 0 then
    baseInteger = baseInteger.duplicate()
    baseInteger.deleteAt(1)
    resultIsNegative = TRUE
  end if
  repeat with i = baseInteger.count down to 1
    multiplier = integer(power(p_base, baseInteger.count - i))
    anInteger = anInteger + baseInteger[i] * multiplier
  end repeat
  
  if resultIsNegative then return -anInteger
  return anInteger
end baseIntegerToInteger

on test me
  --copyright 2004 Daniel W. Nelson
  
  --integers correctly mapping back and forth
  repeat with i = 1 to 100
    anInteger = 458529558 ---random(the maxInteger - 1)
    baseInteger = me.makeIntegerIntoBaseNumber(anInteger)
    anotherInteger = me.baseIntegerToInteger(baseInteger)
    if anInteger <> anotherInteger then
      put(anInteger && baseInteger && anotherInteger)
      exit repeat
    end if
  end repeat
  --end: integers correctly mapping back and forth
  
  --integers correctly adding
  repeat with i = 1 to 100
    anInteger = random(the maxInteger / 2 - 1)
    if random(2) = 1 then anInteger = - anInteger
    anotherInteger = random(the maxInteger / 2 - 1)
    if random(2) = 1 then anotherInteger = - anotherInteger
    --    anInteger = 980207055
    --    anotherInteger = -1001414088
    baseInteger = me.makeIntegerIntoBaseNumber(anInteger)
    anotherBaseInteger = me.makeIntegerIntoBaseNumber(anotherInteger)
    
    sum = anInteger + anotherInteger
    largeSum = me.add(baseInteger, anotherBaseInteger)
    anotherSum = me.baseIntegerToInteger(largeSum)
    
    if sum <> anotherSum then
      put(anInteger && anotherInteger && sum && anotherSum)
      exit repeat
    end if
  end repeat
  --end: integers correctly adding
  
  
  
  --integers correctly multiplying
  repeat with i = 1 to 100
    anInteger = random(100)
    if random(2) = 1 then anInteger = - anInteger
    anotherInteger = random(100)
    if random(2) = 1 then anotherInteger = - anotherInteger
    
    baseInteger = me.makeIntegerIntoBaseNumber(anInteger)
    anotherBaseInteger = me.makeIntegerIntoBaseNumber(anotherInteger)
    
    multiple = anInteger * anotherInteger
    largeMultiple = me.multiply(baseInteger, anotherBaseInteger)
    anotherMultiple = me.baseIntegerToInteger(largeMultiple)
    
    if multiple <> anotherMultiple then
      put(anInteger && anotherInteger && multiple && anotherMultiple)
      exit repeat
    end if
  end repeat
  --end: integers correctly multiplying
  
  
  
  --integers correctly multiplying
  repeat with i = 1 to 100
    anInteger = random(100)
    if random(2) = 1 then anInteger = - anInteger
    anotherInteger = random(100)
    if random(2) = 1 then anotherInteger = - anotherInteger
    
    baseInteger = me.makeIntegerIntoBaseNumber(anInteger)
    anotherBaseInteger = me.makeIntegerIntoBaseNumber(anotherInteger)
    
    multiple = anInteger / anotherInteger
    largeMultiple = me.divide(baseInteger, anotherBaseInteger)
    anotherMultiple = me.baseIntegerToInteger(largeMultiple)
    
    if multiple <> anotherMultiple then
      put(anInteger && anotherInteger && multiple && anotherMultiple)
      exit repeat
    end if
  end repeat
  --end: integers correctly dividing
end test

on _simplifybaseInteger me, baseInteger
  --copyright 2004 Daniel W. Nelson
  --modify baseInteger so that zero is always considered positive and no leading zeros are found
  --prior to a non-zero (except for the singles digit)
  
  if baseInteger[1] < 0 then
    baseInteger = baseInteger.duplicate()
    baseInteger.deleteAt(1)
    resultIsNegative = TRUE
  end if
  
  repeat while baseInteger.count > 1 and baseInteger[1] = 0
    baseInteger.deleteAt(1)
  end repeat
  
  if baseInteger.count = 1 and baseInteger[1] = 0 then return [0] --keep zero as positive
  
  if resultIsNegative then baseInteger.addAt(1, -1)
  return baseInteger
end _simplifybaseInteger

 


Contact

MMI
36 South Court Sq
Suite 300
Newnan, GA 30263
USA

Send e-mail