Giter VIP home page Giter VIP logo

kactl's Introduction

KACTL

This repo hosts KACTL, KTH's ICPC team reference document. It consists of 25 pages of copy-pasteable C++ code, for use in ICPC-style programming competitions.

See kactl.pdf for the final, browsable version, and content/ for raw source code.

Aspirations

KACTL algorithms should be: useful, short, fast enough, well tested, and if relevant, readable and easy to modify. They should not be overly generic, since code is manually typed and that just adds overhead. Due to space issues, we also exclude algorithms that are very common/simple (e.g., Dijkstra), or very uncommon (general weighted matching).

If you feel that something is missing, could be cleaned up, or notice a bug, please file an issue or send a pull request!

Customizing KACTL

While KACTL is usable as is, it's also easy to modify if you want to create a personalized copy. In particular, you may want to change the cover page, or make your own choice of algorithms to include -- due to space concerns, not all algorithms in the repo are included in the pdf. You may also want to enable colored syntax highlighting.

content/kactl.tex is the main file of KACTL, and can be edited to change team name, logo, syntax highlighting, etc. It imports chapter.tex files from each of the content/ subdirectories, which define the contents of each chapter. These include source code, text and math in the form of LaTeX. To add/remove code from a chapter, add/remove a corresponding \kactlimport line from the chapter.tex file. For nicer alignment you might want to insert \hardcolumnbreak, \columnbreak or \newpage commands, though this is usually only done before important contests, and not on the main branch. The algorithms that are not included in the pdf are left commented out in chapter.tex.

To build KACTL, type make kactl (or make fast) on a *nix machine -- this will update kactl.pdf. (Windows might work as well, but is not tested.) doc/README has a few more notes about this.

Tips:

  1. Check out what's excluded by default by running make showexcluded. The default configuration is chosen to be a reasonable balance for beginners and advanced teams.
  2. Take advantage of the hashing when typing in these algorithms. Each algorithm has a 6 character MD5 hash in the upper right. This hash can be generated by using hash.sh or the :Hash command from the .vimrc. The hashing ignores whitespace and comments.

Coding style

KACTL uses a relatively terse coding style, with a handful of macros/typedefs defined in the template that help shorten the code. Line width is 63 chars, with tabs for indentation (tab = 2 spaces in the pdf).

Each algorithm contains a header with the author of the code, the date it was added, a description of the algorithm, its testing status, and preferably also source, license and time complexity.

kactl.pdf is to be kept to 25 pages + cover page. Occasionally the generated kactl.pdf is committed to the repo for convenience, but not too often because it makes git operations slower.

Testing

KACTL aims for a high level of confidence in algorithm correctness. Testing is done both on online judges and (for newer algorithms) with stress tests that compare output to a more naive algorithm for a large amount of randomly generated cases. These tests live in the stress-tests directory, and are run with CI on every commit. The CI also verifies that all headers compile (except for an exclude list in docs/scripts/skip_headers) and that the latex compiles.

old-unit-tests contains a couple of broken unit tests, last touched about ten years ago.

License

As usual for competitive programming, the licensing situation is a bit unclear. Many source files are marked with license (we try to go with CC0), but many also aren't. Presumably good will is to be assumed from other authors, though, and in many cases permission should not be needed since the code is not distributed. To help trace things back, sources and authors are noted in source files.

Everything in stress-tests is implicitly CC0, except reference implementations taken from around the Internet.

kactl's People

Contributors

anton avatar barrenszeppelin avatar bjorn-martinsson avatar blin00 avatar chillee avatar gullesnuffs avatar hakante avatar halfvoxel avatar ivanrenison avatar jsannemo avatar ludwikjaniuk avatar matistjati avatar mohammad-yasser avatar mukundan314 avatar nullchilly avatar pbbx0 avatar robotolev avatar simonlindholm avatar tyilo avatar ulflund avatar veladus avatar xylofo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kactl's Issues

Faster simplex

