├── .gitignore ├── CHANGES.md ├── README.md ├── browser-example ├── Scrubbing.min.js ├── demo.css ├── demo.html └── units.edn ├── dev └── user.clj ├── examples ├── browser │ └── frinj.cljs ├── examples-infix.clj ├── examples.clj └── node │ ├── example.cljs │ └── externs.js ├── project.clj ├── resources ├── units.edn └── units.txt ├── src └── frinj │ ├── core.clj │ ├── cross.clj │ ├── feeds.clj │ ├── infix.clj │ ├── jvm.clj │ ├── node.cljs │ ├── ops.clj │ ├── parser.clj │ └── repl.clj └── test └── frinj └── test ├── core_test.clj ├── ops.clj ├── parser.clj ├── pfxnorm.clj └── samplecalc.clj /.gitignore: -------------------------------------------------------------------------------- 1 | out.txt 2 | pom.xml 3 | *jar 4 | lib/* 5 | classes/* 6 | target/* 7 | out/* 8 | .lein-failures 9 | .lein-deps-sum 10 | *.dot 11 | *.png 12 | *~ 13 | browser-example/frinj.js 14 | frinj-node.js 15 | .lein-repl-history 16 | /.nrepl-port 17 | /TAGS 18 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | 0.2.5 2 | === 3 | 4 | Features 5 | --- 6 | * ClojureScript support 7 | * Node.JS example 8 | * frinj.repl convenience ns added 9 | 10 | 0.2.1 11 | === 12 | 13 | Fixes 14 | --- 15 | 16 | * better output from find-units and find-fundamentals 17 | 18 | 0.2.0 19 | === 20 | 21 | Features 22 | --- 23 | 24 | * Breaking namespace changes 25 | 26 | utils -> core, calc + repl -> ops 27 | 28 | * New operators 29 | 30 | find-units, find-fundamentals 31 | 32 | 0.1.5 33 | === 34 | 35 | Fixes 36 | --- 37 | 38 | * Clojure 1.5.1 39 | * Some cleanup 40 | 41 | 0.1.4 42 | === 43 | 44 | Features 45 | --- 46 | 47 | * Infix operator calculations (with $= macro) 48 | 49 | 0.1.3 50 | ==== 51 | 52 | Features 53 | --- 54 | 55 | * Live unit feeds 56 | * frinj.repl namespace 57 | 58 | Fixes 59 | --- 60 | 61 | * bug in fj-equal? 62 | 63 | 0.1.2 64 | ==== 65 | 66 | Fixes 67 | --- 68 | 69 | Renamed frinj.core functions; 70 | 71 | * fj-less -> fj-less? 72 | * fj-greater -> fj-greater? 73 | 74 | 75 | 0.1.1 76 | === 77 | 78 | First public release 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Frinj 2 | 3 | Frinj is a practical unit-of-measure calculator DSL for Clojure / ClojureScript. 4 | 5 | Key features; 6 | 7 | * Tracks units of measure through all calculations allowing you to mix units of measure transparently 8 | * Comes with a huge database of units and conversion factors 9 | * Supports live unit feeds like currency conversion 10 | * Inspired by the Frink project (http://futureboy.us/frinkdocs/) 11 | * Tries to combine Frink's fluent calculation style with idiomatic Clojure 12 | * Supports infix calculation style 13 | 14 | ## Usage 15 | 16 | Add the following line into your Leiningen :dependencies `[frinj "0.2.5"]`. 17 | 18 | ### Clojure 19 | 20 | ```clojure 21 | user=> (use 'frinj.repl) 22 | user=> (frinj-init!) 23 | user=> (override-operators!) 24 | user=> (fj 2000 :Calories :per :day :to :watts) 25 | ;; "1163/12 (approx. 96.91666666666667) [dimensionless]" 26 | ``` 27 | 28 | ### ClojureScript 29 | 30 | There are a few differences when running Frinj on Node / in a browser. See [this wiki page](https://github.com/martintrojer/frinj/wiki/ClojureScript) for details. 31 | 32 | #### Browser demo 33 | 34 | [Visit the browser demo here](http://martintrojer.github.io/frinj-demo/) 35 | 36 | To generate the browser demo, run `lein cljsbuild once`. Then start a web server in the [browser-example](browser-example) directory, and visit it. You should see the examples page. 37 | 38 | For example; 39 | 40 | ``` 41 | $ cd browser-demo 42 | $ python -m SimpleHTTPServer 43 | ``` 44 | 45 | then visit `http://localhost:8000` in a browser and click on the `demo.html` file. 46 | 47 | ## Examples 48 | 49 | * See [example calculations](https://github.com/martintrojer/frinj/blob/master/examples/examples.clj) for ideas... 50 | 51 | * [Video of a frinj talk](http://skillsmatter.com/podcast/home/frinj-having-fun-with-units-3861) 52 | 53 | * Examples using infix calcuation style [here](https://github.com/martintrojer/frinj/blob/master/examples/examples-infix.clj) 54 | 55 | * Live units for currencies, precious metals etc, [some examples](https://github.com/martintrojer/frinj/wiki/Live-Unit-Feeds#examples) 56 | 57 | Finally, check out the [wiki](https://github.com/martintrojer/frinj/wiki) from more info. 58 | 59 | ## License 60 | 61 | Copyright (C) 2013 Martin Trojer 62 | 63 | Distributed under the Eclipse Public License, the same as Clojure. 64 | -------------------------------------------------------------------------------- /browser-example/Scrubbing.min.js: -------------------------------------------------------------------------------- 1 | /*! Scrubbing 02-01-2014 2 | * Fabrice Weinberg 3 | */ 4 | !function(a,b){var c=function(a,b){return a?"string"==typeof a?b[a]:a:void 0},d=function(a,b,d,e,f){a[f]=b?c(b[f],e)||d[f]:d[f]},e=function(a,b,c,d,e){Array.isArray(a)?a.forEach(function(a){a[b](c,d,e)}):a[b](c,d,e)},f={init:function(){},start:function(a){return parseInt(a.node.textContent,10)},change:function(a,b){a.node.textContent=b},end:function(){}},g=function(a,b,c,d){this.name=a,this.prop=b,this.factor=c||1,this.divider=d||1};g.prototype={coordinate:function(a){return a[this.prop]},value:function(a,b){return this.factor*Math.floor((b-a)/this.divider)}};var h=function(a,b,c){return function(d){return new g(a,b,c,d)}},i=h("Horizontal","clientX"),j=h("Vertical","clientY",-1),k=function(){var b,c,d=function(){this.removeEventListener("mousemove",b,!1),c&&c.options.adapter.end(c)},e=function(e){if(e.target.scrubbingElement){e.preventDefault(),c=e.target.scrubbingElement;var f=c.options.adapter.start(c),g=function(a){return c.options.resolver.coordinate(a)},h=g(e);return b=function(a){if(1===a.which){var b=c.options.resolver.value(h,g(a));c.options.adapter.change(c,f+b,b)}else d()},a.addEventListener("mousemove",b,!1),a.addEventListener("mouseup",d,!1),!0}},f=function(){a.addEventListener("mousedown",e,!1),f=function(){}};return{init:function(){f()},remove:function(){}}}(),l=function(a){var b,c,d=function(){a.removeEventListener("touchmove",c,!1),b&&b.options.adapter.end(b)},e=function(d){if(1===d.targetTouches.length){var e=d.targetTouches[0];if(e.target.scrubbingElement){d.preventDefault(),b=e.target.scrubbingElement;var f=b.options.adapter.start(b),g=function(a){return b.options.resolver.coordinate(a)},h=g(e);c=function(a){if(1===a.targetTouches.length){a.preventDefault();var c=b.options.resolver.value(h,g(a.targetTouches[0]));b.options.adapter.change(b,f+c,c)}},a.addEventListener("touchmove",c,!1)}}},f=function(){a.addEventListener("touchcancel",d,!1),a.addEventListener("touchend",d,!1),f=function(){}};return{init:function(a){f(),a.node.addEventListener("touchstart",e,!1)},remove:function(){}}}(a,b),m=function(){return{init:function(a){a.node.addEventListener("mousewheel",function(b){b.preventDefault();var c=a.options.adapter.start(a);a.options.adapter.change(a,c-b.wheelDelta,b.wheelDelta)},!1)},remove:function(){}}}(a),n={driver:[l,k],resolver:i(),adapter:f},o=function(a,b){return this instanceof o?(this.node=a,this.options={},d(this.options,b,n,o.driver,"driver"),d(this.options,b,n,o.resolver,"resolver"),d(this.options,b,n,o.adapter,"adapter"),this.node.dataset.scrubOrientation=this.options.resolver.name,a.scrubbingElement=this,this.options.adapter.init(this),e(this.options.driver,"init",this),void 0):new o(b)};o.prototype={remove:function(){delete node.scrubbingElement,e(this.options.driver,"remove",this)}},o.driver={Mouse:k,MouseWheel:m,Touch:l},o.adapter={BasicNode:f},o.resolver={DefaultHorizontal:i(),DefaultVertical:j(),HorizontalProvider:i,VerticalProvider:j},a.Scrubbing=o}(window); -------------------------------------------------------------------------------- /browser-example/demo.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 1rem auto; 3 | padding: 0 15rem; 4 | position: relative; 5 | max-width: 50rem; 6 | } 7 | 8 | p { 9 | line-height: 1.3em; 10 | } 11 | 12 | h2 a { 13 | font-size: small; 14 | } 15 | 16 | nav { 17 | border-right: 0.1rem solid black; 18 | left: 0; 19 | margin: 3rem 0.5rem 0 0; 20 | padding: 0 0.5rem; 21 | position: absolute; 22 | text-align: right; 23 | width: 13.5rem; 24 | } 25 | 26 | nav ol { 27 | margin: 0; 28 | padding-left: 0; 29 | } 30 | 31 | nav li { 32 | margin-left: 0; 33 | list-style-type: none; 34 | } 35 | 36 | nav li ol { 37 | border-right: 0.1rem solid black; 38 | margin-top: 0.3rem; 39 | padding-right: 0.5rem; 40 | } 41 | 42 | blockquote:before { 43 | content: '\201C'; 44 | } 45 | 46 | blockquote:after { 47 | content: '\201D'; 48 | } 49 | 50 | aside { 51 | border-left: 0.1rem solid black; 52 | font-size: smaller; 53 | margin: 0 0 0 0.5rem; 54 | padding: 0.2rem; 55 | position: absolute; 56 | right: 0; 57 | width: 14rem; 58 | } 59 | 60 | [data-scrub-orientation] { 61 | border-bottom: 1px dashed black; 62 | cursor: col-resize; 63 | } 64 | 65 | output { 66 | font-weight: bold; 67 | } 68 | 69 | code output, output.normal { 70 | font-weight: normal; 71 | } 72 | 73 | .inactive { 74 | background: #f4f4f4; 75 | } 76 | 77 | .result { 78 | font-style: italic; 79 | } 80 | 81 | .hidden { 82 | display: none; 83 | } 84 | 85 | @media (max-width: 50rem) { 86 | body { 87 | padding: 0; 88 | } 89 | 90 | aside, nav { 91 | margin: 0 2rem; 92 | position: relative; 93 | width: auto; 94 | } 95 | 96 | nav, nav li ol { 97 | border: none; 98 | text-align: center; 99 | } 100 | 101 | nav li { 102 | display: inline; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /browser-example/demo.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |48 | Let's say you wanted to fill your bedroom up with water. How much water would it take? 49 | Let's say your room measures 10 feet by 50 | 2 feet wide by 51 | 8 feet high. 52 |
53 | 54 |
55 |
56 | (-> (fj :feet :feet :feet :to :gallons) str)
57 |
58 |
59 |
60 | ;; 7181.298701298701 [dimensionless]
61 |
62 |
65 | It would take approximately gallons to fill it.
66 | How much would that weigh, if you filled it with water? Frinj has the unit
67 | :water
which stands for the density of water.
68 |
71 |
72 | (-> (fj :feet :feet :feet :water :to :pounds) str)
73 |
74 |
75 |
76 | ;; 59930.842153098834 [dimensionless]
77 |
78 |
89 | So it would weigh almost pounds. What if you 90 | knew that your floor could only support 2 tons? 91 | How deep could you fill the room with water? 92 |
93 | 94 |
95 |
96 | (-> (fj_ (fj :tons)
97 | (fj :feet :feet :water))
98 | (to :feet) str)
99 |
100 |
101 |
102 | ;; 0.5339487791320046 [dimensionless]
103 |
104 |
107 | So you could only fill it about feet deep. It'll 108 | be a pretty sad pool party. 109 |
110 | 111 |114 | Let's say you want to define a new unit representing the amount of alcohol in a can of 115 | (quality) 3.2 beer. Keep in mind 116 | that beer is measured by 117 | alcohol/weight, while almost all other liquors (and many beers) are usually measured 118 | in alcohol/volume. The density ratio between water and alcohol is given by: 119 |
120 | 121 |
122 |
123 | (-> (fj :water :per :alcohol) str)
124 |
125 |
126 | (-> (fj :water :per :alcohol) str)
127 |
128 |
129 |
130 | ;; 1.2669453946534905 [dimensionless]
131 |
132 |
140 | Water is thus 1.267 times denser than alcohol. 141 | beer (measured by weight) is 142 | thus actually percent alcohol as measured by 143 | volume. Now let's set that variable in terms of a beer's density of alcohol per volume 144 | so we can compare: 145 |
146 | 147 |
148 |
149 | (add-unit! :beer (fj 12 :floz :percent :water :per :alcohol))
150 |
151 |
154 | Then, you wanted to find out how many beers a big bottle of champagne is equal to: 155 |
156 | 157 |
158 |
159 | (-> (fj :magnum 13.5 :percent :to :beer) str)
160 |
161 |
162 |
163 | ;; 14.07449256252434 [dimensionless]
164 |
165 |
168 | You probably don't want to drink that whole bottle. Now let's say you're mixing Jungle 169 | Juice (using a 1.75 liter bottle 170 | of Everclear (190 proof!)) and Kool-Aid to fill 171 | a 5-gallon bucket (any resemblance to my college 172 | parties is completely intentional). What percent alcohol is that stuff? 173 |
174 | 175 |
176 |
177 | (add-unit! :junglejuice
178 | (fj_ (fj :liter :proof) (fj :gallon)))
179 |
180 |
183 |
184 | (-> (fj :junglejuice :to :percent) str)
185 |
186 |
187 |
188 | ;; 8.783720740908436 [dimensionless]
189 |
190 |
193 | It's really not that strong. About %. 194 | But if you drink 5 cups of that, at 195 | 12 fluid ounces each, how many beers have you had? 196 |
197 | 198 |
199 |
200 | (-> (fj :floz :junglejuice :to :beer) str)
201 |
202 |
203 |
204 | ;; 10.832798094998477 [dimensionless]
205 |
206 |
209 | Maybe that's why people were getting punched in the head. QED. 210 |
211 | 212 |215 | Some more useful calculations, most thanks to the lovely Steve Clymer: 216 |
217 | 218 |
219 | How many cases in a keg? (A keg is a normal-sized keg, what those in the beer industry
220 | would call a "half barrel," or 1/2 :beerbarrel
in Frinj notation. I don't
221 | think they sell full barrels. I've never seen one. It would weigh 258 pounds. A "pony
222 | keg" is a "quarter barrel" or, in Frinj notation, :ponykeg
or
223 | 1/4 :beerbarrel
).
224 |
227 | How many 12 fluid ounce drinks (i.e. cans o' beer) 228 | in a keg? 229 |
230 | 231 |
232 |
233 | (-> (fj :keg) (to :floz) str)
234 |
235 |
236 |
237 | ;; 165.33333333333334 [dimensionless]
238 |
239 |
242 | What is the price in dollars per fluid ounce of alcohol when buying a keg of 243 | 3.2 beer? (Remember that 244 | beer is measured in 245 | alcohol/weight, so we correct by the density ratio of water/alcohol to get alcohol by 246 | volume:) 247 |
248 | 249 |
250 |
251 | (-> (fj_ (fj 60 :dollars)
252 | (fj :keg :percent :water :per :alcohol))
253 | (to :dollars :per :floz) str)
254 |
255 |
256 |
257 | ;; 0.7459362399193548 [dimensionless]
258 |
259 |
262 | A bottle of cheap wine? (A :winebottle
is the standard 750 ml size.)
263 |
266 |
267 | (-> (fj_ (fj 6.99 :dollars)
268 | (fj :winebottle 13 :percent))
269 | (to :dollars :per :floz) str)
270 |
271 |
272 |
273 | ;; 2.1201945809423077 [dimensionless]
274 |
275 |
278 | A big plastic bottle of really bad vodka? 279 |
280 | 281 |
282 |
283 | (-> (fj_ (fj 13.99 :dollars)
284 | (fj 1750 :ml 80 :proof))
285 | (to :dollars :per :floz) str)
286 |
287 |
288 |
289 | ;; 0.59104811225625 [dimensionless]
290 |
291 |
296 | In the movie Independence Day, the alien mother ship is said to be 297 | 500 km in diameter and have a mass 298 | 0.25 that of earth's moon. If 299 | the mother ship were a sphere, what would its density be? (The volume of a sphere is 300 | 4/3 pi radius3) 301 |
302 | 303 |
304 |
305 | (-> (fj_ (fj :moonmass)
306 | (fj 4/3 :pi)
307 | (fj** (fj /2 :km) 3))
308 | (to :water) str)
309 |
310 |
311 | (-> (fj_ (fj_ (fj :moonmass)
312 | (fj 4/3 :pi)
313 | (fj** (fj /2 :km) 3))
314 | 7.87)
315 | (to :water) str)
316 |
317 |
318 | (-> (fj_ (fj_ (fj :moonmass)
319 | (fj 4/3 :pi)
320 | (fj** (fj /2 :km) 3))
321 | 22.4)
322 | (to :water) str)
323 |
324 |
325 |
326 | ;; 280.68438439732194 [dimensionless]
327 |
328 |
331 | This makes the ship times denser than water. 332 | This is times denser than iron and 333 | times denser than any known element! As the ship 334 | is actually more a thin disc than a sphere, it would actually be even denser. Since it 335 | contains lots of empty space, parts of it would have to be much, much denser. 336 |
337 | 338 |339 | If the object is this dense and has such a large mass, what is its surface gravity? 340 | Surface gravity is given by G mass / radius2, where G is the gravitational 341 | constant (which Frinj knows about): 342 |
343 | 344 |
345 |
346 | (-> (fj_ (fj :G :moonmass)
347 | (fj** (fj /2 :km) 2))
348 | (to :gravity) str)
349 |
350 |
351 |
352 | ;; 2.000331549387406 [dimensionless]
353 |
354 |
357 | The surface gravity of the spaceship is thus at least 358 | times earth's gravity — and that's on the 359 | rim where gravity is weakest. It would actually be much higher since it's much, much 360 | flatter than a sphere. I hope you're not the alien that has to go outside and paint 361 | it. 362 |
363 | 364 |372 | You can calculate the day that your company will run out of cash, based on their 373 | financial statements. The following is an example for a real company, based on SEC 374 | filings, which read as the following: 375 |
376 | 377 |December 31, 2000 | June 30, 2001 |
---|---|
$86,481 | $41,601 |
388 |
389 | (add-unit! :burnrate
390 | (fj_
391 | (fj (- 86481 41601) :thousand :dollars)
392 | (fj- (fj :#2001-06-30) (fj :#2000-12-31))))
393 |
394 |
395 |
396 | (-> (to (fj :burnrate) :dollars :per :day) str)
397 |
398 |
399 |
400 | ;; 248012.8943126871 [dimensionless]
401 |
402 |
405 | You can calculate the number of days until the money runs out at this rate: 406 |
407 | 408 |
409 |
410 | (-> (fj_ (fj 41601 :thousand :dollars)
411 | (fj :burnrate))
412 | (to :days) str)
413 |
414 |
415 |
416 | ;; 167.7372465463458 [dimensionless]
417 |
418 |
421 | Using date/time math, starting from the last report date (June 30, 2001) you can find 422 | out the exact date this corresponds to: 423 |
424 | 425 |
426 |
427 | (-> (fj+ (fj :#2001-06-30)
428 | (fj_ (fj 41601 :thousand :dollars)
429 | (fj :burnrate)))
430 | to-date)
431 |
432 |
433 |
434 | ;; Fri Dec 14 16:41:38 GMT 2001
435 |
436 |
441 | At the moment, I'm watching CNN which is discussing some land-mines used in
442 | Afghanistan. They showed a very small mine (about the size of a bran muffin)
443 | containing "51 grams of TNT" and they asked how
444 | much destructive force that carries. Frinj's data file includes how much energy is in
445 | a mass of TNT, specified by the unit :TNT
. How many feet in the air
446 | could grams of TNT throw me,
447 | assuming perfect efficiency, and knowing energy = mass * gravity * height?
448 |
451 |
452 | (-> (fj :grams :TNT) (to 185 :pounds :gravity :feet) str)
453 |
454 |
455 |
456 | ;; 937.7628167428614 [dimensionless]
457 |
458 |
461 | Yikes. feet. But the only difference between 462 | explosives and other combustible fuels is the rapidity of combustion, not in the 463 | quantity of energy. How much gasoline contains the same amount of energy? 464 |
465 | 466 |
467 |
468 | (-> (fj :grams :TNT) (to :teaspoons :gasoline) str)
469 |
470 |
471 |
472 | ;; 1.2903255594255887 [dimensionless]
473 |
474 |
477 | teaspoons? That's not much at all. 478 | You're buying a huge amount of energy when you fill up your car. 479 |
480 | 481 |484 | I need a monocle, but I don't want to pay a lot for it. The eBay monocle auction ends 485 | in 7 hours and 44 minutes... what time do I need to set the alarm clock for to remind 486 | me? 487 |
488 | 489 |
490 |
491 | (-> (fj+ (fj :#now) (fj 7 :hours) (fj 44 :min)) to-date)
492 |
493 |
494 |
495 | ;; Sat Nov 17 14:13:51 MST 2001
496 |
497 |
500 | Epilogue 2001: I didn't get the damned monocle. 501 |
502 | 503 |506 | I can't watch Junkyard Wars (or lots of other television shows) without having 507 | Frinj at my side. This week the team has to float a submerged half-ton Cooper 508 | Mini... how many oil barrels will they need to use as floats? 509 |
510 | 511 |
512 |
513 | (-> (fj :half :ton) (to :barrels :water) str)
514 |
515 |
516 |
517 | ;; 2.853010174211824 [dimensionless]
518 |
519 |
522 | They're trying to hand-pump air down to the barrels, submerged 523 | "2 fathoms" below the water. If the guy can 524 | sustain 40 watts of pumping power, how many 525 | minutes will it take to fill the barrel? 526 |
527 | 528 |
529 |
530 | (-> (fj :fathoms :water :gravity :barrel) (to :watts :minutes) str)
531 |
532 |
533 |
534 | ;; 2.376123072093987 [dimensionless]
535 |
536 |
539 | And how many food Calories (a food Calorie (with a capital "C") equals 1,000 calories 540 | with a small "c") will he burn to fill a barrel? 541 |
542 | 543 |
544 |
545 | (-> (fj :fathoms :water :gravity :barrel :to :Calories) str)
546 |
547 |
548 |
549 | ;; 1.362065389563764 [dimensionless]
550 |
551 |
554 | Better eat a Tic-Tac first. 555 |
556 | 557 |560 | I've seen lots of figures about how much heat the human body produces. You can easily 561 | calculate the upper limit based on how much food you eat a day. Say, you eat 562 | 2,000 Calories a day (again, food 563 | Calories with a capital "C" are equal to 1,000 calories with a little "c"). 564 |
565 | 566 |
567 |
568 | (-> (fj :Calories :per :day :to :watts) str)
569 |
570 |
571 |
572 | ;; 96.91666666666667 [dimensionless]
573 |
574 |
577 | So, your average power and/or heat output is slightly less than a 100-watt bulb. 578 | (Note that your heat is radiated over a much larger area so the temperature is much 579 | lower.) Many days I could be replaced entirely with a 100-watt bulb and have no 580 | discernible effect on the universe. 581 |
582 | 583 |586 | I'm heating up yummy mustard greens in my microwave, but I don't want to overheat 587 | them. I just want to warm them up. If I run my 588 | 1,100 watt microwave for 589 | 30 seconds, how much will their temperature 590 | increase? I have a big 27 ounce (mass) can, and I'll assume that their specific heat 591 | is about the same as that of water (1 calorie/gram/degC): 592 |
593 | 594 |
595 |
596 | (-> (fj_ (fj :W :sec)
597 | (fj :oz 1 :calorie :per :gram :per :degC))
598 | (to :degF) str)
599 |
600 |
601 |
602 | ;; 18.53509035279376 [dimensionless]
603 |
604 |
607 | seconds should raise the 608 | temperature by around degrees Fahrenheit, 609 | assuming perfect transfer of microwave energy to heat. Knowing this, I could see how 610 | efficiently my microwave actually heats food. I could heat a quantity of water and 611 | measure the temperature change in the water. I'll do that sometime if I can find my 612 | good thermometer. 613 |
614 | 615 |618 | Superman is always rescuing school buses that are falling off of cliffs, flying to the 619 | moon, lifting cars over his head, and generally showing off. So why does he still 620 | allow so many accidents to happen? Shouldn't he be able to rescue everybody who has a 621 | Volkswagen parked on their chest? While searching for answers, I found out three 622 | interesting things about Superman: 623 |
624 | 625 |
632 | This is enough information to find some answers. Frinj has units
633 | called :sunpower
(the total power radiated by the sun) and
634 | :sundist
(the distance between the earth and the sun). Thus, we can find
635 | the sun's power that strikes an area at the distance of the earth (knowing the surface
636 | area of a sphere is 4 pi radius2):
637 |
640 |
641 | (add-unit! :earthpower (fj_ (fj :sunpower)
642 | (fj* 4 (fj :pi)
643 | (fj** (fj :sundist) 2))))
644 |
645 |
648 |
649 | (-> (fj :earthpower) str)
650 |
651 |
652 | (-> (fj_ (fj :ft :ft) (fj 6.25 :ft)) (to :in) str)
653 |
654 |
655 |
656 | ;; 1372.5422836662622 kg s^-3 [heat_flux_density]
657 |
658 |
661 | This is about watts per square meter. Superman 662 | is a pretty big guy — let's say the surface area he can present to the sun is 663 | 12 square feet. (This is probably a bit high 664 | — it makes him an average of inches wide 665 | over his entire height.) This allows Superman to charge up at a power of: 666 |
667 | 668 |
669 |
670 | (add-unit! :chargerate (fj :earthpower :ft :ft))
671 |
672 |
675 |
676 | (-> (fj :chargerate :to :watts) str)
677 |
678 |
679 |
680 | ;; 1530.1602081736573 [dimensionless]
681 |
682 |
685 | Superman thus charges up at the rate of 686 | joules/sec or watts. At this 687 | rate, how long does he have to charge up before he can lift a 688 | 2 ton truck over his head? (Knowing energy = 689 | mass * height * gravity) 690 |
691 | 692 |
693 |
694 | (-> (fj :ton 7 :feet :gravity :per :chargerate) (to :sec) str)
695 |
696 |
697 |
698 | ;; 24.80975674997478 [dimensionless]
699 |
700 |
703 | So, charging up for seconds allows him to save 704 | one dumb kid who is acting as a speed bump. So his power is huge but not infinite. He 705 | couldn't sustain a higher rate (unless he showed off less by lifting the car only a 706 | foot or two). Lifting a truck every 30 seconds or so isn't bad, though. He could be 707 | saving a lot more people. So why doesn't he? 708 |
709 | 710 |711 | Well, we've all seen the movie. He's using his super-powers to pick up chicks. 712 | Literally. Superman decides to take a break from saving lives and takes Lois Lane up 713 | in the sky for a joyride. So how long does he have to charge up with solar energy to 714 | fly himself and Lois Lane (let's say she weighs 715 | 135 pounds) up to 716 | 15,000 feet? 717 |
718 | 719 |
720 |
721 | (-> (fj (+ 225 ) :pounds :feet :gravity :per :chargerate) (to :minutes) str)
722 |
723 |
724 | (-> (fj_ (fj (+ 225 ) :pounds :feet :gravity) (fj :ton 7 :feet :gravity)) str)
725 |
726 |
727 |
728 | ;; 79.7456466963475 [dimensionless]
729 |
730 |
733 | So, Superman has to charge up with solar energy for an hour to cart Lois up there. 734 | With the same energy, he could have saved 735 | trapped kids. Keep in mind that Lois could do her part, too. If she left her purse 736 | behind or didn't weigh as much, he'd have more energy left over to save people. If she 737 | would manage to shed just two pounds of cargo weight, Superman would have enough 738 | energy to save another kid's life. 739 |
740 | 741 |742 | Sure, he's a great guy, and, sure, he's the Defender of Truth, Justice, and the 743 | American Way, but can't he find a better use for his super-powers than schlepping some 744 | shiksa into the stratosphere? Shovel my walk, he could, in 3 seconds — and me 745 | with the sciatica. 746 |
747 | 748 |751 | "If you fart continuously for 6 years and 9 months, you'll have enough gas to create 752 | the equivalent of an atomic bomb." Hee hee. Cute. The Hiroshima bomb had a yield of 753 | 12.5 kilotons of TNT, which is a very small bomb by today's standards. How many 754 | horsepower would that be? 755 |
756 | 757 |
758 |
759 | (-> (fj_ (fj 12.5 :kilotons :TNT)
760 | (fj+ (fj 6 :years) (fj 9 :months)))
761 | (to :horsepower) str)
762 |
763 |
764 |
765 | ;; 329.26013859711395 [dimensionless]
766 |
767 |
770 | Can you produce a 329-horsepower blowtorch of a fart? I doubt it. That's the power 771 | produced by a Corvette engine running just at its melting point. A one-second fart 772 | with that much power copuld blow me 1,000 feet straight up. To produce that kind of 773 | energy, how much food would you have to eat a day? 774 |
775 | 776 |
777 |
778 | (-> (fj_ (fj 12.5 :kilotons :TNT)
779 | (fj+ (fj 6 :years) (fj 9 :months)))
780 | (to :Calories :per :day) str)
781 |
782 |
783 |
784 | ;; 5066811.55086559 [dimensionless]
785 |
786 |
789 | Ummm... can you eat over 5 million Calories a day? (Again, note that these are food 790 | Calories with a capital 'c' which are equal to 1,000 calories with a small 'c'.) If 791 | you were a perfect fart factory, converting food energy into farts with 100% 792 | efficiency, and ate a normal 2,000 Calories/day, how many years would it really take? 793 |
794 | 795 |
796 |
797 | (-> (fj_ (fj 12.5 :kilotons :TNT)
798 | (fj 2000 :Calories :per :day))
799 | (to :years) str)
800 |
801 |
802 |
803 | ;; 17100.488984171363 [dimensionless]
804 |
805 |
808 | 17,000 years is still a huge underestimate; I don't know how much of your energy 809 | actually goes into fart production. Oh well. To continue the calculations, let's guess 810 | your butthole has a diameter of 1 inch (no, you go measure it). Let's also guess that 811 | the gas you actually produce in a fart is only 1/10 as combustible as pure natural 812 | gas. What would be the velocity of the gas coming out? 813 |
814 | 815 |
816 |
817 | (-> (fj_ (fj 12.5 :kilotons :TNT)
818 | (fj :natural_gas)
819 | (fj+ (fj 6 :years) (fj 9 :months))
820 | (fj* (fj :pi) (fj** (fj 0.5 :in) 2)))
821 | (fj* 10)
822 | (to :mph) str)
823 |
824 |
825 |
826 | ;; 281.5904462031102 [dimensionless]
827 |
828 |
831 | Nobody likes sitting next to a 280-mile-per-hour fart-machine. Lesson: Even the 832 | smallest atomic bombs are really unbelievably powerful and whoever originally 833 | calculated this isn't any fun to be around if they really fart that much. 834 |
835 | 836 |839 | What do you think are the most flammable gases in a fart? Most people think it's 840 | methane, but I found some medical studies that disprove this. Most people hardly have 841 | any methane in their intestines. For example, one study stated that only 4 out of 11 842 | people had any detectable methane in their intestines! So what's the rest of the gas? 843 |
844 | 845 |Gas | Percent by volume |
---|---|
Nitrogen | 64% |
Carbon Dioxide | 14% |
Hydrogen | 19% |
Methane | 3.2% |
Oxygen | 0.7% |
859 | These studies also note that the average person has 100 milliliters of gas is present 860 | in their intestinal tract at any given time. The average person expels 400-2,000 ml of 861 | gas daily (and I'm not talking about through the mouth and nose). 862 |
863 | 864 |865 | Okay, that's almost enough information to figure out available fart energy. Now all we 866 | need to know is the energy of combustion of the flammable gases. Of the above, only 867 | hydrogen and methane are readily combustible. Looking up their energies of combustion: 868 |
869 | 870 |Gas | Energy of combustion in kJ/mol |
---|---|
Hydrogen (H2) | 295.8 |
Methan (CH4) | 890.8 |
881 | Okay, that's plenty enough information to find out how much energy is released in a 882 | day of farting! Say you're on the farty end of the scale, and you produce 883 | 2,000 ml of gas each day. 884 |
885 | 886 |
887 | Note that the energies above are given in kJ/mol, but we have volumes in milliliters.
888 | As you may have learned in chemistry class, a mole of any gas at standard temperature
889 | and pressure takes up the same volume. Frinj knows this as :molarvolume
.
890 |
893 | The total energy in the hydrogen (keeping in mind that hydrogen makes up 19% of the 894 | ml volume) is given by: 895 |
896 | 897 |
898 |
899 | (add-unit! :h2energy (fj :ml :per :molarvolume 19 :percent 285.8 :kJ :per :mol))
900 |
901 |
902 | (-> (fj :h2energy :to :joules) str)
903 |
904 |
907 | The combustible hydrogen thus produces joules 908 | (per day). Now, for the methane, which makes a smaller percentage, but releases more 909 | energy per mole: 910 |
911 | 912 |
913 |
914 | (add-unit! :methaneenergy (fj :ml :per :molarvolume 3.2 :percent 890.8 :kJ :per :mol))
915 |
916 |
917 | (-> (fj :methaneenergy :to :joules) str)
918 |
919 |
922 | The energy in the combustible methane is thus 923 | joules (per day), about half the energy produced from the hydrogen. Thus, the grand 924 | total of energy produced by combustible farts by a farty person in a day, in food 925 | Calories (with a capital C, remember — these are what a physicist would call a 926 | kilocalorie) is: 927 |
928 | 929 |
930 |
931 | (-> (fj+ (fj :methaneenergy) (fj :h2energy)) (to :Calories) str)
932 |
933 |
934 | (-> (fj :h2energy :to :Calories) str)
935 |
936 |
937 | (-> (fj :methaneenergy :to :Calories) str)
938 |
939 |
940 |
941 | ;; 1.7648151669360923 [dimensionless]
942 |
943 |
946 | Which gives a result of about 947 | Calories/day of energy available from burning your farts. (About 948 | Calories from hydrogen, and about 949 | Calories from methane.) This is 950 | out of the Calories that an average person 951 | eats a day. Or, one part in about 1,133 of the energy in the food you eat is available 952 | in fart energy, (again, for a gassy person). 953 |
954 | 955 |956 | Thus, a good estimate to the problem stated above is that a real (gassy) human would 957 | need to save their farts for: 958 |
959 | 960 |
961 |
962 | (-> (fj_ (fj 12.5 :kilotons :TNT)
963 | (fj_ (fj+ (fj :methaneenergy) (fj :h2energy)) (fj :day)))
964 | (to :days) str)
965 |
966 |
967 |
968 | ;; 7.078157887380842E9 [dimensionless]
969 |
970 |
973 | or about 7 billion years to make the equivalent of the energy in a (small) atomic 974 | bomb! 975 |
976 | 977 |982 | The cruise liner, Queen Elizabeth II, moves only six inches for each gallon of diesel 983 | that it burns. 984 |985 | 986 |
987 | From a page of facts about the 988 | QE2, we find that the ship consumes 18 tons 989 | of fuel per hour at a service speed of 28 knots. 990 | By legislation in many areas, diesel fuel must have a density no higher than 991 | 0.85 kg/liter (if it were watered down, it would be higher). 992 |
993 | 994 |
995 |
996 | (-> (fj_ (fj :tons)
997 | (fj :hour)
998 | (fj :knot)
999 | (fj 0.85 :kg :per :liter))
1000 | (to :feet :per :gallon) str)
1001 |
1002 |
1003 | (-> (fj_ (fj :tons)
1004 | (fj :hour)
1005 | (fj :knot)
1006 | (fj 0.85 :kg :per :liter))
1007 | (to :gallon :per :mile) str)
1008 |
1009 |
1010 | (-> (fj_ (fj :tons)
1011 | (fj :hour)
1012 | (fj :knot)
1013 | (fj 0.85 :kg :per :liter)
1014 | 2)
1015 | (to :feet :per :gallon) str)
1016 |
1017 |
1018 |
1019 | ;; 33.52338503156235 [dimensionless]
1020 |
1021 |
1024 | They're very, very wrong. It actually travels about 1025 | feet per gallon, or 1026 | gallons/mile. They're only off by a 1027 | factor of . Still not great gas mileage, though. 1028 |
1029 | 1030 |1033 | Pound for pound, hamburgers cost more than new cars. 1034 |1035 | 1036 |
1037 | Let's see... let's try with a medium-expensive, light car. A 2001 Corvette Z06 weighs 1038 | 3,115 pounds and costs 1039 | $48,055. 1040 |
1041 | 1042 |
1043 |
1044 | (-> (fj_ (fj :dollars)
1045 | (fj :lb))
1046 | (to :dollars :per :lb) str)
1047 |
1048 |
1049 |
1050 | ;; 15.42696629213483 [dimensionless]
1051 |
1052 |
1055 | I know I don't pay $/lb for hamburger. 1056 |
1057 | 1058 |1061 | So you want to build an ark, do you? And not an Ark of the Covenant, but the boat. 1062 | How bad was that flood? 1063 |
1064 | 1065 |1066 | The bible is also quite precise in its measurement of the flood. Genesis 7:19-20 1067 | states that "And the waters prevailed exceedingly upon the earth; and all the 1068 | mountains, that were under the whole heaven, were covered. Fifteen cubits upward did 1069 | the waters prevail; and the mountains were covered." 1070 |
1071 | 1072 |1073 | Okay, so the highest mountains of the earth were covered, plus an extra 1074 | 15 cubits (approx 1075 | feet) for good measure. The current measurements 1076 | for highest mountain is Mt. Everest at 29,030.8 feet (according to the highly dubious 1077 | and utterly non-trustable 2002 Guinness Book of World Records.) I know that Everest is 1078 | growing slowly, (best estimates are 2.4 inches/year) so we'll discount for that. 1079 |
1080 | 1081 |
1082 |
1083 | (add-unit! :depth (fj+ (fj 29030 :feet)
1084 | (fj :biblicalcubits)
1085 | (fj -2.4 :inches :per :year 4000 :years)))
1086 |
1087 |
1088 | (-> (fj :biblicalcubits :to :feet) str)
1089 |
1090 |
1091 | (-> (fj :depth :to :feet) str)
1092 |
1093 |
1096 | About feet of water. This was deposited over 1097 | 40 days. The rainfall was thus: 1098 |
1099 | 1100 |
1101 |
1102 | (-> (fj_ (fj :depth) (fj :days))
1103 | (to :feet :per :hour) str)
1104 |
1105 |
1106 |
1107 | ;; 29.434635416666666 [dimensionless]
1108 |
1109 |
1112 | About feet/hour. A good rain around here is about 1113 | an inch an hour. The very rainiest places on earth like Cherrapunji get about this 1114 | much rain in a year. (I'm campaigning Colorado farmers to sin a bit more...) 1115 |
1116 | 1117 |1120 | Everyone knows Einstein's E=mc2 equation, but to apply it is often very 1121 | difficult because the units come out so strange. Let's see, I have mass in pounds, and 1122 | the speed of light is 186,282 miles/second... ummm... what does that come out to? In 1123 | Frinj the calculation becomes transparently simple. 1124 |
1125 | 1126 |1127 | If you took the matter in a teaspoon of water, and converted that to energy, how many gallons 1128 | of gasoline would that equal? 1129 |
1130 | 1131 |
1132 |
1133 | (-> (fj :teaspoon :water :c :c) (to :gallons :gasoline) str)
1134 |
1135 |
1136 |
1137 | ;; 3164209.862836101 [dimensionless]
1138 |
1139 |
1142 | Unbelievable. The energy in a teaspoon of water, if we could extract it, is equal to 1143 | burning more than 3 million gallons of gasoline. 1144 |
1145 | 1146 | -------------------------------------------------------------------------------- /browser-example/units.edn: -------------------------------------------------------------------------------- 1 | ../resources/units.edn -------------------------------------------------------------------------------- /dev/user.clj: -------------------------------------------------------------------------------- 1 | (ns user 2 | "Tools for interactive development with the REPL. This file should 3 | not be included in a production build of the application." 4 | (:require 5 | [clojure.java.io :as io] 6 | [clojure.java.javadoc :refer (javadoc)] 7 | [clojure.pprint :refer (pprint print-table)] 8 | [clojure.reflect :refer (reflect)] 9 | [clojure.repl :refer (apropos dir doc find-doc pst source)] 10 | [clojure.set :as set] 11 | [clojure.string :as str] 12 | [clojure.test :as test] 13 | [clojure.tools.trace :refer (trace deftrace trace-forms trace-ns trace-vars)] 14 | [clojure.tools.namespace.repl :refer (refresh refresh-all)]) 15 | (:use [frinj.core :exclude [add-unit! zero one]] 16 | [frinj.ops] 17 | [frinj.jvm] 18 | [frinj.feeds])) 19 | 20 | (def system 21 | "A Var containing an object representing the application under 22 | development." 23 | nil) 24 | 25 | (defn init 26 | "Creates and initializes the system under development in the Var 27 | #'system." 28 | [] 29 | ) 30 | 31 | (defn start 32 | "Starts the system running, updates the Var #'system." 33 | [] 34 | (frinj-init!) 35 | ) 36 | 37 | (defn stop 38 | "Stops the system if it is currently running, updates the Var 39 | #'system." 40 | [] 41 | (shutdown-feeds) 42 | ) 43 | 44 | (defn go 45 | "Initializes and starts the system running." 46 | [] 47 | (init) 48 | (start) 49 | :ready) 50 | 51 | (defn reset 52 | "Stops the system, reloads modified source files, and restarts it." 53 | [] 54 | (stop) 55 | (refresh :after 'user/go)) 56 | 57 | (defn run-all-my-test [] 58 | (reset) 59 | (test/run-all-tests #"frinj\.*test.*")) 60 | -------------------------------------------------------------------------------- /examples/browser/frinj.cljs: -------------------------------------------------------------------------------- 1 | (ns frinj.browser.example 2 | (:require [frinj.core :as core] 3 | [frinj.ops :as ops] 4 | [cljs.reader :as reader] 5 | [goog.net.XhrIo :as xhr] 6 | [goog.i18n.NumberFormat])) 7 | 8 | (enable-console-print!) 9 | 10 | ;; This operates on a page like demo.html, adding scrubbing number inputs and 11 | ;; automatically-updating outputs. To add further examples, follow these rules: 12 | ;; 13 | ;; * Elements with the `data-key` attribute are scrubbable. 14 | ;; * * These elements may also have a `data-step` attribute, indicating the scrubbing step 15 | ;; size. This defaults to 1. 16 | ;; * * If the step size is less than 1, the output elements will be shown to the same 17 | ;; number of decimal places as the step attribute. 18 | ;; 19 | ;; * Elements whose `for` attribute matches the `data-key` attribute of a scrubbable 20 | ;; element will be updated in real time. 21 | ;; * * Output elements with a `data-dp` attribute will be formatted to that number of 22 | ;; decimal places. 23 | ;; * * If the output element's parent is a `code` element, then it is assumed to be a 24 | ;; runnable example. The output will not be formatted with thousands separators, but 25 | ;; will still use the correct number of decimal places. 26 | ;; 27 | ;; * Elements whose `for` attributes matches the `id` attribute of a code example will be 28 | ;; updated when the example's result changes. In other respects, these are the same as 29 | ;; above. 30 | ;; 31 | ;; * Runnable examples are `code` elements wrapped under a common ancestor. 32 | ;; * * If the example has a sibling element with the `result` class, its output will be 33 | ;; updated there, as well as in any output elements. If the result element also has a 34 | ;; `static` class, it will not be updated. 35 | ;; * * If the example has a `data-next` attribute, the example with that ID will run after 36 | ;; the example. (These can be chained.) 37 | ;; * * All examples run when their inputs change. Examples with the class `unit` are also 38 | ;; run on page load, so the units are added if other examples need them. 39 | ;; 40 | 41 | ;; Needed because ClojureScript doesn't support eval. 42 | (def ops-map 43 | {'fj ops/fj 44 | 'fj+ ops/fj+ 45 | 'fj_ ops/fj_ 46 | 'fj* ops/fj* 47 | 'fj** ops/fj** 48 | 'to ops/to 49 | 'add-unit! ops/add-unit! 50 | '- - 51 | '+ +}) 52 | 53 | ;; Allow iterating over DOM elements. 54 | (extend-type js/NodeList ISeqable (-seq [array] (array-seq array 0))) 55 | 56 | (defn- noop []) 57 | (defn- log [s] (println s) s) 58 | (defn- get-data [element key] (aget element "dataset" (name key))) 59 | 60 | (defn- format-result [number dp] 61 | (let [dp (or dp (if (> number 1) 0 2)) 62 | dp-suffix (when (> dp 0) (apply str (cons "." (repeat dp "0")))) 63 | formatter (js/goog.i18n.NumberFormat. (str "#,##0" dp-suffix) "goog.i18n.NumberFormatSymbols_en")] 64 | (.format formatter number))) 65 | 66 | ;; If the text content of the element is a threaded form, ending in `str`, then only take 67 | ;; the middle part. Otherwise, run the whole thing. 68 | ;; 69 | (defn- runnable-part [element] 70 | (let [sexpr (reader/read-string (.-textContent element)) 71 | first (first sexpr) 72 | last (last sexpr) 73 | mid (-> sexpr rest butlast)] 74 | (if (and (= '-> first) (= 'str last)) mid sexpr))) 75 | 76 | ;; ClojureScript doesn't support eval! 77 | ;; 78 | (defn ghetto-eval [sexpr] 79 | (let [[fn & args] (map #(if (list? %) (ghetto-eval %) %) sexpr)] 80 | (apply (or (ops-map fn) fn) args))) 81 | 82 | ;; ClojureScript doesn't support eval!!!!! 83 | ;; 84 | (defn ghetto-thread [value fns] 85 | (if fns 86 | (let [[[fn & args] & next] fns] 87 | (ghetto-thread (ghetto-eval (cons fn (cons value args))) next)) 88 | value)) 89 | 90 | (defn- run-example! 91 | ([element] (run-example! element false)) 92 | ([element skip-next] 93 | (when-let [[data & sexprs] (runnable-part element)] 94 | (let [result (if (coll? data) (ghetto-thread (ghetto-eval data) sexprs) 95 | (ghetto-eval (cons data sexprs))) 96 | id (.-id element) 97 | visible? (not (.contains (.-classList element) "hidden")) 98 | output-element (js/document.querySelector (str "[for=" id "]")) 99 | result-element (.querySelector (.-parentNode element) ".result:not(.static)") 100 | next-example-id (get-data element :next)] 101 | (when output-element 102 | (let [dp (get-data output-element :dp)] 103 | (set! (.-textContent output-element) (format-result (:v result) dp)))) 104 | (when (and visible? result-element) 105 | (set! (.-textContent result-element) (str ";; " (.toString result)))) 106 | (when (and next-example-id (not skip-next)) 107 | (run-example! (js/document.querySelector (str "#" next-example-id)))))))) 108 | 109 | (defn- scrubbing-adapter [element] 110 | (let [key (get-data element :key) 111 | step-attr (or (get-data element :step) "1") 112 | step-dp (let [[before after] (.split step-attr ".")] 113 | (if after (count after) 0)) 114 | step (js/parseFloat step-attr) 115 | result-elements (js/document.querySelectorAll (str "[for=" key "]"))] 116 | (js-obj 117 | "init" noop 118 | "start" (fn [element] 119 | (js/parseFloat (.replace (.-textContent (.-node element)) "," ""))) 120 | "end" noop 121 | "change" (fn [scrubbing-element value delta] 122 | (let [calculated-value (+ (- value delta) (* delta step)) 123 | bounded-value (if (< calculated-value step) step calculated-value) 124 | bounded-value-str (format-result bounded-value step-dp)] 125 | (set! (.-textContent (.-node scrubbing-element)) bounded-value-str) 126 | (doseq [result-element result-elements] 127 | (let [parent (.-parentNode result-element)] 128 | (if (= "CODE" (.-nodeName parent)) 129 | (do 130 | (set! (.-textContent result-element) (.toFixed bounded-value step-dp)) 131 | (run-example! parent)) 132 | (set! (.-textContent result-element) bounded-value-str))))))))) 133 | 134 | (defn- add-scrubbers! [] 135 | (doseq [param (js/document.querySelectorAll "[data-key]")] 136 | (js/Scrubbing. param #js{"adapter" (scrubbing-adapter param) 137 | "driver" #js[js/Scrubbing.driver.Mouse js/Scrubbing.driver.Touch]}))) 138 | 139 | (defn- run-unit-examples! [] 140 | (doseq [unit-example (js/document.querySelectorAll ".example .unit")] 141 | (run-example! unit-example :skip-next))) 142 | 143 | (defn- handler [event] 144 | (let [body (-> event .-target .getResponseText) 145 | state-edn (reader/read-string (str body))] 146 | (core/restore-state! state-edn) 147 | (add-scrubbers!) 148 | (run-unit-examples!))) 149 | 150 | (xhr/send "units.edn" handler "GET") 151 | -------------------------------------------------------------------------------- /examples/examples-infix.clj: -------------------------------------------------------------------------------- 1 | ;; The use and distribution terms for this software are covered by the 2 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 3 | ;; which can be found in the file epl-v10.html at the root of this distribution. 4 | ;; By using this software in any fashion, you are agreeing to be bound by 5 | ;; the terms of this license. 6 | ;; You must not remove this notice, or any other, from this software. 7 | 8 | ;; All these examples are taken from Frink's genius Sample Calculation page 9 | ;; http://futureboy.us/frinkdocs/#SampleCalculations 10 | ;; Alan Eliasen (@aeliasen) deserves all the praise 11 | 12 | (ns frinj.examples 13 | (:use [frinj.repl])) 14 | 15 | ;; setup the environment 16 | 17 | (frinj-init!) 18 | 19 | ;; ================================================================= 20 | ;; Mass and Volume 21 | 22 | ;; Let's say you wanted to fill your bedroom up with water.How much water would it take? 23 | ;; Let's say your room measures 10 feet by 12 feet wide by 8 feet high. 24 | 25 | (-> (fj 10 :feet 12 :feet 8 :feet :to :gallons) str) 26 | ;; "552960/77 (approx. 7181.298701298701) [dimensionless]" 27 | 28 | ;; It would take approximately 7181 gallons to fill it. Note that you get both an exact 29 | ;; fraction and an approximation. How much would that weigh, if you filled it with water? 30 | ;; Frinj has the unit "water" which stands for the density of water. 31 | 32 | (-> (fj 10 :feet 12 :feet 8 :feet :water :to :pounds) str) 33 | ;; "2718417272832/45359237 (approx. 59930.84215309883) [dimensionless]" 34 | 35 | ;; So it would weigh almost 60,000 pounds. What if you knew that your floor could only 36 | ;; support 2 tons? How deep could you fill the room with water? 37 | 38 | (-> ($= (fj 2 :tons) / (fj 10 :feet 12 :feet :water)) 39 | (to :feet) str) 40 | ;; "5669904625/10618817472 (approx. 0.5339487791320047) [dimensionless]" 41 | 42 | ;; So you could only fill it about 0.53 feet deep. It'll be a pretty sad pool party. 43 | 44 | ;; ================================================================= 45 | ;; Liquor 46 | 47 | ;; Let's say you want to define a new unit representing the amount of alcohol in a can 48 | ;; of (quality) 3.2 beer. Keep in mind that 3.2 beer is measured by alcohol/weight, 49 | ;; while almost all other liquors (and many beers) are usually measured in alcohol/volume. 50 | ;; The density ratio between water and alcohol is given by: 51 | 52 | (-> (fj :water :per :alcohol) str) 53 | ;; "1.2669453946534905 [dimensionless]" 54 | 55 | ;; Water is thus 1.267 times denser than alcohol. 3.2 beer (measured by weight) is thus 56 | ;; actually 4.0 percent alcohol as measured by volume. Now let's set that variable in terms 57 | ;; of a beer's density of alcohol per volume so we can compare: 58 | 59 | (add-unit! :beer (fj 12 :floz 3.2 :percent :water :per :alcohol)) 60 | 61 | ;; Then, you wanted to find out how many beers a big bottle of champagne is equal to: 62 | 63 | (-> (fj :magnum 13.5 :percent :to :beer) str) 64 | ;; "14.07449256252434 [dimensionless]" 65 | 66 | ;; You probably don't want to drink that whole bottle. Now let's say you're mixing Jungle 67 | ;; Juice (using a 1.75 liter bottle of Everclear (190 proof!)) and Kool-Aid to fill a 68 | ;; 5-gallon bucket (any resemblance to my college parties is completely intentional.) 69 | ;; What percent alcohol is that stuff? 70 | 71 | (add-unit! :junglejuice ($= (fj 1.75 :liter 190 :proof) / (fj 5 :gallon))) 72 | 73 | (-> (fj :junglejuice :to :percent) str) 74 | ;; "8.783720740908436 [dimensionless]" 75 | 76 | ;; It's really not that strong. About 8.8%. But if you drink 5 cups of that, 77 | ;; at 12 fluid ounces each, how many beers have you had? 78 | 79 | (-> (fj 5 12 :floz :junglejuice :to :beer) str) 80 | ;; "10.832798094998477 [dimensionless]" 81 | 82 | ;; Maybe that's why people were getting punched in the head. QED. 83 | 84 | ;; ================================================================= 85 | ;; More Liquor 86 | 87 | ;; How many cases in a keg? (A keg is a normal-sized keg, what those in the beer 88 | ;; industry would call a "half barrel," or 1/2 beerbarrel in Frinj notation. 89 | ;; I don't think they sell full barrels. I've never seen one. It would weigh 258 pounds. 90 | ;; A "pony keg" is a "quarter barrel" or, in Frinj notation, ponykeg or 1/4 beerbarrel) 91 | 92 | (-> (fj :keg :to :case) str) 93 | ;; "62/9 (approx. 6.888888888888889) [dimensionless]" 94 | 95 | ;; How many 12 fluid ounce drinks (i.e. cans o' beer) in a keg? 96 | 97 | (-> (fj :keg) (to 12 :floz) str) 98 | ;; "496/3 (approx. 165.3333333333333) [dimensionless]" 99 | 100 | ;; What is the price in dollars per fluid ounce of alcohol when buying a keg of 3.2 beer? 101 | ;; (Remember that 3.2 beer is measured in alcohol/weight, so we correct by the density 102 | ;; ratio of water/alcohol to get alcohol by volume: 103 | 104 | (-> ($= (fj 60 :dollars) / (fj :keg 3.2 :percent :water :per :alcohol)) 105 | (to :dollars :per :floz) str) 106 | ;; "0.7459362399193548 [dimensionless]" 107 | 108 | ;; A bottle of cheap wine? (A "winebottle" is the standard 750 ml size.) 109 | 110 | (-> ($= (fj 6.99 :dollars) / (fj :winebottle 13 :percent)) 111 | (to :dollars :per :floz) str) 112 | ;; "2.1201945809423077 [dimensionless]" 113 | 114 | ;; A big plastic bottle of really bad vodka? 115 | 116 | (-> ($= (fj 13.99 :dollars) / (fj 1750 :ml 80 :proof)) 117 | (to :dollars :per :floz) str) 118 | ;; "0.59104811225625 [dimensionless]" 119 | 120 | ;; ================================================================= 121 | ;; Movie magic 122 | 123 | ;; In the movie Independence Day, the alien mother ship is said to be 500 km in diameter 124 | ;; and have a mass 1/4 that of earth's moon. If the mother ship were a sphere, what would 125 | ;; its density be? (The volume of a sphere is 4/3 pi radius3) 126 | 127 | (-> ($= (fj 1/4 :moonmass) / ($= (fj 4/3 :pi) * (fj 500/2 :km) ** 3)) 128 | (to :water) str) 129 | ;; "280.68438439732194 [dimensionless]" 130 | 131 | ;; This makes the ship 280 times denser than water. This is 36 times denser than iron and 132 | ;; more than 12 times denser than any known element! As the ship is actually more a thin disc 133 | ;; than a sphere, it would actually be even denser. Since it contains lots of empty space, 134 | ;; parts of it would have to be much, much denser. 135 | 136 | ;; If the object is this dense and has such a large mass, what is its surface gravity? 137 | ;; Surface gravity is given by G mass / radius2, where G is the gravitational constant 138 | ;; (which Frinj knows about): 139 | 140 | (-> ($= (fj :G 1/4 :moonmass) / (fj 500/2 :km) ** 2) 141 | (to :gravity) str) 142 | ;; "2.000331549387406 [dimensionless]" 143 | 144 | ;; The surface gravity of the spaceship is thus at least twice earth's gravity-- and that's 145 | ;; on the rim where gravity is weakest. It would actually be much higher since it's much, 146 | ;; much flatter than a sphere. I hope you're not the alien that has to go outside and paint it. 147 | 148 | ;; ================================================================= 149 | ;; Fiscal Calculations 150 | 151 | ;; You can calculate the day that your company will run out of cash, based on their financial 152 | ;; statements. The following is an example for a real company, based on SEC filings, which 153 | ;; read as the following: 154 | ;; Cash and Cash Equivalents (in thousands) 155 | ;; December 31, 2000 June 30, 2001 156 | ;; $86,481 $41,601 157 | 158 | (add-unit! :burnrate 159 | ($= (fj (- 86481 41601) :thousand :dollars) / 160 | ($= (fj :#2001-06-30) - (fj :#2000-12-31)))) 161 | 162 | (-> (to (fj :burnrate) :dollars :per :day) str) 163 | ;; "1077120000/4343 (approx. 248012.8943126871) [dimensionless]" 164 | 165 | ;; You can calculate the number of days until the money runs out at this rate: 166 | 167 | (-> ($= (fj 41601 :thousand :dollars) / (fj :burnrate)) 168 | (to :days) str) 169 | ;; "60224381/359040 (approx. 167.7372465463458) [dimensionless]" 170 | 171 | ;; Using date/time math, starting from the last report date (June 30, 2001) you can 172 | ;; find out the exact date this corresponds to: 173 | 174 | (-> ($= (fj :#2001-06-30) + (fj 41601 :thousand :dollars) / (fj :burnrate)) 175 | to-date) 176 | ;; "Fri Dec 14 16:41:38 GMT 2001" 177 | 178 | ;; ================================================================= 179 | ;; Ouch! 180 | 181 | ;; At the moment, I'm watching CNN which is discussing some land-mines used in Afghanistan. 182 | ;; They showed a very small mine (about the size of a bran muffin) containing "51 grams of TNT" 183 | ;; and they asked how much destructive force that carries. Frinj's data file includes how much 184 | ;; energy is in a mass of TNT, specified by the unit "TNT". How many feet in the air could 51 185 | ;; grams of TNT throw me, assuming perfect efficiency, and knowing energy = mass * gravity * height? 186 | 187 | (-> (fj 51 :grams :TNT) (to 185 :pounds :gravity :feet) str) 188 | ;; "(approx. 937.7628167428614) [dimensionless]" 189 | 190 | ;; Yikes. 937 feet. But the only difference between explosives and other combustible fuels 191 | ;; is the rapidity of combustion, not in the quantity of energy. How much gasoline contains 192 | ;; the same amount of energy? 193 | 194 | (-> (fj 51 :grams :TNT) (to :teaspoons :gasoline) str) 195 | ;; "1.2903255594255887 [dimensionless]" 196 | 197 | ;; 1.29 teaspoons? That's not much at all. You're buying a huge amount of energy when you fill 198 | ;; up your car. 199 | 200 | ;; ================================================================= 201 | ;; Sniping eBay Auctions 202 | 203 | ;; I need a monocle, but I don't want to pay a lot for it. The eBay monocle auction ends in 204 | ;; 7 hours and 44 minutes... what time do I need to set the alarm clock for to remind me? 205 | 206 | (-> ($= (fj :#now) + (fj 7 :hours) + (fj 44 :min)) to-date) 207 | 208 | ;; ================================================================= 209 | ;; Junkyard Wars 210 | 211 | ;; I can't watch Junkyard Wars (or lots of other television shows) without having Frinj at 212 | ;; my side. This week the team has to float a submerged half-ton Cooper Mini... how many oil 213 | ;; barrels will they need to use as floats? 214 | 215 | (-> (fj :half :ton) (to :barrels :water) str) 216 | ;; "368175625/129048129 (approx. 2.853010174211824) [dimensionless]" 217 | 218 | ;; They're trying to hand-pump air down to the barrels, submerged "2 fathoms" below the water. 219 | ;; If the guy can sustain 40 watts of pumping power, how many minutes will it take to fill 220 | ;; the barrel? 221 | 222 | (-> (fj 2 :fathoms :water :gravity :barrel) (to 40 :watts :minutes) str) 223 | ;; "46037384521821/19375000000000 (approx. 2.376123072093987) [dimensionless]" 224 | 225 | ;; And how many food Calories (a food Calorie (with a capital 'C') equals 1000 calories with 226 | ;; a small 'c') will he burn to fill a barrel? 227 | 228 | (-> (fj 2 :fathoms :water :gravity :barrel :to :Calories) str) 229 | ;; "15345794840607/11266562500000 (approx. 1.362065389563764) [dimensionless]" 230 | 231 | ;; Better eat a Tic-Tac first. 232 | 233 | ;; ================================================================= 234 | ;; Body Heat 235 | 236 | ;; I've seen lots of figures about how much heat the human body produces. You can easily 237 | ;; calculate the upper limit based on how much food you eat a day. Say, you eat 2000 Calories 238 | ;; a day (again, food Calories with a capital "C" are equal to 1000 calories with a little "c".) 239 | 240 | (-> (fj 2000 :Calories :per :day :to :watts) str) 241 | ;; "1163/12 (approx. 96.91666666666667) [dimensionless]" 242 | 243 | ;; So, your average power and/or heat output is slightly less than a 100-watt bulb. 244 | ;; (Note that your heat is radiated over a much larger area so the temperature is much lower.) 245 | ;; Many days I could be replaced entirely with a 100-watt bulb and have no discernible effect 246 | ;; on the universe. 247 | 248 | ;; ================================================================= 249 | ;; Microwave Cookery 250 | 251 | ;; I'm heating up yummy mustard greens in my microwave, but I don't want to overheat them. 252 | ;; I just want to warm them up. If I run my 1100 watt microwave for 30 seconds, how much will 253 | ;; their temperature increase? I have a big 27 ounce (mass) can, and I'll assume that their 254 | ;; specific heat is about the same as that of water (1 calorie/gram/degC): 255 | 256 | (-> ($= (fj 1100 :W 30 :sec) / (fj 27 :oz 1 :calorie :per :gram :per :degC)) 257 | (to :degF) str) 258 | ;; "800000000000/43161375789 (approx. 18.53509035279376) [dimensionless]" 259 | 260 | ;; 30 seconds should raise the temperature by no more than 18 degrees Fahrenheit, assuming 261 | ;; perfect transfer of microwave energy to heat. 262 | ;; Knowing this, I could see how efficiently my microwave actually heats food. I could heat a 263 | ;; quantity of water and measure the temperature change in the water. I'll do that sometime if 264 | ;; I can find my good thermometer. 265 | 266 | ;; ================================================================= 267 | ;; Why is Superman so Lazy? 268 | 269 | ;; Superman is always rescuing school buses that are falling off of cliffs, flying to the moon, 270 | ;; lifting cars over his head, and generally showing off. So why does he still allow so many 271 | ;; accidents to happen? Shouldn't he be able to rescue everybody who has a Volkswagen parked 272 | ;; on their chest? 273 | ;; While searching for answers, I found out three interesting things about Superman: 274 | 275 | ;; 1. He's 6 feet 3 inches tall. 276 | ;; 2. He weighs 225 pounds. 277 | ;; 3. He gets his strength from being charged up with solar energy. 278 | 279 | ;; This is enough information to find some answers. Frinj has units called sunpower 280 | ;; (the total power radiated by the sun) and sundist (the distance between the earth and the sun.) 281 | ;; Thus, we can find the sun's power that strikes an area at the distance of the earth 282 | ;; (knowing the surface area of a sphere is 4 pi radius2): 283 | 284 | (add-unit! :earthpower ($= (fj :sunpower) / 285 | ($= 4 * (fj :pi) * (fj :sundist) ** 2))) 286 | (str (fj :earthpower)) 287 | ;; "1372.5422836662622 kg s^-3 [heat_flux_density]" 288 | 289 | ;; This is about 1372 watts per square meter. Superman is a pretty big guy--let's say the 290 | ;; surface area he can present to the sun is 12 square feet. (This is probably a bit high-- 291 | ;; it makes him an average of 23 inches wide over his entire height.) 292 | ;; This allows Superman to charge up at a power of: 293 | 294 | (add-unit! :chargerate (fj :earthpower 12 :ft :ft)) 295 | (-> (fj :chargerate :to :watts) str) 296 | ;; "1530.1602081736573 [dimensionless]" 297 | 298 | ;; Superman thus charges up at the rate of 1530 joules/sec or 1530 watts. At this rate, 299 | ;; how long does he have to charge up before he can lift a 2 ton truck over his head? 300 | ;; (Knowing energy = mass * height * gravity) 301 | 302 | (-> (fj 2 :ton 7 :feet :gravity :per :chargerate) (to :sec) str) 303 | ;; "24.80975674997478 [dimensionless]" 304 | 305 | ;; So, charging up for 25 seconds allows him to save one dumb kid who is acting as a speed bump. 306 | ;; So his power is huge but not infinite. He couldn't sustain a higher rate (unless he showed 307 | ;; off less by lifting the car only a foot or two.) Lifting a truck every 30 seconds or so 308 | ;; isn't bad, though. He could be saving a lot more people. So why doesn't he? 309 | 310 | ;; Well, we've all seen the movie. He's using his super-powers to pick up chicks. Literally. 311 | ;; Superman decides to take a break from saving lives and takes Lois Lane up in the sky for a 312 | ;; joyride. So how long does he have to charge up with solar energy to fly himself and Lois Lane 313 | ;; (let's say she weighs 135 pounds) up to 15,000 feet? 314 | 315 | (-> (fj (+ 225 135) :pounds 15000 :feet :gravity :per :chargerate) (to :minutes) str) 316 | ;; "79.7456466963475 [dimensionless]" 317 | 318 | ;; So, Superman has to charge up with solar energy for an hour to cart Lois up there. 319 | ;; With the same energy, he could have saved over 120 trapped kids. Keep in mind that Lois 320 | ;; could do her part, too. If she left her purse behind or didn't weigh as much, he'd have 321 | ;; more energy left over to save people. If she would manage to shed just two pounds of 322 | ;; cargo weight, Superman would have enough energy to save another kid's life. 323 | 324 | ;; Sure, he's a great guy, and, sure, he's the Defender of Truth, Justice, and the American Way, 325 | ;; but can't he find a better use for his super-powers than schlepping some shiksa into the 326 | ;; stratosphere? Shovel my walk, he could, in 3 seconds--and me with the sciatica. 327 | 328 | ;; ================================================================= 329 | ;; Fart Jokes 330 | 331 | ;; "if you fart continuously for 6 years and 9 months, you'll have enough gas to create the 332 | ;; equivalent of an atomic bomb." Hee hee. Cute. 333 | ;; The Hiroshima bomb had a yield of 12.5 kilotons of TNT, which is a very small bomb by today's 334 | ;; standards. How many horsepower would that be? 335 | 336 | (-> ($= (fj 12.5 :kilotons :TNT) / 337 | ($= (fj 6 :years) + (fj 9 :months))) 338 | (to :horsepower) str) 339 | ;; "329.26013859711395 [dimensionless]" 340 | 341 | ;; Can you produce a 329-horsepower blowtorch of a fart? I doubt it. That's the power produced 342 | ;; by a Corvette engine running just at its melting point. A one-second fart with that much power 343 | ;; could blow me 1000 feet straight up. To produce that kind of energy, how much food would you 344 | ;; have to eat a day? 345 | 346 | (-> ($= (fj 12.5 :kilotons :TNT) / 347 | ($= (fj 6 :years) + (fj 9 :months))) 348 | (to :Calories :per :day) str) 349 | ;; "5066811.55086559 [dimensionless]" 350 | 351 | ;; Ummm... can you eat over 5 million Calories a day? (Again, note that these are food Calories 352 | ;; with a capital 'c' which are equal to 1000 calories with a small 'c'.) If you were a perfect 353 | ;; fart factory, converting food energy into farts with 100% efficiency, and ate a normal 2000 354 | ;; Calories/day, how many years would it really take? 355 | 356 | (-> ($= (fj 12.5 :kilotons :TNT) / 357 | (fj 2000 :Calories :per :day)) 358 | (to :years) str) 359 | ;; "17100.488984171363 [dimensionless]" 360 | 361 | ;; 17,000 years is still a huge underestimate; I don't know how much of your energy actually goes 362 | ;; into fart production. Oh well. To continue the calculations, let's guess your butthole has a 363 | ;; diameter of 1 inch (no, you go measure it.) Let's also guess that the gas you actually produce 364 | ;; in a fart is only 1/10 as combustible as pure natural gas. What would be the velocity of the gas 365 | ;; coming out? 366 | 367 | (-> ($= (fj 12.5 :kilotons :TNT) / 368 | ($= (fj :natural_gas) * ($= (fj 6 :years) + (fj 9 :months)) * (fj :pi) * (fj 0.5 :in) ** 2) 369 | * 10) 370 | (to :mph) str) 371 | ;; "281.5904462031102 [dimensionless]" 372 | 373 | ;; Nobody likes sitting next to a 280-mile-per-hour fart-machine. Lesson: Even the smallest atomic 374 | ;; bombs are really unbelievably powerful and whoever originally calculated this isn't any fun 375 | ;; to be around if they really fart that much. 376 | 377 | ;; ================================================================= 378 | ;; Advanced Farting 379 | 380 | ;; What do you think are the most flammable gases in a fart? Most people think it's methane, 381 | ;; but I found some medical studies that disprove this. Most people hardly have any methane in 382 | ;; their intestines. For example, one study stated that only 4 out of 11 people had any detectable 383 | ;; methane in their intestines! So what's the rest of the gas? 384 | 385 | ;; Gas Percent by Volume 386 | ;; Nitrogen 64% 387 | ;; Carbon Dioxide 14% 388 | ;; Hydrogen 19% 389 | ;; Methane 3.2% 390 | ;; Oxygen 0.7% 391 | 392 | ;; These studies also note that the average person has 100 milliliters of gas is present in their 393 | ;; intestinal tract at any given time. The average person expels 400-2000 ml of gas daily 394 | ;; (and I'm not talking about through the mouth and nose.) 395 | 396 | ;; Okay, that's almost enough information to figure out available fart energy. Now all we need 397 | ;; to know is the energy of combustion of the flammable gases. Of the above, only hydrogen and 398 | ;; methane are readily combustible. Looking up their energies of combustion: 399 | 400 | ;; Gas Energy of Combustion in kJ/mol 401 | ;; Hydrogen (H2) 285.8 402 | ;; Methane (CH4) 890.8 403 | 404 | ;; Okay, that's plenty enough information to find out how much energy is released in a day of 405 | ;; farting! Say you're on the farty end of the scale, and you produce the 2000 ml of gas each day. 406 | 407 | ;; Note that the energies above are given in kJ/mol, but we have volumes in milliliters. 408 | ;; As you may have learned in chemistry class, a mole of any gas at standard temperature and 409 | ;; pressure takes up the same volume. Frinj knows this as molarvolume. 410 | 411 | ;; The total energy in the hydrogen (keeping in mind that hydrogen makes up 19% of the 2000 412 | ;; ml volume) is given by: 413 | 414 | (add-unit! :h2energy (fj 2000 :ml :per :molarvolume 19 :percent 285.8 :kJ :per :mol)) 415 | 416 | ;; The combustible hydrogen thus produces 4800 joules (per day.) Now, for the methane, 417 | ;; which makes a smaller percentage, but releases more energy per mole: 418 | 419 | (add-unit! :methanenergy (fj 2000 :ml :per :molarvolume 3.2 :percent 890.8 :kJ :per :mol)) 420 | 421 | ;; The energy in the combustible methane is thus about 2500 joules (per day), about half the 422 | ;; energy produced from the hydrogen. Thus, the grand total of energy produced by combustible 423 | ;; farts by a farty person in a day, in food Calories (with a capital C, remember--these are 424 | ;; what a physicist would call a kilocalorie) is: 425 | 426 | (-> ($= (fj :methanenergy) + (fj :h2energy)) (to :Calories) str) 427 | ;; "1.764815166936092 [dimensionless]" 428 | 429 | ;; Which gives a result of about 1.76 Calories/day of energy available from burning your farts. 430 | ;; (About 1.16 Calories from hydrogen, and about 0.60 Calories from methane.) This is out of the 431 | ;; 2000 Calories that an average person eats a day. Or, one part in about 1133 of the energy in 432 | ;; the food you eat is available in fart energy, (again, for a gassy person.) 433 | 434 | ;; Thus, a good estimate to the problem stated above is that a real (gassy) human would need to 435 | ;; save their farts for: 436 | 437 | (-> ($= (fj 12.5 :kilotons :TNT) / 438 | ($= ($= (fj :methanenergy) + (fj :h2energy)) / (fj :day))) 439 | (to :years) str) 440 | ;; "7.078157887380842E9 [dimensionless]" 441 | 442 | ;; or about 7 billion years to make the equivalent of the energy in a (small) atomic bomb! 443 | 444 | ;; ================================================================= 445 | ;; Incorrect facts 446 | 447 | ;; ----------- 448 | ;; QE2 449 | 450 | ;; "The cruise liner, Queen Elizabeth II, moves only six inches for each gallon of diesel 451 | ;; that it burns." 452 | 453 | ;; From a page of facts about the QE2, we find that the ship consumes 18 tons of fuel per 454 | ;; hour at a service speed of 28 knots. By legislation in many areas, diesel fuel must have 455 | ;; a density no higher than 0.85 kg/liter (if it were watered down, it would be higher.) 456 | 457 | (-> ($= (fj 18 :tons) / (fj :hour) / (fj 28 :knot) / (fj 0.85 :kg :per :liter)) 458 | (to :feet :per :gallon) str) 459 | ;; "33.52338503156234 [dimensionless]" 460 | 461 | ;; They're very, very wrong. It actually travels about 33.5 feet per gallon, or 157 gallons/mile. 462 | ;; They're only off by a factor of 67. Still not great gas mileage, though. 463 | 464 | ;; ----------- 465 | ;; Hamburgers and Cars 466 | 467 | ;; "pound for pound, hamburgers cost more than new cars." 468 | 469 | ;; Let's see... let's try with a medium-expensive, light car. A 2001 Corvette Z06 weighs 470 | ;; 3,115 pounds and costs $48,055. 471 | 472 | (-> ($= (fj 48055 :dollars) / (fj 3115 :lb)) 473 | (to :dollars :per :lb) str) 474 | ;; "1373/89 (approx. 15.42696629213483) [dimensionless]" 475 | 476 | ;; I know I don't pay $15/lb for hamburger. 477 | 478 | ;; ================================================================= 479 | ;; Biblical References 480 | 481 | ;; So you want to build an ark, do you? And not an Ark of the Covenant, but the boat. 482 | ;; How bad was that flood? 483 | 484 | ;; The bible is also quite precise in its measurement of the flood. Genesis 7:19-20 states 485 | ;; that "And the waters prevailed exceedingly upon the earth; and all the mountains, that were 486 | ;; under the whole heaven, were covered. Fifteen cubits upward did the waters prevail; and the 487 | ;;mountains were covered." 488 | 489 | ;; Okay, so the highest mountains of the earth were covered, plus an extra 15 cubits 490 | ;; (approx 27 feet) for good measure. The current measurements for highest mountain is 491 | ;; Mt. Everest at 29030.8 feet (according to the highly dubious and utterly non-trustable 2002 492 | ;; Guinness Book of World Records.) I know that Everest is growing slowly, 493 | ;; (best estimates are 2.4 inches/year) so we'll discount for that. 494 | 495 | (add-unit! :depth ($= (fj 29030 :feet) + (fj 15 :biblicalcubits) + (fj -2.4 :inches :per :year 4000 :years))) 496 | 497 | ;; About 28257 feet of water. This was deposited over 40 days. The rainfall was thus: 498 | 499 | (-> ($= (fj :depth) / (fj 40 :days)) 500 | (to :inches :per :hour) str) 501 | ;; "353.21562499999993 [dimensionless]" 502 | 503 | ;; About 29 feet/hour. A good rain around here is about an inch an hour. 504 | ;; The very rainiest places on earth like Cherrapunji get about this much rain in a year. 505 | ;; (I'm campaigning Colorado farmers to sin a bit more...) 506 | 507 | ;; ================================================================= 508 | ;; E=mc2 509 | 510 | ;; Everyone knows Einstein's E=mc2 equation, but to apply it is often very difficult because 511 | ;; the units come out so strange. Let's see, I have mass in pounds, and the speed of light is 512 | ;; 186,282 miles/second... ummm... what does that come out to? In Frinj the calculation becomes 513 | ;; transparently simple. 514 | 515 | ;; If you took the matter in a teaspoon of water, and converted that to energy, how many gallons 516 | ;; of gasoline would that equal? 517 | 518 | (-> ($= (fj :teaspoon :water) * (fj :c) ** 2) (to :gallons :gasoline) str) 519 | ;; "3164209.862836101 [dimensionless]" 520 | 521 | ;; Unbelievable. The energy in a teaspoon of water, if we could extract it, is equal to burning 522 | ;; more than 3 million gallons of gasoline. 523 | -------------------------------------------------------------------------------- /examples/examples.clj: -------------------------------------------------------------------------------- 1 | ;; The use and distribution terms for this software are covered by the 2 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 3 | ;; which can be found in the file epl-v10.html at the root of this distribution. 4 | ;; By using this software in any fashion, you are agreeing to be bound by 5 | ;; the terms of this license. 6 | ;; You must not remove this notice, or any other, from this software. 7 | 8 | ;; All these examples are taken from Frink's genius Sample Calculation page 9 | ;; http://futureboy.us/frinkdocs/#SampleCalculations 10 | ;; Alan Eliasen (@aeliasen) deserves all the praise 11 | 12 | (ns frinj.examples 13 | (:use [frinj.repl])) 14 | 15 | ;; setup the environment 16 | 17 | (frinj-init!) 18 | 19 | ;; ================================================================= 20 | ;; Mass and Volume 21 | 22 | ;; Let's say you wanted to fill your bedroom up with water.How much water would it take? 23 | ;; Let's say your room measures 10 feet by 12 feet wide by 8 feet high. 24 | 25 | (-> (fj 10 :feet 12 :feet 8 :feet :to :gallons) str) 26 | ;; "552960/77 (approx. 7181.298701298701) [dimensionless]" 27 | 28 | ;; It would take approximately 7181 gallons to fill it. Note that you get both an exact 29 | ;; fraction and an approximation. How much would that weigh, if you filled it with water? 30 | ;; Frinj has the unit "water" which stands for the density of water. 31 | 32 | (-> (fj 10 :feet 12 :feet 8 :feet :water :to :pounds) str) 33 | ;; "2718417272832/45359237 (approx. 59930.84215309883) [dimensionless]" 34 | 35 | ;; So it would weigh almost 60,000 pounds. What if you knew that your floor could only 36 | ;; support 2 tons? How deep could you fill the room with water? 37 | 38 | (-> (fj_ (fj 2 :tons) 39 | (fj 10 :feet 12 :feet :water)) 40 | (to :feet) str) 41 | ;; "5669904625/10618817472 (approx. 0.5339487791320047) [dimensionless]" 42 | 43 | ;; fj_ is the same as fj-div 44 | ;; see https://github.com/martintrojer/frinj/wiki/User-Guide for more details 45 | 46 | ;; So you could only fill it about 0.53 feet deep. It'll be a pretty sad pool party. 47 | 48 | ;; ================================================================= 49 | ;; Liquor 50 | 51 | ;; Let's say you want to define a new unit representing the amount of alcohol in a can 52 | ;; of (quality) 3.2 beer. Keep in mind that 3.2 beer is measured by alcohol/weight, 53 | ;; while almost all other liquors (and many beers) are usually measured in alcohol/volume. 54 | ;; The density ratio between water and alcohol is given by: 55 | 56 | (-> (fj :water :per :alcohol) str) 57 | ;; "1.2669453946534905 [dimensionless]" 58 | 59 | ;; Water is thus 1.267 times denser than alcohol. 3.2 beer (measured by weight) is thus 60 | ;; actually 4.0 percent alcohol as measured by volume. Now let's set that variable in terms 61 | ;; of a beer's density of alcohol per volume so we can compare: 62 | 63 | (add-unit! :beer (fj 12 :floz 3.2 :percent :water :per :alcohol)) 64 | 65 | ;; Then, you wanted to find out how many beers a big bottle of champagne is equal to: 66 | 67 | (-> (fj :magnum 13.5 :percent :to :beer) str) 68 | ;; "14.07449256252434 [dimensionless]" 69 | 70 | ;; You probably don't want to drink that whole bottle. Now let's say you're mixing Jungle 71 | ;; Juice (using a 1.75 liter bottle of Everclear (190 proof!)) and Kool-Aid to fill a 72 | ;; 5-gallon bucket (any resemblance to my college parties is completely intentional.) 73 | ;; What percent alcohol is that stuff? 74 | 75 | (add-unit! :junglejuice 76 | (fj_ (fj 1.75 :liter 190 :proof) (fj 5 :gallon))) 77 | 78 | (-> (fj :junglejuice :to :percent) str) 79 | ;; "8.783720740908436 [dimensionless]" 80 | 81 | ;; It's really not that strong. About 8.8%. But if you drink 5 cups of that, 82 | ;; at 12 fluid ounces each, how many beers have you had? 83 | 84 | (-> (fj 5 12 :floz :junglejuice :to :beer) str) 85 | ;; "10.832798094998477 [dimensionless]" 86 | 87 | ;; Maybe that's why people were getting punched in the head. QED. 88 | 89 | ;; ================================================================= 90 | ;; More Liquor 91 | 92 | ;; How many cases in a keg? (A keg is a normal-sized keg, what those in the beer 93 | ;; industry would call a "half barrel," or 1/2 beerbarrel in Frinj notation. 94 | ;; I don't think they sell full barrels. I've never seen one. It would weigh 258 pounds. 95 | ;; A "pony keg" is a "quarter barrel" or, in Frinj notation, ponykeg or 1/4 beerbarrel) 96 | 97 | (-> (fj :keg :to :case) str) 98 | ;; "62/9 (approx. 6.888888888888889) [dimensionless]" 99 | 100 | ;; How many 12 fluid ounce drinks (i.e. cans o' beer) in a keg? 101 | 102 | (-> (fj :keg) (to 12 :floz) str) 103 | ;; "496/3 (approx. 165.3333333333333) [dimensionless]" 104 | 105 | ;; What is the price in dollars per fluid ounce of alcohol when buying a keg of 3.2 beer? 106 | ;; (Remember that 3.2 beer is measured in alcohol/weight, so we correct by the density 107 | ;; ratio of water/alcohol to get alcohol by volume: 108 | 109 | (-> (fj_ (fj 60 :dollars) 110 | (fj :keg 3.2 :percent :water :per :alcohol)) 111 | (to :dollars :per :floz) str) 112 | ;; "0.7459362399193548 [dimensionless]" 113 | 114 | ;; A bottle of cheap wine? (A "winebottle" is the standard 750 ml size.) 115 | 116 | (-> (fj_ (fj 6.99 :dollars) 117 | (fj :winebottle 13 :percent)) 118 | (to :dollars :per :floz) str) 119 | ;; "2.1201945809423077 [dimensionless]" 120 | 121 | ;; A big plastic bottle of really bad vodka? 122 | 123 | (-> (fj_ (fj 13.99 :dollars) 124 | (fj 1750 :ml 80 :proof)) 125 | (to :dollars :per :floz) str) 126 | ;; "0.59104811225625 [dimensionless]" 127 | 128 | ;; ================================================================= 129 | ;; Movie magic 130 | 131 | ;; In the movie Independence Day, the alien mother ship is said to be 500 km in diameter 132 | ;; and have a mass 1/4 that of earth's moon. If the mother ship were a sphere, what would 133 | ;; its density be? (The volume of a sphere is 4/3 pi radius3) 134 | 135 | (-> (fj_ (fj 1/4 :moonmass) 136 | (fj 4/3 :pi) 137 | (fj** (fj 500/2 :km) 3)) 138 | (to :water) str) 139 | ;; "280.68438439732194 [dimensionless]" 140 | 141 | ;; This makes the ship 280 times denser than water. This is 36 times denser than iron and 142 | ;; more than 12 times denser than any known element! As the ship is actually more a thin disc 143 | ;; than a sphere, it would actually be even denser. Since it contains lots of empty space, 144 | ;; parts of it would have to be much, much denser. 145 | 146 | ;; If the object is this dense and has such a large mass, what is its surface gravity? 147 | ;; Surface gravity is given by G mass / radius2, where G is the gravitational constant 148 | ;; (which Frinj knows about): 149 | 150 | (-> (fj_ (fj :G 1/4 :moonmass) 151 | (fj** (fj 500/2 :km) 2)) 152 | (to :gravity) str) 153 | ;; "2.000331549387406 [dimensionless]" 154 | 155 | ;; The surface gravity of the spaceship is thus at least twice earth's gravity-- and that's 156 | ;; on the rim where gravity is weakest. It would actually be much higher since it's much, 157 | ;; much flatter than a sphere. I hope you're not the alien that has to go outside and paint it. 158 | 159 | ;; ================================================================= 160 | ;; Fiscal Calculations 161 | 162 | ;; You can calculate the day that your company will run out of cash, based on their financial 163 | ;; statements. The following is an example for a real company, based on SEC filings, which 164 | ;; read as the following: 165 | ;; Cash and Cash Equivalents (in thousands) 166 | ;; December 31, 2000 June 30, 2001 167 | ;; $86,481 $41,601 168 | 169 | (add-unit! :burnrate 170 | (fj_ 171 | (fj (- 86481 41601) :thousand :dollars) 172 | (fj- (fj :#2001-06-30) (fj :#2000-12-31)))) 173 | 174 | (-> (to (fj :burnrate) :dollars :per :day) str) 175 | 176 | ;; "1077120000/4343 (approx. 248012.8943126871) [dimensionless]" 177 | 178 | ;; You can calculate the number of days until the money runs out at this rate: 179 | 180 | (-> (fj_ (fj 41601 :thousand :dollars) 181 | (fj :burnrate)) 182 | (to :days) str) 183 | ;; "60224381/359040 (approx. 167.7372465463458) [dimensionless]" 184 | 185 | ;; Using date/time math, starting from the last report date (June 30, 2001) you can 186 | ;; find out the exact date this corresponds to: 187 | 188 | (-> (fj+ (fj :#2001-06-30) 189 | (fj_ (fj 41601 :thousand :dollars) 190 | (fj :burnrate))) 191 | to-date) 192 | ;; "Fri Dec 14 16:41:38 GMT 2001" 193 | 194 | ;; ================================================================= 195 | ;; Ouch! 196 | 197 | ;; At the moment, I'm watching CNN which is discussing some land-mines used in Afghanistan. 198 | ;; They showed a very small mine (about the size of a bran muffin) containing "51 grams of TNT" 199 | ;; and they asked how much destructive force that carries. Frinj's data file includes how much 200 | ;; energy is in a mass of TNT, specified by the unit "TNT". How many feet in the air could 51 201 | ;; grams of TNT throw me, assuming perfect efficiency, and knowing energy = mass * gravity * height? 202 | 203 | (-> (fj 51 :grams :TNT) (to 185 :pounds :gravity :feet) str) 204 | ;; "(approx. 937.7628167428614) [dimensionless]" 205 | 206 | ;; Yikes. 937 feet. But the only difference between explosives and other combustible fuels 207 | ;; is the rapidity of combustion, not in the quantity of energy. How much gasoline contains 208 | ;; the same amount of energy? 209 | 210 | (-> (fj 51 :grams :TNT) (to :teaspoons :gasoline) str) 211 | ;; "1.2903255594255887 [dimensionless]" 212 | 213 | ;; 1.29 teaspoons? That's not much at all. You're buying a huge amount of energy when you fill 214 | ;; up your car. 215 | 216 | ;; ================================================================= 217 | ;; Sniping eBay Auctions 218 | 219 | ;; I need a monocle, but I don't want to pay a lot for it. The eBay monocle auction ends in 220 | ;; 7 hours and 44 minutes... what time do I need to set the alarm clock for to remind me? 221 | 222 | (-> (fj+ (fj :#now) (fj 7 :hours) (fj 44 :min)) to-date) 223 | 224 | ;; ================================================================= 225 | ;; Junkyard Wars 226 | 227 | ;; I can't watch Junkyard Wars (or lots of other television shows) without having Frinj at 228 | ;; my side. This week the team has to float a submerged half-ton Cooper Mini... how many oil 229 | ;; barrels will they need to use as floats? 230 | 231 | (-> (fj :half :ton) (to :barrels :water) str) 232 | ;; "368175625/129048129 (approx. 2.853010174211824) [dimensionless]" 233 | 234 | ;; They're trying to hand-pump air down to the barrels, submerged "2 fathoms" below the water. 235 | ;; If the guy can sustain 40 watts of pumping power, how many minutes will it take to fill 236 | ;; the barrel? 237 | 238 | (-> (fj 2 :fathoms :water :gravity :barrel) (to 40 :watts :minutes) str) 239 | ;; "46037384521821/19375000000000 (approx. 2.376123072093987) [dimensionless]" 240 | 241 | ;; And how many food Calories (a food Calorie (with a capital 'C') equals 1000 calories with 242 | ;; a small 'c') will he burn to fill a barrel? 243 | 244 | (-> (fj 2 :fathoms :water :gravity :barrel :to :Calories) str) 245 | ;; "15345794840607/11266562500000 (approx. 1.362065389563764) [dimensionless]" 246 | 247 | ;; Better eat a Tic-Tac first. 248 | 249 | ;; ================================================================= 250 | ;; Body Heat 251 | 252 | ;; I've seen lots of figures about how much heat the human body produces. You can easily 253 | ;; calculate the upper limit based on how much food you eat a day. Say, you eat 2000 Calories 254 | ;; a day (again, food Calories with a capital "C" are equal to 1000 calories with a little "c".) 255 | 256 | (-> (fj 2000 :Calories :per :day :to :watts) str) 257 | ;; "1163/12 (approx. 96.91666666666667) [dimensionless]" 258 | 259 | ;; So, your average power and/or heat output is slightly less than a 100-watt bulb. 260 | ;; (Note that your heat is radiated over a much larger area so the temperature is much lower.) 261 | ;; Many days I could be replaced entirely with a 100-watt bulb and have no discernible effect 262 | ;; on the universe. 263 | 264 | ;; ================================================================= 265 | ;; Microwave Cookery 266 | 267 | ;; I'm heating up yummy mustard greens in my microwave, but I don't want to overheat them. 268 | ;; I just want to warm them up. If I run my 1100 watt microwave for 30 seconds, how much will 269 | ;; their temperature increase? I have a big 27 ounce (mass) can, and I'll assume that their 270 | ;; specific heat is about the same as that of water (1 calorie/gram/degC): 271 | 272 | (-> (fj_ (fj 1100 :W 30 :sec) 273 | (fj 27 :oz 1 :calorie :per :gram :per :degC)) 274 | (to :degF) str) 275 | ;; "800000000000/43161375789 (approx. 18.53509035279376) [dimensionless]" 276 | 277 | ;; 30 seconds should raise the temperature by no more than 18 degrees Fahrenheit, assuming 278 | ;; perfect transfer of microwave energy to heat. 279 | ;; Knowing this, I could see how efficiently my microwave actually heats food. I could heat a 280 | ;; quantity of water and measure the temperature change in the water. I'll do that sometime if 281 | ;; I can find my good thermometer. 282 | 283 | ;; ================================================================= 284 | ;; Why is Superman so Lazy? 285 | 286 | ;; Superman is always rescuing school buses that are falling off of cliffs, flying to the moon, 287 | ;; lifting cars over his head, and generally showing off. So why does he still allow so many 288 | ;; accidents to happen? Shouldn't he be able to rescue everybody who has a Volkswagen parked 289 | ;; on their chest? 290 | ;; While searching for answers, I found out three interesting things about Superman: 291 | 292 | ;; 1. He's 6 feet 3 inches tall. 293 | ;; 2. He weighs 225 pounds. 294 | ;; 3. He gets his strength from being charged up with solar energy. 295 | 296 | ;; This is enough information to find some answers. Frinj has units called sunpower 297 | ;; (the total power radiated by the sun) and sundist (the distance between the earth and the sun.) 298 | ;; Thus, we can find the sun's power that strikes an area at the distance of the earth 299 | ;; (knowing the surface area of a sphere is 4 pi radius2): 300 | 301 | (add-unit! :earthpower (fj_ (fj :sunpower) 302 | (fj* 4 (fj :pi) 303 | (fj** (fj :sundist) 2)))) 304 | (str (fj :earthpower)) 305 | ;; "1372.5422836662622 kg s^-3 [heat_flux_density]" 306 | 307 | ;; This is about 1372 watts per square meter. Superman is a pretty big guy--let's say the 308 | ;; surface area he can present to the sun is 12 square feet. (This is probably a bit high-- 309 | ;; it makes him an average of 23 inches wide over his entire height.) 310 | ;; This allows Superman to charge up at a power of: 311 | 312 | (add-unit! :chargerate (fj :earthpower 12 :ft :ft)) 313 | (-> (fj :chargerate :to :watts) str) 314 | ;; "1530.1602081736573 [dimensionless]" 315 | 316 | ;; Superman thus charges up at the rate of 1530 joules/sec or 1530 watts. At this rate, 317 | ;; how long does he have to charge up before he can lift a 2 ton truck over his head? 318 | ;; (Knowing energy = mass * height * gravity) 319 | 320 | (-> (fj 2 :ton 7 :feet :gravity :per :chargerate) (to :sec) str) 321 | ;; "24.80975674997478 [dimensionless]" 322 | 323 | ;; So, charging up for 25 seconds allows him to save one dumb kid who is acting as a speed bump. 324 | ;; So his power is huge but not infinite. He couldn't sustain a higher rate (unless he showed 325 | ;; off less by lifting the car only a foot or two.) Lifting a truck every 30 seconds or so 326 | ;; isn't bad, though. He could be saving a lot more people. So why doesn't he? 327 | 328 | ;; Well, we've all seen the movie. He's using his super-powers to pick up chicks. Literally. 329 | ;; Superman decides to take a break from saving lives and takes Lois Lane up in the sky for a 330 | ;; joyride. So how long does he have to charge up with solar energy to fly himself and Lois Lane 331 | ;; (let's say she weighs 135 pounds) up to 15,000 feet? 332 | 333 | (-> (fj (+ 225 135) :pounds 15000 :feet :gravity :per :chargerate) (to :minutes) str) 334 | ;; "79.7456466963475 [dimensionless]" 335 | 336 | ;; So, Superman has to charge up with solar energy for an hour to cart Lois up there. 337 | ;; With the same energy, he could have saved over 120 trapped kids. Keep in mind that Lois 338 | ;; could do her part, too. If she left her purse behind or didn't weigh as much, he'd have 339 | ;; more energy left over to save people. If she would manage to shed just two pounds of 340 | ;; cargo weight, Superman would have enough energy to save another kid's life. 341 | 342 | ;; Sure, he's a great guy, and, sure, he's the Defender of Truth, Justice, and the American Way, 343 | ;; but can't he find a better use for his super-powers than schlepping some shiksa into the 344 | ;; stratosphere? Shovel my walk, he could, in 3 seconds--and me with the sciatica. 345 | 346 | ;; ================================================================= 347 | ;; Fart Jokes 348 | 349 | ;; "if you fart continuously for 6 years and 9 months, you'll have enough gas to create the 350 | ;; equivalent of an atomic bomb." Hee hee. Cute. 351 | ;; The Hiroshima bomb had a yield of 12.5 kilotons of TNT, which is a very small bomb by today's 352 | ;; standards. How many horsepower would that be? 353 | 354 | (-> (fj_ (fj 12.5 :kilotons :TNT) 355 | (fj+ (fj 6 :years) (fj 9 :months))) 356 | (to :horsepower) str) 357 | ;; "329.26013859711395 [dimensionless]" 358 | 359 | ;; Can you produce a 329-horsepower blowtorch of a fart? I doubt it. That's the power produced 360 | ;; by a Corvette engine running just at its melting point. A one-second fart with that much power 361 | ;; could blow me 1000 feet straight up. To produce that kind of energy, how much food would you 362 | ;; have to eat a day? 363 | 364 | (-> (fj_ (fj 12.5 :kilotons :TNT) 365 | (fj+ (fj 6 :years) (fj 9 :months))) 366 | (to :Calories :per :day) str) 367 | ;; "5066811.55086559 [dimensionless]" 368 | 369 | ;; Ummm... can you eat over 5 million Calories a day? (Again, note that these are food Calories 370 | ;; with a capital 'c' which are equal to 1000 calories with a small 'c'.) If you were a perfect 371 | ;; fart factory, converting food energy into farts with 100% efficiency, and ate a normal 2000 372 | ;; Calories/day, how many years would it really take? 373 | 374 | (-> (fj_ (fj 12.5 :kilotons :TNT) 375 | (fj 2000 :Calories :per :day)) 376 | (to :years) str) 377 | ;; "17100.488984171363 [dimensionless]" 378 | 379 | ;; 17,000 years is still a huge underestimate; I don't know how much of your energy actually goes 380 | ;; into fart production. Oh well. To continue the calculations, let's guess your butthole has a 381 | ;; diameter of 1 inch (no, you go measure it.) Let's also guess that the gas you actually produce 382 | ;; in a fart is only 1/10 as combustible as pure natural gas. What would be the velocity of the gas 383 | ;; coming out? 384 | 385 | (-> (fj_ (fj 12.5 :kilotons :TNT) 386 | (fj :natural_gas) 387 | (fj+ (fj 6 :years) (fj 9 :months)) 388 | (fj* (fj :pi) (fj** (fj 0.5 :in) 2))) 389 | (fj* 10) 390 | (to :mph) str) 391 | ;; "281.5904462031102 [dimensionless]" 392 | 393 | ;; Nobody likes sitting next to a 280-mile-per-hour fart-machine. Lesson: Even the smallest atomic 394 | ;; bombs are really unbelievably powerful and whoever originally calculated this isn't any fun 395 | ;; to be around if they really fart that much. 396 | 397 | ;; ================================================================= 398 | ;; Advanced Farting 399 | 400 | ;; What do you think are the most flammable gases in a fart? Most people think it's methane, 401 | ;; but I found some medical studies that disprove this. Most people hardly have any methane in 402 | ;; their intestines. For example, one study stated that only 4 out of 11 people had any detectable 403 | ;; methane in their intestines! So what's the rest of the gas? 404 | 405 | ;; Gas Percent by Volume 406 | ;; Nitrogen 64% 407 | ;; Carbon Dioxide 14% 408 | ;; Hydrogen 19% 409 | ;; Methane 3.2% 410 | ;; Oxygen 0.7% 411 | 412 | ;; These studies also note that the average person has 100 milliliters of gas is present in their 413 | ;; intestinal tract at any given time. The average person expels 400-2000 ml of gas daily 414 | ;; (and I'm not talking about through the mouth and nose.) 415 | 416 | ;; Okay, that's almost enough information to figure out available fart energy. Now all we need 417 | ;; to know is the energy of combustion of the flammable gases. Of the above, only hydrogen and 418 | ;; methane are readily combustible. Looking up their energies of combustion: 419 | 420 | ;; Gas Energy of Combustion in kJ/mol 421 | ;; Hydrogen (H2) 285.8 422 | ;; Methane (CH4) 890.8 423 | 424 | ;; Okay, that's plenty enough information to find out how much energy is released in a day of 425 | ;; farting! Say you're on the farty end of the scale, and you produce the 2000 ml of gas each day. 426 | 427 | ;; Note that the energies above are given in kJ/mol, but we have volumes in milliliters. 428 | ;; As you may have learned in chemistry class, a mole of any gas at standard temperature and 429 | ;; pressure takes up the same volume. Frinj knows this as molarvolume. 430 | 431 | ;; The total energy in the hydrogen (keeping in mind that hydrogen makes up 19% of the 2000 432 | ;; ml volume) is given by: 433 | 434 | (add-unit! :h2energy (fj 2000 :ml :per :molarvolume 19 :percent 285.8 :kJ :per :mol)) 435 | 436 | ;; The combustible hydrogen thus produces 4800 joules (per day.) Now, for the methane, 437 | ;; which makes a smaller percentage, but releases more energy per mole: 438 | 439 | (add-unit! :methanenergy (fj 2000 :ml :per :molarvolume 3.2 :percent 890.8 :kJ :per :mol)) 440 | 441 | ;; The energy in the combustible methane is thus about 2500 joules (per day), about half the 442 | ;; energy produced from the hydrogen. Thus, the grand total of energy produced by combustible 443 | ;; farts by a farty person in a day, in food Calories (with a capital C, remember--these are 444 | ;; what a physicist would call a kilocalorie) is: 445 | 446 | (-> (fj+ (fj :methanenergy) (fj :h2energy)) (to :Calories) str) 447 | ;; "1.764815166936092 [dimensionless]" 448 | 449 | ;; Which gives a result of about 1.76 Calories/day of energy available from burning your farts. 450 | ;; (About 1.16 Calories from hydrogen, and about 0.60 Calories from methane.) This is out of the 451 | ;; 2000 Calories that an average person eats a day. Or, one part in about 1133 of the energy in 452 | ;; the food you eat is available in fart energy, (again, for a gassy person.) 453 | 454 | ;; Thus, a good estimate to the problem stated above is that a real (gassy) human would need to 455 | ;; save their farts for: 456 | 457 | (-> (fj_ (fj 12.5 :kilotons :TNT) 458 | (fj_ (fj+ (fj :methanenergy) (fj :h2energy)) (fj :day))) 459 | (to :days) str) 460 | ;; "7.078157887380842E9 [dimensionless]" 461 | 462 | ;; or about 7 billion years to make the equivalent of the energy in a (small) atomic bomb! 463 | 464 | ;; ================================================================= 465 | ;; Incorrect facts 466 | 467 | ;; ----------- 468 | ;; QE2 469 | 470 | ;; "The cruise liner, Queen Elizabeth II, moves only six inches for each gallon of diesel 471 | ;; that it burns." 472 | 473 | ;; From a page of facts about the QE2, we find that the ship consumes 18 tons of fuel per 474 | ;; hour at a service speed of 28 knots. By legislation in many areas, diesel fuel must have 475 | ;; a density no higher than 0.85 kg/liter (if it were watered down, it would be higher.) 476 | 477 | (-> (fj_ (fj 18 :tons) 478 | (fj :hour) 479 | (fj 28 :knot) 480 | (fj 0.85 :kg :per :liter)) 481 | (to :feet :per :gallon) str) 482 | ;; "33.52338503156234 [dimensionless]" 483 | 484 | ;; They're very, very wrong. It actually travels about 33.5 feet per gallon, or 157 gallons/mile. 485 | ;; They're only off by a factor of 67. Still not great gas mileage, though. 486 | 487 | ;; ----------- 488 | ;; Hamburgers and Cars 489 | 490 | ;; "pound for pound, hamburgers cost more than new cars." 491 | 492 | ;; Let's see... let's try with a medium-expensive, light car. A 2001 Corvette Z06 weighs 493 | ;; 3,115 pounds and costs $48,055. 494 | 495 | (-> (fj_ (fj 48055 :dollars) 496 | (fj 3115 :lb)) 497 | (to :dollars :per :lb) str) 498 | ;; "1373/89 (approx. 15.42696629213483) [dimensionless]" 499 | 500 | ;; I know I don't pay $15/lb for hamburger. 501 | 502 | ;; ================================================================= 503 | ;; Biblical References 504 | 505 | ;; So you want to build an ark, do you? And not an Ark of the Covenant, but the boat. 506 | ;; How bad was that flood? 507 | 508 | ;; The bible is also quite precise in its measurement of the flood. Genesis 7:19-20 states 509 | ;; that "And the waters prevailed exceedingly upon the earth; and all the mountains, that were 510 | ;; under the whole heaven, were covered. Fifteen cubits upward did the waters prevail; and the 511 | ;;mountains were covered." 512 | 513 | ;; Okay, so the highest mountains of the earth were covered, plus an extra 15 cubits 514 | ;; (approx 27 feet) for good measure. The current measurements for highest mountain is 515 | ;; Mt. Everest at 29030.8 feet (according to the highly dubious and utterly non-trustable 2002 516 | ;; Guinness Book of World Records.) I know that Everest is growing slowly, 517 | ;; (best estimates are 2.4 inches/year) so we'll discount for that. 518 | 519 | (add-unit! :depth (fj+ (fj 29030 :feet) 520 | (fj 15 :biblicalcubits) 521 | (fj -2.4 :inches :per :year 4000 :years))) 522 | 523 | ;; About 28257 feet of water. This was deposited over 40 days. The rainfall was thus: 524 | 525 | (-> (fj_ (fj :depth) (fj 40 :days)) 526 | (to :inch :per :hour) str) 527 | ;; "353.21562499999993 [dimensionless]" 528 | 529 | ;; About 29 feet/hour. A good rain around here is about an inch an hour. 530 | ;; The very rainiest places on earth like Cherrapunji get about this much rain in a year. 531 | ;; (I'm campaigning Colorado farmers to sin a bit more...) 532 | 533 | ;; ================================================================= 534 | ;; E=mc2 535 | 536 | ;; Everyone knows Einstein's E=mc2 equation, but to apply it is often very difficult because 537 | ;; the units come out so strange. Let's see, I have mass in pounds, and the speed of light is 538 | ;; 186,282 miles/second... ummm... what does that come out to? In Frinj the calculation becomes 539 | ;; transparently simple. 540 | 541 | ;; If you took the matter in a teaspoon of water, and converted that to energy, how many gallons 542 | ;; of gasoline would that equal? 543 | 544 | (-> (fj :teaspoon :water :c :c) (to :gallons :gasoline) str) 545 | ;; "3164209.862836101 [dimensionless]" 546 | 547 | ;; Unbelievable. The energy in a teaspoon of water, if we could extract it, is equal to burning 548 | ;; more than 3 million gallons of gasoline. 549 | -------------------------------------------------------------------------------- /examples/node/example.cljs: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Martin Trojer. All rights reserved. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns frinj.node.example 10 | (:require [frinj.node :refer (frinj-init!)] 11 | [frinj.ops :refer (fj to)])) 12 | 13 | (defn -main [& args] 14 | (frinj-init! "resources/units.edn") 15 | 16 | (println (fj :inch)) 17 | (println (fj :cm)) 18 | (println (-> (fj :teaspoon :water :c :c) (to :gallons :gasoline) str))) 19 | 20 | (set! *main-cli-fn* -main) 21 | -------------------------------------------------------------------------------- /examples/node/externs.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Martin Trojer. All rights reserved. 2 | // The use and distribution terms for this software are covered by the 3 | // Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | // which can be found in the file epl-v10.html at the root of this distribution. 5 | // By using this software in any fashion, you are agreeing to be bound by 6 | // the terms of this license. 7 | // You must not remove this notice, or any other, from this software. 8 | 9 | var fs = {}; 10 | fs.readFileSync = function() {}; 11 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject frinj "0.2.6-SNAPSHOT" 2 | :description "Practical unit-of-measure calculator DSL for Clojure" 3 | :url "https://github.com/martintrojer/frinj" 4 | 5 | :dependencies [[org.clojure/clojure "1.6.0"] 6 | [org.clojure/clojurescript "0.0-2371"]] 7 | 8 | ;; Clojure 9 | :profiles {:dev {:dependencies [[org.clojure/tools.namespace "0.2.4"] 10 | [org.clojure/tools.trace "0.7.6"]] 11 | :source-paths ["dev"]}} 12 | :repl-options {:init (user/go)} 13 | 14 | ;; CLJS 15 | :plugins [[lein-cljsbuild "0.3.2"]] 16 | :hooks [leiningen.cljsbuild] 17 | :cljsbuild {:crossovers [frinj.cross frinj.core frinj.ops frinj.parser] 18 | :crossover-jar true 19 | :builds [{:source-paths ["examples/node"] 20 | :compiler {:output-to "frinj-node.js" 21 | :target :nodejs 22 | :externs ["examples/node/externs.js"] 23 | :optimizations :advanced 24 | :pretty-print false}} 25 | {:source-paths ["examples/browser"] 26 | :compiler {:output-to "browser-example/frinj.js" 27 | :optimizations :whitespace 28 | :pretty-print false}}]}) 29 | -------------------------------------------------------------------------------- /resources/units.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martintrojer/frinj/e588c8e022f13a59e529c19bc78657a4284d4e72/resources/units.txt -------------------------------------------------------------------------------- /src/frinj/core.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Martin Trojer. All rights reserved. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns frinj.core 10 | (:require [frinj.cross :as cross])) 11 | 12 | (def ^:dynamic *debug* (atom false)) 13 | (defn enable-debug! [] (reset! *debug* true)) 14 | 15 | (def state (atom nil)) 16 | 17 | ;; ================================================================= 18 | ;; The core "Number" class 19 | 20 | (declare clean-us) 21 | 22 | ;; fjv is the core type representing values w/ UOM. 23 | ;; :v is the value 24 | ;; :u is the unit, represented by a map. 25 | ;; keys are the dimentsions, and the value is the magnitude 26 | 27 | (defrecord fjv [v u] 28 | Object 29 | (toString [this] 30 | (str (if (cross/ratio? v) (str v " (approx. " (double v) ")") v) " " 31 | (str 32 | (reduce (fn [acc [k v]] (str acc (if (= v 1) (str k " ") (str k "^" v " ")))) 33 | "" (->> u clean-us (into (sorted-map)))) 34 | "[" (get (:fundamental-units @state) (clean-us u) "") "]")))) 35 | 36 | (def one (fjv. 1 {})) 37 | (def zero (fjv. 0 {})) 38 | 39 | ;; ================================================================= 40 | ;; manipulate the state 41 | 42 | (defn reset-state! 43 | "Total reset of the core unit states (to empty)" 44 | [] 45 | (reset! state 46 | {:units {} 47 | :prefixes {} 48 | :standalone-prefixes {} 49 | :fundamental-units {} 50 | :fundamentals #{}})) 51 | 52 | (defn restore-state! 53 | "Restores state given a units.edn data structure" 54 | [data] 55 | (let [map->fjv (fn [[n m]] [n (fjv. (:v m) (:u m))])] 56 | (reset! state 57 | (assoc data 58 | :units (->> data :units (map map->fjv) (into {})) 59 | :prefixes (->> data :prefixes (map map->fjv) (into {})) 60 | :standalone-prefixes (->> data :standalone-prefixes (map map->fjv) (into {})))))) 61 | 62 | (defn add-with-plurals! 63 | "Adds a unit to the state (and it's potential plural)" 64 | [k uname fj] 65 | (let [uname (name uname)] 66 | (swap! state assoc-in [k uname] fj) 67 | (when-not (or (= \s (last uname)) (= 1 (count uname))) 68 | (swap! state assoc-in [k (str uname "s")] fj)) 69 | fj)) 70 | 71 | (defn add-unit! 72 | "Adds a unit to the state" 73 | [name fj] 74 | (add-with-plurals! :units name fj)) 75 | 76 | (defn export-state [] 77 | (let [fjv->map (fn [[n fj]] [n (into {} fj)])] 78 | (assoc @state 79 | :units (->> @state :units (map fjv->map) (into {})) 80 | :prefixes (->> @state :prefixes (map fjv->map) (into {})) 81 | :standalone-prefixes (->> @state :standalone-prefixes (map fjv->map) (into {}))))) 82 | 83 | ;; ------------------------ 84 | ;; queries 85 | 86 | (defn prefix? 87 | "It this a prefix?" 88 | [p] 89 | (or (get-in @state [:standalone-prefixes p]) 90 | (get-in @state [:prefixes p]))) 91 | 92 | (defn all-prefix-names 93 | "Get all list of all prefix names" 94 | [] 95 | (concat (-> @state :standalone-prefixes keys) 96 | (-> @state :prefixes keys))) 97 | 98 | (defn lookup-prefix 99 | "Get the value of the given prefix" 100 | [p] 101 | (get-in @state [:standalone-prefixes p] 102 | (get-in @state [:prefixes p]))) 103 | 104 | ;; ================================================================= 105 | ;; Helpers 106 | 107 | (defn- clean-us 108 | "Remove all zero-values entries" 109 | [m] 110 | (->> m 111 | (remove (fn [[_ v]] (zero? v))) 112 | (into {}))) 113 | 114 | (defn clean-units 115 | "Remove 0-ed units from a fjv" 116 | [fj] 117 | (fjv. (:v fj) (clean-us (:u fj)))) 118 | 119 | (defn- to-fjs 120 | "create fjvs from numbers" 121 | [nums] 122 | (map #(if (= fjv (type %)) % (fjv. % {})) nums)) 123 | 124 | (defn- enfore-units 125 | "Enforce all fjs' units are the same" 126 | [fjs] 127 | (when (> (->> fjs (map :u) (map clean-us) set count) 1) 128 | (cross/throw-exception "Cannot use operator on units with different dimensions"))) 129 | 130 | ;; ================================================================= 131 | ;; Primitve math functions 132 | 133 | (defn add-units [& us] (apply merge-with (fnil + 0) us)) 134 | 135 | (defn- add-sub 136 | [op fjs] 137 | (when-let [fjs (-> fjs to-fjs seq)] 138 | (when @*debug* (println "add-sub" fjs (map :v fjs) (map :u fjs))) 139 | (fjv. (reduce op (map :v fjs)) (-> fjs first :u)))) 140 | 141 | (defn fj-add 142 | "Adds fjvs" 143 | [& fjs] 144 | (enfore-units fjs) 145 | (add-sub + fjs)) 146 | 147 | (defn fj-sub 148 | "Subtracts fjvs" 149 | [& fjs] 150 | (enfore-units fjs) 151 | (add-sub - fjs)) 152 | 153 | (defn- flip-sign 154 | "Swaps the sign of units" 155 | [us] 156 | (->> us 157 | (map (fn [[k v]] [k (- v)])) 158 | (into {}))) 159 | 160 | (defn fj-mul 161 | [& fjs] 162 | (when-let [fjs (-> fjs to-fjs seq)] 163 | (when @*debug* (println "*" fjs)) 164 | (fjv. (reduce * (map :v fjs)) 165 | (apply add-units (map :u fjs))))) 166 | 167 | (defn fj-div 168 | [& fjs] 169 | (when-let [fjs (-> fjs to-fjs seq)] 170 | (when @*debug* (println "/" fjs)) 171 | (fjv. (reduce / (map :v fjs)) 172 | (apply add-units (concat [(-> fjs first :u)] 173 | (->> fjs rest (map :u) (map flip-sign))))))) 174 | 175 | (defn fj-inverse 176 | [fj] 177 | (fj-div 1 fj)) 178 | 179 | (defn fj-int-pow 180 | "Power operator, only integers!" 181 | [fj exp] 182 | (if (integer? exp) 183 | (if (pos? exp) 184 | (reduce (fn [acc _] (fj-mul acc fj)) one (range exp)) 185 | (reduce (fn [acc _] (fj-div acc fj)) one (range (- exp)))) 186 | (cross/throw-exception "only integers supported"))) 187 | 188 | (defn fj-equal? 189 | [& fjs] 190 | (enfore-units fjs) 191 | (->> fjs (map clean-units) (apply =))) 192 | 193 | (defn fj-not-equal? 194 | [& fjs] 195 | (enfore-units fjs) 196 | (not (apply fj-equal? fjs))) 197 | 198 | (defn fj-less? 199 | [& fjs] 200 | (enfore-units fjs) 201 | (->> fjs (map :v) (apply <))) 202 | 203 | (defn fj-greater? 204 | [& fjs] 205 | (enfore-units fjs) 206 | (->> fjs (map :v) (apply >))) 207 | 208 | (defn fj-less-or-equal? [& fjs] 209 | (enfore-units fjs) 210 | (or (apply fj-equal? fjs) (apply fj-less? fjs))) 211 | 212 | (defn fj-greater-or-equal? [& fjs] 213 | (enfore-units fjs) 214 | (or (apply fj-equal? fjs) (apply fj-greater? fjs))) 215 | 216 | ;; ================================================================= 217 | ;; prefix transforms 218 | 219 | (defn resolve-prefixed-unit 220 | "Finds the longest prefix in a unit name and replaces it with with factor" 221 | [uname] 222 | (when @*debug* (println "resolve" uname)) 223 | (if-let [fj (get-in @state [:units uname])] 224 | [one uname] ;; if name = unit, just return the unit 225 | (if-let [pfx (->> (filter #(cross/starts-with uname %) (all-prefix-names)) 226 | (sort-by #(.length %)) 227 | reverse 228 | (filter #(contains? (:units @state) (cross/sub-string uname %))) 229 | first)] 230 | [(lookup-prefix pfx) (cross/sub-string uname pfx)] 231 | ;; no match, return the uname 232 | [one uname]))) 233 | 234 | ;; {prefix:u1 1, prefix:u2 -1} -> (fj-val. fact {u1:1, u2:-1}) 235 | (defn resolve-unit-prefixes 236 | "Replaces all units with prefix + new unit" 237 | [u] 238 | (when @*debug* (println "resolve-units" u)) 239 | (reduce (fn [acc [k v]] 240 | (let [[fact u] (resolve-prefixed-unit k)] 241 | (if (pos? v) 242 | (fj-mul (fjv. (:v acc) (add-units (:u acc) {u v})) 243 | (fj-int-pow fact v)) 244 | (fj-div (fjv. (:v acc) (add-units (:u acc) {u v})) 245 | (fj-int-pow fact (Math/abs v)))))) 246 | one u)) 247 | 248 | ;; ================================================================= 249 | ;; unit normaliztion 250 | 251 | ;; (fj-val. fact {u1:1, u2:-1} -> (fl-val. nfact {u0:1, u2:-1} 252 | (defn normalize-units 253 | "Replaces units with already defined ones, and remove zero units" 254 | [fj] 255 | (when @*debug* (println "norm" fj)) 256 | (-> 257 | (reduce (fn [acc [k v]] 258 | (let [fj (get-in @state [:units k]) 259 | fj (if fj fj (get-in @state [:standalone-prefixes k]))] 260 | (if fj 261 | (if (get-in @state [:fundamentals k]) ;;(= fj one) 262 | acc 263 | (fj-div 264 | (if (pos? v) 265 | (fj-mul acc (fj-int-pow fj v)) 266 | (fj-div acc (fj-int-pow fj (Math/abs v)))) 267 | (fjv. 1 {k v}))) 268 | acc))) 269 | fj (:u fj)) 270 | (clean-units))) 271 | 272 | (defn resolve-and-normalize-units 273 | "Resolve all units with prefixes, and normalized the result" 274 | [u] 275 | (-> u resolve-unit-prefixes normalize-units)) 276 | 277 | ;; ================================================================= 278 | ;; unit conversion 279 | 280 | (defn convert 281 | "Converts a fjv to a given unit, will resolve and normalize. Will reverse if units 'mirrored'" 282 | [fj u] 283 | (when @*debug* (println "convert" fj u)) 284 | (let [nf (resolve-and-normalize-units (:u fj)) 285 | nfj (fjv. (* (:v nf) (:v fj)) (:u nf)) 286 | nu (resolve-and-normalize-units u)] 287 | (when @*debug* (println " convert post-norm" nfj nu)) 288 | (if (= (:u nfj) (:u nu)) 289 | (fj-div nfj nu) 290 | (if (= (:u (fj-inverse nfj)) (:u nu)) 291 | (fj-div (fj-inverse nfj) nu) 292 | (cross/throw-exception "cannot convert to a different unit"))))) 293 | -------------------------------------------------------------------------------- /src/frinj/cross.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Martin Trojer. All rights reserved. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns frinj.cross 10 | (:refer-clojure :exclude [ratio?])) 11 | 12 | ;; Some functions to abstract JVM / NodeJS platform differences. 13 | ;; Implementations here are "CLJS safe". 14 | 15 | ;; --- overwritten by jvm.clj 16 | 17 | (defn starts-with [s prefix] 18 | (let [l (.-length prefix)] 19 | (= (apply str (take l s)) prefix))) 20 | 21 | (defn sub-string [s pfx] 22 | (apply str (drop (.-length pfx) s))) 23 | 24 | (defn ratio? [n] false) 25 | 26 | (defn throw-exception [s] 27 | (throw s)) 28 | -------------------------------------------------------------------------------- /src/frinj/feeds.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Martin Trojer. All rights reserved. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns frinj.feeds 10 | (:use [frinj.core] 11 | [clojure.xml :only [parse]] 12 | [clojure.zip :only [xml-zip children down]]) 13 | (:import frinj.core.fjv 14 | [java.util.concurrent ScheduledThreadPoolExecutor TimeUnit])) 15 | 16 | ;; ================================================================= 17 | 18 | (def feed-pool (atom nil)) 19 | 20 | (defn start-feed 21 | "Schedule a feed" 22 | [f period scale] 23 | (when-not @feed-pool 24 | (reset! feed-pool (ScheduledThreadPoolExecutor. 1))) 25 | (.scheduleAtFixedRate @feed-pool f 0 period scale)) 26 | 27 | (defn stop-feed 28 | [f] 29 | (when @feed-pool 30 | (.cancel f true))) 31 | 32 | (defn restart-feed [f period scale] 33 | (try 34 | (stop-feed f) 35 | (catch Exception e)) 36 | (start-feed f period scale)) 37 | 38 | (defn shutdown-feeds 39 | "Shutdown all scheduled feeds" 40 | [] 41 | (when @feed-pool 42 | (.shutdown @feed-pool) 43 | (reset! feed-pool nil))) 44 | 45 | ;; ================================================================= 46 | 47 | (defn- fetch-url 48 | "Fetch data from an URL and return data as a string" 49 | [adr] 50 | (let [url (java.net.URL. adr)] 51 | (with-open [stream (.openStream url)] 52 | (let [buf (java.io.BufferedReader. (java.io.InputStreamReader. stream))] 53 | (apply str (line-seq buf)))))) 54 | 55 | (defn- zip-str [s] 56 | (xml-zip (parse (java.io.ByteArrayInputStream. (.getBytes s))))) 57 | 58 | (defn update-units! 59 | "Updates the frinj state with provided units. Unit-map in the form {target-unit [rate source-unit]}" 60 | [unit-map] 61 | (loop [[[cur [rate src]] & rst] (vec unit-map)] 62 | (when @*debug* (println "update-units!" cur rate src)) 63 | (when cur 64 | (add-unit! cur (fj-mul rate src)) 65 | (recur rst)))) 66 | 67 | ;; ================================================================= 68 | 69 | (defn- get-themoneyconverter-com-rates [] 70 | (let [get-target (fn [r] (-> (filter #(= (:tag %) :title) r) 71 | first 72 | :content 73 | first 74 | (.split "/") 75 | first)) 76 | get-rate (fn [r] (->> (filter #(= (:tag %) :description) r) 77 | first 78 | :content 79 | first 80 | (re-seq #"[0-9\.]+") 81 | second 82 | read-string)) 83 | rates (->> "http://themoneyconverter.com/rss-feed/USD/rss.xml" 84 | fetch-url 85 | zip-str 86 | down 87 | children 88 | (filter #(= (:tag %) :item)) 89 | (map :content))] 90 | (reduce (fn [acc r] (assoc acc (get-target r) [(/ 1 (get-rate r)) (fjv. 1 {"USD" 1})])) 91 | {} rates))) 92 | 93 | (comment 94 | [{:tag :title, :attrs nil, :content ["ARS/USD"]} 95 | {:tag :link, :attrs nil, :content ["http://themoneyconverter.com/USD/ARS.aspx"]} 96 | {:tag :guid, :attrs nil, :content ["aa3d0bcc-8e6b-4b4a-9a83-256319785fbb"]} 97 | {:tag :pubDate, :attrs nil, :content ["Wed, 14 Mar 2012 07:08:20 GMT"]} 98 | {:tag :description, :attrs nil, :content ["1 US Dollar = 4.34709 Argentine Peso"]} 99 | {:tag :category, :attrs nil, :content ["South America"]}]) 100 | 101 | (defn- update-themoneyconverter-com-rates [] 102 | (add-unit! "USD" (fjv. 1 {"dollar" 1})) 103 | (update-units! (get-themoneyconverter-com-rates))) 104 | 105 | (defn restart-exchange-feed! [] 106 | (restart-feed update-themoneyconverter-com-rates 15 TimeUnit/MINUTES)) 107 | 108 | ;; ================================================================= 109 | 110 | (defn- capitalize [s] 111 | (str (.toUpperCase (.substring s 0 1)) (.substring s 1))) 112 | 113 | (defn- get-xmlcharts-com-rates [url] 114 | (let [get-target (fn [r] (capitalize (:commodity (:attrs r)))) 115 | get-rate (fn [r] (-> r 116 | :content 117 | first 118 | read-string)) 119 | get-weight (fn [r] (-> 120 | (:per (:attrs r)) 121 | (.replace " " "-"))) 122 | rates (->> url 123 | fetch-url 124 | zip-str 125 | children 126 | first ;; TODO; not good, filter for usd here 127 | :content)] 128 | 129 | (reduce (fn [acc r] (assoc acc (get-target r) [(get-rate r) (fjv. 1 {"dollar" 1 (get-weight r) -1})])) 130 | {} rates))) 131 | 132 | (comment 133 | [{:tag :price, :attrs {:timestamp "1331732940", :per "ozt", :commodity "gold"}, :content ["1647.80"]} 134 | {:tag :price, :attrs {:timestamp "1331732760", :per "ozt", :commodity "palladium"}, :content ["699.00"]} 135 | {:tag :price, :attrs {:timestamp "1331732880", :per "ozt", :commodity "platinum"}, :content ["1679.50"]} 136 | {:tag :price, :attrs {:timestamp "1331732940", :per "ozt", :commodity "silver"}, :content ["32.88"]}]) 137 | 138 | (defn- update-precious-metal-rates [] 139 | (add-unit! "ozt" (fjv. 1 {"oz" 1})) 140 | (update-units! (get-xmlcharts-com-rates "http://www.xmlcharts.com/cache/precious-metals.xml"))) 141 | 142 | (defn- update-industrial-metal-rates [] 143 | (update-units! (get-xmlcharts-com-rates "http://www.xmlcharts.com/cache/industrial-metals.xml"))) 144 | 145 | (defn- update-agrarian-rates [] 146 | (add-unit! "short-hundredweight" (fjv. 100 {"lb" 1})) 147 | (update-units! (get-xmlcharts-com-rates "http://www.xmlcharts.com/cache/agrarian.xml"))) 148 | 149 | (defn restart-precious-metal-feed! [] 150 | (restart-feed update-precious-metal-rates 1 TimeUnit/HOURS)) 151 | 152 | (defn restart-industrial-metal-feed! [] 153 | (restart-feed update-industrial-metal-rates 1 TimeUnit/HOURS)) 154 | 155 | (defn restart-agrarian-feed! [] 156 | (restart-feed update-agrarian-rates 1 TimeUnit/HOURS)) 157 | 158 | ;; ================================================================= 159 | 160 | ;; historical 161 | ;; http://www.measuringworth.com/ppoweruk/result.php?year_result=2005&amount=1&use%5B%5D=CPI&year_source= 162 | 163 | ;; ================================================================= 164 | 165 | (defn setup-feeds [] 166 | (restart-exchange-feed!) 167 | (restart-precious-metal-feed!) 168 | (restart-industrial-metal-feed!) 169 | (restart-agrarian-feed!)) 170 | -------------------------------------------------------------------------------- /src/frinj/infix.clj: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 | ;; File : infix.clj 3 | ;; Function : Infix Math library 4 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 5 | ;; Copyright (c) 2008, J. Bester 6 | ;; All rights reserved. 7 | ;; 8 | ;; Redistribution and use in source and binary forms, with or without 9 | ;; modification, are permitted provided that the following conditions are met: 10 | ;; * Redistributions of source code must retain the above copyright 11 | ;; notice, this list of conditions and the following disclaimer. 12 | ;; * Redistributions in binary form must reproduce the above copyright 13 | ;; notice, this list of conditions and the following disclaimer in the 14 | ;; documentation and/or other materials provided with the distribution. 15 | ;; 16 | ;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY 17 | ;; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | ;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | ;; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 20 | ;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | ;; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | ;; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ;; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | ;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | ;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 27 | 28 | (ns ^{:doc "Library for converting infix mathematical formula to prefix expressions" 29 | :author "J. Bester"} 30 | frinj.infix 31 | (:use [frinj.core])) 32 | 33 | ;; operator precedence for formula macro 34 | (def +precedence-table+ (ref {})) 35 | 36 | ;; symbol translation for symbols in formula 37 | ;; (only supports binary operators) 38 | (def +translation-table+ (ref {})) 39 | 40 | (def +highest-precedence+ (ref 0)) 41 | 42 | (defn defop 43 | "Define operators for formula macro" 44 | ([op prec & [trans]] 45 | (dosync (ref-set +precedence-table+ (assoc @+precedence-table+ op prec))) 46 | (when-not (nil? trans) 47 | (dosync (ref-set +translation-table+ (assoc @+translation-table+ op trans)))) 48 | (dosync (ref-set +highest-precedence+ (apply max (map val @+precedence-table+)))))) 49 | 50 | 51 | ;; == operators == 52 | (defop '== 30 fj-equal?) 53 | (defop '!= 30 fj-not-equal?) 54 | (defop '< 40 fj-less?) 55 | (defop '> 40 fj-greater?) 56 | (defop '<= 40 fj-less-or-equal?) 57 | (defop '>= 40 fj-greater-or-equal?) 58 | 59 | (defop '- 60 fj-sub) 60 | (defop '+ 60 fj-add) 61 | (defop '/ 80 fj-div) 62 | (defop '* 80 fj-mul) 63 | (defop '** 100 fj-int-pow) 64 | 65 | (defn- operator? 66 | "Check if is valid operator" 67 | ([sym] 68 | (not (nil? (get @+precedence-table+ sym))))) 69 | 70 | (defn- find-lowest-precedence 71 | "find the operator with lowest precedence; search from left to right" 72 | ([col] 73 | ;; loop through terms in the coluence 74 | (loop [idx 0 75 | col col 76 | lowest-idx nil 77 | lowest-prec @+highest-precedence+] 78 | ;; nothing left to process 79 | (if (empty? col) 80 | ;; return lowest found 81 | lowest-idx 82 | ;; otherwise check if current term is lower 83 | (let [prec (get @+precedence-table+ (first col))] 84 | ;; is of lower or equal precedence 85 | (if (and prec (<= prec lowest-prec)) 86 | (recur (inc idx) (rest col) 87 | idx prec) 88 | ;; is of high precedence therefore skip for now 89 | (recur (inc idx) (rest col) 90 | lowest-idx lowest-prec))))))) 91 | 92 | (defn- translate-op 93 | "Translation of symbol => symbol for binary op allows for 94 | user defined operators" 95 | ([op] 96 | (if (contains? @+translation-table+ op) 97 | (get @+translation-table+ op) 98 | op))) 99 | 100 | (defn infix-to-prefix 101 | "Convert from infix notation to prefix notation" 102 | ([col] 103 | (cond 104 | ;; handle term only 105 | (not (seq? col)) col 106 | ;; handle sequence containing one term (i.e. handle parens) 107 | (= (count col) 1) (infix-to-prefix (first col)) 108 | ;; handle all other cases 109 | true (let [lowest (find-lowest-precedence col)] 110 | (if (nil? lowest) ;; nothing to split 111 | col 112 | ;; (a b c) bind a to hd, c to tl, and b to op 113 | (let [[hd [op & tl]] (split-at lowest col)] 114 | ;; recurse 115 | (list (translate-op op) 116 | (infix-to-prefix hd) 117 | (infix-to-prefix tl)))))))) 118 | 119 | (defmacro $= 120 | "Convert from infix notation to prefix notation" 121 | ([& equation] 122 | (infix-to-prefix equation))) 123 | -------------------------------------------------------------------------------- /src/frinj/jvm.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Martin Trojer. All rights reserved. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns frinj.jvm 10 | (:require [frinj.core :as core] 11 | [frinj.cross :as cross] 12 | [frinj.parser :as parser] 13 | [frinj.feeds :as feeds] 14 | [clojure.java.io :as io]) 15 | (:import frinj.core.fjv)) 16 | 17 | ;; JVM specific implementations and functions 18 | 19 | ;; cross.clj overrides 20 | 21 | (alter-var-root #'cross/starts-with (fn [_] (fn [^String s ^String prefix] (.startsWith s prefix)))) 22 | (alter-var-root #'cross/sub-string (fn [_] (fn [^String s ^String prefix] (.substring s (.length prefix))))) 23 | (alter-var-root #'cross/ratio? (fn [_] ratio?)) 24 | (alter-var-root #'cross/throw-exception (fn [_] (fn [^String s] (throw (Exception. s))))) 25 | 26 | (def ^:private unit-clj-file (io/resource "units.edn")) 27 | 28 | (defn frinj-init! 29 | "Init the frinj envrionment. Will try to load the clj-unit file - if that fails the unit.txt file" 30 | [] 31 | (-> unit-clj-file slurp read-string core/restore-state!) 32 | (feeds/setup-feeds) 33 | :done) 34 | 35 | (def ^:private unit-txt-file (io/resource "units.txt")) 36 | 37 | (defn load-unit-txt-file! 38 | "Resets the states and loads units from the frink units.txt file" 39 | [] 40 | (with-open [rdr (io/reader unit-txt-file)] 41 | (parser/restore-state-from-text! (line-seq rdr)))) 42 | 43 | (defn override-operators! [] 44 | (eval '(do 45 | (def + fj+) 46 | (def - fj-) 47 | (def * fj*) 48 | (def / fj_) 49 | (def < fj<) 50 | (def > fj>) 51 | (def <= fj<=) 52 | (def >= fj>=))) 53 | 54 | (defmethod clojure.core/print-method fjv [x writer] 55 | (.write writer (str x)))) 56 | -------------------------------------------------------------------------------- /src/frinj/node.cljs: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Martin Trojer. All rights reserved. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns frinj.node 10 | (:require [frinj.core :as core] 11 | [cljs.reader :as reader])) 12 | 13 | ;; NodeJS specific functions 14 | 15 | (defn frinj-init! [filename] 16 | (let [fs (js/require "fs") 17 | edn (fs/readFileSync "resources/units.edn") 18 | state-edn (reader/read-string (str edn))] 19 | (core/restore-state! state-edn))) 20 | -------------------------------------------------------------------------------- /src/frinj/ops.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Martin Trojer. All rights reserved. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns frinj.ops 10 | (:require [frinj.core :as core] 11 | [frinj.cross :as cross] 12 | [frinj.parser :as parser]) 13 | (:import frinj.core.fjv)) 14 | 15 | ;; ================================================================= 16 | ;; fjv creation and conversion 17 | 18 | (defn- get-seconds 19 | "Get number of seconds since EPOC given a yyyy-mm-dd datestring" 20 | [ds] 21 | (let [df (java.text.SimpleDateFormat. "yyyy-MM-dd") 22 | date (if (= (.toLowerCase ds) "now") (java.util.Date.) (.parse df ds))] 23 | (/ (.getTime date) 1000))) 24 | 25 | (defn- map-fj-operators 26 | "Maps (fj) operators to tokens" 27 | [os] 28 | (when @core/*debug* (println "map-fj-ops" os)) 29 | (loop [acc [], to nil, [fst snd & rst] os] 30 | (let [r (into rst [snd])] 31 | (if-not (nil? fst) 32 | (cond 33 | (number? fst) (recur (conj acc [:number fst]) to r) 34 | (= :per fst) (recur (conj acc [:divide]) to r) 35 | (= :to fst) (if (and (not (nil? snd)) (or (keyword? snd) (string? snd))) 36 | (let [tos (name snd)] 37 | (if-not (= tos "to") 38 | (recur acc tos rst) 39 | (cross/throw-exception "invalid to target"))) 40 | (cross/throw-exception "invalid to target")) 41 | (string? fst) (recur (conj acc [:unit fst]) to r) 42 | (keyword? fst) (let [name (name fst)] 43 | (if (cross/starts-with name "#") 44 | (recur (into acc [[:number (get-seconds (cross/sub-string name " "))] 45 | [:unit "s"]]) 46 | to r) 47 | (recur (conj acc [:unit name]) to r))) 48 | :else (cross/throw-exception (str "unsupported operator " fst))) 49 | [acc to])))) 50 | 51 | ;; ================================================================= 52 | ;; Operators 53 | 54 | (def add-unit! core/add-unit!) 55 | (def one core/one) 56 | (def zero core/zero) 57 | 58 | (defn fj 59 | "Convenience function to build fjv's" 60 | ([] core/one) 61 | ([& os] 62 | (let [[toks to] (map-fj-operators os) 63 | [u fact _] (parser/eat-units toks) 64 | fj (core/resolve-and-normalize-units u) 65 | res (fjv. (* fact (:v fj)) (:u fj))] 66 | (if to 67 | (core/clean-units (core/convert res {to 1})) 68 | res)))) 69 | 70 | (defn to 71 | "Convert a fjv to a unit. Unit is specified with (fj) ops" 72 | [fj & os] 73 | (when @core/*debug* (println "to" fj os)) 74 | (let [[toks _] (map-fj-operators os) 75 | [u fact] (parser/eat-units toks) 76 | cfj (core/convert fj u) 77 | res (fjv. (/ (:v cfj) fact) (:u cfj))] 78 | (core/clean-units res))) 79 | 80 | (defn to-date 81 | "Convert a fj of units {s 1} to a date string" 82 | [fj] 83 | (let [fj (core/clean-units fj)] 84 | (if (= (:u fj) {"s" 1}) 85 | (let [date (java.util.Date. (long (* 1000 (:v fj))))] 86 | (str date)) 87 | (cross/throw-exception "cannot convert type to a date")))) 88 | 89 | (defn find-units [s] 90 | (let [pat (re-pattern s)] 91 | (->> @core/state :units 92 | (filter #(re-find pat (first %))) 93 | (map (fn [[n u]] {:name n :unit (str u)}))))) 94 | 95 | (defn find-fundamentals [s] 96 | (let [fus (->> @core/state :fundamental-units) 97 | pat (re-pattern s)] 98 | (->> (zipmap (vals fus) (keys fus)) 99 | (filter #(re-find pat (first %))) 100 | (map (fn [[n us]] {:name n :unit us}))))) 101 | 102 | (def fj+ core/fj-add) 103 | (def fj- core/fj-sub) 104 | (def fj* core/fj-mul) 105 | (def fj_ core/fj-div) ;; can't find a better character for div! 106 | (def fj** core/fj-int-pow) 107 | (def fj= core/fj-equal?) 108 | (def fj< core/fj-less?) 109 | (def fj> core/fj-greater?) 110 | (def fj<= core/fj-less-or-equal?) 111 | (def fj>= core/fj-greater-or-equal?) 112 | -------------------------------------------------------------------------------- /src/frinj/parser.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Martin Trojer. All rights reserved. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns frinj.parser 10 | (:require [frinj.core :as core] 11 | [frinj.cross :as cross]) 12 | (:import frinj.core.fjv)) 13 | 14 | (def ^{:dynamic true} *trace* (atom false)) ;; trace parse results 15 | (defn enable-trace! [] (reset! *trace* true)) 16 | 17 | ;; ================================================================= 18 | ;; tokenizer 19 | 20 | ;; operators 21 | ;; ::- prefix to unit and stand-by-itelsef value 22 | ;; :- prefix to unit (can't stand by itself) 23 | ;; := assignment 24 | ;; =!= fundamental type (base dimension) 25 | ;; ||| human readable name 26 | 27 | (defn tokenize 28 | "Convert a string to a vector of (tagged-list) tokens" 29 | [data] 30 | (loop [res [], st :name, acc "", [fst snd thrd & rst] data] 31 | (let [r (into rst [thrd snd]) 32 | r2 (into rst [thrd]) 33 | append-acc (fn [] (if (empty? acc) 34 | res 35 | (let [acc (if (= (first acc) \.) (str "0" acc) acc) 36 | r (try ;; handle frinks "1ee12" form 37 | (read-string acc) 38 | (catch Exception e 39 | (try 40 | (read-string (.replaceFirst acc "ee" "e")) 41 | (catch Exception e acc))))] 42 | (if (number? r) 43 | (conj res [:number r]) 44 | (if (= st :name) 45 | (conj res [:name acc]) 46 | (conj res [:unit acc]))))))] 47 | (cond 48 | (nil? fst) (append-acc) 49 | ;; "=!=" 50 | (and (= fst \=) (= snd \!) (= thrd \=)) (recur 51 | (conj (append-acc) 52 | [:fundamental]) 53 | :unit "" rst) 54 | ;; "|||" 55 | (and (= fst \|) (= snd \|) (= thrd \|)) (recur 56 | (conj (append-acc) 57 | [:unit-combination]) 58 | :unit "" rst) 59 | ;; "::-" 60 | (and (= fst \:) (= snd \:) (= thrd \-)) (recur 61 | (conj (append-acc) 62 | [:standalone-prefix]) 63 | :unit "" rst) 64 | ;; ":-" 65 | (and (= fst \:) (= snd \-)) (recur (conj (append-acc) [:prefix]) 66 | :unit "" r2) 67 | ;; ":=" 68 | (and (= fst \:) (= snd \=)) (recur (conj (append-acc) [:assign]) 69 | :unit "" r2) 70 | 71 | ;; "//" 72 | (and (= fst \/) (= snd \/)) (let [c (take-while #(not= % \newline) r2) 73 | rst (drop (inc (count c)) r2)] 74 | (recur 75 | (conj (append-acc) [:comment (.trim (apply str c))]) 76 | ; (append-acc) ; drop all comments 77 | :name "" rst)) 78 | 79 | (= fst \() (recur (conj (append-acc) [:open]) st "" r) 80 | (= fst \)) (recur (conj (append-acc) [:close]) st "" r) 81 | (= fst \[) (recur (conj (append-acc) [:open-bracket]) st "" r) 82 | (= fst \]) (recur (conj (append-acc) [:close-bracket]) st "" r) 83 | (= fst \+) (recur (conj (append-acc) [:plus]) st "" r) 84 | ;; (= fst \-) (recur (conj (append-acc) [:minus]) st "" r) 85 | (= fst \*) (recur (conj (append-acc) [:multiply]) st "" r) 86 | (= fst \/) (recur (conj (append-acc) [:divide]) st "" r) 87 | (= fst \^) (recur (conj (append-acc) [:exp]) st "" r) 88 | (= fst \newline) (recur (append-acc) :name "" r) 89 | (Character/isWhitespace fst) (recur (append-acc) st "" r) 90 | :else (recur res st (str acc fst) r))))) 91 | 92 | ;; (time (def a (tokenize (slurp "units.txt")))) 93 | ;; (count a) 94 | 95 | ;; ================================================================= 96 | ;; parser 97 | 98 | (defn eat-number 99 | "Parse a set of tokens extracing a number in finite set of formats (as seen in frink's unit.txt file" 100 | [[[t1 v1] [t2 v2 :as snd] [t3 v3 :as thrd] [t4 v4 :as foth] [t5 _ :as ffth] & rst]] 101 | (cond 102 | ;; (num / num) 103 | (and (= t1 :open) (= t2 :number) (= t3 :divide) (= t4 :number) (= t5 :close)) 104 | [(/ v2 v4) rst] 105 | ;; num / num 106 | (and (= t1 :number) (= t2 :divide) (= t3 :number)) 107 | [(/ v1 v3) (into rst [ffth foth])] 108 | ;; num ^ num 109 | (and (= t1 :number) (= t2 :exp) (= t3 :number)) 110 | [(Math/pow v1 v3) (into rst [ffth foth])] 111 | ;; num 112 | (= t1 :number) 113 | [v1 (into rst [ffth foth thrd snd])] 114 | :else (cross/throw-exception "parse error, number expected"))) 115 | 116 | (defn eat-units 117 | "Parse a set of tokens accumulating factors and units, frink style syntax with impied muls" 118 | [toks] 119 | (loop [acc {}, acc-fact 1, s :n, in-par false, 120 | [[t1 v1] [t2 _ :as snd] & rst :as toks] toks] 121 | 122 | (when @core/*debug* (println "eat-units" acc acc-fact s in-par toks)) 123 | (let [r (into rst [snd]) 124 | old (get acc v1) 125 | old (if (nil? old) 0 old)] 126 | (cond 127 | ;; u^number 128 | (and (= t1 :unit) (= t2 :exp)) (let [[n rst] (eat-number rst)] 129 | (recur 130 | (assoc acc v1 131 | (if (= s :n) 132 | (+ old n) 133 | (- old n))) 134 | acc-fact s in-par rst)) 135 | ;; u 136 | (= t1 :unit) (recur (assoc acc v1 (if (= s :n) 137 | (+ old 1) 138 | (- old 1))) 139 | acc-fact (if in-par s :n) in-par r) 140 | ;; switch fron nom to demon 141 | (= t1 :divide) (recur acc acc-fact :d in-par r) 142 | ;; this is hacky, nested pars will not work 143 | (= t1 :open) (recur acc acc-fact true true r) 144 | (= t1 :close) (recur acc acc-fact false false r) 145 | ;; mul is implied 146 | (= t1 :multiply) (recur acc acc-fact s in-par r) 147 | ;; skip comments 148 | (= t1 :comment) (recur acc acc-fact s in-par r) 149 | (= t1 :number) (recur acc 150 | (if (= s :n) 151 | (* acc-fact v1) 152 | (/ acc-fact v1)) 153 | (if in-par s :n) in-par r) 154 | (= t1 :plus) (cross/throw-exception "unexpected operator") 155 | :else [acc acc-fact toks])))) 156 | 157 | ;; the parser mutates the state directly, this is because of the nature of 158 | ;; the frink configuration file where dependencies between prefixes and units 159 | ;; are incrementally built up 160 | 161 | (defn parse! 162 | "Parses a list of tokens, mutates units/fundamenta/prefix state directly" 163 | [toks] 164 | (letfn [ 165 | (eat-prefix [[[t v :as fst] & rst]] 166 | (when @core/*debug* (println "eat-prefix" fst)) 167 | (cond 168 | (= t :number) 169 | (let [[n rst] (eat-number (into rst [fst]))] 170 | (if (= (ffirst rst) :unit) 171 | ;; name ::- number unit+ 172 | (let [[u fact rst] (eat-units rst)] 173 | (when @core/*debug* (println "eat-pfx" u fact)) 174 | [(fjv. (* fact n) u) rst]) 175 | ;; name ::- number 176 | [(fjv. n {}) rst])) 177 | (= t :unit) 178 | (if (core/prefix? v) 179 | [(core/lookup-prefix v) rst] 180 | (cross/throw-exception "trying to assign to unknown prefix")))) 181 | 182 | (do-parse [acc [[t1 v1 :as fst] [t2 v2 :as snd] [t3 v3 :as thrd] & rst :as toks]] 183 | ;; (println "parse" toks) 184 | (let [r (into rst [thrd snd])] 185 | (cond 186 | (nil? t1) true 187 | 188 | ;; prefix definitions 189 | (and (= t1 :name) (= t2 :standalone-prefix)) 190 | (let [[fj rst] (eat-prefix (into rst [thrd])) 191 | rfj (core/resolve-and-normalize-units (:u fj)) 192 | nfj (fjv. (* (:v fj) (:v rfj)) (:u rfj))] 193 | (when @*trace* (println v1 "::-" nfj)) 194 | (core/add-with-plurals! :standalone-prefixes v1 nfj) 195 | (recur [] rst)) 196 | (and (= t1 :name) (= t2 :prefix)) 197 | (let [[fj rst] (eat-prefix (into rst [thrd])) 198 | rfj (core/resolve-and-normalize-units (:u fj)) 199 | nfj (fjv. (* (:v fj) (:v rfj)) (:u rfj))] 200 | (when @*trace* (println v1 ":-" nfj)) 201 | (swap! core/state assoc-in [:prefixes v1] nfj) 202 | (recur [] rst)) 203 | 204 | ;; fundamentals 205 | (and (= t1 :name) (= t2 :fundamental) (= t3 :unit)) 206 | (let [u {v3 1}] 207 | (swap! core/state assoc-in [:fundamental-units u] v1) 208 | (swap! core/state update-in [:fundamentals] #(conj % v3)) 209 | (swap! core/state assoc-in [:units v3] core/one) 210 | (when @*trace* (println v1 "=!=" u)) 211 | (recur [] rst)) 212 | 213 | ;; unit combinations 214 | (and (= t1 :unit-combination) (= t2 :unit) (not (empty? acc))) 215 | (let [acc (map (fn [[t v]] [(if (= t :name) :unit t) v]) acc) 216 | [u _ _] (eat-units acc) 217 | rv (core/normalize-units (fjv. 1 u))] 218 | (swap! core/state assoc-in [:fundamental-units (:u rv)] v2) 219 | (when @*trace* (println v2 "|||" (:u rv))) 220 | (recur [] (into rst [thrd]))) 221 | 222 | ;; unit definition 223 | ;; name := number unit+ 224 | (and (= t1 :name) (= t2 :assign) (= t3 :number)) 225 | (let [[n rst] (eat-number (into rst [thrd])) 226 | [u fact rst] (eat-units rst) 227 | fj (core/resolve-and-normalize-units u) 228 | nfact (* fact n (:v fj)) 229 | nu (:u fj)] 230 | (core/add-unit! v1 (fjv. nfact nu)) 231 | (when @*trace* (println v1 ":=" nfact nu)) 232 | (recur [] rst)) 233 | ;; name := unit+ 234 | (and (= t1 :name) (= t2 :assign) (= t3 :unit)) 235 | (let [[u fact rst] (eat-units (into rst [thrd])) 236 | fj (core/resolve-and-normalize-units u) 237 | nfact (* fact (:v fj)) 238 | nu (:u fj)] 239 | (core/add-unit! v1 (fjv. nfact nu)) 240 | (when @*trace* (println v1 ":=" nfact nu )) 241 | (recur [] rst)) 242 | 243 | :else (recur (conj acc fst) r))))] 244 | 245 | (do-parse [] toks))) 246 | 247 | (defn restore-state-from-text! 248 | "Restores state by parsing a sequence of Frink lines" 249 | [lines] 250 | (core/reset-state!) 251 | (doseq [line lines] 252 | (-> line tokenize parse!))) 253 | -------------------------------------------------------------------------------- /src/frinj/repl.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Martin Trojer. All rights reserved. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns frinj.repl 10 | (:require [frinj.jvm] 11 | [frinj.infix] 12 | [frinj.ops])) 13 | 14 | (defn immigrate 15 | "Create a public var in this namespace for each public var in the 16 | namespaces named by ns-names. The created vars have the same name, value 17 | and metadata as the original except that their :ns metadata value is this 18 | namespace." 19 | [& ns-names] 20 | (doseq [ns ns-names] 21 | (doseq [[sym var] (ns-publics ns)] 22 | (let [sym (with-meta sym (assoc (meta var) :orig-ns ns))] 23 | (if (.isBound var) 24 | (intern *ns* sym (var-get var)) 25 | (intern *ns* sym)))))) 26 | 27 | (immigrate 'frinj.ops 'frinj.infix 'frinj.jvm) 28 | -------------------------------------------------------------------------------- /test/frinj/test/core_test.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Martin Trojer. All rights reserved. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns frinj.test.core-test 10 | (:use [clojure.test] 11 | [frinj.core]) 12 | (:import frinj.core.fjv)) 13 | 14 | (defn- core-test-fixture [f] 15 | (reset-state!) 16 | (f)) 17 | 18 | (use-fixtures :each core-test-fixture) 19 | 20 | (deftest add-u 21 | (do 22 | (add-unit! "one" one) 23 | (is (= (:units @state) {"one" one "ones" one}))) 24 | ) 25 | 26 | (deftest add-u2 27 | (do 28 | (add-unit! "his" one) 29 | (is (= (:units @state) {"his" one})))) 30 | 31 | 32 | (deftest add-u3 33 | (do 34 | (add-unit! "k" one) 35 | (is (= (:units @state) {"k" one}))) 36 | ) 37 | 38 | (deftest add-us 39 | (is (= nil (add-units))) 40 | (is (= {} (add-units {}))) 41 | (is (= {:m 1} (add-units {:m 1}))) 42 | (is (= {:m 1 :s 1} (add-units {:m 1 :s 1}))) 43 | (is (= {:m 3 :b 0} (add-units {:m 1} {:m 2 :b 0}))) 44 | (is (= {:m 6 :watts 23} (add-units {:m 1} {:m 2} {:m 3 :watts 23}))) 45 | (is (= {:m 0} (add-units {:m 1} {:m -1}))) 46 | (is (= {:m 1} (add-units {:m -1} {:m 2}))) 47 | ) 48 | 49 | (deftest clean 50 | (is (= one (clean-units one))) 51 | (is (= (fjv. 1 {:m 1}) (clean-units (fjv. 1 {:m 1})))) 52 | (is (= (fjv. 1 {:m 1}) (clean-units (fjv. 1 {:m 1 :s 0})))) 53 | (is (= one (clean-units (fjv. 1 {:m 0 :s 0})))) 54 | (is (= (fjv. 1 {:m -1}) (clean-units (fjv. 1 {:m -1, :s 0, :k 0})))) 55 | ) 56 | 57 | (deftest add 58 | (is (nil? (fj-add))) 59 | (is (= one (fj-add one))) 60 | (is (= (fjv. 2 {}) (fj-add one 1))) 61 | (is (= (fjv. 1 {:m 1}) (fj-add (fjv. 1 {:m 1})))) 62 | (is (= (fjv. 2 {:m 1}) (fj-add (fjv. 1 {:m 1}) (fjv. 1 {:m 1})))) 63 | (is (= (fjv. 0 {:m 1}) (fj-add (fjv. 1 {:m 1}) (fjv. -1 {:m 1})))) 64 | (is (= (fjv. 2 {:m 1 :s -1}) 65 | (fj-add (fjv. 1 {:m 1 :s -1}) (fjv. 1 {:m 1 :s -1})))) 66 | (is (= (fjv. 3 {}) (fj-add one one one))) 67 | (is (thrown? Exception (fj-add (fjv. 1 {:m 1}) one))) 68 | (is (thrown? Exception (fj-add (fjv. 1 {:m 1}) (fjv. 1 {:s 1})))) 69 | 70 | (is (= (fjv. 2 {}) (fj-add 1 1))) 71 | (is (= (fjv. 2 {}) (fj-add 1 one))) 72 | (is (thrown? Exception (fj-add 1 (fjv. 1 {"m" 1})))) 73 | ) 74 | 75 | (deftest sub 76 | (is (nil? (fj-sub))) 77 | (is (= one (fj-sub one))) 78 | (is (= zero (fj-sub one 1))) 79 | (is (= (fjv. 1 {:m 1}) (fj-sub (fjv. 1 {:m 1})))) 80 | (is (= (fjv. 0 {:m 1}) (fj-sub (fjv. 1 {:m 1}) (fjv. 1 {:m 1})))) 81 | (is (= (fjv. 2 {:m 1}) (fj-sub (fjv. 1 {:m 1}) (fjv. -1 {:m 1})))) 82 | (is (= (fjv. 0 {:m 1 :s -1}) 83 | (fj-sub (fjv. 1 {:m 1 :s -1}) (fjv. 1 {:m 1 :s -1})))) 84 | (is (= (fjv. -1 {}) (fj-sub one one one))) 85 | (is (thrown? Exception (fj-sub (fjv. 1 {:m 1}) one))) 86 | (is (thrown? Exception (fj-sub (fjv. 1 {:m 1}) (fjv. 1 {:s 1})))) 87 | ) 88 | 89 | (deftest mul 90 | (is (nil? (fj-mul))) 91 | (is (= one (fj-mul one))) 92 | (is (= one (fj-mul one one))) 93 | (is (= one (fj-mul one 1))) 94 | (is (= (fjv. 1 {:m 2}) (fj-mul (fjv. 1 {:m 1}) (fjv. 1 {:m 1})))) 95 | (is (= (fjv. 1 {:m 0}) (fj-mul (fjv. 1 {:m 1}) (fjv. 1 {:m -1})))) 96 | (is (= (fjv. 9 {:m 1 :s -1}) 97 | (fj-mul (fjv. 3 {:m 1}) (fjv. 3 {:s -1})))) 98 | (is (= (fjv. 3.14 {:m 1}) 99 | (fj-mul one (fjv. 3.14 {:m 1})))) 100 | (is (= (fjv. 0 {:m 1}) (fj-mul (fjv. 3 {:m 1}) zero))) 101 | 102 | (is (= (fjv. 2 {}) (fj-mul 1 2))) 103 | (is (= (fjv. 2 {}) (fj-mul 1 (fjv. 2 {})))) 104 | (is (= (fjv. 2 {"m" 1}) (fj-mul 2 (fjv. 1 {"m" 1})))) 105 | ) 106 | 107 | (deftest div 108 | (is (nil? (fj-div))) 109 | (is (= one (fj-div one))) 110 | (is (= one (fj-div one one))) 111 | (is (= one (fj-div one 1))) 112 | (is (= (fjv. 1 {:m 0}) (fj-div (fjv. 1 {:m 1}) (fjv. 1 {:m 1})))) 113 | (is (= (fjv. 1 {:m -2}) (fj-div one (fjv. 1 {:m 2})))) 114 | (is (= (fjv. 1/2 {:m 1}) (fj-div one (fjv. 2 {:m -1})))) 115 | (is (= (fjv. 3 {:m 1 :s -1}) 116 | (fj-div (fjv. 1 {:m 1}) (fjv. 1/3 {:s 1})))) 117 | (is (= (fjv. 9 {:m 1}) (fj-div one (fjv. 1/9 {:m -1})))) 118 | (is (thrown? Exception (fj-div one zero))) 119 | (is (= (fjv. 0 {:m -1}) (fj-div zero (fjv. 1 {:m 1})))) 120 | ) 121 | 122 | (deftest power 123 | (is (= one (fj-int-pow one 1))) 124 | (is (= one (fj-int-pow one 0))) 125 | (is (= (fjv. 1 {:m 2}) (fj-int-pow (fjv. 1 {:m 1}) 2))) 126 | (is (= (fjv. 8 {:m -3}) (fj-int-pow (fjv. 2 {:m -1}) 3))) 127 | (is (= (fjv. 4 {:m 2 :s -2}) 128 | (fj-int-pow (fjv. 2 {:m 1 :s -1}) 2))) 129 | (is (thrown? Exception (fj-int-pow one 0.5))) 130 | (is (= one (fj-int-pow one -1))) 131 | (is (= (fjv. 4 {}) (fj-int-pow (fjv. 1/2 {}) -2))) 132 | (is (= (fjv. 1/4 {:m -2}) 133 | (fj-int-pow (fjv. 2 {:m 1}) -2))) 134 | (is (= (fjv. 1 {:m 3}) (fj-int-pow (fjv. 1 {:m -1}) -3))) 135 | (is (thrown? Exception (fj-int-pow zero -1))) 136 | ) 137 | 138 | (deftest inverse 139 | (is (= one (fj-inverse one))) 140 | (is (= (fjv. 1/2 {"m" -1 "s" 1}) 141 | (fj-inverse (fjv. 2 {"m" 1 "s" -1})))) 142 | ) 143 | 144 | (deftest equal 145 | (is (fj-equal? one)) 146 | (is (fj-equal? one one)) 147 | (is (fj-equal? one one one)) 148 | (is (not (fj-equal? one zero))) 149 | (is (not (fj-equal? one one zero))) 150 | (is (fj-equal? (fjv. 1 {:m 1}) (fjv. 1 {:m 1}))) 151 | (is (fj-equal? (fjv. 1.1 {:m 1}) (fjv. 1.1 {:m 1}))) 152 | (is (fj-equal? (fjv. 1/2 {:s -1 :m 1}) (fjv. 1/2 {:m 1 :s -1}))) 153 | (is (fj-equal? (fjv. 1 {:m 1}) (fjv. 1 {:m 1 :s 0}))) 154 | ) 155 | 156 | (deftest not-equal 157 | (is (not (fj-not-equal? one))) 158 | (is (not (fj-not-equal? one one))) 159 | (is (not (fj-not-equal? one one one))) 160 | (is (fj-not-equal? one zero)) 161 | (is (fj-not-equal? one one zero)) 162 | ) 163 | 164 | (deftest less 165 | (is (fj-less? one)) 166 | (is (fj-less? zero one)) 167 | (is (fj-less? zero one (fjv. 2 {}))) 168 | (is (not (fj-less? one one))) 169 | (is (not (fj-less? one zero))) 170 | ) 171 | 172 | (deftest greater 173 | (is (fj-greater? one)) 174 | (is (fj-greater? one zero)) 175 | (is (fj-greater? (fjv. 2 {}) one zero)) 176 | (is (not (fj-greater? one one))) 177 | (is (not (fj-greater? zero one))) 178 | ) 179 | 180 | (deftest less-or-equal 181 | (is (fj-less-or-equal? one)) 182 | (is (fj-less-or-equal? one one)) 183 | (is (fj-less-or-equal? zero one)) 184 | (is (fj-less-or-equal? zero one (fjv. 2 {}))) 185 | (is (not (fj-less-or-equal? one zero))) 186 | (is (fj-less-or-equal? (fjv. 1.1 {:m 1}) (fjv. 1.1 {:m 1}))) 187 | ) 188 | 189 | (deftest greater-or-equal 190 | (is (fj-greater-or-equal? one)) 191 | (is (fj-greater-or-equal? one zero)) 192 | (is (fj-greater-or-equal? (fjv. 2 {}) one zero)) 193 | (is (fj-greater-or-equal? one one)) 194 | (is (not (fj-greater-or-equal? zero one))) 195 | (is (fj-greater-or-equal? (fjv. 1.1 {:m 1}) (fjv. 1.1 {:m 1}))) 196 | ) 197 | -------------------------------------------------------------------------------- /test/frinj/test/ops.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Martin Trojer. All rights reserved. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns frinj.test.ops 10 | (:use [clojure.test] 11 | [frinj.core :only [reset-state! state]] 12 | [frinj.ops]) 13 | (:import frinj.core.fjv)) 14 | 15 | (defn- calc-test-fixture [f] 16 | (reset-state!) 17 | (swap! state assoc-in [:prefixes "d"] (fjv. 1/10 {})) 18 | (swap! state assoc-in [:prefixes "c"] (fjv. 1/100 {})) 19 | (swap! state assoc-in [:standalone-prefixes "kilo"] (fjv. 1000 {})) 20 | (swap! state assoc-in [:prefixes "k"] (fjv. 1000 {})) 21 | (swap! state assoc-in [:units "m"] (fjv. 1 {})) 22 | (swap! state assoc-in [:units "s"] (fjv. 1 {})) 23 | (swap! state assoc-in [:units "pi"] (fjv. 3.14 {})) 24 | (swap! state assoc-in [:units "inch"] (fjv. 127/5000 {"m" 1})) 25 | (swap! state assoc-in [:fundamental-units {"m" 1}] (fjv. 1 {})) 26 | (swap! state update-in [:fundamentals] #(conj % "m")) 27 | (swap! state update-in [:fundamentals] #(conj % "s")) 28 | (f)) 29 | 30 | (use-fixtures :once calc-test-fixture) 31 | 32 | (deftest test-fj 33 | (is (= one (fj))) 34 | (is (= (fjv. 1 {"m" 1}) (fj "m"))) 35 | (is (= (fjv. 1 {"m" 1}) (fj :m))) 36 | (is (= (fjv. 1/50 {"m" 1}) (fj 2 :cm))) 37 | (is (= (fjv. 1/5 {"m" 1}) (fj :dm 2))) 38 | 39 | (is (= (fjv. 3.14 {"m" 1 "s" -1}) 40 | (fj 3.14 :m :per :s))) 41 | (is (= (fjv. 1/2 {"m" 1 "s" 1}) 42 | (fj :m :per 2 :s))) 43 | (is (= (fjv. 1/2 {"m" 1 "s" -1}) 44 | (fj :m :per 2 :per :s))) 45 | 46 | (is (= (fjv. 50/127 {}) (fj :cm :to :inch))) 47 | (is (= one (fj :cm :to :cm))) 48 | (is (= one (fj :cm :per :cm))) 49 | (is (thrown? Exception (fj :cm :to))) 50 | (is (thrown? Exception (fj :cm :to :to))) 51 | (is (thrown? Exception (fj :cm :to 1))) 52 | 53 | (is (= (fjv. 3.14 {"s" -1}) (fj :pi :per :s))) 54 | (is (thrown? Exception (fj :cm :to :s))) 55 | 56 | (is (= (fjv. 1286492400 {"s" 1}) 57 | (fj :#2010-10-08))) 58 | 59 | (is (fj<= (fj :#now) (fj :#now))) 60 | 61 | (is (= (fjv. 1/2 {}) (fj 1 :per 2))) 62 | ) 63 | 64 | (deftest test-to 65 | (is (= one (to one 1))) 66 | (is (= (fjv. 1/2 {}) (to one 2))) 67 | (is (= (fjv. 50/127 {}) (to (fjv. 1 {"cm" 1}) "inch"))) 68 | (is (= (fjv. 50/127 {}) (to (fjv. 1 {"cm" 1}) :inch))) 69 | (is (= (fjv. 127/500 {}) (to (fjv. 1 {"inch" 1}) 10 :cm))) 70 | ) 71 | -------------------------------------------------------------------------------- /test/frinj/test/parser.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Martin Trojer. All rights reserved. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns frinj.test.parser 10 | (:use [clojure.test] 11 | [frinj.core :only [state reset-state! lookup-prefix]] 12 | [frinj.ops] 13 | [frinj.parser] 14 | [frinj.jvm]) 15 | (:import frinj.core.fjv)) 16 | 17 | (defn- parser-test-fixture [f] 18 | (reset-state!) 19 | 20 | (print "parsing...") 21 | (flush) 22 | (let [start (System/currentTimeMillis)] 23 | (load-unit-txt-file!) 24 | (println (- (System/currentTimeMillis) start) "ms")) 25 | (f)) 26 | 27 | (use-fixtures :once parser-test-fixture) 28 | 29 | (deftest parsed-units 30 | (is (= (fjv. 0.06032246 {"m" 2}) (get-in @state [:units "lettersize"]))) 31 | (is (= (fjv. 0.06032246 {"m" 2}) (get-in @state [:units "lettersizes"]))) 32 | (is (= (fjv. 1.2566370614359173E-6 {"A" -2, "s" -2, "m" 1, "kg" 1}) 33 | (get-in @state [:units "mu0"]))) 34 | (is (= (fjv. 1.0545717253362894E-34 {"kg" 1 "m" 2 "s" -1}) 35 | (get-in @state [:units "hbar"]))) 36 | (is (= (fjv. 127/5000 {"m" 1}) (get-in @state [:units "inch"]))) 37 | (is (= (fjv. 381/1250 {"m" 1}) (get-in @state [:units "foot"]))) 38 | (is (= (fjv. 5.067074790974977E-10 {"m" 2}) 39 | (get-in @state [:units "circularmil"]))) 40 | (is (= (fjv. 1.0e-26 {"kg" 1 "s" -2}) (get-in @state [:units "fluxunit"]))) 41 | (is (= (fjv. 498951607/4800000000 {"kg" 1}) 42 | (get-in @state [:units "liang"]))) 43 | (is (= one (get-in @state [:units "steradian"]))) 44 | (is (= one (get-in @state [:units "sr"]))) 45 | (is (= (fjv. 99/200 {"m" 1}) (get-in @state [:units "sumeriancubit"]))) 46 | (is (= (fjv. 1 {"m" -1}) (get-in @state [:units "diopter"]))) 47 | (is (= (fjv. 1.0594630943592953 {}) (get-in @state [:units "semitone"]))) 48 | (is (= (fjv. 2728034111/10000000000 {"kg" 1}) 49 | (get-in @state [:units "romanaspound"]))) 50 | (is (= (fjv. 45359237/9290304000 {"m" -2 "kg" 1}) 51 | (get-in @state [:units "poundboxboard"]))) 52 | (is (= (fjv. 9.27400967298574E-24 {"m" 2, "A" 1}) 53 | (get-in @state [:units "bohrmagneton"]))) 54 | (is (= (fjv. 10967760 {"m" -1}) (get-in @state [:units "R_H"]))) 55 | (is (= (fjv. 2.176509252445312E-8 {"kg" 1}) 56 | (get-in @state [:units "planckmass"]))) 57 | (is (= (get-in @state [:units "lightspeed"]) (get-in @state [:units "c"]))) 58 | (is (= (fjv. 341/250000 {"m" 2}) (get-in @state [:units "B10paper"]))) 59 | (is (= (fjv. 4.67227968768 {"m" 3}) (get-in @state [:units "standard"]))) 60 | (is (= (fjv. 884901456/244140625 {"m" 3}) 61 | (get-in @state [:units "cord"]))) 62 | (is (= (fjv. 0.0010571721050064 {"m" 3}) 63 | (get-in @state [:units "chenice"]))) 64 | (is (= (fjv. 0.042930491929835245 {"m" 1}) 65 | (get-in @state [:units "size2ring"]))) 66 | (is (= (fjv. 3.141592653589793 {}) (get-in @state [:units "pi"]))) 67 | (is (= (fjv. 12.566370614359172 {}) (get-in @state [:units "sphere"]))) 68 | (is (= (fjv. 8047590469303/42500000000000 {"m" 2 "kg" 1}) 69 | (get-in @state [:units "lbledger"]))) 70 | (is (= (fjv. 8047590469303/42500000000000 {"m" 2 "kg" 1}) 71 | (get-in @state [:units "poundledgerpaper"]))) 72 | (is (= (fjv. 45359237/9290304000 {"m" -2 "kg" 1}) 73 | (get-in @state [:units "poundboxboard"]))) 74 | (is (= (fjv. 13900383537887/62500000000000 {"m" 2 "kg" 1}) 75 | (get-in @state [:units "poundbookpaper"]))) 76 | (is (= (fjv. 1/1000 {"m" -2 "kg" 1}) (get-in @state [:units "gsm"]))) 77 | (is (= (fjv. 531441/524288 {}) (get-in @state [:units "pythagoreancomma"]))) 78 | (is (= (fjv. 9/8 {}) (get-in @state [:units "majorsecond"]))) 79 | (is (= (fjv. 0.22201759999999998 {"kg" 1 "mol" -1}) 80 | (get-in @state [:units "radon"]))) 81 | (is (= (fjv. 0.11232296715717348 {"m" 3}) 82 | (get-in @state [:units "irishbarrel"]))) 83 | (is (= (fjv. 5.1103059084E-4 {"m" 3}) (get-in @state [:units "log"]))) 84 | ) 85 | 86 | (deftest parsed-fundamentals 87 | (is (get-in @state [:fundamentals "m"])) 88 | (is (get-in @state [:fundamentals "kg"])) 89 | (is (get-in @state [:fundamentals "A"])) 90 | (is (get-in @state [:fundamentals "dollar"])) 91 | ) 92 | 93 | (deftest parsed-fund-units 94 | (is (= "concentration_by_mass" (get-in @state [:fundamental-units {"mol" 1 "kg" -1}]))) 95 | (is (= "concentration_by_volume" (get-in @state [:fundamental-units {"m" -3 "mol" 1}]))) 96 | (is (= "area" (get-in @state [:fundamental-units {"m" 2}]))) 97 | (is (= "volume" (get-in @state [:fundamental-units {"m" 3}]))) 98 | (is (= "energy" (get-in @state [:fundamental-units {"m" 2 "kg" 1 "s" -2}]))) 99 | (is (= "electric_charge_density" (get-in @state [:fundamental-units {"A" 1 "s" 1 "m" -3}]))) 100 | (is (= "moment_of_inertia" (get-in @state [:fundamental-units {"m" 2 "kg" 1}]))) 101 | ) 102 | 103 | (deftest parsed-prefixes 104 | (is (= (fjv. 1e24 {}) (lookup-prefix "yotta"))) 105 | (is (= (fjv. 1e24 {}) (lookup-prefix "yottas"))) 106 | (is (= (lookup-prefix "Y") (lookup-prefix "yotta"))) 107 | (is (= (fjv. 500000/499999 {}) (lookup-prefix "survey"))) 108 | (is (= (lookup-prefix "geodetic") (lookup-prefix "survey"))) 109 | (is (= (fjv. 1/2 {}) (lookup-prefix "half"))) 110 | (is (= (fjv. 1024.0 {}) (lookup-prefix "kibi"))) 111 | ) 112 | 113 | (deftest tok 114 | (is (= [] (tokenize ""))) 115 | (is (= [[:name "kalle"]] (tokenize "kalle"))) 116 | (is (= [[:comment "hello"]] (tokenize "//hello"))) 117 | (is (= [[:comment "hello"] [:name "world"]] 118 | (tokenize "//hello\nworld"))) 119 | (is (= [[:name "yotta"] [:standalone-prefix] [:number 1e24]] 120 | (tokenize "yotta ::- 1ee24"))) 121 | (is (= [[:name "y"] [:prefix] [:unit "yotta"]] 122 | (tokenize "y :- yotta"))) 123 | (is (= [[:name "vel"] [:assign] [:unit "m"] [:divide] [:unit "s"]] 124 | (tokenize "vel := m / s"))) 125 | (is (= [[:name "acc"] [:assign] [:unit "m"] [:divide] [:unit "s"] [:exp] [:number 2]] 126 | (tokenize "acc := m / s^2"))) 127 | (is (= [[:name "length"] [:fundamental] [:unit "m"]] 128 | (tokenize "length =!= m"))) 129 | (is (= [[:name "m"] [:exp] [:number 2] [:unit-combination] [:unit "area"]] 130 | (tokenize "m^2 ||| area"))) 131 | (is (= [[:name "pi"] [:assign] [:open] [:number 3.14] [:close]] 132 | (tokenize "pi := (3.14)"))) 133 | (is (= [[:name "c"] [:assign] [:number 299792458] [:unit "m"] [:divide] [:unit "s"]] 134 | (tokenize "c := 299792458 m/s"))) 135 | (is (= [[:open-bracket] [:close-bracket] [:plus] [:multiply]] 136 | (tokenize "[]+*"))) 137 | (is (= [[:name "a"] [:assign] [:unit "m"] [:name "b"] [:assign] [:unit "s"]] 138 | (tokenize "a := m \n b := s"))) 139 | (is (= [[:name "a"] [:assign] [:unit "b"]] 140 | (tokenize "a:=b"))) 141 | (is (= [[:name "a"] [:assign] [:unit "m"] [:name "b"] [:assign] [:unit "s"]] 142 | (tokenize "a:=m\nb:=s"))) 143 | ) 144 | -------------------------------------------------------------------------------- /test/frinj/test/pfxnorm.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Martin Trojer. All rights reserved. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns frinj.test.pfxnorm 10 | (:use [clojure.test] 11 | [frinj.core]) 12 | (:import frinj.core.fjv)) 13 | 14 | (defn- utils-test-fixture [f] 15 | (reset-state!) 16 | (swap! state assoc-in [:prefixes "d"] (fjv. 1/10 {})) 17 | (swap! state assoc-in [:prefixes "c"] (fjv. 1/100 {})) 18 | (swap! state assoc-in [:standalone-prefixes "kilo"] (fjv. 1000 {})) 19 | (swap! state assoc-in [:prefixes "k"] (fjv. 1000 {})) 20 | (swap! state assoc-in [:units "m"] (fjv. 1 {})) 21 | (swap! state assoc-in [:units "s"] (fjv. 1 {})) 22 | (swap! state assoc-in [:units "pi"] (fjv. 3.14 {})) 23 | (swap! state assoc-in [:units "inch"] (fjv. 127/5000 {"m" 1})) 24 | (swap! state assoc-in [:fundamental-units {"m" 1}] (fjv. 1 {})) 25 | (swap! state update-in [:fundamentals] #(conj % "m")) 26 | (swap! state update-in [:fundamentals] #(conj % "s")) 27 | (f)) 28 | 29 | (use-fixtures :once utils-test-fixture) 30 | 31 | (deftest pfx? 32 | (is (prefix? "d")) 33 | (is (prefix? "kilo")) 34 | (is (not (prefix? "kalle"))) 35 | ) 36 | 37 | (deftest all-pfx 38 | (is (= #{ "d" "c" "kilo" "k"} (into #{} (all-prefix-names)))) 39 | ) 40 | 41 | (deftest lookup-pfx 42 | (is (= (fjv. 1/10 {}) (lookup-prefix "d"))) 43 | (is (= (fjv. 1000 {}) (lookup-prefix "kilo"))) 44 | (is (nil? (lookup-prefix "kalle"))) 45 | ) 46 | 47 | (deftest resolve-pfxed-unit 48 | (is (= [(fjv. 1/100 {}) "m"] (resolve-prefixed-unit "cm"))) 49 | (is (= [one "cK"] (resolve-prefixed-unit "cK"))) 50 | (is (= [one "centim"] (resolve-prefixed-unit "centim"))) 51 | (is (= [one "pi"] (resolve-prefixed-unit "pi"))) 52 | ) 53 | 54 | (deftest resolve-unit-pfxs 55 | (is (= one (resolve-unit-prefixes {}))) 56 | (is (= (fjv. 1 {"m" 1}) (resolve-unit-prefixes {"m" 1}))) 57 | (is (= (fjv. 1/100 {"m" 1}) (resolve-unit-prefixes {"cm" 1}))) 58 | (is (= (fjv. 1 {"m" 1 "s" -1}) (resolve-unit-prefixes {"cm" 1 "cs" -1}))) 59 | (is (= (fjv. 10 {"s" -1}) (resolve-unit-prefixes {"ds" -1}))) 60 | ) 61 | 62 | (deftest norm-units 63 | (is (= one (normalize-units one))) 64 | (is (= (fjv. 1 {"m" 1 "s" -1}) (normalize-units (fjv. 1 {"m" 1 "s" -1})))) 65 | (is (= (fjv. 3.14 {}) (normalize-units (fjv. 1 {"pi" 1})))) 66 | (is (= (fjv. (* 3.14 3.14) {}) (normalize-units (fjv. 1 {"pi" 2})))) 67 | (is (= (fjv. (/ 1 3.14) {}) (normalize-units (fjv. 1 {"pi" -1})))) 68 | (is (= (fjv. (/ 1 (* 3.14 3.14)){}) 69 | (normalize-units (fjv. 1 {"pi" -2})))) 70 | (is (= (fjv. 3.14 {"m" 2}) (normalize-units (fjv. 1 {"pi" 1, "m" 2})))) 71 | (is (= (fjv. 1000 {"m" 1}) (normalize-units (fjv. 1 {"kilo" 1, "m" 1})))) 72 | ) 73 | 74 | (deftest r-n-m 75 | (is (= one (resolve-and-normalize-units {}))) 76 | (is (= (fjv. 3140.0 {"m" 1}) (resolve-and-normalize-units {"pi" 1 "km" 1}))) 77 | ) 78 | 79 | (deftest conv 80 | (is (= one (convert one {}))) 81 | (is (= (fjv. 3.14 {"m" 0 "s" 0}) (convert (fjv. 3.14 {"m" 1 "s" -1}) 82 | {"m" 1 "s" -1}))) 83 | (is (= (fjv. 1/100 {"m" 0}) (convert (fjv. 1 {"cm" 1}) {"m" 1}))) 84 | (is (= (fjv. 100 {"m" 0}) (convert (fjv. 1 {"m" 1}) {"cm" 1}))) 85 | (is (= (fjv. 1/50 {"m" 0}) (convert (fjv. 2 {"cm" 1}) {"m" 1}))) 86 | (is (= (fjv. 1/254 {"m" 0}) (convert (fjv. 1 {"dm" 1}) {"kinch" 1}))) 87 | 88 | (is (thrown? Exception (convert (fjv. 1 {"m" 1}) {"s" 1}))) 89 | 90 | ;; implicit inversion 91 | (is (= (fjv. 1/2 {"m" 0}) (convert (fjv. 2 {"m" 1}) {"m" -1}))) 92 | (is (= (fjv. 1/3 {"m" 0 "s" 0}) (convert (fjv. 3 {"m" -1 "s" 1}) {"m" 1 "s" -1}))) 93 | (is (thrown? Exception (convert (fjv. 3 {"m" -1 "s" 1}) {"m" 1 "s" 1}))) 94 | ) 95 | -------------------------------------------------------------------------------- /test/frinj/test/samplecalc.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Martin Trojer. All rights reserved. 2 | ;; The use and distribution terms for this software are covered by the 3 | ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ;; which can be found in the file epl-v10.html at the root of this distribution. 5 | ;; By using this software in any fashion, you are agreeing to be bound by 6 | ;; the terms of this license. 7 | ;; You must not remove this notice, or any other, from this software. 8 | 9 | (ns frinj.test.samplecalc 10 | (:use [clojure.test] 11 | [frinj.jvm] 12 | [frinj.ops]) 13 | (:import frinj.core.fjv)) 14 | 15 | (defn- samplecalc-test-fixture [f] 16 | (frinj-init!) 17 | 18 | (add-unit! :burnrate 19 | (fj_ 20 | (fj (- 86481 41601) :thousand :dollars) 21 | (fj- (fj :#2001-06-30) (fj :#2000-12-31)))) 22 | 23 | (f)) 24 | 25 | (use-fixtures :once samplecalc-test-fixture) 26 | 27 | (deftest samples 28 | (is (= (fjv. 552960/77 {}) 29 | (fj 10 :feet 12 :feet 8 :feet :to :gallons))) 30 | 31 | (is (= (fjv. 2718417272832/45359237 {}) 32 | (fj 10 :feet 12 :feet 8 :feet :water :to :pounds))) 33 | 34 | (is (= (fjv. 5669904625/10618817472 {}) 35 | (-> (fj_ (fj 2 :tons) 36 | (fj 10 :feet 12 :feet :water)) 37 | (to :feet)))) 38 | 39 | (is (= (fjv. 60224381/359040 {}) 40 | (-> (fj_ (fj 41601 :thousand :dollars) 41 | (fj :burnrate)) 42 | (to :days)))) 43 | 44 | (is (= "Fri Dec 14 16:41:38 GMT 2001" 45 | (-> (fj+ (fj :#2001-06-30) 46 | (fj_ (fj 41601 :thousand :dollars) 47 | (fj :burnrate))) 48 | to-date))) 49 | 50 | (is (= (fjv. 368175625/129048129 {}) 51 | (-> (fj :half :ton) (to :barrels :water)))) 52 | 53 | (is (= (fjv. 46037384521821/19375000000000 {}) 54 | (-> (fj 2 :fathoms :water :gravity :barrel) (to 40 :watts :minutes)))) 55 | 56 | (is (= (fjv. 15345794840607/11266562500000 {}) 57 | (fj 2 :fathoms :water :gravity :barrel :to :Calories))) 58 | 59 | (is (= (fjv. 1163/12 {}) 60 | (fj 2000 :Calories :per :day :to :watts))) 61 | 62 | (is (= (fjv. 800000000000/43161375789 {}) 63 | (-> (fj_ (fj 1100 :W 30 :sec) 64 | (fj 27 :oz 1 :calorie :per :gram :per :degC)) 65 | (to :degF)))) 66 | 67 | ) 68 | --------------------------------------------------------------------------------