GithubHelp home page GithubHelp logo

verivote / beast Goto Github PK

View Code? Open in Web Editor NEW
6.0 7.0 2.0 83.15 MB

BEAST aims to make the verification of voting rules easier

License: MIT License

Java 89.41% ANTLR 2.97% Batchfile 0.04% C 2.55% HTML 4.76% CSS 0.27%
cbmc beast

beast's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

sgrebing skypr

beast's Issues

Symbolic variables are declared multiple times when also used as bound variables in quantified expressions

Expected behavior

Variables which are bound by quantified expressions only matter within those expressions, not outside of them.

Actual behavior

Defined symbolic variables can be used as bound variables in quantified expressions. The resulting C code thus declares this variable two times, once in the beginning and once in the loop itself, more precisely as the loop index which is declared and initialized.

Proposed solution

Forbid symbolic variables as bound variables in quantified expressions, i.e., only allow new/fresh variables to be bound by quantifiers.
Alternatively, a new variable could be produced, but I would rather have a clear distinction already in the property editor.

Steps to reproduce the behavior

In the property editor, I specify as symbolic variables a candidate Alice and a voter Bob, with the following precondition:

VOTER_AT_POS(0) == Bob;
!EXISTS_ONE_VOTER(Bob) :VOTES1(Bob) == Alice;

and the following postcondition:

ELECT1 != Alice;
VOTER_AT_POS(0) == Bob;

Among other code, the following c code is produced:

unsigned int Bob = nondet_uint();
assume (0 <= Bob && Bob < V);
// ...
for (unsigned int Bob = 0; Bob < V && !thereExists_0; Bob++) { /* ... */ }
// ...
unsigned int comparison_2 = 1;
comparison_2 = voterAtPos_1 == Bob;
assert (comparison_2);

Note that Bob is declared two times. Apparently, gcc is not picky about this program itself, but it is at least confusing to the reader and I am not too sure whether this is the intended behaviour.

Can't change electionType in the C-Editor anymore

If you try to create a new ElectionDescription, you can't pick which voting type you want to use.

It also prints a stack trace then, so something deeper down seems to be going wrong, and not just stringfiles.

at java.util.ArrayList.elementData(ArrayList.java:418)
at java.util.ArrayList.get(ArrayList.java:431)
at edu.pse.beast.celectiondescriptioneditor.ElectionTemplates.ElectionTemplateChooser.lambda$0(ElectionTemplateChooser.java:48)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2348)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
at java.awt.Component.processMouseEvent(Component.java:6535)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
at java.awt.Component.processEvent(Component.java:6300)
at java.awt.Container.processEvent(Container.java:2236)
at java.awt.Component.dispatchEventImpl(Component.java:4891)
at java.awt.Container.dispatchEventImpl(Container.java:2294)
at java.awt.Component.dispatchEvent(Component.java:4713)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4525)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466)
at java.awt.Container.dispatchEventImpl(Container.java:2280)
at java.awt.Window.dispatchEventImpl(Window.java:2750)
at java.awt.Component.dispatchEvent(Component.java:4713)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.awt.EventQueue$4.run(EventQueue.java:731)
at java.awt.EventQueue$4.run(EventQueue.java:729)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

Possible way to save some processing time

While profiling our program, I noticed that even when the checkers are running, the "ErrorFinder" and the "FindWordsConcurrently" keep on running and take up a lot of processing time. In my opinion we could just let them sleep while the checks are still running, to save that time and give cbmc possibly a bit more power.
beast_processortime

Ballot profile size adjustment stops working when reducing size

Expected behavior

The table to insert a ballot profile should resize with the numbers above (single choice with voters..)

Actual behavior

Increasing the size does work, but when trying to decrease the size of the table it works once, throws an exception and then stops working