https://en.wikipedia.org/wiki/Revised_simplex_method ? Seems kindof complicated... Annoying details according to wikipedia include a presolve step for getting rid of redundant constraints (which can take O(mn^2) time!) and needing periodic refactorizations.

https://github.com/pakwah/Revised-Simplex-Method/blob/master/RevisedSimplex/main.cpp
https://github.com/YimingYAN/cppipm/blob/master/src/cppipm.cpp
are two not overly long implementations.

Could also maybe do something with the Big M method for when a viable solution is known in advance - it costs some numerical stability but won a factor 2 in one case. E.g.:

T solveWithGuess(vvd& A, vd& b, vd& c, vd& x) {
	int n = sz(c), m = sz(A);
	rep(i,0,m) {
		vd& row = A[i];
		row.push_back(0);
		T &la = row[n], &co = b[i];
		rep(j,0,n)
			la -= row[j] * x[j], co -= row[j] * x[j];
	}

	const T M = 1e7;
	c.push_back(M);

	vd x2(n+1, 0);
	x2[n] = 1;
	A.push_back(x2);
	b.push_back(1);

	T res = LPSolver(A, b, c).solve(x2);
	if (abs(res) == inf) return res;
	assert(abs(x2[n] - 1) < eps);

	x = x2;
	x.pop_back();

	return res - M;
}

https://github.com/dacin21/dacin21_codebook/blob/master/numerical/simplex_lp.cpp claims a 5x win via "steepest edge pricing", should be investigated.

Closest pair is too long

In fact, since it appears so seldom maybe one could just replace it with a short text description.

Function minimization

