;; ============================================= ;; Script: base-convert.r ;; downloaded from: www.REBOL.org ;; on: 28-Mar-2024 ;; at: 21:01:47.156129 UTC ;; owner: sunanda [script library member who can ;; update this script] ;; ============================================= REBOL [ 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