Steps to reproduce the behavior

  1. open BEAST
  2. Switch to ballot profile tab
  3. Set voters to 10
  4. Set voters to 5 (should throw an exception
  5. Set voters to 10 again (this will not work)

Output type in elections for a parliament

In general, when voting for a parliament, the result type should be stricter than just a pointer in order to (1) avoid confusion for the user, (2) not have any problems with presenting the result or specifying the property, (3) be a little more efficient as invalid pointer access would not need to be checked additionally.
I would propose something like:
"struct result { unsigned int arr[C]; };"
or
"struct result { unsigned int arr[S]; };".
(at least the first one should definitely be implemented, for voting rules such as STV the second one appears to be more sensible though.)

By the way, which of these result types (amount of seats per party/candidate or ordered list of all seats assigning each a candidate) is currently implemented in BEAST?

Bug in C Code generation

When havin an EXISTS_ONE phrase in either the post or pre properties, it leads to an error when trying to test the property.

CodeArea of the PreAndPostProperties BackspaceHandling

How to recreate this bug:
Start the Program
Create a new Property
Select the Pre or PostpropertyArea
Press the backspace key.
The following Error (or similar) should appear:

Mär 11, 2017 3:18:18 PM edu.pse.beast.codearea.InputToCode.UserInsertToCode removeToTheLeft
SCHWERWIEGEND: null
javax.swing.text.BadLocationException: Invalid location

This shouldn't be an exception because many people will try to delete all of there input by just holding down the backspace key

Bug found on Version 1.4.05

Loading new properties fails

If you try to load a new property, the StaticSaverLoaders.SaverLoaderHelper throws a StringIndexOutOfBoundsException.

Recreate:
Load a property from Toolbar in PropertyEditor

Problem found in version 1.4.09

Nullpointer Exception in "AutoCompletionController.java"

I found it after updating to commit ce2306c

How to replicate it:

Just starting the tests did it for me.

Stack trace:

at edu.pse.beast.codearea.Autocompletion.AutocompletionController.ancestorMoved(AutocompletionController.java:180)
at javax.swing.AncestorNotifier.fireAncestorMoved(Unknown Source)
at javax.swing.AncestorNotifier.componentMoved(Unknown Source)
at java.awt.Component.processComponentEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Window.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException

Nondeterministic variables are not available within the voting function

Some voting rules, e.g., instant runoff voting, require either some rudimentary nondeterminism or arbitrary tiebreakers, e.g., for eliminating the (potentially non-unique) weakest candidate in many preferential voting rules. In standard C this is not possible as such, however CBMC provides such measures via the keyword "__CPROVER_assume (..);" and, e.g., "nondet_uint()".
Right now, BEAST only offers this on a global level via symbolic variables and pre- and postconditions.
However, this is sometimes needed at a local level, i.e., within some loop in the voting function, which currently yields errors in the static error search ("-Wimplicit-function-declaration") and hence cannot be executed.
Examples can be seen in the zip-file "Weitere Wahlverfahren" provided within the PSE course on the website (e.g., the variable "choose" in the file "instant-runoff.h").

Refactoring SaverLoader Classes

I was thinking about refactoring the saverloader classes to all use a helper class which would
encapsulate the common format of the save strings. The helper class would also parse given save strings
and provide access to their content via key-value pairs. I feel this change is somewhat necessary since the current method of saving strings isn't foolproof since it relies on certain patterns not appearing in the strings which are saved, for example patterns such as . The helper would instead use the length of a given string to save it, in the format . I have already implemented the helper class and used it in the electiondescriptionsaverloader, and it works really well, however it would require resaving all the projects we currently use for testing. I think this would be well worth it. What do you guys think

Code generation speed up for cbmc

In the for loop "for_all_voters", place assumes inside, because less variables and clauses are created by cbmc in that case, which should speed it up a bit for bigger numbers for voters / candidates

Possibility to extend and remove blocked code area

You can extend the gray and/or blocked area to further (previously non-blocked) parts of the code and hence also remove the previously blocked part.

Example:
1.) (Starting new voting method)
"unsigned int voting(unsigned int votes[V]) {
}"
2.) (Write "a" in first "legal" line)
"unsigned int voting(unsigned int votes[V]) {
a
} "
3.) (Press backspace at the first position of the line which includes the character "a")
"unsigned int voting(unsigned int votes[V]) { a
} "
Now you cannot change or remove the character "a" again. It gets even stranger when you now revert your last action as then the gray area extends to the first inner line. If you now press enter at the end of the signature line, the gray area gets bigger (in this case by adding non-blocked lines).
Different variations of keystrokes can now lead to various unexpected behaviours (e.g., the "end" key may now even lead to an "IllegalArgumentException" in some swing component).

You might want to check for different hot keys inside your codearea and their effects on your blocked code area. I used the current (2017-03-09 23:58:24) standalone version from the svn repository on a linux (fedora) machine.

absolute path used

In the class "FileSaver.java" the file to be deleted is always set to a fixed place, which will not work when the program is installed

GUI buttons differ from window to window

Bei den editoren bestehen unterschiede in der Reihenfolge der Knöpfe. wie sie angeordnet sind. Hier eine Auflistung aller Menüleisten (von links nach rechts)

  • Parametereditor ordner, speichern, speichern unter, Spezialknöpfe
  • Eigenschaftenliste: neu, ordner, speichern, speichern unter, rückgängig
  • Eigenschaftenedior: neu, rückgängig, vorwärts, speichern, speichern unter, ordner, kopieren, auschschneiden, einfügen
  • C-Editor: neu, speichern, speichern unter, ordner, rückgängig, vorwärts, kopieren, auschschneiden, einfügen

Meiner Meinung nach sollten wir uns auf ein einheitliches system einigen, sodass die betroffenden Fenster dann geändert werden können.

Candidate Indexing in the Counterexample

The indexes of the Candidates should start from 0 (not from 1). So they match the given votes (they start from 0 as well)
If the Candidate is the same number as C (the max number of Candidates) the counterexample should state "unentschieden" or something similar. Candidate C is NOT a Candidate but used as a 'not valid' statement.

Error in the Result Presentation

If you have multiple Checker running at the same time (for example four) and the third finishes before the second, the Result of the third Checker is displayed in the Result slot of the second.
More general speaking: Results are presented in the order they have finished, not the order of the list that started the results.

Possible fixes in my opinion:

  1. just wait for all all results to be finished until you present (fix would just have to be made in the BeastCommunicator) (bad fix imo, because no status updates for the user)

  2. traverse the loop that checks if the results are presentable with a foor loop, and give the current index variable of the loop to the presenter, so it knows in which order the results have to be presented

  3. let the results have an index on their own that they get assigned during their creation, so the presenter can conclude which result he has to present where.

NullpointerException in the SaverLoader of Project

How you can recreate the Bug:
Load the project-file "PreferenceBug" into the program.
Try to open another project-file. Press safe changes.
Now the NullpointerException is thrown.
Bug found on 1.4.05

Error in CBMCResult

When you try to read a one dimensional variable with the methode "readOneDimVar", and the array to be read is of the form {{...},{...}}, it is still registered, even though it shouldn't

Code generation error

When generating code which BEAST claims as valid, and then starting CBMC, an error occurs:

Expected behavior

The user should be able to compare two voting arrays.

Actual behavior

If the user wants to compare two voting arrays, cbmc fails with the error:

member operator requires structure type on left hand side but got `unsigned int [1]'
CONVERSION ERROR
Numeric exception : 0

Steps to reproduce the behavior

Open BEAST, create a new Project with In: Single Choice, Out: Single Candidate.
In the code window: write
return votes[0];
In the PreProperty window, add:
VOTES1 == VOTES2;
In the PostProperty window add:
ELECT1 != ELECT2;

An excerpt of the code which causes the error is:
` unsigned int votes1[V];
for(unsigned int counter_0 = 0; counter_0 < V1; counter_0++){
votes1[counter_0] = nondet_uint();
assume((0 <= votes1[counter_0]) && (votes1[counter_0] < C1));
}

    unsigned int votes2[V];
    for(unsigned int counter_0 = 0; counter_0 < V2; counter_0++){
            votes2[counter_0] = nondet_uint();
            assume((0 <= votes2[counter_0]) && (votes2[counter_0] < C2));
    }
    
    unsigned int comparison_0 = 1;
    for(unsigned int count_0 = 0; comparison_0 && count_0 < V; ++count_0) {
            comparison_0 = votes1.arr[count_0] == votes2.arr[count_0];
    }`

The error appears because votesN is not a struct with the content ".arr", as expected, but just a normal array.

Analysis can be done for empty election method -> NPE in ResultPresenterWindow

It is possible to do a check for invalid election/voting methods, e.g., the empty method already available at startup. The problem occurs rather late only when attempting to inspect the counterexample, which results in a NullPointerException:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at edu.pse.beast.propertylist.View.ResultPresenterWindow.writeElectedOneCandidate(ResultPresenterWindow.java:296)
at edu.pse.beast.propertylist.View.ResultPresenterWindow.presentFailureExample(ResultPresenterWindow.java:252)
at edu.pse.beast.propertylist.View.ListItem.passMessageToResultWindow(ListItem.java:216)
at edu.pse.beast.propertylist.View.ListItem.access$1(ListItem.java:201)
at edu.pse.beast.propertylist.View.ListItem$1.actionPerformed(ListItem.java:81)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2348)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
at java.awt.Component.processMouseEvent(Component.java:6533)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
at java.awt.Component.processEvent(Component.java:6298)
at java.awt.Container.processEvent(Container.java:2236)
at java.awt.Component.dispatchEventImpl(Component.java:4889)
at java.awt.Container.dispatchEventImpl(Container.java:2294)
at java.awt.Component.dispatchEvent(Component.java:4711)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4525)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466)
at java.awt.Container.dispatchEventImpl(Container.java:2280)
at java.awt.Window.dispatchEventImpl(Window.java:2746)
at java.awt.Component.dispatchEvent(Component.java:4711)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:90)
at java.awt.EventQueue$4.run(EventQueue.java:731)
at java.awt.EventQueue$4.run(EventQueue.java:729)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException

Loading can lead to an exception being thrown

If you load a project that isn't formatted correctly (for example the old file "SequentialTestTrueFalse.beast") an exception gets thrown, but no message is displayed to the user, that tells him that the file doesn't work.

Below I will post the stack dump from my crash:

at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.valueOf(Integer.java:766)
at edu.pse.beast.saverloader.StaticSaverLoaders.SaverLoaderHelper.getNumberAndRemoveNumberPartFromString(SaverLoaderHelper.java:53)
at edu.pse.beast.saverloader.StaticSaverLoaders.SaverLoaderHelper.parseSaveString(SaverLoaderHelper.java:32)
at edu.pse.beast.saverloader.ProjectSaverLoader.createFromSaveString(ProjectSaverLoader.java:47)
at edu.pse.beast.saverloader.FileChooser.loadObject(FileChooser.java:155)
at edu.pse.beast.parametereditor.UserActions.LoadProjectUserAction.perform(LoadProjectUserAction.java:50)
at edu.pse.beast.parametereditor.ParameterEditorBuilder$1.actionPerformed(ParameterEditorBuilder.java:208)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2348)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
at java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:289)
at java.awt.Component.processMouseEvent(Component.java:6535)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
at java.awt.Component.processEvent(Component.java:6300)
at java.awt.Container.processEvent(Container.java:2236)
at java.awt.Component.dispatchEventImpl(Component.java:4891)
at java.awt.Container.dispatchEventImpl(Container.java:2294)
at java.awt.Component.dispatchEvent(Component.java:4713)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4525)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466)
at java.awt.Container.dispatchEventImpl(Container.java:2280)
at java.awt.Window.dispatchEventImpl(Window.java:2750)
at java.awt.Component.dispatchEvent(Component.java:4713)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.awt.EventQueue$4.run(EventQueue.java:731)
at java.awt.EventQueue$4.run(EventQueue.java:729)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

Exception in thread "AWT-EventQueue-0" java.lang.NumberFormatException: For input string: "

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.