Spire is a numeric library for Scala which is intended to be generic, fast, and precise.
See the companion microsite
Powerful new number types and numeric abstractions for Scala.
Home Page: http://typelevel.org/spire/
License: MIT License
Spire is a numeric library for Scala which is intended to be generic, fast, and precise.
See the companion microsite
Right now NRoot[A] has a log, which is pretty strange given that there's no guarantee we can represent e.
We should probably move the natural log to Trig[A], and/or give NRoot#log a base argument.
Number
currently defines signum
with a result type of Int
.
This is wrong, and will break as soon as you put almost anything non-Int
into it.
For instance, what's the result of Double.NaN.signum
? Not 0
, for sure.
Another example is Complex
: The result of complex.signum
is another Complex
value, not an Int
.
signum
should have a result type of type T
where T
is the actual numeric type.
In console for spire 2.10.0 branch (project benchmark)
scala> import spire.syntax.cfor._
import spire.syntax.cfor._
scala> val a = Array.fill(10)(1)
a: Array[Int] = Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
scala> cfor(0)(_<10,_+1){ i => a.map(_+i) }
<console>:21: error: not found: value ClassTag
cfor(0)(_<10,_+1){ i => a.map(_+i) }
also happening for spire 0.3 and 0.4
ScalaNumber is a bit of a pain to use. We largely have to duplicate it's functionality anyway to get seamless integration working. So the question is, should we bother with it?
This task involves looking at the bytecode generated for things like 1 + Rational(2,3)
and seeing if there's a real advantage over the bytecode we get using Spire's implicits and n + Rational(2,3)
.
It would be nice to have functions to produce e and pi to a desired precision that we could use for Real, BigDecimal, etc.
Add compatibility and interoperability such that JScience features can be used seamlessly with Spire.
Spire has accumulated some code which was used for prototyping, or for testing different approaches. One example of this was the binaryGcd algorithm.
It would be great to go through and move all this ancillary code to an appropriate subproject. We often want to keep this kind of thing around to be able to track benchmarks.
It would be interesting to have our own implementation of these classes. Some possible improvements:
With version 0.3.0, I get serialization errors when I try to work with Spark and Spire. I can get around this by inheriting from the type classes and Serializable at the same time. For example
implicit object DoubleIsNumeric extends spire.math.DoubleIsNumeric with Serializable
This task basically involves making sure that the same symbols/method names get used consistently for all Spire's number types (and type classes).
I think any operation with an integer (i.e. SafeLong, BigInt) and an number (i.e. Number, Double) should lead to a Number.
Now you get:
scala> SafeLong(7) + Number(1)
:36: error: could not find implicit value for evidence parameter of type spire.algebra.AdditiveMonoid[scala.math.ScalaNumericConversions with scala.math.Ordered[_ >: spire.math.SafeLong with spire.math.Number <: scala.math.ScalaNumericConversions with scala.math.Ordered[_ >: spire.math.SafeLong with spire.math.Number <: scala.math.ScalaNumericConversions]]]
scala> SafeLong(7) * 1.0
:36: error: could not find implicit value for evidence parameter of type spire.algebra.MultiplicativeSemigroup[Any]
SafeLong(7) * 1.0
scala> BigInt(1) * 1.0
:36: error: could not find implicit value for evidence parameter of type spire.algebra.MultiplicativeSemigroup[Any]
BigInt(1) * 1.0
(or did i just forget to import something?)
scala> val f = Fraction(1,2)
f: Fraction = (1 / 2)
scala> 1 + f
:37: error: type mismatch;
found : Int(1)
required: ?{def +(x$1: ? >: Fraction): ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method literalIntOps in trait OpsImplicits of type (lhs: Int)spire.LiteralIntOps
and method FractionFromInt of type (i: Int)Fraction
are possible conversion functions from Int(1) to ?{def +(x$1: ? >: Fraction): ?}
1 + f
^
scala> -f
:37: error: could not find implicit value for evidence parameter of type spire.algebra.AdditiveGroup[Fraction]
-f
^
:37: error: value unary_- is not a member of Fraction
-f
^
scala> 0 - f
:37: error: type mismatch;
found : Int(0)
required: ?{def -(x$1: ? >: Fraction): ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method literalIntOps in trait OpsImplicits of type (lhs: Int)spire.LiteralIntOps
and method FractionFromInt of type (i: Int)Fraction
are possible conversion functions from Int(0) to ?{def -(x$1: ? >: Fraction): ?}
0 - f
^
scala> 1 * f
:37: error: type mismatch;
found : Int(1)
required: ?{def *(x$1: ? >: Fraction): ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method literalIntOps in trait OpsImplicits of type (lhs: Int)spire.LiteralIntOps
and method FractionFromInt of type (i: Int)Fraction
are possible conversion functions from Int(1) to ?{def *(x$1: ? >: Fraction): ?}
1 * f
^
scala> 1 / f
:37: error: type mismatch;
found : Int(1)
required: ?{def /(x$1: ? >: Fraction): ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method literalIntOps in trait OpsImplicits of type (lhs: Int)spire.LiteralIntOps
and method FractionFromInt of type (i: Int)Fraction
are possible conversion functions from Int(1) to ?{def /(x$1: ? >: Fraction): ?}
1 / f
^
scala> 1.0 + f
:37: error: could not find implicit value for parameter ev: spire.algebra.Ring[Fraction]
1.0 + f
^
scala> 1.0 - f
:37: error: could not find implicit value for parameter ev: spire.algebra.Ring[Fraction]
1.0 - f
^
scala> 1.0 * f
:37: error: could not find implicit value for parameter ev: spire.algebra.Ring[Fraction]
1.0 * f
^
scala> 1.0 / f
:37: error: could not find implicit value for parameter ev: spire.algebra.Field[Fraction]
1.0 / f
^
scala> f + 1.0
:37: error: could not find implicit value for evidence parameter of type spire.algebra.AdditiveSemigroup[Fraction]
f + 1.0
^
scala> f - 1.0
:37: error: could not find implicit value for evidence parameter of type spire.algebra.AdditiveGroup[Fraction]
f - 1.0
^
scala> f / 1.0
:37: error: could not find implicit value for evidence parameter of type spire.algebra.MultiplicativeGroup[Fraction]
f / 1.0
^
scala> f * 1.0
:37: error: could not find implicit value for evidence parameter of type spire.algebra.MultiplicativeSemigroup[Fraction]
f * 1.0
^
scala> Fraction(SafeLong(1), 2)
:36: error: type mismatch;
found : spire.math.SafeLong
required: BigInt
Fraction(SafeLong(1), 2)
^
From console:
scala> import spire.syntax._
import spire.syntax._
scala> import scala.{ math => jawn }
import scala.{math=>jawn}
scala> cfor(0)(_<10,_+1){ i => println(jawn.min(5,i)) }
<console>:12: error: object jawn is not a member of package scala
cfor(0)(_<10,_+1){ i => println(jawn.min(5,i)) }
^
Currently these methods throw exceptions. They should be implemented.
Suddenly double:
scala> 120L pow 5
res343: Double = 2.48832E10
scala> 120L ** 5
res344: Double = 2.48832E10
scala> 120L ** 5L
res345: Double = 2.48832E10
Then I wanted to get a square root:
scala> 120 ** 0.5
:37: error: ambiguous reference to overloaded definition,
both method ** in class LiteralIntOps of type [A](rhs: spire.math.Complex[A])(implicit evidence$15: spire.math.Fractional[A], implicit evidence$16: spire.math.Trig[A])spire.math.Complex[A]
and method ** in class LiteralIntOps of type (rhs: spire.math.Rational)(implicit ev: spire.math.ApproximationContext[spire.math.Rational])spire.math.Rational
match argument types (Double)
120 ** 0.5
scala> Rational(120,1) pow 0.5
:37: error: could not find implicit value for parameter ctxt: spire.math.ApproximationContext[spire.math.Rational]
Rational(120,1) pow 0.5
scala> 120L pow 0.5 //at least this works due to the double conversion above...
res337: Double = 10.954451150103322
And now, the WTF:
scala> SafeLong(100) ** 0.5
res358: spire.math.SafeLong = 1
That does not make any sense.
It seems like there's currently a problem with tuple destructuring:
val arr = Array((1,2), (2,3), (4,5))
var t = 0
cfor(0)(_ < arr.length, _ + 1) { i =>
val (a, b) = arr(i)
t += a + 2 * b
}
Ben Hutchison noticed that Natural(1) - Natural(2) == Natural(18446744073709551615L)
, which is clearly not correct. We should either throw an ArithmeticException in this case and/or rename the method and return an Option[Natural] instead.
There should be default instances of Monoids, Modules, VectorSpaces, etc for many of Scala's standard collections (eg. CC[A] <: SeqLike[A, CC[A]], Map[A, B], etc.). This will also ease integration with Twitter's Algebird.
I am pretty sure that the benchmark memory settings are no longer being passed to Caliper.
This is a problem because we'd like to test gigantic arrays--currently some of the benchmarks overflow the heap.
javaOptions used to work, but seems to no longer.
scala> big"100"
res168: scala.math.BigInt = 100
scala> big"1000"
error: exception during macro expansion:
java.lang.NumberFormatException: illegal SI BigInt constant
at spire.macrosk.Macros$.handleBigInt(Macros.scala:186)
at spire.macrosk.Macros$.siBigInt(Macros.scala:205)
scala> BigInt("1000")
res170: scala.math.BigInt = 1000
Dear all,
I need an implementation of RationalNumber in Spire, so I am in the process of making one.
I am struggling with the implementation of "equals", especially with respect to non-Number classes such as Rational, BigDecimal, etc...
What is the contract that the Number trait is supposed to obey ?
Also, I wondered how should a Number should behave with respect to hashCode; it is probably impossible to design a good solution for that.
Cheers,
Denis
spire/random/Generator.scala, line 100, 0.4 version:
/**
Currently these methods throw exceptions. They should be implemented.
scala> 0.1 - Number(0.0)
res29: spire.math.Number = 0.0
scala> 0.1 * Number(0.1)
res31: spire.math.Number = 0.0
scala> 0.1 + Number(0.1)
res32: spire.math.Number = 0.1
scala> 0.1 / Number(0.1)
res33: spire.math.Number = 0.0
Create versions of most type classes for Tuple2->22 that just operate on each element individually. These can be autogenerated fairly easily.
I get these while testing the wip-spire-stats branch, it boils down to this:
scala> spire.math.Integral.IntIsIntegral.fromInt(42)
java.lang.StackOverflowError
at spire.math.Integral$IntIsIntegral$.fromInt$mcI$sp(Integral.scala:18)
at spire.math.ConvertableToInt$class.fromInt(Convertable.scala:53)
at spire.math.Integral$IntIsIntegral$.spire$math$IntIsIntegral$$super$fromInt(Integral.scala:18)
at spire.math.IntIsIntegral$class.fromInt$mcI$sp(Integral.scala:28)
at spire.math.Integral$IntIsIntegral$.fromInt$mcI$sp(Integral.scala:18)
at spire.math.ConvertableToInt$class.fromInt(Convertable.scala:53)
at spire.math.Integral$IntIsIntegral$.spire$math$IntIsIntegral$$super$fromInt(Integral.scala:18)
at spire.math.IntIsIntegral$class.fromInt$mcI$sp(Integral.scala:28)
at spire.math.Integral$IntIsIntegral$.fromInt$mcI$sp(Integral.scala:18)
...
The boiled down example above uses no spire imports.
You cannot divide an int by a SafeLong:
scala> 10 / SafeLong(2)
:36: error: could not find implicit value for parameter ev: spire.algebra.Field[spire.math.SafeLong]
10 / SafeLong(2)
dividing by an actual long works ofc, although dividing by a BigInt does not work. So if it should be allowed, depends on if you consider SafeLong to be a safe long, or a faster BigInt...
spire.macrosk.Syntax.Util.inlineApplyRecursive
performs name-based replacements instead of using symbols.
As a result, hell breaks loose in case of symbol collision:
import spire.syntax._
import scala.collection.mutable.ArrayBuffer
object Test extends App {
val result = ArrayBuffer[Int]()
cfor(0)(_ < 3, _ + 1) { x => {
cfor(0)(_ < 2, _ + 1) { y =>
val x = y
result += x // name-based replace of x causes this to use the outer x...
}
}}
println(result)
assert(result.toList == List(0, 1, 0, 1, 0, 1))
}
Result ends up being:
(0, 0, 1, 1, 2, 2)
Using following build.sbt
:
scalaVersion := "2.10.0"
libraryDependencies += "org.spire-math" %% "spire" % "0.3.0-RC1"
Each of the following cfor
tests causes a compilation crash:
test("capture value in closure") {
val b = collection.mutable.ArrayBuffer[() => Int]()
cfor(0)(_ < 3, _ + 1) { x =>
b += (() => x)
}
assert(b.toList === List(0, 1, 2))
}
test("capture value in inner class") {
val b = collection.mutable.ArrayBuffer[Int]()
cfor(0)(_ < 3, _ + 1) { x => {
class A { def f = x }
b += (new A().f)
}}
assert(b.toList === List(0, 1, 2))
}
(test cases derived from Scalaxy/Loops optimized Range tests)
Excerpt from the crash log:
[error] (core/test:compile) java.lang.AssertionError: assertion failed:
[error] while compiling: /Users/ochafik/Documents/Perso/spire/core/src/test/scala/spire/SyntaxTest.scala
[error] during phase: constructors
[error] library version: version 2.10.0
[error] compiler version: version 2.10.0
[error] reconstructed args: -optimise -feature -Yinline -Yclosure-elim -bootclasspath /Library/Java/JavaVirtualMachines/jdk1.7.0_13.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_13.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_13.jdk/Contents/Home/jre/lib/sunrsasign.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_13.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_13.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_13.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_13.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_13.jdk/Contents/Home/jre/lib/JObjC.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_13.jdk/Contents/Home/jre/classes:/Users/ochafik/.sbt/0.12.2/boot/scala-2.10.0/lib/scala-library.jar -unchecked -language:experimental.macros -language:higherKinds -language:implicitConversions -Ydead-code -deprecation -Yinline-warnings -Yinline-handlers -d /Users/ochafik/Documents/Perso/spire/core/target/scala-2.10/test-classes -classpath /Users/ochafik/Documents/Perso/spire/core/target/scala-2.10/test-classes:/Users/ochafik/Documents/Perso/spire/core/target/scala-2.10/classes:/Users/ochafik/.ivy2/cache/org.scala-lang/scala-reflect/jars/scala-reflect-2.10.0.jar:/Users/ochafik/.ivy2/cache/org.scalatest/scalatest_2.10/jars/scalatest_2.10-1.9.1.jar:/Users/ochafik/.ivy2/cache/org.scala-lang/scala-actors/jars/scala-actors-2.10.0.jar:/Users/ochafik/.ivy2/cache/org.scalacheck/scalacheck_2.10/jars/scalacheck_2.10-1.10.0.jar:/Users/ochafik/.ivy2/cache/org.scala-tools.testing/test-interface/jars/test-interface-0.5.jar
[error]
[error] last tree to typer: Assign
[error] symbol: null
[error] symbol definition: null
[error] tpe: Unit
[error] symbol owners:
[error] context owners: class A$1 -> anonymous class $anonfun$4 -> class SyntaxTest -> package spire
[error]
...
== Expanded type of tree ==
TypeRef(TypeSymbol(final abstract class Unit extends ))
1 not in List(value $outer, value 1)
at scala.tools.nsc.transform.Constructors$ConstructorTransformer.scala$tools$nsc$transform$Constructors$ConstructorTransformer$$parameterNamed$1(Constructors.scala:71)
at scala.tools.nsc.transform.Constructors$ConstructorTransformer.scala$tools$nsc$transform$Constructors$ConstructorTransformer$$parameter$1(Constructors.scala:63)
at scala.tools.nsc.transform.Constructors$ConstructorTransformer$$anonfun$8.apply(Constructors.scala:268)
at scala.tools.nsc.transform.Constructors$ConstructorTransformer$$anonfun$8.apply(Constructors.scala:259)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
at scala.collection.immutable.List.foreach(List.scala:309)
Usually you can do
scala> BigInt(3)+":"
res0: String = 3:
to create a string.
With spire loaded:
scala> BigInt(3)+":"
:39: error: type mismatch;
found : scala.math.BigInt
required: ?{def +(x$1: ? >: String(":")): ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method any2stringadd in object Predef of type (x: Any)scala.runtime.StringAdd
and method additiveMonoidOps in trait OpsImplicits of type [A](a: A)(implicit evidence$53: spire.algebra.AdditiveSemigroup[A])spire.algebra.AdditiveSemigroupOps[A]
are possible conversion functions from scala.math.BigInt to ?{def +(x$1: ? >: String(":")): ?}
BigInt(3)+":"
^
It would be nice, if you could use the spire numbers with the sum/product methods on collections:
scala> Array(Number(1), Number(2), Number(3)) sum
:48: error: could not find implicit value for parameter num: Numeric[spire.math.Number]
Array(Number(1), Number(2), Number(3)) sum
^
scala> Array(Number(1), Number(2), Number(3)) product
:48: error: could not find implicit value for parameter num: Numeric[spire.math.Number]
Array(Number(1), Number(2), Number(3)) product
scala> Array(SafeLong(1), SafeLong(2)) sum
:48: error: could not find implicit value for parameter num: Numeric[spire.math.SafeLong]
Array(SafeLong(1), SafeLong(2)) sum
Its big enough and significant enough to warrant one IMO.
in Number, it seems like the implicits are not always taking in the correct types.
implicit def byteToNumber(n:Int) = Number(n)
implicit def shortToNumber(n:Int) = Number(n)
Maybe should be
implicit def byteToNumber(n:Byte) = Number(n)
implicit def shortToNumber(n:Short) = Number(n)
Sign current leaves all its values (eg. Positive, Negative, and Zero) in spire.algebra. It would be nice to move these into Sign, especially given that Zero may cause people confusion.
Anyone have any problems with this?
scala> BigInt(1) + SafeLong(2)
:36: error: could not find implicit value for evidence parameter of type spire.algebra.AdditiveSemigroup[scala.math.ScalaNumericConversions]
BigInt(1) + SafeLong(2)
^
scala> BigInt(1) - SafeLong(2)
:36: error: could not find implicit value for evidence parameter of type spire.algebra.AdditiveGroup[scala.math.ScalaNumericConversions]
BigInt(1) - SafeLong(2)
^
scala> BigInt(1) * SafeLong(2)
:36: error: could not find implicit value for evidence parameter of type spire.algebra.MultiplicativeSemigroup[scala.math.ScalaNumericConversions]
BigInt(1) * SafeLong(2)
^
scala> BigInt(1) / SafeLong(2)
:36: error: could not find implicit value for evidence parameter of type spire.algebra.MultiplicativeGroup[scala.math.ScalaNumericConversions]
BigInt(1) / SafeLong(2)
scala> BigInt(1) + Number(2)
:36: error: could not find implicit value for evidence parameter of type spire.algebra.AdditiveSemigroup[scala.math.BigInt]
BigInt(1) + Number(2)
scala> BigInt(1) - Number(2)
:36: error: could not find implicit value for evidence parameter of type spire.algebra.AdditiveGroup[scala.math.BigInt]
BigInt(1) - Number(2)
scala> BigInt(1) * Number(2)
:36: error: could not find implicit value for evidence parameter of type spire.algebra.MultiplicativeSemigroup[scala.math.BigInt]
BigInt(1) * Number(2)
scala> BigInt(1) / Number(2)
:36: error: could not find implicit value for evidence parameter of type spire.algebra.MultiplicativeSemigroup[scala.math.BigInt]
BigInt(1) / Number(2)
(the other way around works:
scala> SafeLong(1) + BigInt(2)
res82: spire.math.SafeLong = 3
scala> Number(1) + BigInt(2)
res83: spire.math.Number = IntNumber(3)
)
BigInt has the useful BigInt(7).modPow(2, 10) function, SafeLong does not...
Or perhaps you can write a macro that replaces (x ** y) % z by x.modPow(y, z)?
I noticed that the euclid method in EuclideanRing uses ==. Since we are trying to avoid equality within our typeclasses, we should probably add an implicit Eq[A] to gcd and use that equality. I imagine pretty much all gcd algorithms will require Eq for some stopping condition, so I think it'll fit:
def gcd(x: A, y: A)(implicit A: Eq[A])
not an issue, more an enquiry into future plans:
being very impressed by the mathematically rigorous and computationally efficient (at runtime) approach taken by spire, i wonder whether there are any concrete plans of implementing vector spaces (similar to, and building upon your implementation of fields), matrices, and ultimately the linear algebra that flows from that?
It would be great to generate and post a Scaladoc for 0.4.0.
If the expression of the test, increment or loop functions given to cfor contain any side-effect, these side effects are executed at each iteration (in other terms, params are passed "by name" instead of "by value"):
test("functions with side effects") {
val b = mutable.ArrayBuffer.empty[Int]
var v = 0
cfor(0)({ v += 1; _ < 3 }, { v += 10; _ + 1})({
v += 100
x => {
b += x
}
})
assert(v == 111, s"v == $v")
assert(b.toList === List(0, 1, 2), s"b.toList == ${b.toList}")
}
(v ends up being 334)
scala> SafeLong(2) to SafeLong(80)
:36: error: value to is not a member of spire.math.SafeLong
SafeLong(2) to SafeLong(80)
Right now Number supports Long/BigInt, Double and BigDecimal. It would be great to support Rational also.
Rex brought up the issue that we should be able to plug in other RNGs.
Would be great to allow for the following:
val y = x * 2.1 + 4.2
val z = 8.5 + 4.1 * y
without using implicitly [Numeric[T}].fromDouble() for every literal
where x, y, and z are all Numeric[T], and where T is Double, Complex, for Float. Int is not so clear. note that:
val y = 2 + 5 * x // works, but only with Ints
val z = x * 5 + 2 // compilation error
I'm not sure if this is possible without breaking everything else or messing with the intent of the class to begin with. Even an optional import would be better than sprinkling fromDouble() everywhere I have a literal constant. Would obviously produce much cleaner code and readable.
Thanks for all of your contributions.
David
There are no bit operations available in SafeLong/Number, like needed for these functions:
def isPowerOf2(i: SafeLong) = ((i-1) & i) == 0
def even(x: SafeLong) = (x & 1) == 0
def even(x: Number) = (x & 1) == 0
(might be useful to have such functions as well)
The x16 prefix does not work for large numbers. E.g:
scala> x16"ABABABAB"
error: exception during macro expansion:
java.lang.NumberFormatException: For input string: "ABABABAB"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:495)
at spire.macrosk.Macros$.radix(Macros.scala:228)
Right now Number supports Long/BigInt, Double and BigDecimal. It would be great to support Real also.
I'm not sure why I did this. We can get rid of it this requirement.
Given that 0.3.0 had some bugs and 0.4.0 has a lot of nice new features, we should try to backport it to 2.9.x and publish it on Sonatype.
As long as we're doing this we might as well do it right.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.