Skip to content

Instantly share code, notes, and snippets.

@masyukun
Created May 24, 2015 17:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save masyukun/5edbd21b0f1c1d5f3d82 to your computer and use it in GitHub Desktop.
Save masyukun/5edbd21b0f1c1d5f3d82 to your computer and use it in GitHub Desktop.
cube root estimation
(:
Estimate the cube root of a number.
WARNINGS: 1) Does not work well with many digits due to xs:double size limitation.
2) Does not generate complex numbers.
based on http://www4.wittenberg.edu/academics/mathcomp/bjsdir/CubeRootTalk.pdf
:)
declare function local:sqrt3($number as xs:double) {
let $answer := ()
let $negative := $number lt 0
let $number := if ($negative) then $number * -1 else $number
let $numLength := fn:string-length(fn:replace(fn:string($number), "[^0-9]", ""))
let $hasDecimal := fn:matches(xs:string($number), "\.")
let $beforeDecimal := fn:replace(xs:string($number), "^([0-9]+?)\.[0-9]*$", "$1")
let $afterDecimal := if ($hasDecimal) then fn:replace(xs:string($number), "^[0-9]+?\.([0-9]*)$", "$1") else ()
(: Group numbers by threes, starting before then after the decimal point :)
let $beforeGroups := fn:reverse(
for $i in (1 to xs:integer(math:ceil(fn:string-length($beforeDecimal) div 3)))
return xs:double(fn:replace($beforeDecimal, "^.*?([0-9]{1,3})[0-9]{"||($i - 1) * 3||"}$", "$1"))
)
let $beforeGroups :=
if (fn:count($beforeGroups) eq 1 and $beforeDecimal eq "0") then () else $beforeGroups
let $afterGroups :=
for $i in (1 to xs:integer(math:ceil(fn:string-length($afterDecimal) div 3)))
return xs:double(fn:replace($afterDecimal, "^[0-9]{"||($i - 1) * 3||"}([0-9]{1,3}).*?$", "$1"))
let $numPieces := ($beforeGroups, $afterGroups)
let $newLast :=
let $num := xs:string($numPieces[fn:last()])
return
if (fn:string-length($num) eq 1) then xs:double($num || "00")
else if (fn:string-length($num) eq 2) then xs:double($num || "0")
else ()
let $numPieces := if ($newLast) then ($numPieces[1 to (fn:last() - 1)], $newLast) else $numPieces
let $numPieces := (
$numPieces
,
let $precisionDeficit := ($numLength - fn:count($numPieces))
return
if ($precisionDeficit gt 0) then
for $i in (1 to $precisionDeficit)
return "000"
else ()
)
(: Find the largest cube of a single digit number less than the first group :)
let $largestCube := 1
let $_ :=
for $j in (2 to 9)
return
if (math:pow($j, 3) le $numPieces[1]) then
xdmp:set($largestCube, $j)
else ()
let $_ := xdmp:set($answer, ($answer, xs:string($largestCube)))
let $diff := $numPieces[1] - math:pow($largestCube, 3)
let $diffNext := ()
let $numAnswer := ()
let $combo := ()
(: Find the largest factor of a single digit number less than all the rest of the groups :)
let $_ :=
try {
for $i in (2 to fn:count($numPieces) )
let $numAnswer := xs:double(fn:string-join($answer)) * 10
let $_ := xdmp:set($diffNext, xs:double(xs:string($diff) || $numPieces[$i]))
let $largestFactor := 1
let $_ :=
for $j in (1 to 9)
let $_ := xdmp:set($combo, $j * ((3 * math:pow($numAnswer,2)) + ((3 * $numAnswer) * $j) + math:pow($j,2)))
return
if ($combo le $diffNext) then
xdmp:set($largestFactor, $j)
else ()
let $_ := xdmp:set($answer, ($answer, xs:string($largestFactor)))
return xdmp:set($diff, ($diffNext - ($largestFactor * ((3 * math:pow($numAnswer,2)) + ((3 * $numAnswer) * $largestFactor) + math:pow($largestFactor,2)))) )
} catch ($exception) {}
return
xs:double(fn:string-join(
for $num at $i in $answer
return (
if ($i - 1 eq fn:count($beforeGroups)) then
if (fn:count($beforeGroups) eq 1 and $beforeDecimal eq "0") then "0."
else "."
else ()
,
$num
)
)) * (if ($negative) then -1 else 1)
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment