Initial import of criterion-plot-0.4.3. am: 835fea47b9 am: 4a63e537f1 am: aeb48a98ef am: e9e30d1bd0

Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/criterion-plot/+/1621100

Change-Id: Ie64662094fd81a0973ff1b1fe4814817cb07365d
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..931fc83
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,5 @@
+{
+  "git": {
+    "sha1": "3fcbcd237e14306d102f2ea2f1285cd6285e086c"
+  }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100755
index 0000000..726c7b6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*.bk
+Cargo.lock
+target/*
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100755
index 0000000..44fcc63
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1 @@
+../CONTRIBUTING.md
\ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..7ae1c42
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,47 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+edition = "2018"
+name = "criterion-plot"
+version = "0.4.3"
+authors = ["Jorge Aparicio <japaricious@gmail.com>", "Brook Heisler <brookheisler@gmail.com>"]
+description = "Criterion's plotting library"
+readme = "README.md"
+keywords = ["plotting", "gnuplot", "criterion"]
+categories = ["visualization"]
+license = "MIT/Apache-2.0"
+repository = "https://github.com/bheisler/criterion.rs"
+[dependencies.cast]
+version = "0.2"
+
+[dependencies.itertools]
+version = "0.9"
+[dev-dependencies.itertools-num]
+version = "0.1"
+
+[dev-dependencies.num-complex]
+version = "0.2"
+features = ["std"]
+default-features = false
+
+[dev-dependencies.rand]
+version = "0.4"
+[badges.appveyor]
+id = "4255ads9ctpupcl2"
+repository = "bheisler/criterion.rs"
+
+[badges.maintenance]
+status = "looking-for-maintainer"
+
+[badges.travis-ci]
+repository = "bheisler/criterion.rs"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100755
index 0000000..162931a
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,26 @@
+[package]
+authors = ["Jorge Aparicio <japaricious@gmail.com>", "Brook Heisler <brookheisler@gmail.com>"]
+name = "criterion-plot"
+version = "0.4.3"
+edition = "2018"
+
+description = "Criterion's plotting library"
+repository = "https://github.com/bheisler/criterion.rs"
+readme = "README.md"
+keywords = ["plotting", "gnuplot", "criterion"]
+categories = ["visualization"]
+license = "MIT/Apache-2.0"
+
+[dependencies]
+cast = "0.2"
+itertools = "0.9"
+
+[dev-dependencies]
+itertools-num = "0.1"
+num-complex = { version = "0.2", default-features = false, features = ["std"] }
+rand = "0.4"
+
+[badges]
+travis-ci = { repository = "bheisler/criterion.rs" }
+appveyor = { repository = "bheisler/criterion.rs", id = "4255ads9ctpupcl2" }
+maintenance = { status = "looking-for-maintainer" }
diff --git a/README.md b/README.md
new file mode 100755
index 0000000..6c403e0
--- /dev/null
+++ b/README.md
@@ -0,0 +1,38 @@
+# `criterion-plot`
+
+> Graphing sub-crate of [Criterion.rs].
+
+This is an unstable implementation detail of [Criterion.rs]. Anything may change
+at any time with no warning, including the public API. For further information,
+see [Criterion.rs].
+
+`criterion-plot` is currently looking for a new maintainer. See 
+[this thread](https://users.rust-lang.org/t/call-for-maintainers-criterion-plot/24413) for details.
+
+## License
+
+This project is licensed under either of
+
+* [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
+  ([LICENSE-APACHE](LICENSE-APACHE))
+
+* [MIT License](http://opensource.org/licenses/MIT)
+  ([LICENSE-MIT](LICENSE-MIT))
+
+at your option.
+
+## Contributing
+
+We welcome all people who want to contribute.
+Please see the [contributing instructions] for more information.
+
+Contributions in any form (issues, pull requests, etc.) to this project
+must adhere to Rust's [Code of Conduct].
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
+be dual licensed as above, without any additional terms or conditions.
+
+[Code of Conduct]: https://www.rust-lang.org/en-US/conduct.html
+[Criterion.rs]: https://github.com/bheisler/criterion.rs
+[contributing instructions]: CONTRIBUTING.md
diff --git a/src/axis.rs b/src/axis.rs
new file mode 100755
index 0000000..97fd0cb
--- /dev/null
+++ b/src/axis.rs
@@ -0,0 +1,198 @@
+//! Coordinate axis
+
+use std::borrow::Cow;
+use std::iter::IntoIterator;
+
+use crate::map;
+use crate::traits::{Configure, Data, Set};
+use crate::{
+    grid, Axis, Default, Display, Grid, Label, Range, Scale, ScaleFactor, Script, TicLabels,
+};
+
+/// Properties of the coordinate axes
+#[derive(Clone)]
+pub struct Properties {
+    grids: map::grid::Map<grid::Properties>,
+    hidden: bool,
+    label: Option<Cow<'static, str>>,
+    logarithmic: bool,
+    range: Option<(f64, f64)>,
+    scale_factor: f64,
+    tics: Option<String>,
+}
+
+impl Default for Properties {
+    fn default() -> Properties {
+        Properties {
+            grids: map::grid::Map::new(),
+            hidden: false,
+            label: None,
+            logarithmic: false,
+            range: None,
+            scale_factor: 1.,
+            tics: None,
+        }
+    }
+}
+
+impl Properties {
+    /// Hides the axis
+    ///
+    /// **Note** The `TopX` and `RightY` axes are hidden by default
+    pub fn hide(&mut self) -> &mut Properties {
+        self.hidden = true;
+        self
+    }
+
+    /// Makes the axis visible
+    ///
+    /// **Note** The `BottomX` and `LeftY` axes are visible by default
+    pub fn show(&mut self) -> &mut Properties {
+        self.hidden = false;
+        self
+    }
+}
+
+impl Configure<Grid> for Properties {
+    type Properties = grid::Properties;
+
+    /// Configures the gridlines
+    fn configure<F>(&mut self, grid: Grid, configure: F) -> &mut Properties
+    where
+        F: FnOnce(&mut grid::Properties) -> &mut grid::Properties,
+    {
+        if self.grids.contains_key(grid) {
+            configure(self.grids.get_mut(grid).unwrap());
+        } else {
+            let mut properties = Default::default();
+            configure(&mut properties);
+            self.grids.insert(grid, properties);
+        }
+
+        self
+    }
+}
+
+impl Set<Label> for Properties {
+    /// Attaches a label to the axis
+    fn set(&mut self, label: Label) -> &mut Properties {
+        self.label = Some(label.0);
+        self
+    }
+}
+
+impl Set<Range> for Properties {
+    /// Changes the range of the axis that will be shown
+    ///
+    /// **Note** All axes are auto-scaled by default
+    fn set(&mut self, range: Range) -> &mut Properties {
+        self.hidden = false;
+
+        match range {
+            Range::Auto => self.range = None,
+            Range::Limits(low, high) => self.range = Some((low, high)),
+        }
+
+        self
+    }
+}
+
+impl Set<Scale> for Properties {
+    /// Sets the scale of the axis
+    ///
+    /// **Note** All axes use a linear scale by default
+    fn set(&mut self, scale: Scale) -> &mut Properties {
+        self.hidden = false;
+
+        match scale {
+            Scale::Linear => self.logarithmic = false,
+            Scale::Logarithmic => self.logarithmic = true,
+        }
+
+        self
+    }
+}
+
+impl Set<ScaleFactor> for Properties {
+    /// Changes the *scale factor* of the axis.
+    ///
+    /// All the data plotted against this axis will have its corresponding coordinate scaled with
+    /// this factor before being plotted.
+    ///
+    /// **Note** The default scale factor is `1`.
+    fn set(&mut self, factor: ScaleFactor) -> &mut Properties {
+        self.scale_factor = factor.0;
+
+        self
+    }
+}
+
+impl<P, L> Set<TicLabels<P, L>> for Properties
+where
+    L: IntoIterator,
+    L::Item: AsRef<str>,
+    P: IntoIterator,
+    P::Item: Data,
+{
+    /// Attaches labels to the tics of an axis
+    fn set(&mut self, tics: TicLabels<P, L>) -> &mut Properties {
+        let TicLabels { positions, labels } = tics;
+
+        let pairs = positions
+            .into_iter()
+            .zip(labels.into_iter())
+            .map(|(pos, label)| format!("'{}' {}", label.as_ref(), pos.f64()))
+            .collect::<Vec<_>>();
+
+        if pairs.is_empty() {
+            self.tics = None
+        } else {
+            self.tics = Some(pairs.join(", "));
+        }
+
+        self
+    }
+}
+
+impl<'a> Script for (Axis, &'a Properties) {
+    fn script(&self) -> String {
+        let &(axis, properties) = self;
+        let axis_ = axis.display();
+
+        let mut script = if properties.hidden {
+            return format!("unset {}tics\n", axis_);
+        } else {
+            format!("set {}tics nomirror ", axis_)
+        };
+
+        if let Some(ref tics) = properties.tics {
+            script.push_str(&format!("({})", tics))
+        }
+
+        script.push('\n');
+
+        if let Some(ref label) = properties.label {
+            script.push_str(&format!("set {}label '{}'\n", axis_, label))
+        }
+
+        if let Some((low, high)) = properties.range {
+            script.push_str(&format!("set {}range [{}:{}]\n", axis_, low, high))
+        }
+
+        if properties.logarithmic {
+            script.push_str(&format!("set logscale {}\n", axis_));
+        }
+
+        for (grid, properties) in properties.grids.iter() {
+            script.push_str(&(axis, grid, properties).script());
+        }
+
+        script
+    }
+}
+
+impl crate::ScaleFactorTrait for Properties {
+    fn scale_factor(&self) -> f64 {
+        self.scale_factor
+    }
+}
diff --git a/src/candlestick.rs b/src/candlestick.rs
new file mode 100755
index 0000000..e0a5cbe
--- /dev/null
+++ b/src/candlestick.rs
@@ -0,0 +1,151 @@
+//! "Candlestick" plots
+
+use std::borrow::Cow;
+use std::iter::IntoIterator;
+
+use crate::data::Matrix;
+use crate::traits::{self, Data, Set};
+use crate::{Color, Default, Display, Figure, Label, LineType, LineWidth, Plot, Script};
+
+/// Properties common to candlestick plots
+pub struct Properties {
+    color: Option<Color>,
+    label: Option<Cow<'static, str>>,
+    line_type: LineType,
+    linewidth: Option<f64>,
+}
+
+impl Default for Properties {
+    fn default() -> Properties {
+        Properties {
+            color: None,
+            label: None,
+            line_type: LineType::Solid,
+            linewidth: None,
+        }
+    }
+}
+
+impl Script for Properties {
+    fn script(&self) -> String {
+        let mut script = String::from("with candlesticks ");
+
+        script.push_str(&format!("lt {} ", self.line_type.display()));
+
+        if let Some(lw) = self.linewidth {
+            script.push_str(&format!("lw {} ", lw))
+        }
+
+        if let Some(color) = self.color {
+            script.push_str(&format!("lc rgb '{}' ", color.display()));
+        }
+
+        if let Some(ref label) = self.label {
+            script.push_str("title '");
+            script.push_str(label);
+            script.push('\'')
+        } else {
+            script.push_str("notitle")
+        }
+
+        script
+    }
+}
+
+impl Set<Color> for Properties {
+    /// Sets the line color
+    fn set(&mut self, color: Color) -> &mut Properties {
+        self.color = Some(color);
+        self
+    }
+}
+
+impl Set<Label> for Properties {
+    /// Sets the legend label
+    fn set(&mut self, label: Label) -> &mut Properties {
+        self.label = Some(label.0);
+        self
+    }
+}
+
+impl Set<LineType> for Properties {
+    /// Changes the line type
+    ///
+    /// **Note** By default `Solid` lines are used
+    fn set(&mut self, lt: LineType) -> &mut Properties {
+        self.line_type = lt;
+        self
+    }
+}
+
+impl Set<LineWidth> for Properties {
+    /// Changes the width of the line
+    ///
+    /// # Panics
+    ///
+    /// Panics if `width` is a non-positive value
+    fn set(&mut self, lw: LineWidth) -> &mut Properties {
+        let lw = lw.0;
+
+        assert!(lw > 0.);
+
+        self.linewidth = Some(lw);
+        self
+    }
+}
+
+/// A candlestick consists of a box and two whiskers that extend beyond the box
+pub struct Candlesticks<X, WM, BM, BH, WH> {
+    /// X coordinate of the candlestick
+    pub x: X,
+    /// Y coordinate of the end point of the bottom whisker
+    pub whisker_min: WM,
+    /// Y coordinate of the bottom of the box
+    pub box_min: BM,
+    /// Y coordinate of the top of the box
+    pub box_high: BH,
+    /// Y coordinate of the end point of the top whisker
+    pub whisker_high: WH,
+}
+
+impl<X, WM, BM, BH, WH> traits::Plot<Candlesticks<X, WM, BM, BH, WH>> for Figure
+where
+    BH: IntoIterator,
+    BH::Item: Data,
+    BM: IntoIterator,
+    BM::Item: Data,
+    WH: IntoIterator,
+    WH::Item: Data,
+    WM: IntoIterator,
+    WM::Item: Data,
+    X: IntoIterator,
+    X::Item: Data,
+{
+    type Properties = Properties;
+
+    fn plot<F>(
+        &mut self,
+        candlesticks: Candlesticks<X, WM, BM, BH, WH>,
+        configure: F,
+    ) -> &mut Figure
+    where
+        F: FnOnce(&mut Properties) -> &mut Properties,
+    {
+        let (x_factor, y_factor) = crate::scale_factor(&self.axes, crate::Axes::BottomXLeftY);
+        let Candlesticks {
+            x,
+            whisker_min,
+            box_min,
+            box_high,
+            whisker_high,
+        } = candlesticks;
+
+        let data = Matrix::new(
+            izip!(x, box_min, whisker_min, whisker_high, box_high),
+            (x_factor, y_factor, y_factor, y_factor, y_factor),
+        );
+        self.plots
+            .push(Plot::new(data, configure(&mut Default::default())));
+        self
+    }
+}
diff --git a/src/curve.rs b/src/curve.rs
new file mode 100755
index 0000000..bbddeff
--- /dev/null
+++ b/src/curve.rs
@@ -0,0 +1,270 @@
+//! Simple "curve" like plots
+
+use std::borrow::Cow;
+use std::iter::IntoIterator;
+
+use crate::data::Matrix;
+use crate::traits::{self, Data, Set};
+use crate::{
+    Axes, Color, CurveDefault, Display, Figure, Label, LineType, LineWidth, Plot, PointSize,
+    PointType, Script,
+};
+
+/// Properties common to simple "curve" like plots
+pub struct Properties {
+    axes: Option<Axes>,
+    color: Option<Color>,
+    label: Option<Cow<'static, str>>,
+    line_type: LineType,
+    linewidth: Option<f64>,
+    point_type: Option<PointType>,
+    point_size: Option<f64>,
+    style: Style,
+}
+
+impl CurveDefault<Style> for Properties {
+    fn default(style: Style) -> Properties {
+        Properties {
+            axes: None,
+            color: None,
+            label: None,
+            line_type: LineType::Solid,
+            linewidth: None,
+            point_size: None,
+            point_type: None,
+            style,
+        }
+    }
+}
+
+impl Script for Properties {
+    fn script(&self) -> String {
+        let mut script = if let Some(axes) = self.axes {
+            format!("axes {} ", axes.display())
+        } else {
+            String::new()
+        };
+
+        script.push_str(&format!("with {} ", self.style.display()));
+        script.push_str(&format!("lt {} ", self.line_type.display()));
+
+        if let Some(lw) = self.linewidth {
+            script.push_str(&format!("lw {} ", lw))
+        }
+
+        if let Some(color) = self.color {
+            script.push_str(&format!("lc rgb '{}' ", color.display()))
+        }
+
+        if let Some(pt) = self.point_type {
+            script.push_str(&format!("pt {} ", pt.display()))
+        }
+
+        if let Some(ps) = self.point_size {
+            script.push_str(&format!("ps {} ", ps))
+        }
+
+        if let Some(ref label) = self.label {
+            script.push_str("title '");
+            script.push_str(label);
+            script.push('\'')
+        } else {
+            script.push_str("notitle")
+        }
+
+        script
+    }
+}
+
+impl Set<Axes> for Properties {
+    /// Select the axes to plot against
+    ///
+    /// **Note** By default, the `BottomXLeftY` axes are used
+    fn set(&mut self, axes: Axes) -> &mut Properties {
+        self.axes = Some(axes);
+        self
+    }
+}
+
+impl Set<Color> for Properties {
+    /// Sets the line color
+    fn set(&mut self, color: Color) -> &mut Properties {
+        self.color = Some(color);
+        self
+    }
+}
+
+impl Set<Label> for Properties {
+    /// Sets the legend label
+    fn set(&mut self, label: Label) -> &mut Properties {
+        self.label = Some(label.0);
+        self
+    }
+}
+
+impl Set<LineType> for Properties {
+    /// Changes the line type
+    ///
+    /// **Note** By default `Solid` lines are used
+    fn set(&mut self, lt: LineType) -> &mut Properties {
+        self.line_type = lt;
+        self
+    }
+}
+
+impl Set<LineWidth> for Properties {
+    /// Changes the width of the line
+    ///
+    /// # Panics
+    ///
+    /// Panics if `width` is a non-positive value
+    fn set(&mut self, lw: LineWidth) -> &mut Properties {
+        let lw = lw.0;
+
+        assert!(lw > 0.);
+
+        self.linewidth = Some(lw);
+        self
+    }
+}
+
+impl Set<PointSize> for Properties {
+    /// Changes the size of the points
+    ///
+    /// # Panics
+    ///
+    /// Panics if `size` is a non-positive value
+    fn set(&mut self, ps: PointSize) -> &mut Properties {
+        let ps = ps.0;
+
+        assert!(ps > 0.);
+
+        self.point_size = Some(ps);
+        self
+    }
+}
+
+impl Set<PointType> for Properties {
+    /// Changes the point type
+    fn set(&mut self, pt: PointType) -> &mut Properties {
+        self.point_type = Some(pt);
+        self
+    }
+}
+
+/// Types of "curve" plots
+pub enum Curve<X, Y> {
+    /// A minimally sized dot on each data point
+    Dots {
+        /// X coordinate of the data points
+        x: X,
+        /// Y coordinate of the data points
+        y: Y,
+    },
+    /// A vertical "impulse" on each data point
+    Impulses {
+        /// X coordinate of the data points
+        x: X,
+        /// Y coordinate of the data points
+        y: Y,
+    },
+    /// Line that joins the data points
+    Lines {
+        /// X coordinate of the data points
+        x: X,
+        /// Y coordinate of the data points
+        y: Y,
+    },
+    /// Line with a point on each data point
+    LinesPoints {
+        /// X coordinate of the data points
+        x: X,
+        /// Y coordinate of the data points
+        y: Y,
+    },
+    /// A point on each data point
+    Points {
+        /// X coordinate of the data points
+        x: X,
+        /// Y coordinate of the data points
+        y: Y,
+    },
+    /// An step `_|` between each data point
+    Steps {
+        /// X coordinate of the data points
+        x: X,
+        /// Y coordinate of the data points
+        y: Y,
+    },
+}
+
+impl<X, Y> Curve<X, Y> {
+    fn style(&self) -> Style {
+        match *self {
+            Curve::Dots { .. } => Style::Dots,
+            Curve::Impulses { .. } => Style::Impulses,
+            Curve::Lines { .. } => Style::Lines,
+            Curve::LinesPoints { .. } => Style::LinesPoints,
+            Curve::Points { .. } => Style::Points,
+            Curve::Steps { .. } => Style::Steps,
+        }
+    }
+}
+
+#[derive(Clone, Copy)]
+enum Style {
+    Dots,
+    Impulses,
+    Lines,
+    LinesPoints,
+    Points,
+    Steps,
+}
+
+impl Display<&'static str> for Style {
+    fn display(&self) -> &'static str {
+        match *self {
+            Style::Dots => "dots",
+            Style::Impulses => "impulses",
+            Style::Lines => "lines",
+            Style::LinesPoints => "linespoints",
+            Style::Points => "points",
+            Style::Steps => "steps",
+        }
+    }
+}
+
+impl<X, Y> traits::Plot<Curve<X, Y>> for Figure
+where
+    X: IntoIterator,
+    X::Item: Data,
+    Y: IntoIterator,
+    Y::Item: Data,
+{
+    type Properties = Properties;
+
+    fn plot<F>(&mut self, curve: Curve<X, Y>, configure: F) -> &mut Figure
+    where
+        F: FnOnce(&mut Properties) -> &mut Properties,
+    {
+        let style = curve.style();
+        let (x, y) = match curve {
+            Curve::Dots { x, y }
+            | Curve::Impulses { x, y }
+            | Curve::Lines { x, y }
+            | Curve::LinesPoints { x, y }
+            | Curve::Points { x, y }
+            | Curve::Steps { x, y } => (x, y),
+        };
+
+        let mut props = CurveDefault::default(style);
+        configure(&mut props);
+
+        let (x_factor, y_factor) =
+            crate::scale_factor(&self.axes, props.axes.unwrap_or(crate::Axes::BottomXLeftY));
+
+        let data = Matrix::new(izip!(x, y), (x_factor, y_factor));
+        self.plots.push(Plot::new(data, &props));
+        self
+    }
+}
diff --git a/src/data.rs b/src/data.rs
new file mode 100755
index 0000000..eb1f738
--- /dev/null
+++ b/src/data.rs
@@ -0,0 +1,174 @@
+#![allow(deprecated)]
+
+use std::mem;
+
+use cast::From as _0;
+
+use crate::traits::Data;
+
+macro_rules! impl_data {
+    ($($ty:ty),+) => {
+        $(
+            impl Data for $ty {
+                fn f64(self) -> f64 {
+                    f64::cast(self)
+                }
+            }
+
+            impl<'a> Data for &'a $ty {
+                fn f64(self) -> f64 {
+                    f64::cast(*self)
+                }
+            }
+        )+
+    }
+}
+
+impl_data!(f32, f64, i16, i32, i64, i8, isize, u16, u32, u64, u8, usize);
+
+#[derive(Clone)]
+pub struct Matrix {
+    bytes: Vec<u8>,
+    ncols: usize,
+    nrows: usize,
+}
+
+impl Matrix {
+    pub fn new<I>(rows: I, scale: <I::Item as Row>::Scale) -> Matrix
+    where
+        I: Iterator,
+        I::Item: Row,
+    {
+        let ncols = I::Item::ncols();
+        let bytes_per_row = ncols * mem::size_of::<f64>();
+        let mut bytes = Vec::with_capacity(rows.size_hint().0 * bytes_per_row);
+
+        let mut nrows = 0;
+        for row in rows {
+            nrows += 1;
+            row.append_to(&mut bytes, scale);
+        }
+
+        Matrix {
+            bytes,
+            ncols,
+            nrows,
+        }
+    }
+
+    pub fn bytes(&self) -> &[u8] {
+        &self.bytes
+    }
+
+    pub fn ncols(&self) -> usize {
+        self.ncols
+    }
+
+    pub fn nrows(&self) -> usize {
+        self.nrows
+    }
+}
+
+/// Data that can serve as a row of the data matrix
+pub trait Row {
+    /// Private
+    type Scale: Copy;
+
+    /// Append this row to a buffer
+    fn append_to(self, buffer: &mut Vec<u8>, scale: Self::Scale);
+    /// Number of columns of the row
+    fn ncols() -> usize;
+}
+
+fn write_f64(w: &mut impl std::io::Write, f: f64) -> std::io::Result<()> {
+    w.write_all(&f.to_bits().to_le_bytes())
+}
+
+impl<A, B> Row for (A, B)
+where
+    A: Data,
+    B: Data,
+{
+    type Scale = (f64, f64);
+
+    fn append_to(self, buffer: &mut Vec<u8>, scale: (f64, f64)) {
+        let (a, b) = self;
+
+        write_f64(buffer, a.f64() * scale.0).unwrap();
+        write_f64(buffer, b.f64() * scale.1).unwrap();
+    }
+
+    fn ncols() -> usize {
+        2
+    }
+}
+
+impl<A, B, C> Row for (A, B, C)
+where
+    A: Data,
+    B: Data,
+    C: Data,
+{
+    type Scale = (f64, f64, f64);
+
+    fn append_to(self, buffer: &mut Vec<u8>, scale: (f64, f64, f64)) {
+        let (a, b, c) = self;
+
+        write_f64(buffer, a.f64() * scale.0).unwrap();
+        write_f64(buffer, b.f64() * scale.1).unwrap();
+        write_f64(buffer, c.f64() * scale.2).unwrap();
+    }
+
+    fn ncols() -> usize {
+        3
+    }
+}
+
+impl<A, B, C, D> Row for (A, B, C, D)
+where
+    A: Data,
+    B: Data,
+    C: Data,
+    D: Data,
+{
+    type Scale = (f64, f64, f64, f64);
+
+    fn append_to(self, buffer: &mut Vec<u8>, scale: (f64, f64, f64, f64)) {
+        let (a, b, c, d) = self;
+
+        write_f64(buffer, a.f64() * scale.0).unwrap();
+        write_f64(buffer, b.f64() * scale.1).unwrap();
+        write_f64(buffer, c.f64() * scale.2).unwrap();
+        write_f64(buffer, d.f64() * scale.3).unwrap();
+    }
+
+    fn ncols() -> usize {
+        4
+    }
+}
+
+impl<A, B, C, D, E> Row for (A, B, C, D, E)
+where
+    A: Data,
+    B: Data,
+    C: Data,
+    D: Data,
+    E: Data,
+{
+    type Scale = (f64, f64, f64, f64, f64);
+
+    #[cfg_attr(feature = "cargo-clippy", allow(clippy::many_single_char_names))]
+    fn append_to(self, buffer: &mut Vec<u8>, scale: (f64, f64, f64, f64, f64)) {
+        let (a, b, c, d, e) = self;
+
+        write_f64(buffer, a.f64() * scale.0).unwrap();
+        write_f64(buffer, b.f64() * scale.1).unwrap();
+        write_f64(buffer, c.f64() * scale.2).unwrap();
+        write_f64(buffer, d.f64() * scale.3).unwrap();
+        write_f64(buffer, e.f64() * scale.4).unwrap();
+    }
+
+    fn ncols() -> usize {
+        5
+    }
+}
diff --git a/src/display.rs b/src/display.rs
new file mode 100755
index 0000000..8f6f1e1
--- /dev/null
+++ b/src/display.rs
@@ -0,0 +1,139 @@
+use std::borrow::Cow;
+
+use crate::key::{Horizontal, Justification, Order, Stacked, Vertical};
+use crate::{Axes, Axis, Color, Display, Grid, LineType, PointType, Terminal};
+
+impl Display<&'static str> for Axis {
+    fn display(&self) -> &'static str {
+        match *self {
+            Axis::BottomX => "x",
+            Axis::LeftY => "y",
+            Axis::RightY => "y2",
+            Axis::TopX => "x2",
+        }
+    }
+}
+
+impl Display<&'static str> for Axes {
+    fn display(&self) -> &'static str {
+        match *self {
+            Axes::BottomXLeftY => "x1y1",
+            Axes::BottomXRightY => "x1y2",
+            Axes::TopXLeftY => "x2y1",
+            Axes::TopXRightY => "x2y2",
+        }
+    }
+}
+
+impl Display<Cow<'static, str>> for Color {
+    fn display(&self) -> Cow<'static, str> {
+        match *self {
+            Color::Black => Cow::from("black"),
+            Color::Blue => Cow::from("blue"),
+            Color::Cyan => Cow::from("cyan"),
+            Color::DarkViolet => Cow::from("dark-violet"),
+            Color::ForestGreen => Cow::from("forest-green"),
+            Color::Gold => Cow::from("gold"),
+            Color::Gray => Cow::from("gray"),
+            Color::Green => Cow::from("green"),
+            Color::Magenta => Cow::from("magenta"),
+            Color::Red => Cow::from("red"),
+            Color::Rgb(r, g, b) => Cow::from(format!("#{:02x}{:02x}{:02x}", r, g, b)),
+            Color::White => Cow::from("white"),
+            Color::Yellow => Cow::from("yellow"),
+        }
+    }
+}
+
+impl Display<&'static str> for Grid {
+    fn display(&self) -> &'static str {
+        match *self {
+            Grid::Major => "",
+            Grid::Minor => "m",
+        }
+    }
+}
+
+impl Display<&'static str> for Horizontal {
+    fn display(&self) -> &'static str {
+        match *self {
+            Horizontal::Center => "center",
+            Horizontal::Left => "left",
+            Horizontal::Right => "right",
+        }
+    }
+}
+
+impl Display<&'static str> for Justification {
+    fn display(&self) -> &'static str {
+        match *self {
+            Justification::Left => "Left",
+            Justification::Right => "Right",
+        }
+    }
+}
+
+impl Display<&'static str> for LineType {
+    fn display(&self) -> &'static str {
+        match *self {
+            LineType::Dash => "2",
+            LineType::Dot => "3",
+            LineType::DotDash => "4",
+            LineType::DotDotDash => "5",
+            LineType::SmallDot => "0",
+            LineType::Solid => "1",
+        }
+    }
+}
+
+impl Display<&'static str> for Order {
+    fn display(&self) -> &'static str {
+        match *self {
+            Order::TextSample => "noreverse",
+            Order::SampleText => "reverse",
+        }
+    }
+}
+
+impl Display<&'static str> for PointType {
+    fn display(&self) -> &'static str {
+        match *self {
+            PointType::Circle => "6",
+            PointType::FilledCircle => "7",
+            PointType::FilledSquare => "5",
+            PointType::FilledTriangle => "9",
+            PointType::Plus => "1",
+            PointType::Square => "4",
+            PointType::Star => "3",
+            PointType::Triangle => "8",
+            PointType::X => "2",
+        }
+    }
+}
+
+impl Display<&'static str> for Stacked {
+    fn display(&self) -> &'static str {
+        match *self {
+            Stacked::Horizontally => "horizontal",
+            Stacked::Vertically => "vertical",
+        }
+    }
+}
+
+impl Display<&'static str> for Terminal {
+    fn display(&self) -> &'static str {
+        match *self {
+            Terminal::Svg => "svg dynamic",
+        }
+    }
+}
+
+impl Display<&'static str> for Vertical {
+    fn display(&self) -> &'static str {
+        match *self {
+            Vertical::Bottom => "bottom",
+            Vertical::Center => "center",
+            Vertical::Top => "top",
+        }
+    }
+}
diff --git a/src/errorbar.rs b/src/errorbar.rs
new file mode 100755
index 0000000..7efd23e
--- /dev/null
+++ b/src/errorbar.rs
@@ -0,0 +1,290 @@
+//! Error bar plots
+
+use std::borrow::Cow;
+use std::iter::IntoIterator;
+
+use crate::data::Matrix;
+use crate::traits::{self, Data, Set};
+use crate::{
+    Color, Display, ErrorBarDefault, Figure, Label, LineType, LineWidth, Plot, PointSize,
+    PointType, Script,
+};
+
+/// Properties common to error bar plots
+pub struct Properties {
+    color: Option<Color>,
+    label: Option<Cow<'static, str>>,
+    line_type: LineType,
+    linewidth: Option<f64>,
+    point_size: Option<f64>,
+    point_type: Option<PointType>,
+    style: Style,
+}
+
+impl ErrorBarDefault<Style> for Properties {
+    fn default(style: Style) -> Properties {
+        Properties {
+            color: None,
+            label: None,
+            line_type: LineType::Solid,
+            linewidth: None,
+            point_type: None,
+            point_size: None,
+            style,
+        }
+    }
+}
+
+impl Script for Properties {
+    fn script(&self) -> String {
+        let mut script = format!("with {} ", self.style.display());
+
+        script.push_str(&format!("lt {} ", self.line_type.display()));
+
+        if let Some(lw) = self.linewidth {
+            script.push_str(&format!("lw {} ", lw))
+        }
+
+        if let Some(color) = self.color {
+            script.push_str(&format!("lc rgb '{}' ", color.display()))
+        }
+
+        if let Some(pt) = self.point_type {
+            script.push_str(&format!("pt {} ", pt.display()))
+        }
+
+        if let Some(ps) = self.point_size {
+            script.push_str(&format!("ps {} ", ps))
+        }
+
+        if let Some(ref label) = self.label {
+            script.push_str("title '");
+            script.push_str(label);
+            script.push('\'')
+        } else {
+            script.push_str("notitle")
+        }
+
+        script
+    }
+}
+
+impl Set<Color> for Properties {
+    /// Changes the color of the error bars
+    fn set(&mut self, color: Color) -> &mut Properties {
+        self.color = Some(color);
+        self
+    }
+}
+
+impl Set<Label> for Properties {
+    /// Sets the legend label
+    fn set(&mut self, label: Label) -> &mut Properties {
+        self.label = Some(label.0);
+        self
+    }
+}
+
+impl Set<LineType> for Properties {
+    /// Change the line type
+    ///
+    /// **Note** By default `Solid` lines are used
+    fn set(&mut self, lt: LineType) -> &mut Properties {
+        self.line_type = lt;
+        self
+    }
+}
+
+impl Set<LineWidth> for Properties {
+    /// Changes the linewidth
+    ///
+    /// # Panics
+    ///
+    /// Panics if `lw` is a non-positive value
+    fn set(&mut self, lw: LineWidth) -> &mut Properties {
+        let lw = lw.0;
+
+        assert!(lw > 0.);
+
+        self.linewidth = Some(lw);
+        self
+    }
+}
+
+impl Set<PointSize> for Properties {
+    /// Changes the size of the points
+    ///
+    /// # Panics
+    ///
+    /// Panics if `size` is a non-positive value
+    fn set(&mut self, ps: PointSize) -> &mut Properties {
+        let ps = ps.0;
+
+        assert!(ps > 0.);
+
+        self.point_size = Some(ps);
+        self
+    }
+}
+
+impl Set<PointType> for Properties {
+    /// Changes the point type
+    fn set(&mut self, pt: PointType) -> &mut Properties {
+        self.point_type = Some(pt);
+        self
+    }
+}
+
+#[derive(Clone, Copy)]
+enum Style {
+    XErrorBars,
+    XErrorLines,
+    YErrorBars,
+    YErrorLines,
+}
+
+impl Display<&'static str> for Style {
+    fn display(&self) -> &'static str {
+        match *self {
+            Style::XErrorBars => "xerrorbars",
+            Style::XErrorLines => "xerrorlines",
+            Style::YErrorBars => "yerrorbars",
+            Style::YErrorLines => "yerrorlines",
+        }
+    }
+}
+
+/// Asymmetric error bar plots
+pub enum ErrorBar<X, Y, L, H> {
+    /// Horizontal error bars
+    XErrorBars {
+        /// X coordinate of the data points
+        x: X,
+        /// Y coordinate of the data points
+        y: Y,
+        /// X coordinate of the left end of the error bar
+        x_low: L,
+        /// Y coordinate of the right end of the error bar
+        x_high: H,
+    },
+    /// Horizontal error bars, where each point is joined by a line
+    XErrorLines {
+        /// X coordinate of the data points
+        x: X,
+        /// Y coordinate of the data points
+        y: Y,
+        /// X coordinate of the left end of the error bar
+        x_low: L,
+        /// Y coordinate of the right end of the error bar
+        x_high: H,
+    },
+    /// Vertical error bars
+    YErrorBars {
+        /// X coordinate of the data points
+        x: X,
+        /// Y coordinate of the data points
+        y: Y,
+        /// Y coordinate of the bottom of the error bar
+        y_low: L,
+        /// Y coordinate of the top of the error bar
+        y_high: H,
+    },
+    /// Vertical error bars, where each point is joined by a line
+    YErrorLines {
+        /// X coordinate of the data points
+        x: X,
+        /// Y coordinate of the data points
+        y: Y,
+        /// Y coordinate of the bottom of the error bar
+        y_low: L,
+        /// Y coordinate of the top of the error bar
+        y_high: H,
+    },
+}
+
+impl<X, Y, L, H> ErrorBar<X, Y, L, H> {
+    fn style(&self) -> Style {
+        match *self {
+            ErrorBar::XErrorBars { .. } => Style::XErrorBars,
+            ErrorBar::XErrorLines { .. } => Style::XErrorLines,
+            ErrorBar::YErrorBars { .. } => Style::YErrorBars,
+            ErrorBar::YErrorLines { .. } => Style::YErrorLines,
+        }
+    }
+}
+
+impl<X, Y, L, H> traits::Plot<ErrorBar<X, Y, L, H>> for Figure
+where
+    H: IntoIterator,
+    H::Item: Data,
+    L: IntoIterator,
+    L::Item: Data,
+    X: IntoIterator,
+    X::Item: Data,
+    Y: IntoIterator,
+    Y::Item: Data,
+{
+    type Properties = Properties;
+
+    fn plot<F>(&mut self, e: ErrorBar<X, Y, L, H>, configure: F) -> &mut Figure
+    where
+        F: FnOnce(&mut Properties) -> &mut Properties,
+    {
+        let (x_factor, y_factor) = crate::scale_factor(&self.axes, crate::Axes::BottomXLeftY);
+
+        let style = e.style();
+        let (x, y, length, height, e_factor) = match e {
+            ErrorBar::XErrorBars {
+                x,
+                y,
+                x_low,
+                x_high,
+            }
+            | ErrorBar::XErrorLines {
+                x,
+                y,
+                x_low,
+                x_high,
+            } => (x, y, x_low, x_high, x_factor),
+            ErrorBar::YErrorBars {
+                x,
+                y,
+                y_low,
+                y_high,
+            }
+            | ErrorBar::YErrorLines {
+                x,
+                y,
+                y_low,
+                y_high,
+            } => (x, y, y_low, y_high, y_factor),
+        };
+        let data = Matrix::new(
+            izip!(x, y, length, height),
+            (x_factor, y_factor, e_factor, e_factor),
+        );
+        self.plots.push(Plot::new(
+            data,
+            configure(&mut ErrorBarDefault::default(style)),
+        ));
+        self
+    }
+}
+
+// TODO XY error bar
+// pub struct XyErrorBar<X, Y, XL, XH, YL, YH> {
+// x: X,
+// y: Y,
+// x_low: XL,
+// x_high: XH,
+// y_low: YL,
+// y_high: YH,
+// }
+
+// TODO Symmetric error bars
+// pub enum SymmetricErrorBar {
+// XSymmetricErrorBar { x: X, y: Y, x_delta: D },
+// XSymmetricErrorLines { x: X, y: Y, x_delta: D },
+// YSymmetricErrorBar { x: X, y: Y, y_delta: D },
+// YSymmetricErrorLines { x: X, y: Y, y_delta: D },
+// }
diff --git a/src/filledcurve.rs b/src/filledcurve.rs
new file mode 100755
index 0000000..f79dbdd
--- /dev/null
+++ b/src/filledcurve.rs
@@ -0,0 +1,140 @@
+//! Filled curve plots
+
+use std::borrow::Cow;
+use std::iter::IntoIterator;
+
+use crate::data::Matrix;
+use crate::traits::{self, Data, Set};
+use crate::{Axes, Color, Default, Display, Figure, Label, Opacity, Plot, Script};
+
+/// Properties common to filled curve plots
+pub struct Properties {
+    axes: Option<Axes>,
+    color: Option<Color>,
+    label: Option<Cow<'static, str>>,
+    opacity: Option<f64>,
+}
+
+impl Default for Properties {
+    fn default() -> Properties {
+        Properties {
+            axes: None,
+            color: None,
+            label: None,
+            opacity: None,
+        }
+    }
+}
+
+impl Script for Properties {
+    fn script(&self) -> String {
+        let mut script = if let Some(axes) = self.axes {
+            format!("axes {} ", axes.display())
+        } else {
+            String::new()
+        };
+        script.push_str("with filledcurves ");
+
+        script.push_str("fillstyle ");
+
+        if let Some(opacity) = self.opacity {
+            script.push_str(&format!("solid {} ", opacity))
+        }
+
+        // TODO border shoulde be configurable
+        script.push_str("noborder ");
+
+        if let Some(color) = self.color {
+            script.push_str(&format!("lc rgb '{}' ", color.display()));
+        }
+
+        if let Some(ref label) = self.label {
+            script.push_str("title '");
+            script.push_str(label);
+            script.push('\'')
+        } else {
+            script.push_str("notitle")
+        }
+
+        script
+    }
+}
+
+impl Set<Axes> for Properties {
+    /// Select axes to plot against
+    ///
+    /// **Note** By default, the `BottomXLeftY` axes are used
+    fn set(&mut self, axes: Axes) -> &mut Properties {
+        self.axes = Some(axes);
+        self
+    }
+}
+
+impl Set<Color> for Properties {
+    /// Sets the fill color
+    fn set(&mut self, color: Color) -> &mut Properties {
+        self.color = Some(color);
+        self
+    }
+}
+
+impl Set<Label> for Properties {
+    /// Sets the legend label
+    fn set(&mut self, label: Label) -> &mut Properties {
+        self.label = Some(label.0);
+        self
+    }
+}
+
+impl Set<Opacity> for Properties {
+    /// Changes the opacity of the fill color
+    ///
+    /// **Note** By default, the fill color is totally opaque (`opacity = 1.0`)
+    ///
+    /// # Panics
+    ///
+    /// Panics if `opacity` is outside the range `[0, 1]`
+    fn set(&mut self, opacity: Opacity) -> &mut Properties {
+        self.opacity = Some(opacity.0);
+        self
+    }
+}
+
+/// Fills the area between two curves
+pub struct FilledCurve<X, Y1, Y2> {
+    /// X coordinate of the data points of both curves
+    pub x: X,
+    /// Y coordinate of the data points of the first curve
+    pub y1: Y1,
+    /// Y coordinate of the data points of the second curve
+    pub y2: Y2,
+}
+
+impl<X, Y1, Y2> traits::Plot<FilledCurve<X, Y1, Y2>> for Figure
+where
+    X: IntoIterator,
+    X::Item: Data,
+    Y1: IntoIterator,
+    Y1::Item: Data,
+    Y2: IntoIterator,
+    Y2::Item: Data,
+{
+    type Properties = Properties;
+
+    fn plot<F>(&mut self, fc: FilledCurve<X, Y1, Y2>, configure: F) -> &mut Figure
+    where
+        F: FnOnce(&mut Properties) -> &mut Properties,
+    {
+        let FilledCurve { x, y1, y2 } = fc;
+
+        let mut props = Default::default();
+        configure(&mut props);
+
+        let (x_factor, y_factor) =
+            crate::scale_factor(&self.axes, props.axes.unwrap_or(crate::Axes::BottomXLeftY));
+
+        let data = Matrix::new(izip!(x, y1, y2), (x_factor, y_factor, y_factor));
+        self.plots.push(Plot::new(data, &props));
+        self
+    }
+}
diff --git a/src/grid.rs b/src/grid.rs
new file mode 100755
index 0000000..b6adb2f
--- /dev/null
+++ b/src/grid.rs
@@ -0,0 +1,46 @@
+//! Gridline
+
+use crate::{Axis, Default, Display, Grid, Script};
+
+/// Gridline properties
+#[derive(Clone, Copy)]
+pub struct Properties {
+    hidden: bool,
+}
+
+impl Default for Properties {
+    fn default() -> Properties {
+        Properties { hidden: true }
+    }
+}
+
+// TODO Lots of configuration pending: linetype, linewidth, etc
+impl Properties {
+    /// Hides the gridlines
+    ///
+    /// **Note** Both `Major` and `Minor` gridlines are hidden by default
+    pub fn hide(&mut self) -> &mut Properties {
+        self.hidden = true;
+        self
+    }
+
+    /// Shows the gridlines
+    pub fn show(&mut self) -> &mut Properties {
+        self.hidden = false;
+        self
+    }
+}
+
+impl<'a> Script for (Axis, Grid, &'a Properties) {
+    fn script(&self) -> String {
+        let &(axis, grid, properties) = self;
+        let axis = axis.display();
+        let grid = grid.display();
+
+        if properties.hidden {
+            String::new()
+        } else {
+            format!("set grid {}{}tics\n", grid, axis)
+        }
+    }
+}
diff --git a/src/key.rs b/src/key.rs
new file mode 100755
index 0000000..e8847d6
--- /dev/null
+++ b/src/key.rs
@@ -0,0 +1,218 @@
+//! Key (or legend)
+
+use std::borrow::Cow;
+
+use crate::traits::Set;
+use crate::{Default, Display, Script, Title};
+
+/// Properties of the key
+#[derive(Clone)]
+pub struct Properties {
+    boxed: bool,
+    hidden: bool,
+    justification: Option<Justification>,
+    order: Option<Order>,
+    position: Option<Position>,
+    stacked: Option<Stacked>,
+    title: Option<Cow<'static, str>>,
+}
+
+impl Default for Properties {
+    fn default() -> Properties {
+        Properties {
+            boxed: false,
+            hidden: false,
+            justification: None,
+            order: None,
+            position: None,
+            stacked: None,
+            title: None,
+        }
+    }
+}
+
+impl Properties {
+    /// Hides the key
+    pub fn hide(&mut self) -> &mut Properties {
+        self.hidden = true;
+        self
+    }
+
+    /// Shows the key
+    ///
+    /// **Note** The key is shown by default
+    pub fn show(&mut self) -> &mut Properties {
+        self.hidden = false;
+        self
+    }
+}
+
+impl Script for Properties {
+    fn script(&self) -> String {
+        let mut script = if self.hidden {
+            return String::from("set key off\n");
+        } else {
+            String::from("set key on ")
+        };
+
+        match self.position {
+            None => {}
+            Some(Position::Inside(v, h)) => {
+                script.push_str(&format!("inside {} {} ", v.display(), h.display()))
+            }
+            Some(Position::Outside(v, h)) => {
+                script.push_str(&format!("outside {} {} ", v.display(), h.display()))
+            }
+        }
+
+        if let Some(stacked) = self.stacked {
+            script.push_str(stacked.display());
+            script.push(' ');
+        }
+
+        if let Some(justification) = self.justification {
+            script.push_str(justification.display());
+            script.push(' ');
+        }
+
+        if let Some(order) = self.order {
+            script.push_str(order.display());
+            script.push(' ');
+        }
+
+        if let Some(ref title) = self.title {
+            script.push_str(&format!("title '{}' ", title))
+        }
+
+        if self.boxed {
+            script.push_str("box ")
+        }
+
+        script.push('\n');
+        script
+    }
+}
+
+impl Set<Boxed> for Properties {
+    /// Select if the key will be surrounded with a box or not
+    ///
+    /// **Note** The key is not boxed by default
+    fn set(&mut self, boxed: Boxed) -> &mut Properties {
+        match boxed {
+            Boxed::No => self.boxed = false,
+            Boxed::Yes => self.boxed = true,
+        }
+
+        self
+    }
+}
+
+impl Set<Justification> for Properties {
+    /// Changes the justification of the text of each entry
+    ///
+    /// **Note** The text is `RightJustified` by default
+    fn set(&mut self, justification: Justification) -> &mut Properties {
+        self.justification = Some(justification);
+        self
+    }
+}
+
+impl Set<Order> for Properties {
+    /// How to order each entry
+    ///
+    /// **Note** The default order is `TextSample`
+    fn set(&mut self, order: Order) -> &mut Properties {
+        self.order = Some(order);
+        self
+    }
+}
+
+impl Set<Position> for Properties {
+    /// Selects where to place the key
+    ///
+    /// **Note** By default, the key is placed `Inside(Vertical::Top, Horizontal::Right)`
+    fn set(&mut self, position: Position) -> &mut Properties {
+        self.position = Some(position);
+        self
+    }
+}
+
+impl Set<Stacked> for Properties {
+    /// Changes how the entries of the key are stacked
+    fn set(&mut self, stacked: Stacked) -> &mut Properties {
+        self.stacked = Some(stacked);
+        self
+    }
+}
+
+impl Set<Title> for Properties {
+    fn set(&mut self, title: Title) -> &mut Properties {
+        self.title = Some(title.0);
+        self
+    }
+}
+
+/// Whether the key is surrounded by a box or not
+#[allow(missing_docs)]
+#[derive(Clone, Copy)]
+pub enum Boxed {
+    No,
+    Yes,
+}
+
+/// Horizontal position of the key
+#[derive(Clone, Copy)]
+pub enum Horizontal {
+    /// Center of the figure
+    Center,
+    /// Left border of the figure
+    Left,
+    /// Right border of the figure
+    Right,
+}
+
+/// Text justification of the key
+#[allow(missing_docs)]
+#[derive(Clone, Copy)]
+pub enum Justification {
+    Left,
+    Right,
+}
+
+/// Order of the elements of the key
+#[derive(Clone, Copy)]
+pub enum Order {
+    /// Sample first, then text
+    SampleText,
+    /// Text first, then sample
+    TextSample,
+}
+
+/// Position of the key
+// TODO XY position
+#[derive(Clone, Copy)]
+pub enum Position {
+    /// Inside the area surrounded by the four (BottomX, TopX, LeftY and RightY) axes
+    Inside(Vertical, Horizontal),
+    /// Outside of that area
+    Outside(Vertical, Horizontal),
+}
+
+/// How the entries of the key are stacked
+#[allow(missing_docs)]
+#[derive(Clone, Copy)]
+pub enum Stacked {
+    Horizontally,
+    Vertically,
+}
+
+/// Vertical position of the key
+#[derive(Clone, Copy)]
+pub enum Vertical {
+    /// Bottom border of the figure
+    Bottom,
+    /// Center of the figure
+    Center,
+    /// Top border of the figure
+    Top,
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100755
index 0000000..151f019
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,1090 @@
+//! [Criterion]'s plotting library.
+//!
+//! [Criterion]: https://github.com/bheisler/criterion.rs
+//!
+//! **WARNING** This library is criterion's implementation detail and there no plans to stabilize
+//! it. In other words, the API may break at any time without notice.
+//!
+//! # Examples
+//!
+//! - Simple "curves" (based on [`simple.dem`](http://gnuplot.sourceforge.net/demo/simple.html))
+//!
+//! ![Plot](curve.svg)
+//!
+//! ```
+//! # use std::fs;
+//! # use std::path::Path;
+//! use itertools_num::linspace;
+//! use criterion_plot::prelude::*;
+//!
+//! # if let Err(_) = criterion_plot::version() {
+//! #     return;
+//! # }
+//! let ref xs = linspace::<f64>(-10., 10., 51).collect::<Vec<_>>();
+//!
+//! # fs::create_dir_all(Path::new("target/doc/criterion_plot")).unwrap();
+//! # assert_eq!(Some(String::new()),
+//! Figure::new()
+//! #   .set(Font("Helvetica"))
+//! #   .set(FontSize(12.))
+//! #   .set(Output(Path::new("target/doc/criterion_plot/curve.svg")))
+//! #   .set(Size(1280, 720))
+//!     .configure(Key, |k| {
+//!         k.set(Boxed::Yes)
+//!          .set(Position::Inside(Vertical::Top, Horizontal::Left))
+//!     })
+//!     .plot(LinesPoints {
+//!               x: xs,
+//!               y: xs.iter().map(|x| x.sin()),
+//!           },
+//!           |lp| {
+//!               lp.set(Color::DarkViolet)
+//!                 .set(Label("sin(x)"))
+//!                 .set(LineType::Dash)
+//!                 .set(PointSize(1.5))
+//!                 .set(PointType::Circle)
+//!           })
+//!     .plot(Steps {
+//!               x: xs,
+//!               y: xs.iter().map(|x| x.atan()),
+//!           },
+//!           |s| {
+//!               s.set(Color::Rgb(0, 158, 115))
+//!                .set(Label("atan(x)"))
+//!                .set(LineWidth(2.))
+//!           })
+//!     .plot(Impulses {
+//!               x: xs,
+//!               y: xs.iter().map(|x| x.atan().cos()),
+//!           },
+//!           |i| {
+//!               i.set(Color::Rgb(86, 180, 233))
+//!                .set(Label("cos(atan(x))"))
+//!           })
+//!     .draw()  // (rest of the chain has been omitted)
+//! #   .ok()
+//! #   .and_then(|gnuplot| {
+//! #       gnuplot.wait_with_output().ok().and_then(|p| String::from_utf8(p.stderr).ok())
+//! #   }));
+//! ```
+//!
+//! - error bars (based on
+//! [Julia plotting tutorial](https://plot.ly/julia/error-bars/#Colored-and-Styled-Error-Bars))
+//!
+//! ![Plot](error_bar.svg)
+//!
+//! ```
+//! # use std::fs;
+//! # use std::path::Path;
+//! use std::f64::consts::PI;
+//!
+//! use itertools_num::linspace;
+//! use rand::{Rng, XorShiftRng};
+//! use criterion_plot::prelude::*;
+//!
+//! fn sinc(mut x: f64) -> f64 {
+//!     if x == 0. {
+//!         1.
+//!     } else {
+//!         x *= PI;
+//!         x.sin() / x
+//!     }
+//! }
+//!
+//! # if let Err(_) = criterion_plot::version() {
+//! #     return;
+//! # }
+//! let ref xs_ = linspace::<f64>(-4., 4., 101).collect::<Vec<_>>();
+//!
+//! // Fake some data
+//! let ref mut rng: XorShiftRng = rand::thread_rng().gen();
+//! let xs = linspace::<f64>(-4., 4., 13).skip(1).take(11);
+//! let ys = xs.map(|x| sinc(x) + 0.05 * rng.gen::<f64>() - 0.025).collect::<Vec<_>>();
+//! let y_low = ys.iter().map(|&y| y - 0.025 - 0.075 * rng.gen::<f64>()).collect::<Vec<_>>();
+//! let y_high = ys.iter().map(|&y| y + 0.025 + 0.075 * rng.gen::<f64>()).collect::<Vec<_>>();
+//! let xs = linspace::<f64>(-4., 4., 13).skip(1).take(11);
+//! let xs = xs.map(|x| x + 0.2 * rng.gen::<f64>() - 0.1);
+//!
+//! # fs::create_dir_all(Path::new("target/doc/criterion_plot")).unwrap();
+//! # assert_eq!(Some(String::new()),
+//! Figure::new()
+//! #   .set(Font("Helvetica"))
+//! #   .set(FontSize(12.))
+//! #   .set(Output(Path::new("target/doc/criterion_plot/error_bar.svg")))
+//! #   .set(Size(1280, 720))
+//!     .configure(Axis::BottomX, |a| {
+//!         a.set(TicLabels {
+//!             labels: &["-π", "0", "π"],
+//!             positions: &[-PI, 0., PI],
+//!         })
+//!     })
+//!     .configure(Key,
+//!                |k| k.set(Position::Outside(Vertical::Top, Horizontal::Right)))
+//!     .plot(Lines {
+//!               x: xs_,
+//!               y: xs_.iter().cloned().map(sinc),
+//!           },
+//!           |l| {
+//!               l.set(Color::Rgb(0, 158, 115))
+//!                .set(Label("sinc(x)"))
+//!                .set(LineWidth(2.))
+//!           })
+//!     .plot(YErrorBars {
+//!               x: xs,
+//!               y: &ys,
+//!               y_low: &y_low,
+//!               y_high: &y_high,
+//!           },
+//!           |eb| {
+//!               eb.set(Color::DarkViolet)
+//!                 .set(LineWidth(2.))
+//!                 .set(PointType::FilledCircle)
+//!                 .set(Label("measured"))
+//!           })
+//!     .draw()  // (rest of the chain has been omitted)
+//! #   .ok()
+//! #   .and_then(|gnuplot| {
+//! #       gnuplot.wait_with_output().ok().and_then(|p| String::from_utf8(p.stderr).ok())
+//! #   }));
+//! ```
+//!
+//! - Candlesticks (based on
+//! [`candlesticks.dem`](http://gnuplot.sourceforge.net/demo/candlesticks.html))
+//!
+//! ![Plot](candlesticks.svg)
+//!
+//! ```
+//! # use std::fs;
+//! # use std::path::Path;
+//! use criterion_plot::prelude::*;
+//! use rand::Rng;
+//!
+//! # if let Err(_) = criterion_plot::version() {
+//! #     return;
+//! # }
+//! let xs = 1..11;
+//!
+//! // Fake some data
+//! let mut rng = rand::thread_rng();
+//! let bh = xs.clone().map(|_| 5f64 + 2.5 * rng.gen::<f64>()).collect::<Vec<_>>();
+//! let bm = xs.clone().map(|_| 2.5f64 + 2.5 * rng.gen::<f64>()).collect::<Vec<_>>();
+//! let wh = bh.iter().map(|&y| y + (10. - y) * rng.gen::<f64>()).collect::<Vec<_>>();
+//! let wm = bm.iter().map(|&y| y * rng.gen::<f64>()).collect::<Vec<_>>();
+//! let m = bm.iter().zip(bh.iter()).map(|(&l, &h)| (h - l) * rng.gen::<f64>() + l)
+//!     .collect::<Vec<_>>();
+//!
+//! # fs::create_dir_all(Path::new("target/doc/criterion_plot")).unwrap();
+//! # assert_eq!(Some(String::new()),
+//! Figure::new()
+//! #   .set(Font("Helvetica"))
+//! #   .set(FontSize(12.))
+//! #   .set(Output(Path::new("target/doc/criterion_plot/candlesticks.svg")))
+//! #   .set(Size(1280, 720))
+//!     .set(BoxWidth(0.2))
+//!     .configure(Axis::BottomX, |a| a.set(Range::Limits(0., 11.)))
+//!     .plot(Candlesticks {
+//!               x: xs.clone(),
+//!               whisker_min: &wm,
+//!               box_min: &bm,
+//!               box_high: &bh,
+//!               whisker_high: &wh,
+//!           },
+//!           |cs| {
+//!               cs.set(Color::Rgb(86, 180, 233))
+//!                 .set(Label("Quartiles"))
+//!                 .set(LineWidth(2.))
+//!           })
+//!     // trick to plot the median
+//!     .plot(Candlesticks {
+//!               x: xs,
+//!               whisker_min: &m,
+//!               box_min: &m,
+//!               box_high: &m,
+//!               whisker_high: &m,
+//!           },
+//!           |cs| {
+//!               cs.set(Color::Black)
+//!                 .set(LineWidth(2.))
+//!           })
+//!     .draw()  // (rest of the chain has been omitted)
+//! #   .ok()
+//! #   .and_then(|gnuplot| {
+//! #       gnuplot.wait_with_output().ok().and_then(|p| String::from_utf8(p.stderr).ok())
+//! #   }));
+//! ```
+//!
+//! - Multiaxis (based on [`multiaxis.dem`](http://gnuplot.sourceforge.net/demo/multiaxis.html))
+//!
+//! ![Plot](multiaxis.svg)
+//!
+//! ```
+//! # use std::fs;
+//! # use std::path::Path;
+//! use std::f64::consts::PI;
+//!
+//! use itertools_num::linspace;
+//! use num_complex::Complex;
+//! use criterion_plot::prelude::*;
+//!
+//! fn tf(x: f64) -> Complex<f64> {
+//!     Complex::new(0., x) / Complex::new(10., x) / Complex::new(1., x / 10_000.)
+//! }
+//!
+//! # if let Err(_) = criterion_plot::version() {
+//! #     return;
+//! # }
+//! let (start, end): (f64, f64) = (1.1, 90_000.);
+//! let ref xs = linspace(start.ln(), end.ln(), 101).map(|x| x.exp()).collect::<Vec<_>>();
+//! let phase = xs.iter().map(|&x| tf(x).arg() * 180. / PI);
+//! let magnitude = xs.iter().map(|&x| tf(x).norm());
+//!
+//! # fs::create_dir_all(Path::new("target/doc/criterion_plot")).unwrap();
+//! # assert_eq!(Some(String::new()),
+//! Figure::new().
+//! #   set(Font("Helvetica")).
+//! #   set(FontSize(12.)).
+//! #   set(Output(Path::new("target/doc/criterion_plot/multiaxis.svg"))).
+//! #   set(Size(1280, 720)).
+//!     set(Title("Frequency response")).
+//!     configure(Axis::BottomX, |a| a.
+//!         configure(Grid::Major, |g| g.
+//!             show()).
+//!         set(Label("Angular frequency (rad/s)")).
+//!         set(Range::Limits(start, end)).
+//!         set(Scale::Logarithmic)).
+//!     configure(Axis::LeftY, |a| a.
+//!         set(Label("Gain")).
+//!         set(Scale::Logarithmic)).
+//!     configure(Axis::RightY, |a| a.
+//!         configure(Grid::Major, |g| g.
+//!             show()).
+//!         set(Label("Phase shift (°)"))).
+//!     configure(Key, |k| k.
+//!         set(Position::Inside(Vertical::Top, Horizontal::Center)).
+//!         set(Title(" "))).
+//!     plot(Lines {
+//!         x: xs,
+//!         y: magnitude,
+//!     }, |l| l.
+//!         set(Color::DarkViolet).
+//!         set(Label("Magnitude")).
+//!         set(LineWidth(2.))).
+//!     plot(Lines {
+//!         x: xs,
+//!         y: phase,
+//!     }, |l| l.
+//!         set(Axes::BottomXRightY).
+//!         set(Color::Rgb(0, 158, 115)).
+//!         set(Label("Phase")).
+//!         set(LineWidth(2.))).
+//!     draw().  // (rest of the chain has been omitted)
+//! #   ok().and_then(|gnuplot| {
+//! #       gnuplot.wait_with_output().ok().and_then(|p| {
+//! #           String::from_utf8(p.stderr).ok()
+//! #       })
+//! #   }));
+//! ```
+//! - Filled curves (based on
+//! [`transparent.dem`](http://gnuplot.sourceforge.net/demo/transparent.html))
+//!
+//! ![Plot](filled_curve.svg)
+//!
+//! ```
+//! # use std::fs;
+//! # use std::path::Path;
+//! use std::f64::consts::PI;
+//! use std::iter;
+//!
+//! use itertools_num::linspace;
+//! use criterion_plot::prelude::*;
+//!
+//! # if let Err(_) = criterion_plot::version() {
+//! #     return;
+//! # }
+//! let (start, end) = (-5., 5.);
+//! let ref xs = linspace(start, end, 101).collect::<Vec<_>>();
+//! let zeros = iter::repeat(0);
+//!
+//! fn gaussian(x: f64, mu: f64, sigma: f64) -> f64 {
+//!     1. / (((x - mu).powi(2) / 2. / sigma.powi(2)).exp() * sigma * (2. * PI).sqrt())
+//! }
+//!
+//! # fs::create_dir_all(Path::new("target/doc/criterion_plot")).unwrap();
+//! # assert_eq!(Some(String::new()),
+//! Figure::new()
+//! #   .set(Font("Helvetica"))
+//! #   .set(FontSize(12.))
+//! #   .set(Output(Path::new("target/doc/criterion_plot/filled_curve.svg")))
+//! #   .set(Size(1280, 720))
+//!     .set(Title("Transparent filled curve"))
+//!     .configure(Axis::BottomX, |a| a.set(Range::Limits(start, end)))
+//!     .configure(Axis::LeftY, |a| a.set(Range::Limits(0., 1.)))
+//!     .configure(Key, |k| {
+//!         k.set(Justification::Left)
+//!          .set(Order::SampleText)
+//!          .set(Position::Inside(Vertical::Top, Horizontal::Left))
+//!          .set(Title("Gaussian Distribution"))
+//!     })
+//!     .plot(FilledCurve {
+//!               x: xs,
+//!               y1: xs.iter().map(|&x| gaussian(x, 0.5, 0.5)),
+//!               y2: zeros.clone(),
+//!           },
+//!           |fc| {
+//!               fc.set(Color::ForestGreen)
+//!                 .set(Label("μ = 0.5 σ = 0.5"))
+//!           })
+//!     .plot(FilledCurve {
+//!               x: xs,
+//!               y1: xs.iter().map(|&x| gaussian(x, 2.0, 1.0)),
+//!               y2: zeros.clone(),
+//!           },
+//!           |fc| {
+//!               fc.set(Color::Gold)
+//!                 .set(Label("μ = 2.0 σ = 1.0"))
+//!                 .set(Opacity(0.5))
+//!           })
+//!     .plot(FilledCurve {
+//!               x: xs,
+//!               y1: xs.iter().map(|&x| gaussian(x, -1.0, 2.0)),
+//!               y2: zeros,
+//!           },
+//!           |fc| {
+//!               fc.set(Color::Red)
+//!                 .set(Label("μ = -1.0 σ = 2.0"))
+//!                 .set(Opacity(0.5))
+//!           })
+//!     .draw()
+//!     .ok()
+//!     .and_then(|gnuplot| {
+//!         gnuplot.wait_with_output().ok().and_then(|p| String::from_utf8(p.stderr).ok())
+//!     }));
+//! ```
+
+#![deny(missing_docs)]
+#![deny(warnings)]
+#![deny(bare_trait_objects)]
+// This lint has lots of false positives ATM, see
+// https://github.com/Manishearth/rust-clippy/issues/761
+#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))]
+// False positives with images
+#![cfg_attr(feature = "cargo-clippy", allow(clippy::doc_markdown))]
+#![cfg_attr(feature = "cargo-clippy", allow(clippy::many_single_char_names))]
+
+extern crate cast;
+#[macro_use]
+extern crate itertools;
+
+use std::borrow::Cow;
+use std::fmt;
+use std::fs::File;
+use std::io;
+use std::num::ParseIntError;
+use std::path::Path;
+use std::process::{Child, Command};
+use std::str;
+
+use crate::data::Matrix;
+use crate::traits::{Configure, Set};
+
+mod data;
+mod display;
+mod map;
+
+pub mod axis;
+pub mod candlestick;
+pub mod curve;
+pub mod errorbar;
+pub mod filledcurve;
+pub mod grid;
+pub mod key;
+pub mod prelude;
+pub mod proxy;
+pub mod traits;
+
+/// Plot container
+#[derive(Clone)]
+pub struct Figure {
+    alpha: Option<f64>,
+    axes: map::axis::Map<axis::Properties>,
+    box_width: Option<f64>,
+    font: Option<Cow<'static, str>>,
+    font_size: Option<f64>,
+    key: Option<key::Properties>,
+    output: Cow<'static, Path>,
+    plots: Vec<Plot>,
+    size: Option<(usize, usize)>,
+    terminal: Terminal,
+    tics: map::axis::Map<String>,
+    title: Option<Cow<'static, str>>,
+}
+
+impl Figure {
+    /// Creates an empty figure
+    pub fn new() -> Figure {
+        Figure {
+            alpha: None,
+            axes: map::axis::Map::new(),
+            box_width: None,
+            font: None,
+            font_size: None,
+            key: None,
+            output: Cow::Borrowed(Path::new("output.plot")),
+            plots: Vec::new(),
+            size: None,
+            terminal: Terminal::Svg,
+            tics: map::axis::Map::new(),
+            title: None,
+        }
+    }
+
+    fn script(&self) -> Vec<u8> {
+        let mut s = String::new();
+
+        s.push_str(&format!(
+            "set output '{}'\n",
+            self.output.display().to_string().replace("'", "''")
+        ));
+
+        if let Some(width) = self.box_width {
+            s.push_str(&format!("set boxwidth {}\n", width))
+        }
+
+        if let Some(ref title) = self.title {
+            s.push_str(&format!("set title '{}'\n", title))
+        }
+
+        for axis in self.axes.iter() {
+            s.push_str(&axis.script());
+        }
+
+        for (_, script) in self.tics.iter() {
+            s.push_str(script);
+        }
+
+        if let Some(ref key) = self.key {
+            s.push_str(&key.script())
+        }
+
+        if let Some(alpha) = self.alpha {
+            s.push_str(&format!("set style fill transparent solid {}\n", alpha))
+        }
+
+        s.push_str(&format!("set terminal {} dashed", self.terminal.display()));
+
+        if let Some((width, height)) = self.size {
+            s.push_str(&format!(" size {}, {}", width, height))
+        }
+
+        if let Some(ref name) = self.font {
+            if let Some(size) = self.font_size {
+                s.push_str(&format!(" font '{},{}'", name, size))
+            } else {
+                s.push_str(&format!(" font '{}'", name))
+            }
+        }
+
+        // TODO This removes the crossbars from the ends of error bars, but should be configurable
+        s.push_str("\nunset bars\n");
+
+        let mut is_first_plot = true;
+        for plot in &self.plots {
+            let data = plot.data();
+
+            if data.bytes().is_empty() {
+                continue;
+            }
+
+            if is_first_plot {
+                s.push_str("plot ");
+                is_first_plot = false;
+            } else {
+                s.push_str(", ");
+            }
+
+            s.push_str(&format!(
+                "'-' binary endian=little record={} format='%float64' using ",
+                data.nrows()
+            ));
+
+            let mut is_first_col = true;
+            for col in 0..data.ncols() {
+                if is_first_col {
+                    is_first_col = false;
+                } else {
+                    s.push(':');
+                }
+                s.push_str(&(col + 1).to_string());
+            }
+            s.push(' ');
+
+            s.push_str(plot.script());
+        }
+
+        let mut buffer = s.into_bytes();
+        let mut is_first = true;
+        for plot in &self.plots {
+            if is_first {
+                is_first = false;
+                buffer.push(b'\n');
+            }
+            buffer.extend_from_slice(plot.data().bytes());
+        }
+
+        buffer
+    }
+
+    /// Spawns a drawing child process
+    ///
+    /// NOTE: stderr, stdin, and stdout are piped
+    pub fn draw(&mut self) -> io::Result<Child> {
+        use std::process::Stdio;
+
+        let mut gnuplot = Command::new("gnuplot")
+            .stderr(Stdio::piped())
+            .stdin(Stdio::piped())
+            .stdout(Stdio::piped())
+            .spawn()?;
+        self.dump(gnuplot.stdin.as_mut().unwrap())?;
+        Ok(gnuplot)
+    }
+
+    /// Dumps the script required to produce the figure into `sink`
+    pub fn dump<W>(&mut self, sink: &mut W) -> io::Result<&mut Figure>
+    where
+        W: io::Write,
+    {
+        sink.write_all(&self.script())?;
+        Ok(self)
+    }
+
+    /// Saves the script required to produce the figure to `path`
+    pub fn save(&self, path: &Path) -> io::Result<&Figure> {
+        use std::io::Write;
+
+        File::create(path)?.write_all(&self.script())?;
+        Ok(self)
+    }
+}
+
+impl Configure<Axis> for Figure {
+    type Properties = axis::Properties;
+
+    /// Configures an axis
+    fn configure<F>(&mut self, axis: Axis, configure: F) -> &mut Figure
+    where
+        F: FnOnce(&mut axis::Properties) -> &mut axis::Properties,
+    {
+        if self.axes.contains_key(axis) {
+            configure(self.axes.get_mut(axis).unwrap());
+        } else {
+            let mut properties = Default::default();
+            configure(&mut properties);
+            self.axes.insert(axis, properties);
+        }
+        self
+    }
+}
+
+impl Configure<Key> for Figure {
+    type Properties = key::Properties;
+
+    /// Configures the key (legend)
+    fn configure<F>(&mut self, _: Key, configure: F) -> &mut Figure
+    where
+        F: FnOnce(&mut key::Properties) -> &mut key::Properties,
+    {
+        if self.key.is_some() {
+            configure(self.key.as_mut().unwrap());
+        } else {
+            let mut key = Default::default();
+            configure(&mut key);
+            self.key = Some(key);
+        }
+        self
+    }
+}
+
+impl Set<BoxWidth> for Figure {
+    /// Changes the box width of all the box related plots (bars, candlesticks, etc)
+    ///
+    /// **Note** The default value is 0
+    ///
+    /// # Panics
+    ///
+    /// Panics if `width` is a negative value
+    fn set(&mut self, width: BoxWidth) -> &mut Figure {
+        let width = width.0;
+
+        assert!(width >= 0.);
+
+        self.box_width = Some(width);
+        self
+    }
+}
+
+impl Set<Font> for Figure {
+    /// Changes the font
+    fn set(&mut self, font: Font) -> &mut Figure {
+        self.font = Some(font.0);
+        self
+    }
+}
+
+impl Set<FontSize> for Figure {
+    /// Changes the size of the font
+    ///
+    /// # Panics
+    ///
+    /// Panics if `size` is a non-positive value
+    fn set(&mut self, size: FontSize) -> &mut Figure {
+        let size = size.0;
+
+        assert!(size >= 0.);
+
+        self.font_size = Some(size);
+        self
+    }
+}
+
+impl Set<Output> for Figure {
+    /// Changes the output file
+    ///
+    /// **Note** The default output file is `output.plot`
+    fn set(&mut self, output: Output) -> &mut Figure {
+        self.output = output.0;
+        self
+    }
+}
+
+impl Set<Size> for Figure {
+    /// Changes the figure size
+    fn set(&mut self, size: Size) -> &mut Figure {
+        self.size = Some((size.0, size.1));
+        self
+    }
+}
+
+impl Set<Terminal> for Figure {
+    /// Changes the output terminal
+    ///
+    /// **Note** By default, the terminal is set to `Svg`
+    fn set(&mut self, terminal: Terminal) -> &mut Figure {
+        self.terminal = terminal;
+        self
+    }
+}
+
+impl Set<Title> for Figure {
+    /// Sets the title
+    fn set(&mut self, title: Title) -> &mut Figure {
+        self.title = Some(title.0);
+        self
+    }
+}
+
+impl Default for Figure {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+/// Box width for box-related plots: bars, candlesticks, etc
+#[derive(Clone, Copy)]
+pub struct BoxWidth(pub f64);
+
+/// A font name
+pub struct Font(Cow<'static, str>);
+
+/// The size of a font
+#[derive(Clone, Copy)]
+pub struct FontSize(pub f64);
+
+/// The key or legend
+#[derive(Clone, Copy)]
+pub struct Key;
+
+/// Plot label
+pub struct Label(Cow<'static, str>);
+
+/// Width of the lines
+#[derive(Clone, Copy)]
+pub struct LineWidth(pub f64);
+
+/// Fill color opacity
+#[derive(Clone, Copy)]
+pub struct Opacity(pub f64);
+
+/// Output file path
+pub struct Output(Cow<'static, Path>);
+
+/// Size of the points
+#[derive(Clone, Copy)]
+pub struct PointSize(pub f64);
+
+/// Axis range
+#[derive(Clone, Copy)]
+pub enum Range {
+    /// Autoscale the axis
+    Auto,
+    /// Set the limits of the axis
+    Limits(f64, f64),
+}
+
+/// Figure size
+#[derive(Clone, Copy)]
+pub struct Size(pub usize, pub usize);
+
+/// Labels attached to the tics of an axis
+pub struct TicLabels<P, L> {
+    /// Labels to attach to the tics
+    pub labels: L,
+    /// Position of the tics on the axis
+    pub positions: P,
+}
+
+/// Figure title
+pub struct Title(Cow<'static, str>);
+
+/// A pair of axes that define a coordinate system
+#[allow(missing_docs)]
+#[derive(Clone, Copy)]
+pub enum Axes {
+    BottomXLeftY,
+    BottomXRightY,
+    TopXLeftY,
+    TopXRightY,
+}
+
+/// A coordinate axis
+#[derive(Clone, Copy)]
+pub enum Axis {
+    /// X axis on the bottom side of the figure
+    BottomX,
+    /// Y axis on the left side of the figure
+    LeftY,
+    /// Y axis on the right side of the figure
+    RightY,
+    /// X axis on the top side of the figure
+    TopX,
+}
+
+impl Axis {
+    fn next(self) -> Option<Axis> {
+        use crate::Axis::*;
+
+        match self {
+            BottomX => Some(LeftY),
+            LeftY => Some(RightY),
+            RightY => Some(TopX),
+            TopX => None,
+        }
+    }
+}
+
+/// Color
+#[allow(missing_docs)]
+#[derive(Clone, Copy)]
+pub enum Color {
+    Black,
+    Blue,
+    Cyan,
+    DarkViolet,
+    ForestGreen,
+    Gold,
+    Gray,
+    Green,
+    Magenta,
+    Red,
+    /// Custom RGB color
+    Rgb(u8, u8, u8),
+    White,
+    Yellow,
+}
+
+/// Grid line
+#[derive(Clone, Copy)]
+pub enum Grid {
+    /// Major gridlines
+    Major,
+    /// Minor gridlines
+    Minor,
+}
+
+impl Grid {
+    fn next(self) -> Option<Grid> {
+        use crate::Grid::*;
+
+        match self {
+            Major => Some(Minor),
+            Minor => None,
+        }
+    }
+}
+
+/// Line type
+#[allow(missing_docs)]
+#[derive(Clone, Copy)]
+pub enum LineType {
+    Dash,
+    Dot,
+    DotDash,
+    DotDotDash,
+    /// Line made of minimally sized dots
+    SmallDot,
+    Solid,
+}
+
+/// Point type
+#[allow(missing_docs)]
+#[derive(Clone, Copy)]
+pub enum PointType {
+    Circle,
+    FilledCircle,
+    FilledSquare,
+    FilledTriangle,
+    Plus,
+    Square,
+    Star,
+    Triangle,
+    X,
+}
+
+/// Axis scale
+#[allow(missing_docs)]
+#[derive(Clone, Copy)]
+pub enum Scale {
+    Linear,
+    Logarithmic,
+}
+
+/// Axis scale factor
+#[allow(missing_docs)]
+#[derive(Clone, Copy)]
+pub struct ScaleFactor(pub f64);
+
+/// Output terminal
+#[allow(missing_docs)]
+#[derive(Clone, Copy)]
+pub enum Terminal {
+    Svg,
+}
+
+/// Not public version of `std::default::Default`, used to not leak default constructors into the
+/// public API
+trait Default {
+    /// Creates `Properties` with default configuration
+    fn default() -> Self;
+}
+
+/// Enums that can produce gnuplot code
+trait Display<S> {
+    /// Translates the enum in gnuplot code
+    fn display(&self) -> S;
+}
+
+/// Curve variant of Default
+trait CurveDefault<S> {
+    /// Creates `curve::Properties` with default configuration
+    fn default(s: S) -> Self;
+}
+
+/// Error bar variant of Default
+trait ErrorBarDefault<S> {
+    /// Creates `errorbar::Properties` with default configuration
+    fn default(s: S) -> Self;
+}
+
+/// Structs that can produce gnuplot code
+trait Script {
+    /// Translates some configuration struct into gnuplot code
+    fn script(&self) -> String;
+}
+
+#[derive(Clone)]
+struct Plot {
+    data: Matrix,
+    script: String,
+}
+
+impl Plot {
+    fn new<S>(data: Matrix, script: &S) -> Plot
+    where
+        S: Script,
+    {
+        Plot {
+            data,
+            script: script.script(),
+        }
+    }
+
+    fn data(&self) -> &Matrix {
+        &self.data
+    }
+
+    fn script(&self) -> &str {
+        &self.script
+    }
+}
+
+/// Possible errors when parsing gnuplot's version string
+#[derive(Debug)]
+pub enum VersionError {
+    /// The `gnuplot` command couldn't be executed
+    Exec(io::Error),
+    /// The `gnuplot` command returned an error message
+    Error(String),
+    /// The `gnuplot` command returned invalid utf-8
+    OutputError,
+    /// The `gnuplot` command returned an unparseable string
+    ParseError(String),
+}
+impl fmt::Display for VersionError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            VersionError::Exec(err) => write!(f, "`gnuplot --version` failed: {}", err),
+            VersionError::Error(msg) => {
+                write!(f, "`gnuplot --version` failed with error message:\n{}", msg)
+            }
+            VersionError::OutputError => write!(f, "`gnuplot --version` returned invalid utf-8"),
+            VersionError::ParseError(msg) => write!(
+                f,
+                "`gnuplot --version` returned an unparseable version string: {}",
+                msg
+            ),
+        }
+    }
+}
+impl ::std::error::Error for VersionError {
+    fn description(&self) -> &str {
+        match self {
+            VersionError::Exec(_) => "Execution Error",
+            VersionError::Error(_) => "Other Error",
+            VersionError::OutputError => "Output Error",
+            VersionError::ParseError(_) => "Parse Error",
+        }
+    }
+
+    fn cause(&self) -> Option<&dyn ::std::error::Error> {
+        match self {
+            VersionError::Exec(err) => Some(err),
+            _ => None,
+        }
+    }
+}
+
+/// Structure representing a gnuplot version number.
+pub struct Version {
+    /// The major version number
+    pub major: usize,
+    /// The minor version number
+    pub minor: usize,
+    /// The patch level
+    pub patch: String,
+}
+
+/// Returns `gnuplot` version
+pub fn version() -> Result<Version, VersionError> {
+    let command_output = Command::new("gnuplot")
+        .arg("--version")
+        .output()
+        .map_err(VersionError::Exec)?;
+    if !command_output.status.success() {
+        let error =
+            String::from_utf8(command_output.stderr).map_err(|_| VersionError::OutputError)?;
+        return Err(VersionError::Error(error));
+    }
+
+    let output = String::from_utf8(command_output.stdout).map_err(|_| VersionError::OutputError)?;
+
+    parse_version(&output).map_err(|_| VersionError::ParseError(output.clone()))
+}
+
+fn parse_version(version_str: &str) -> Result<Version, Option<ParseIntError>> {
+    let mut words = version_str.split_whitespace().skip(1);
+    let mut version = words.next().ok_or(None)?.split('.');
+    let major = version.next().ok_or(None)?.parse()?;
+    let minor = version.next().ok_or(None)?.parse()?;
+    let patchlevel = words.nth(1).ok_or(None)?.to_owned();
+
+    Ok(Version {
+        major,
+        minor,
+        patch: patchlevel,
+    })
+}
+
+fn scale_factor(map: &map::axis::Map<axis::Properties>, axes: Axes) -> (f64, f64) {
+    use crate::Axes::*;
+    use crate::Axis::*;
+
+    match axes {
+        BottomXLeftY => (
+            map.get(BottomX).map_or(1., ScaleFactorTrait::scale_factor),
+            map.get(LeftY).map_or(1., ScaleFactorTrait::scale_factor),
+        ),
+        BottomXRightY => (
+            map.get(BottomX).map_or(1., ScaleFactorTrait::scale_factor),
+            map.get(RightY).map_or(1., ScaleFactorTrait::scale_factor),
+        ),
+        TopXLeftY => (
+            map.get(TopX).map_or(1., ScaleFactorTrait::scale_factor),
+            map.get(LeftY).map_or(1., ScaleFactorTrait::scale_factor),
+        ),
+        TopXRightY => (
+            map.get(TopX).map_or(1., ScaleFactorTrait::scale_factor),
+            map.get(RightY).map_or(1., ScaleFactorTrait::scale_factor),
+        ),
+    }
+}
+
+// XXX :-1: to intra-crate privacy rules
+/// Private
+trait ScaleFactorTrait {
+    /// Private
+    fn scale_factor(&self) -> f64;
+}
+
+#[cfg(test)]
+mod test {
+    #[test]
+    fn version() {
+        if let Ok(version) = super::version() {
+            assert!(version.major >= 4);
+        } else {
+            println!("Gnuplot not installed.");
+        }
+    }
+
+    #[test]
+    fn test_parse_version_on_valid_string() {
+        let string = "gnuplot 5.0 patchlevel 7";
+        let version = super::parse_version(&string).unwrap();
+        assert_eq!(5, version.major);
+        assert_eq!(0, version.minor);
+        assert_eq!("7", &version.patch);
+    }
+
+    #[test]
+    fn test_parse_gentoo_version() {
+        let string = "gnuplot 5.2 patchlevel 5a (Gentoo revision r0)";
+        let version = super::parse_version(&string).unwrap();
+        assert_eq!(5, version.major);
+        assert_eq!(2, version.minor);
+        assert_eq!("5a", &version.patch);
+    }
+
+    #[test]
+    fn test_parse_version_returns_error_on_invalid_strings() {
+        let strings = [
+            "",
+            "foobar",
+            "gnuplot 50 patchlevel 7",
+            "gnuplot 5.0 patchlevel",
+            "gnuplot foo.bar patchlevel 7",
+        ];
+        for string in &strings {
+            assert!(super::parse_version(string).is_err());
+        }
+    }
+}
diff --git a/src/map.rs b/src/map.rs
new file mode 100755
index 0000000..7099a96
--- /dev/null
+++ b/src/map.rs
@@ -0,0 +1,168 @@
+//! Enum Maps
+
+pub mod axis {
+    use crate::Axis;
+
+    const LENGTH: usize = 4;
+
+    pub struct Items<'a, T>
+    where
+        T: 'a,
+    {
+        map: &'a Map<T>,
+        state: Option<Axis>,
+    }
+
+    impl<'a, T> Iterator for Items<'a, T> {
+        type Item = (Axis, &'a T);
+
+        fn next(&mut self) -> Option<(Axis, &'a T)> {
+            while let Some(key) = self.state {
+                self.state = key.next();
+
+                if let Some(value) = self.map.get(key) {
+                    return Some((key, value));
+                }
+            }
+
+            None
+        }
+    }
+
+    pub struct Map<T>([Option<T>; LENGTH]);
+
+    impl<T> Default for Map<T> {
+        fn default() -> Self {
+            Self::new()
+        }
+    }
+
+    impl<T> Map<T> {
+        pub fn new() -> Map<T> {
+            Map([None, None, None, None])
+        }
+
+        pub fn contains_key(&self, key: Axis) -> bool {
+            self.0[key as usize].is_some()
+        }
+
+        pub fn get(&self, key: Axis) -> Option<&T> {
+            self.0[key as usize].as_ref()
+        }
+
+        pub fn get_mut(&mut self, key: Axis) -> Option<&mut T> {
+            self.0[key as usize].as_mut()
+        }
+
+        pub fn insert(&mut self, key: Axis, value: T) -> Option<T> {
+            let key = key as usize;
+            let old = self.0[key].take();
+
+            self.0[key] = Some(value);
+
+            old
+        }
+
+        pub fn iter(&self) -> Items<T> {
+            Items {
+                map: self,
+                state: Some(Axis::BottomX),
+            }
+        }
+    }
+
+    impl<T> Clone for Map<T>
+    where
+        T: Clone,
+    {
+        fn clone(&self) -> Map<T> {
+            Map([
+                self.0[0].clone(),
+                self.0[1].clone(),
+                self.0[2].clone(),
+                self.0[3].clone(),
+            ])
+        }
+    }
+}
+
+pub mod grid {
+    use crate::Grid;
+
+    const LENGTH: usize = 2;
+
+    pub struct Items<'a, T>
+    where
+        T: 'a,
+    {
+        map: &'a Map<T>,
+        state: Option<Grid>,
+    }
+
+    impl<'a, T> Iterator for Items<'a, T> {
+        type Item = (Grid, &'a T);
+
+        fn next(&mut self) -> Option<(Grid, &'a T)> {
+            while let Some(key) = self.state {
+                self.state = key.next();
+
+                if let Some(value) = self.map.get(key) {
+                    return Some((key, value));
+                }
+            }
+
+            None
+        }
+    }
+
+    pub struct Map<T>([Option<T>; LENGTH]);
+
+    impl<T> Map<T> {
+        pub fn new() -> Map<T> {
+            Map([None, None])
+        }
+
+        pub fn contains_key(&self, key: Grid) -> bool {
+            self.0[key as usize].is_some()
+        }
+
+        pub fn get(&self, key: Grid) -> Option<&T> {
+            self.0[key as usize].as_ref()
+        }
+
+        pub fn get_mut(&mut self, key: Grid) -> Option<&mut T> {
+            self.0[key as usize].as_mut()
+        }
+
+        pub fn insert(&mut self, key: Grid, value: T) -> Option<T> {
+            let key = key as usize;
+            let old = self.0[key].take();
+
+            self.0[key] = Some(value);
+
+            old
+        }
+
+        pub fn iter(&self) -> Items<T> {
+            Items {
+                map: self,
+                state: Some(Grid::Major),
+            }
+        }
+    }
+
+    impl<T> Clone for Map<T>
+    where
+        T: Clone,
+    {
+        fn clone(&self) -> Map<T> {
+            Map([self.0[0].clone(), self.0[1].clone()])
+        }
+    }
+
+    impl<T> Default for Map<T> {
+        fn default() -> Self {
+            Self::new()
+        }
+    }
+}
diff --git a/src/prelude.rs b/src/prelude.rs
new file mode 100755
index 0000000..e42c45f
--- /dev/null
+++ b/src/prelude.rs
@@ -0,0 +1,13 @@
+//! A collection of the most used traits, structs and enums
+
+pub use crate::candlestick::Candlesticks;
+pub use crate::curve::Curve::{Dots, Impulses, Lines, LinesPoints, Points, Steps};
+pub use crate::errorbar::ErrorBar::{XErrorBars, XErrorLines, YErrorBars, YErrorLines};
+pub use crate::filledcurve::FilledCurve;
+pub use crate::key::{Boxed, Horizontal, Justification, Order, Position, Stacked, Vertical};
+pub use crate::proxy::{Font, Label, Output, Title};
+pub use crate::traits::{Configure, Plot, Set};
+pub use crate::{
+    Axes, Axis, BoxWidth, Color, Figure, FontSize, Grid, Key, LineType, LineWidth, Opacity,
+    PointSize, PointType, Range, Scale, ScaleFactor, Size, Terminal, TicLabels,
+};
diff --git a/src/proxy.rs b/src/proxy.rs
new file mode 100755
index 0000000..401b7f9
--- /dev/null
+++ b/src/proxy.rs
@@ -0,0 +1,47 @@
+//! Generic constructors for newtypes
+
+#![allow(non_snake_case)]
+
+use crate::{Font as FontType, Label as LabelType, Output as OutputType, Title as TitleType};
+use std::borrow::Cow;
+use std::path::Path;
+
+/// Generic constructor for `Font`
+#[cfg_attr(feature = "cargo-clippy", allow(clippy::inline_always))]
+#[inline(always)]
+pub fn Font<S>(string: S) -> FontType
+where
+    S: Into<Cow<'static, str>>,
+{
+    FontType(string.into())
+}
+
+/// Generic constructor for `Label`
+#[cfg_attr(feature = "cargo-clippy", allow(clippy::inline_always))]
+#[inline(always)]
+pub fn Label<S>(string: S) -> LabelType
+where
+    S: Into<Cow<'static, str>>,
+{
+    LabelType(string.into())
+}
+
+/// Generic constructor for `Title`
+#[cfg_attr(feature = "cargo-clippy", allow(clippy::inline_always))]
+#[inline(always)]
+pub fn Title<S>(string: S) -> TitleType
+where
+    S: Into<Cow<'static, str>>,
+{
+    TitleType(string.into())
+}
+
+/// Generic constructor for `Output`
+#[cfg_attr(feature = "cargo-clippy", allow(clippy::inline_always))]
+#[inline(always)]
+pub fn Output<P>(path: P) -> OutputType
+where
+    P: Into<Cow<'static, Path>>,
+{
+    OutputType(path.into())
+}
diff --git a/src/traits.rs b/src/traits.rs
new file mode 100755
index 0000000..52d2233
--- /dev/null
+++ b/src/traits.rs
@@ -0,0 +1,35 @@
+//! Traits
+
+/// Overloaded `configure` method
+pub trait Configure<This> {
+    /// The properties of what's being configured
+    type Properties;
+
+    /// Configure some set of properties
+    fn configure<F>(&mut self, this: This, function: F) -> &mut Self
+    where
+        F: FnOnce(&mut Self::Properties) -> &mut Self::Properties;
+}
+
+/// Types that can be plotted
+pub trait Data {
+    /// Convert the type into a double precision float
+    fn f64(self) -> f64;
+}
+
+/// Overloaded `plot` method
+pub trait Plot<This> {
+    /// The properties associated to the plot
+    type Properties;
+
+    /// Plots some `data` with some `configuration`
+    fn plot<F>(&mut self, this: This, function: F) -> &mut Self
+    where
+        F: FnOnce(&mut Self::Properties) -> &mut Self::Properties;
+}
+
+/// Overloaded `set` method
+pub trait Set<T> {
+    /// Sets some property
+    fn set(&mut self, value: T) -> &mut Self;
+}