Describe other approaches than hill-climbing, in case that ever falls down (e.g. if it's too slow, search space is narrow in diagonal direction, or there are too many dimensions).

tinyKACTL tersely describes the Conjugated Gradient Method, which sounds like it might be relevant:

Search direction in step i is $d_i = g_i + β_i d_{i-1}$, where $g_i$ is the gradient in step i and $β_i = |g_i|^2 / |g_{i-1}|^2$ ($d_1 = g_1$).

Line - line segment intersection

Comes up fairly frequently. Even the code which scales up one of the lines and calls lineSegment - lineSegment intersection would be useful. (edit: but computing line-line intersection and checking sideOf seems more sensible?)

MIT additions

Replaced/duplicate implementations

  • New Link Cut Tree implementation (this submission from NWERC has a short one)
  • Another simplex implementation (they're not really sure...)
  • New Suffix Array implementation
  • New FFT Implementation
  • New NTT Implementation
  • AngleCmp (Various angle comparison utilities)
  • Faster Graph-Clique (SJTU's is prolly better).

Different Algorithms for Existing Implementations

  • Blossom algorithm for general matching (I believe KACTL has a V^3 randomized algorithm for this already)
  • Linear time SA+LCP+Suffix Tree
  • N log N Delaunay
  • FFT-based polynomial class (add, multiply, subtract, floor division, modulo, derivative, integrate,logarithm,exp,pow, multi-point evaluation, interpolation, inverse)
  • FFT-Mod

Math/description additions:

  • Bernoulli numbers (Exponential generating function, sums of powers, euler-maclarin formula for infinite sums)
  • Labeled unrooted trees (Cayley's formula and such)

Completely New Additions (roughly ordered from what I've seen of other notebooks)

  • Half Plane Intersection
  • Directed MST (Minimum Spanning Arborescence)
  • Suffix Automaton
  • Extended-KMP/Z-Function
  • Dominator Tree
  • Nim-product
  • Cycle Counting (Counts 3 and 4 cycles)
  • Schreier Sims

???

  • Graph-Negative-Cycle? (Seems like it's incorrect)

Misc:

  • Colored doc!
  • Hashing to verify code correctness
  • Much longer Makefile

Crossed out means that we decided to not include it.

Translate techniques.txt to English

It seems weird that troubleshoot.txt is in English, but techniques.txt is not.
Also this would make techniques.txt useful for non-Swedish participants.

More understandable suffix tree

The following from e-maxx might work (a bit long, so doesn't fit in KACTL at the moment, and also it seems slower):

struct SuffixTree {
	enum { MAXN = 100 };
	string s;
	int n, sz = 1;

	struct Node {
		int l, r, par, link;
		map<char,int> next;
		Node(int l=0, int r=0, int par=-1)
			: l(l), r(r), par(par), link(-1) {}
		int len() { return r - l; }
		int& get(char c) {
			if (!next.count(c)) next[c] = -1;
			return next[c];
		}
	} t[MAXN*2+5];

	struct State { int v, pos; } ptr{0,0};

	State go(State st, int l, int r) {
		while (l < r)
			if (st.pos == t[st.v].len()) {
				st = {t[st.v].get(s[l]), 0};
				if (st.v == -1) return st;
			}
			else {
				if (s[t[st.v].l + st.pos] != s[l])
					return {-1, -1};
				if (r-l < t[st.v].len() - st.pos)
					return {st.v, st.pos + r-l};
				l += t[st.v].len() - st.pos;
				st.pos = t[st.v].len();
			}
		return st;
	}

	int split(State st) {
		if (st.pos == t[st.v].len()) return st.v;
		if (st.pos == 0) return t[st.v].par;
		Node v = t[st.v];
		int id = sz++;
		t[id] = Node(v.l, v.l+st.pos, v.par);
		t[v.par].get(s[v.l]) = id;
		t[id].get(s[v.l+st.pos]) = st.v;
		t[st.v].par = id;
		t[st.v].l += st.pos;
		return id;
	}

	int link(int v) {
		if (t[v].link != -1) return t[v].link;
		if (t[v].par == -1) return 0;
		int to = link(t[v].par);
		return t[v].link = split(go({to,t[to].len()}, t[v].l + !t[v].par, t[v].r));
	}

	void extend(int pos) { for(;;) {
		State nptr = go(ptr, pos, pos+1);
		if (nptr.v != -1) {
			ptr = nptr;
			return;
		}
		int mid = split(ptr), leaf = sz++;
		t[leaf] = Node(pos, n, mid);
		t[mid].get(s[pos]) = leaf;
		ptr.v = link(mid);
		ptr.pos = t[ptr.v].len();
		if (!mid) break;
	}}

	SuffixTree(int n) : n(n) {}
	SuffixTree(string s) : n(sz(s)) { trav(c, s) append(c); }
	void append(char c) { s += c; extend(sz(s)-1); }

	// example: find longest common substring
	pii best;
	int lcs(Node& node, int i1, int i2, int olen) {
		if (node.l <= i1 && i1 < node.r) return 1;
		if (node.l <= i2 && i2 < node.r) return 2;
		int mask = 0, len = olen + (node.r - node.l);
		trav(pa, node.next)
			mask |= lcs(t[pa.second], i1, i2, len);
		if (mask == 3)
			best = max(best, {len, node.r - len});
		return mask;
	}
	static pii LCS(string s, string t) {
		SuffixTree st(s + '\1' + t + '\2');
		st.lcs(st.t[0], sz(s), sz(s) + 1 + sz(t), 0);
		return st.best;
	}
};

Perhaps we should just try to provide better documentation about how to use it.

Restructure flow algorithms

There's currently 3 flow implementations that (more or less) do similar things. There's HopcroftKarp, EdmondsKarp, PushRelabel, DFSMatching. There's also a 5th commonly used implementation (that KACTL doesn't include): Dinic's, although there is an issue for it #19 .

I've done a lot of benchmarking of flow algorithms, so let's break down these implementations.

DFSMatching: Runs in O(EV) time. Very short. Honestly, I think this is pretty useless nowadays. Maybe it used to be useful in the past, but nowadays Hopcroft-Karp is essentially expected for most matching problems.

HopcroftKarp: Only works on Bipartite Graphs, runs in O(\sqrt{E}V) time. Typically runs very fast (my guess is about 3x faster than Dinic's) on those bipartite graphs. Decently long.

EdmondsKarp: Runs in O(VE^2) time. Strictly inferior to Dinic's in runtime. Fairly short.

Dinic's: Runs in O(V^2E) time, O(VE log U) with scaling. Has some special guarantees: O(sqrt(E)*V) on Bipartite graphs, and O(min(sqrt(V),E^(2/3))*E) on unit graphs.

HLPP: Runs in O(V^2sqrt(E)) time. Runs the fastest in practice on most graphs. Current KACTL version is missing global relabeling optimization, which makes a huge difference (~2x on Chinese flow problem, allows for 0.15 on SPOJ MATCHING).

If we look only at complexities, we see that a combination of Dinic's + HLPP dominates everything else. Provable guarantees are nice, but flow problems are also notoriously difficult to come up with worst cases for. I have a HLPP implementation that beats out my Dinic's on all the test cases I've tried it on (and also has/tied for the #1 spot on SPOJ FASTFLOW, VNSPOJ FASTFLOW, and the chinese flow problem). It also performs about equal to KACTL's Hopcroft-Karp on MATCHING. So, it seems that in practice, that HLPP implementation would probably perform the best.

Thus, I propose that we do a couple things:

  1. Add global relabeling to the current HLPP implementation. This will give us the fastest possible flow implementation in general.
  2. Add Dinic's. This gives us a bunch of nice provable guarantees on certain kinds of graphs, a much more understandable/traditional augmenting-paths flow algorithm, with even stronger guarantees if scaling is added.
  3. With these two, we have both practical efficiency and theoretical guarantees covered, making HopcroftKarp and EdmondsKarp redundant. We can then remove them.
  4. Also remove DFSMatching since I think it's pretty useless.

This leaves us with 2 implementations: HLPP to be insanely fast in practice, and Dinic's to have good guarantees on certain kinds of graphs.

See https://codeforces.com/blog/entry/66006?#comment-500365 for some discussion about HLPP optimizations.

Gomory-Hu tree

Not sure how useful it is, but it seems like the code is rather short, and it would be hard to derive by hand.

Automata

  • NFAs, DFAs, conversion
  • regex parsing
  • minimization
  • reversal
  • products, product minimization

msan

Not all that useful without custom standard library, and when you can debug on paper, but...

clang++ -std=c++11 -g -fsanitize=memory -fsanitize-memory-track-origins
#include <sanitizer/msan_interface.h>

int read() { int x; cin >> x; __msan_unpoison(&x, sizeof(x)); return x; }

or

__attribute__((no_sanitize("memory")))
int read() { int x; cin >> x; return x; }

Maybe this will mature at some point, or maybe it will at least be usable outside of competitions.

Mini-OEIS

I de-bloated the Combinatorial chapter a bit by removing some of the more pointless number sequences (could probably remove even more). But maybe a large collection of sequences for pattern matching is useful if it comes in the form of short one-liners? Among things I just threw out:

  • (involutions) Permutations which are their own inverse: 1, 1, 2, 4, 10, 26, 76, 232, 764, 2620, 9496, 35696
  • (schröder numbers) Number of lattice paths from $(0,0)$ to $(n,n)$ using only steps N,NE,E, never going above the diagonal: 1, 2, 6, 22, 90, 394, 1806, 8558, 41586, 206098
  • (super catalan numbers) Schröder numbers, but following the diagonal is also not allowed. Half the Schröder numbers, except that the first term is 1.
  • (motzkin numbers) Number of ways of drawing any number of nonintersecting chords among $n$ points on a circle. Also the number of lattice paths from $(0,0)$ to $(n,0)$ never going below the $x$-axis, using only steps NE, E, SE. 1, 1, 2, 4, 9, 21, 51, 127, 323, 835, 2188, 5798, 15511, 41835

Porting CP Geo Handbook to KACTL/Other Geo additions

https://codeforces.com/blog/entry/56773
https://vlecomte.github.io/cp-geo.pdf

My main goal in doing this would be the following:

  1. Add more tests for the geometry functions.
  2. Reduce verbosity. Vlecomte's book has a lot of nice implementations, so I'll be picking the better one between the two.
  3. Augment with some new functions. Namely, Halfplane.

For the sake of consistency (and because mostly there's not much of a difference...) I will be using the current KACTL Point class for everything.

  • Point.h
  • lineDistance.h (we could change it to lineSqDist so that it was exact for integers)
  • SegmentDistance.h
  • SegmentIntersection.h
  • SegmentIntersectionQ.h
  • lineIntersection.h
  • sideOf.h
  • onSegment.h
  • linearTransformation.h
  • Angle.h
  • CircleIntersection.h Maybe update API to allow for returning 1.
  • circleTangents.h
  • circumcircle.h
  • MinimumEnclosingCircle.h
  • insidePolygon.h
  • PolygonArea.h
  • PolygonCenter.h
  • PolygonCut.h
  • ConvexHull.h
  • PolygonDiameter.h
  • PointInsideHull.h
  • LineHullIntersection.h
  • ClosestPair.h
  • kdTree.h
  • FastDelaunay.h
  • PolyhedronVolume.h
  • Point3D.h
  • 3dHull.h
  • sphericalDistance.h

Potential new implementations that KACTL doesn't have (may move to separate issue):

  • Circle-Line intersection
  • Convex-Convex polygon intersection in log n time
  • Max distance across convex polygon in log n time

Add hashing for verifying correct input of code

See #63 (comment)

I don't think that hashing sections is worth it. MIT does hashing in 8 snippets: LCT, LinearRecurrence,Simplex.h, Polynomial,CycleCounting,GraphDominator, and both suffix arrays

I would split that into
"Should be split into different sections": Polynomial, CycleCounting
"trying to avoid hashing the typedef": LinearRecurrence
"Has parts that you don't always want": Both suffix arrays (ie: don't always need LCP)
"Not sure": LCT, Simplex, and GraphDominator (I don't know enough about the algorithms to understand whether you pretty much always want all functions)

That's a maximum of 4 snippets where it might be advantageous to have section-wise hashing.

The other argument for hashing sections is that if the hash fails, then you need to look at less of your code. I haven't done many offline contests with a TCR, but from my experience, knowing that you have a mistype in 50 lines of code is only marginally better than knowing you have a mistype in 100 lines of code. Both of these are massively better than not knowing whether you have a mistype or a logic error.

If we were to hash by section, I would propose having some kind of lightweight syntax (like a //<-- ) to demarcate sections, and then putting the hashes (truncated to 5 characters) in the header.

Like so:
image

Another question with hashing is how we deal with things like typedefs, especially if they're typedefs that are likely to be typed multiple times (for example, typedef vector<ll> Poly). I think it's not too big of a deal, I would suggest to just get used to typing them in for the purpose of hashing.

My biggest problem with avoiding them automatically is ambiguity with what hashes represent. "We hash everything that's printed" is obvious. "We hash everything after the typedefs" is less obvious.

Faster FFT

Ours isn't that bad, but something based on bit reversal ought to be faster.
E.g. https://github.com/jaehyunp/stanfordacm/blob/master/code/FFT.cc +

// https://arxiv.org/pdf/1708.01873.pdf
void BitReverse(int n, int L, vector<T>& vec) {
	int rev = 0;
	rep(i,0,n) {
		if (i < rev) swap(vec[i], vec[rev]);
		int tail = i ^ (i + 1), shift = 32 - __builtin_clz(tail);
		rev ^= tail << (L - shift);
	}
}

(although Stanford's code isn't numerically stable; it should really use precomputed roots of unity)

Also, worth mentioning that multiplication of complex<double> is slow, and maybe do something about denormals?

Update Fuzz Tests

Main goals:

  1. Reorganize fuzz tests to match the directory structure of contents.
  2. Standardize tests. This involves a couple things.
    a. assert when they fail/make failures obvious. Currently some tests pass by eyeballing output and determining whether it's close enough.
    b. Perhaps standardize fast/slow tests, and maybe integrate with CI eventually (very stretch goal).
    c. Make sure that tests are always importing their functions, as opposed to hard coding them.
  3. Fix broken tests.
    The following need fixing.
  • MinCostMaxFlow.cpp
  • PointInsideHull.cpp (caused by the current typedef Point P mess conflicting with each other).

Angle difference

I had to derive the following during a contest, which took a bit of time...

Angle diff(Angle a, Angle b) {
    int tu = b.t - a.t;
    a.t = b.t = 0;
    return Angle(a.x * b.x + a.y * b.y, a.x * b.y - a.y * b.x, tu - (b < a));
}

Getting a min cut from PushRelabel

It's a one-liner, and avoids having to write a DFS from scratch:

bool is_cut(int u,int v) {
    return (dist[u] >= n) && (dist[v] < n);
}

Haven't checked if this works with our implementation or if we do something terrible to dist values.

Polynomial interpolation

We have that for the standard 1D case, but we should perhaps include something about higher number of dimensions. From previous notes:

from what I could tell, multivariate polynomial interpolation pretty much requires solveLinear, e.g. since not all sets of interpolation points work.
However, empirically (deg+1)^vars grids work ("are unisolvent"), even if randomly pruned in size to the number of included monomials.

Also, maybe include Neville's algorithm?

def neville(x, y, t): # evaluate f at point t, given that f(x...) = y...
    n = len(x)
    for j in range(1,n):
        for i in range(n-j):
            y[i] = ((t - x[i+j])*y[i] - (t - x[i])*y[i+1]) / (x[i] - x[i+j])
    return y[0]

CRT for large numbers

Requiring each to be <2^31 can be annoying. Maybe CRT for many small numbers simultaneously would help?

Incremental matching/flow

Would have been nice during gcj (Dice Straight) not to have to reinvent incremental matching from scratch (where nodes can be added/removed from the LHS). And incremental maxflow comes up all the time, and we don't really have a good answer for it in KACTL.

Include short descriptions of obscure algorithms

I.e. don't include code, but give a hint about what to do if they against all probability do appear. tinyKACTL does this for Suurballe's algorithm, as thus:

Shortest tour from A to B to A again not using any edge twice, in an undirected graph: Convert the graph to a directed graph. Take the shortest path from A to B. Remove the paths used from A to B, but also negate the lengths of the reverse edges. Take the shortest path again from A to B, using an algorithm which can handle negative-weight edges, such as Bellman-Ford. Note that there is no negative-weight cycles. The shortest tour has the length of the two shortest paths combined.

and Directed Minimum Spanning Tree:

Chu-Liu/Edmonds Algorithm:

  1. Discard the arcs entering the root if any; For each node other than the root, select the entering arc with the smallest cost; Let the selected n − 1 arcs be the set S.
  2. If no cycle formed, G(N, S) is a MST. Otherwise, continue.
  3. For each cycle formed, contract the nodes in the cycle into a pseudo-node (k), and modify the cost of each arc which enters a node (j) in the cycle from some node (i) outside the cycle according to the following equation:
    c(i, k) = c(i, j) − (c(x(j), j) − min_j (c(x(j), j))
    where c(x(j), j) is the cost of the arc in the cycle which enters j.
  4. For each pseudo-node, select the entering arc which has the smallest modified cost; Replace the arc which enters the same real node in S by the new selected arc.
  5. Go to step 2 with the contracted graph.

But we could make that more compact.

Math additions

  • vieta jumping
  • polya enumeration theorem
  • master theorem
  • sum of x^k for general k? (could derive from \sum_{k=0}^n\binom{k}{m}=\binom{n+1}{m+1}, possibly)
  • inequalities?
  • combinatorial sums? E.g., sum_{k=0}^n\binom{k}{m}=\binom{n+1}{m+1}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.