This puzzle was originally posted on [Rosetta Code]: http://rosettacode.org/wiki/Heronian_triangles [aspect] hacked up this demo using [sqlite] and some little expression sugar (`let` and `=` below [Let's assign with let]), but a flaky browser leaves me unable to post it there. Probably `let` and `=` should be eliminated before it's posted, as they're hardly good style (though convenient as heck for prototyping!). It previously used [loop] but that's trivial to flatten and doesn't uglify much. I think using [sqlite] is an interesting innovation vs other solutions on RC, as it's not nearly so trivial in other languages, but speaks well to the reporting requirements given in the task. Just look how pretty `main` is! `primitive_triangles` takes rather a long time to run. I'm sure there are further optimisations that can be applied, though the obvious ones are there. I look forward to some of the local math wizards suggesting triples that could be safely ignored. If someone active on RC wants to post this (perhaps cleaned up?), please be my guest, and annotate this page so that it can be cleaned up or used for something more apt. ====== if {[info commands let] eq ""} { #make some math look nicer: proc let {name args} { tailcall ::set $name [uplevel 1 $args] } interp alias {} = {} expr namespace import ::tcl::mathfunc::* ::tcl::mathop::* interp alias {} sum {} + # a simple adaptation of gcd from http://wiki.tcl.tk/2891 proc coprime {a args} { set gcd $a foreach arg $args { while {$arg != 0} { set t $arg let arg = $gcd % $arg set gcd $t if {$gcd == 1} {return true} } } return false } } namespace eval Hero { # Integer square root: returns 0 if n is not a square. proc isqrt? {n} { let r = entier(sqrt($n)) if {$r**2 == $n} { return $r } else { return 0 } } # The square of a triangle's area proc squarea {a b c} { let s = ($a + $b + $c) / 2.0 let sqrA = $s * ($s - $a) * ($s - $b) * ($s - $c) return $sqrA } proc is_heronian {a b c} { isqrt? [squarea $a $b $c] } proc primitive_triangles {db max} { for {set a 1} {$a <= $max} {incr a} { for {set b $a} {$b <= $max} {incr b} { let maxc = min($a+$b,$max) for {set c $b} {$c <= $maxc} {incr c} { set area [is_heronian $a $b $c] if {$area && [coprime $a $b $c]} { set perimiter [expr {$a + $b + $c}] $db eval {insert into herons (area, perimiter, a, b, c) values ($area, $perimiter, $a, $b, $c)} } } } } } } proc main {db} { $db eval {create table herons (area int, perimiter int, a int, b int, c int)} set max 200 puts "Calculating Primitive Heronian triangles up to size length $max" puts \t[time {Hero::primitive_triangles $db $max} 1] puts "Total Primitive Heronian triangles with side lengths <= $max:" $db eval {select count(1) count from herons} { puts "\t$count" } puts "First ten when ordered by increasing area, perimiter, max side length:" $db eval {select * from herons order by area, perimiter, c limit 10} { puts "\t($a, $b, $c) perimiter = $perimiter; area = $area" } puts "All of area 210:" $db eval {select * from herons where area=210 order by area, perimiter, c} { puts "\t($a, $b, $c) perimiter = $perimiter; area = $area" } } package require sqlite3 sqlite3 db :memory: main db ====== [aspect]: it turns out that contemporaneous with my writing this, [dbohdan] posted [http://danyilbohdan.com/blog/data-munging.html%|%a blog on Data Munging] which highlights a similar technique of ''using sqlite to store and then generate multiple views of structured data''. Similar to Danyil's experience, I also started off by keeping data generated by `primitive_triangles` in nested dictionaries. This led to data representation issues dominating both the nested loop and the reports, making their code both ugly and fragile: if I wanted to optimise representation, the loop and potentially ''all'' the reports would need to be touched. Optimising for readability seemed full of bad tradeoffs. Simply adding `package require sqlite3` made these problems go away. Best of all, (tcl-)sqlite's wonderful binding capabilities eliminate the boilerplate (de-)serialisation code this approach would involve in most other languages. <>Geometry | Sqlite | Example | Rosetta Code