# Typelevel unboxed compile time dimensional analysis over tagged types.

## sbt

**Scala: 2.11.11, 2.12.1, 2.12.2**

`libraryDependencies += "org.rudogma" %% "superquants" % "0.9"`

**ScalaJS (compiled with 0.6.17)**

`libraryDependencies += "org.rudogma" %%% "superquants" % "0.9"`

# Features

- Intellij Idea compatible 100%. Red marks free.
- Typelevel (no boxing for primitives)
- Compile-time
- Using tagged types (library: https://github.com/Rudogma/scala-supertagged)
- Supported physical rules (examples below)
- Auto-plaining after working with complex types (example below)
- Long & Double precision included
- Extended prettify ops
- Support for aliasing complex types. (Like Acceleration or any other)
- Predefined aliases for power (Single, Squared, Cubic & their negative variants)
- Predefined Units
- Length (7 units)
- Time (8 units)
- Mass (17 units)
- Binary (19 units)

- Performance
- There is no any additional garbage for GC! All implicit evidences are reuse singleton objects.
- There is no boxing, so they are much faster then AnyVal, but there is some boilerplate-bytecode, so they are slower then raw primitives without tags. This boilerplate can be removed with micro-scalac-plugin and then there will be no difference between this library and raw primitives.
- Long precision ( ~10x slower on big formulas, than primitive Long)(Slower then Double precision, because additional convertions Double -> Long applied)
- Double precision( ~2-4x slower on big formulas, than primitive Double)( But! It is 7-10x FASTER then based on AnyVal: https://github.com/typelevel/squants and still has no garbage for GC)

- Define your own unit(or extend predefined) in a few lines and automatically get supported all physical rules (see predefined units in sources. For example: Length1 + Length2

# Roadmap

This is a snapshot of pre-release. Release planned at **July'2017** For now almost all physical rules supported for plain, powered and complex(fractional) units. However there some cases that i didn't test yet. I will check them all in a few weeks. Also I plan to add some more features before release to power up usability.

# Usage

###Check out tests for more examples

### Basic terms

I think you already know what is `Tagged types`

and how they work ( or U can read some more at: https://github.com/Rudogma/scala-supertagged ). In `supertagged`

I've made new level of tagging and called this `OverTagged`

. `TaggedType`

and `OverTagged`

- are used upon this library heavily.

Define some val(-r)s

```
import superquants._
import longprecision._
import longprecision.length._
import longprecision.time._
import shapeless.{ ::, HNil, Nat }
val meters:Meters = 5.meters
val seconds:Seconds = Seconds @@ (Time @@ 5L)
val squaredSeconds:Pow[Long, Seconds, PowPlus, Nat._1] = 5L.as[Pow[Long, Seconds, PowPlus, Nat._1]]
// or using alias
val squaredSeconds2:Squared[Seconds] = 5L.as[Squared[Seconds]]
val speed:Complex[Long, Pow[Long, Meters, PowPlus, Nat._1] :: Pow[Long,Seconds,PowMinus, Nat._1]] = 5L.as //as[T] - will auto use explicit type from the left
//aliased
val speed2:Complex[Long, Single[Meters] :: Negative.Single[Seconds] :: HNil] = 5L.as
type Speed = Complex[Long, Single[Meters] :: Negative.Single[Seconds] :: HNil]
val speed22:Speed = 5L.as
/**
* Library based on a simple physical rules. So...
*/
val metersXseconds = 5.meters ** 5.seconds
val speed3:Speed = 5.meters divide (5.seconds ** 5.seconds)
val plainMeters:Meters = speed3 ** (5.seconds ** 5.seconds)
// annihilate units
val plainLong = speed3 ** (5.seconds ** 5.seconds divide 5.meters)
val meters2:Meters = 5.meters ++ 5.meters
//but, U can't do it with primitive
// meters2 = 5.meters ++ 5L // Wil not compile!!! Physicists denied that!
//but, U can multiply unit with raw primitives
val meters3:Meters = 5.meters ** 5L
```

### Limitations

The cost of performance: As U see above I've used '**' for multipying. Because our types are tagged on type level(and so unboxed) and have no materialized mixins at runtime we can't overload primitive operators. Supported operators:

`--`

`++`

`**`

//for`divide`

`/`

, because`//`

- starts comment :)

##### Note!

Because our types are primitives. Primitive operators also works and provide raw primitive value

`val meters:Meters = 5.meters * 5.seconds // fails, because we got raw Long. But compiler sees Meters at the left and will safe us. `

Physicists agree. If we multiply meters on seconds we will get just number. Not seconds, not meters, not other complex unit. Just Number. These remains all primitive operators.

### Aliasing

```
import superquants._
import longprecision._
import longprecision.length._
import longprecision.time._
import shapeless.{::, HNil}
//Using aliases in longprecision.package.scala
type Acceleration = Complex[Long, Single[Meters] :: Squared[Seconds] :: HNil]
//Equivalent in a more verbose: type Acceleration = Complex[Long, Pow[Long, Meters, PowPlus, Nat._1] :: Pow[Long, Seconds, PowMinus, Nat._2] :: HNil]
val acc = 5.meters divide (5.seconds ** 5.seconds)
//Or we can explicitly specify alias if we think we could forget something. Let compiler do some work!
val acc2:Acceleration = acc
// will accept only accelerations
def onlyAcceleration(acc:Acceleration):Unit = {}
```

### Convert

```
import superquants._
import longprecision._
val mg:Milligrams = 5.kg in Grams // Got: 5000 Milligrams
```

### Prettify

```
import superquants._
import render._
import longprecision.mass._
10.kg.pretty shouldEqual "10 kg"
//Or U can specify how much details u need
11123456789L.mg.pretty[Tonnes :: Kilograms :: Grams :: Milligrams :: HNil] shouldEqual "11 t 123 kg 456 g 789 mg"
11123456789L.mg.pretty[Tonnes :: Milligrams :: HNil] shouldEqual "11 t 123456789 mg"
```