View in color | View documentation | License |
Download script | History | Other scripts by: sunanda |
20-Sep 13:42 UTC
[0.072] 18.159k
[0.072] 18.159k
base-convert.rREBOL [
file: %base-convert.r
title: "Base conversion functions"
author: "Sunanda"
date: 01-sep-2005
version: 0.0.2
purpose: {Functions to convert an decimal whole number to and from any arbitrary base}
library: [
level: 'intermediate
platform: [all plugin]
type: [tool function]
domain: [math scientific financial]
tested-under: [win]
support: none
license: 'GPL
see-also: none
plugin: [size: 770x140]
]
history: [
0.0.1 1-aug-2004 {Written}
0.0.2 1-sep-2005 {Added View/browser plugin front end}
]
]
;; ---------------------------------------------------------------------
;; This script has two objects:
;;
;; base-convert -- REBOL/Core script that provides conversions
;; view-base-convert -- REBOL/View, REBOL/Plugin front end to play with
;;
;; If you just want to use the base-convert functions, comment
;; out the view-base-convert/run line at the very end
;; -------------------------------------------------------------------
;; --------------
;; Documentation:
;; --------------
;; see http://www.rebol.org/cgi-bin/cgiwrap/rebol/documentation.r?script=base-convert.r
;; ===================
;; base-convert object
;; ===================
base-convert: make object! [
;; Customisable data items
;; -----------------------
maximum-decimal: 999999999
default-digits: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
case-sensitive: false
error-messages: [
"Maximum base should be "
"Base minimum is 2. Not "
"Base contains duplicate characters: "
"Number to convert must be 0 or greater"
"Number has unrecognised digit: "
"Number larger than "
"Number should be a whole number (no decimal places)"
]
;; ================
;; to-base function
;; ================
to-base: func [number-in [number!]
to-base [integer! string!]
/local
number-out
int-part
frac-part
][
if number-in < 0 [make error! error-messages/4] ;; too small
if 0 <> (number-in // 1) [make error! error-messages/7] ;; not whole number
if number-in > maximum-decimal [make error! join error-messages/6 maximum-decimal] ;; too big
to-base: make-base-string to-base
number-out: copy ""
int-part: number-in
until
[
frac-part: 1 + (int-part // length? to-base)
if frac-part > length? to-base [frac-part: 1]
insert number-out to-base/:frac-part
int-part: to-integer (int-part / length? to-base)
int-part = 0
]
return number-out
] ;; func
;; ==================
;; from-base function
;; ==================
from-base: func [number-in [string!]
from-base [integer! string!]
/local
number-out
curr-digit
][
from-base: make-base-string from-base
number-out: 0.0
foreach digit number-in
[ number-out: number-out * length? from-base
either case-sensitive
[curr-digit: find/case from-base to-string digit]
[curr-digit: find from-base to-string digit]
if none? curr-digit [make error! join error-messages/5 to-string digit] ;; bad digit
number-out: number-out - 1 + index? curr-digit
]
return number-out
] ;; func
;; ====================================
;; internal function: make base string
;; ====================================
make-base-string: func [item [integer! string!]
][
if integer? item
[
if item > length? default-digits
[make error! join error-messages/1 length? default-digits] ;; base too large
item: copy/part default-digits item
]
if not case-sensitive [uppercase item]
if 2 > length? item
[make error! join error-messages/2 length? item] ;; gotta be at least base-2
if all [not case-sensitive (length? item) <> length? unique item] ;; duplicate digits
[make error! join error-messages/3 item]
if all [case-sensitive (length? item) <> length? unique/case item] ;; duplicate
[make error! join error-messages/3 item]
return item
] ;; func
;; =======================================================
;; function to test drive the conversions -- useful if you
;; make changes and want to run some verification tests
;; =======================================================
test-drive: func [/local tests-count
base
number
][
tests-count: 0
forever
[
if 0 = (tests-count // 1000)
[
print [now/time " Tests completed: " tests-count]
]
tests-count: tests-count + 1
;; Test decimal --> base --> decimal
;; ---------------------------------
base: maximum 2 random/secure 36
number: random/secure maximum-decimal
if number <> result: from-base to-base number base base
[
print [tests-count "//1: Failure on base:" base " Number: " number " -- " result]
]
;; Test base --> decimal --> base
;; ------------------------------
base: random/secure copy default-digits
base: copy/part base maximum 2 random/secure length? base
number: copy ""
loop random/secure length? base [append number random/only base]
loop -1 + length? number ;; remove leading "zeroes" from number
[either number/1 = base/1
[number: copy skip number 1]
[break]
]
result: from-base number base
if result < maximum-decimal ;; can't convert back if too large
[result: to-base result base
if result <> number
[
print [tests-count "//2: Failure on base:" base " Number: " number " -- " result]
]
]
] ;; forever
] ;; func
] ;; object
;; ========================
;; view-base-convert object
;; ========================
view-base-convert: make object!
[
;; Data variables for layout
;; -------------------------
from-base:
to-base:
number:
result: none
run: func [
/local
][
unview/all
view/new xx: layout
[
across
banner "Convert between bases"
return
from-base: rotary "xx"
[view-base-convert/convert]
number: field 250 bold white font-color blue
[view-base-convert/convert]
return
to-base: rotary "xx"
[view-base-convert/convert]
result: info 250 bold blue font-color white
return
button "convert!" [view-base-convert/convert]
]
;; Set rotaries
;; ------------
from-base/data: copy []
to-base/data: copy []
for nn 2 36 1
[
append from-base/data join "from " nn
append to-base/data join "to " nn
]
from-base/data: skip from-base/data 8 ;; base 10
to-base/data: skip to-base/data 14 ;; base 16
show from-base
show to-base
;; Fire it up
;; ----------
do-events
return true
]
;; =====================================================
convert: func [
/local
b-from
b-to
base10-value
baseN-value
oops
][
;; ---------------------------------------------
;; We could do this a bit faster by checking
;; if either of the bases is 10, but let's not!
;; ----------------------------------------------
error? oops: try
[
b-from: to-integer skip from-base/text 5
b-to: to-integer skip to-base/text 3
base10-value: base-convert/from-base number/text b-from
baseN-value: base-convert/to-base base10-value b-to
result/text: baseN-value
show result
return true
]
;; Oops -- something has gone wrong
;; --------------------------------
oops: disarm oops
result/text: oops/arg1
show result
return true
]
] ;; object
;; ===============================
;; Start the View/Plugin front end
;; ===============================
view-base-convert/run Notes
|