diff --git a/v2/libtpctacmod/histplot.c b/v2/libtpctacmod/histplot.c
new file mode 100644
index 0000000000000000000000000000000000000000..c22bbe0551f1c87dd4e6ff8f060f733de2eaef9f
--- /dev/null
+++ b/v2/libtpctacmod/histplot.c
@@ -0,0 +1,218 @@
+/// @file histplot.c
+/// @brief Plot histogram in SVG format from dataset stored in TAC structure.
+/// @author Vesa Oikonen
+///
+/*****************************************************************************/
+#include "tpcclibConfig.h"
+/*****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+#include <string.h>
+/*****************************************************************************/
+#include "tpcextensions.h"
+#include "tpctac.h"
+#include "libtpcsvg.h"
+/*****************************************************************************/
+#include "tpctacmod.h"
+/*****************************************************************************/
+
+/*****************************************************************************/
+extern int SVG_INLINE;
+/*****************************************************************************/
+/** SVG plot of histogram from data given in TAC data structure.
+    @return enum tpcerror (TPCERROR_OK when successful).
+    @sa tacPlotLineSVG, tacPlotFitSVG, mtgaPlotSVG
+    @author Vesa Oikonen
+ */
+int tacPlotHistogramSVG(
+  /** Pointer to TAC structure. Only the first dataset is used. */
+  TAC *d,
+  /** String for plot main title, or "". */
+  const char *main_title,
+  /** Start x value; NaN if determined from data. */
+  const double x1,
+  /** End x value; NaN if determined from data. */
+  const double x2,
+  /** Minimum y value; NaN if determined from data. */
+  const double y1,
+  /** Maximum y value; NaN if determined from data. */
+  const double y2,
+  /** SVG file name. */ 
+  const char *fname,
+  /** Pointer to status data; obligatory. */
+  TPCSTATUS *status
+) {
+  if(status==NULL) return TPCERROR_FAIL;
+  if(status->verbose>1)
+    printf("%s(tac, '%s', %g, %g, %g, %g, '%s')\n", __func__, main_title, x1, x2, y1, y2, fname);
+  if(d==NULL || d->sampleNr<1 || d->tacNr<1) {
+    statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_NO_DATA); return(status->error);}
+  if(fname==NULL || strnlen(fname, 1)<1) {
+    statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_INVALID_FILENAME); return(status->error);}
+
+  /* Determine the plot min and max x and y values */
+  double minx, maxx, miny, maxy, tx1, tx2, ty1, ty2;
+  if(tacSampleXRange(d, &tx1, &tx2)) {
+    statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_INVALID_XRANGE); return(status->error);}
+  minx=tx1; maxx=tx2;
+  if(minx>0.0) {double f=maxx-minx; minx-=0.05*f; if(minx<0.0) minx=0.0;}
+  if(isfinite(x1)) minx=x1; 
+  if(isfinite(x2)) maxx=x2;
+  if(status->verbose>1) {printf("  minx := %g\n  maxx := %g\n", minx, maxx); fflush(stdout);}
+  /* Determine the plot min and max y values inside the x range */
+  if(tacYRangeInXRange(d, 0, minx, maxx, &ty1, &ty2, NULL, NULL, NULL, NULL)) {
+    statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_INVALID_XRANGE); return(status->error);}
+//  if(ty1>0.0) ty1=0.0;
+//  if(ty2<0.0) ty2=0.0;
+  miny=ty1; maxy=ty2;
+  if(miny>0.0) {double f=maxy-miny; miny-=0.05*f; if(miny<0.0) miny=0.0;}
+  else if(maxy<0.0) maxy=0.0;
+  if(isfinite(y1)) miny=y1; 
+  if(isfinite(y2)) maxy=y2;
+  if(status->verbose>1) {printf("  miny := %g\n  maxy := %g\n", miny, maxy); fflush(stdout);}
+
+  /* Calculate the axis ticks */
+  struct svg_viewports viewports; svg_init_viewports(&viewports);
+  viewports.x.min=minx; viewports.x.max=maxx;
+  viewports.y.min=miny; viewports.y.max=maxy;
+  viewports.label_area_viewport.is=0; // no plot legends
+  if(svg_calculate_axes(&viewports, status->verbose-1)) {
+    statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_INVALID_VALUE); return(status->error);}
+
+  /* Set the plot window and window area sizes */
+  if(svg_define_viewports(0, 0, strlen(main_title), 0, 0, 0, &viewports, status->verbose-1)) {
+    statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_INVALID_VALUE); return(status->error);}
+
+  /* Initiate graphics file */
+  FILE *fp=svg_initiate(fname, 0, 0, &viewports, NULL, status->verbose-1);
+  if(fp==NULL) {
+    statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_OPEN); return(status->error);}
+
+  /* Put the graph titles into their own viewports */
+  if(svg_create_main_title(fp, main_title, "", &viewports, NULL, status->verbose-2)) {
+    statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE);
+    fclose(fp); return(status->error);
+  }
+  /* Put the plot into its own viewport */
+  if(svg_start_plot_viewport(fp, &viewports, NULL, status->verbose-2)) {
+    statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE);
+    fclose(fp); return(status->error);
+  }
+  /*  Start coordinate area viewport */
+  if(svg_start_coordinate_viewport(fp, &viewports, NULL, status->verbose-3)) {
+    statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE);
+    fclose(fp); return(status->error);
+  }
+  /* Write plot axes */
+  if(svg_write_axes(fp, &viewports, NULL, status->verbose-3)) {
+    statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE);
+    fclose(fp); return(status->error);
+  }
+
+  /*
+   *  Draw the data
+   */
+  char ilc[9], tmp[1024];
+  if(SVG_INLINE) strcpy(ilc, "svg:"); else strcpy(ilc, "");
+  /* Initiate the data object group */
+  sprintf(tmp, "\n<%sg stroke=\"black\" stroke-width=\"25\" fill=\"black\">\n", ilc);
+  if(svg_write(fp, tmp, NULL, status->verbose-5)!=0) {
+    statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE);
+    fclose(fp); return(status->error);
+  }
+  if(d->isframe==0) { // vertical lines
+    /* Open the path */
+    sprintf(tmp, "<%spath fill=\"none\" d=\"", ilc);
+    svg_write(fp, tmp, NULL, status->verbose-5);
+    for(int i=0; i<d->sampleNr; i++) {
+      if(!isfinite(d->x[i]) || !isfinite(d->c[0].y[i])) continue;
+      double nx1, ny1, nx2, ny2;
+      nx1=nx2=viewports.x.origo+d->x[i]*viewports.x.scale;
+      ny1=viewports.coordinate_area_viewport.h-(viewports.y.origo+viewports.y.scale*0.0);
+      ny2=viewports.coordinate_area_viewport.h-(viewports.y.origo+viewports.y.scale*d->c[0].y[i]);
+      /* Do not plot if x is outside viewport */
+      if(nx1<0 && nx2<=0) continue;
+      if(nx1>viewports.coordinate_area_viewport.w+1 || nx1>viewports.coordinate_area_viewport.w+1)
+        continue;
+      /* Do not plot if both y's are outside viewport */
+      if((ny1<0 || ny2>viewports.coordinate_area_viewport.h+1) &&
+         (ny2<0 || ny1>viewports.coordinate_area_viewport.h+1)) continue;
+      /* Set y limit to the viewport border */
+      if(ny1<0) ny1=0.0; else if(ny2<0) ny2=0.0;
+      if(ny1>viewports.coordinate_area_viewport.h+1) ny1=viewports.coordinate_area_viewport.h+1;
+      else if(ny2>viewports.coordinate_area_viewport.h+1) ny2=viewports.coordinate_area_viewport.h+1;
+      /* Draw */
+      sprintf(tmp, " M%.0f %.0f L%.0f %.0f", nx1, ny1, nx2, ny2);
+      svg_write(fp, tmp, NULL, status->verbose-5);
+    }
+    /* Close the path */
+    strcpy(tmp, "\" />\n");
+    svg_write(fp, tmp, NULL, status->verbose-5);
+  } else { // rectangles
+    for(int i=0; i<d->sampleNr; i++) {
+      if(!isfinite(d->x[i]) || !isfinite(d->c[0].y[i])) continue;
+      double nx1, ny1, nx2, ny2;
+      nx1=viewports.x.origo+d->x1[i]*viewports.x.scale;
+      nx2=viewports.x.origo+d->x2[i]*viewports.x.scale;
+      ny1=viewports.coordinate_area_viewport.h-(viewports.y.origo+viewports.y.scale*0.0);
+      ny2=viewports.coordinate_area_viewport.h-(viewports.y.origo+viewports.y.scale*d->c[0].y[i]);
+      /* Do not plot if x is outside viewport */
+      if(nx1<0 && nx2<=0) continue;
+      if(nx1>viewports.coordinate_area_viewport.w+1 || nx1>viewports.coordinate_area_viewport.w+1)
+        continue;
+      /* Do not plot if both y's are outside viewport */
+      if((ny1<0 || ny2>viewports.coordinate_area_viewport.h+1) &&
+         (ny2<0 || ny1>viewports.coordinate_area_viewport.h+1)) continue;
+      /* Set y limit to the viewport border */
+      if(ny1<0) ny1=0.0; else if(ny2<0) ny2=0.0;
+      if(ny1>viewports.coordinate_area_viewport.h+1) ny1=viewports.coordinate_area_viewport.h+1;
+      else if(ny2>viewports.coordinate_area_viewport.h+1) ny2=viewports.coordinate_area_viewport.h+1;
+      if(ny1>ny2) {double f=ny1; ny1=ny2; ny2=f;}
+      /* Draw */
+      sprintf(tmp, " <rect x=\"%.0f\" y=\"%.0f\" width=\"%.0f\" height=\"%.0f\" />\n",
+              nx1, ny1, nx2-nx1, fabs(ny2-ny1));
+      svg_write(fp, tmp, NULL, status->verbose-5);
+    }
+  }
+  /* Close the data object group */
+  sprintf(tmp, "</%sg>\n", ilc);
+  if(svg_write(fp, tmp, NULL, status->verbose-5)!=0) {
+    statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE);
+    fclose(fp); return(status->error);
+  }
+
+  /* Close the coordinate viewport */
+  if(svg_end_coordinate_viewport(fp, NULL, status->verbose-1)) {
+    statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE);
+    fclose(fp); return(status->error);
+  }
+
+  /* Write the axis ticks */
+  if(svg_write_xticks(fp, &viewports, NULL, status->verbose-3)!=0 ||
+     svg_write_yticks(fp, &viewports, NULL, status->verbose-3)!=0) {
+    statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE);
+    fclose(fp); return(status->error);
+  }
+
+  /* Close the plot viewport */
+  if(svg_end_plot_viewport(fp, NULL, status->verbose-2)) {
+    statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE);
+    fclose(fp); return(status->error);
+  }
+
+  /* Close the SVG file */
+  if(svg_close(fp, NULL, status->verbose-1)) {
+    statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_CANNOT_WRITE);
+    fclose(fp); return(status->error);
+  }
+
+  if(status->verbose>0) fprintf(stdout, "  written file %s\n", fname);
+
+  statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_OK);
+  return(TPCERROR_OK);
+}
+/*****************************************************************************/
+
+/*****************************************************************************/
diff --git a/v2/libtpctacmod/test_histplot.c b/v2/libtpctacmod/test_histplot.c
new file mode 100644
index 0000000000000000000000000000000000000000..0cee783d377a03874a290b33e6fe6fabf39d6c08
--- /dev/null
+++ b/v2/libtpctacmod/test_histplot.c
@@ -0,0 +1,111 @@
+/*****************************************************************************/
+#include "tpcclibConfig.h"
+/*****************************************************************************/
+#include "unistd.h"
+/*****************************************************************************/
+#include "tpcextensions.h"
+#include "test_tpctacmod.h"
+/*****************************************************************************/
+
+/*****************************************************************************/
+int test_tacPlotHistogramSVG(
+  TPCSTATUS *status
+) {
+  int verbose=0; if(status!=NULL) verbose=status->verbose;
+  statusSet(status, __func__, __FILE__, __LINE__, 0);
+  if(verbose>0) {
+    printf("\n=====================================\n");
+    printf("\n%s\n", __func__);
+    printf("\n=====================================\n");
+    fflush(stdout);
+  }
+
+  int ret;
+  TAC tac; tacInit(&tac);
+  char fname[128]; strcpy(fname, "");
+
+
+  if(verbose>1) {printf("\n testing with NULL data.\n"); fflush(stdout);}
+  strcpy(fname, "tacplothistogram0.svg");
+  ret=tacPlotHistogramSVG(NULL, NULL, 0.0, 0.0, 0.0, 0.0, fname, status);
+  if(ret==TPCERROR_OK) return(1);
+  if(access(fname, 0)!=-1) return(2);
+  if(verbose>1) printf("\n testing with empty data.\n");
+  ret=tacPlotHistogramSVG(&tac, "Main title", 0.0, 1.0, 0.0, 1.0, fname, status);
+  if(ret==TPCERROR_OK) return(3);
+  if(access(fname, 0)!=-1) return(4);
+
+
+
+  if(verbose>1) {printf("\n creating test data\n"); fflush(stdout);}
+  /* Allocate memory */
+  ret=tacAllocate(&tac, 5, 1); if(ret!=TPCERROR_OK) return(10);
+  tac.tacNr=1; tac.sampleNr=5;
+  /* Set TAC information */
+  tac.isframe=0;
+  /* Set region names */
+  for(int i=0; i<tac.tacNr; i++) sprintf(tac.c[i].name, "tac%d", 1+i);
+  /* Set data contents */
+  int ri=0, fi;
+  fi=0; tac.x1[fi]=0.0; tac.x2[fi]=2.0;  tac.x[fi]=1.0;  tac.c[ri].y[fi]=20.0;
+  fi=1; tac.x1[fi]=2.0; tac.x2[fi]=4.0;  tac.x[fi]=3.0;  tac.c[ri].y[fi]=10.0;
+  fi=2; tac.x1[fi]=4.0; tac.x2[fi]=6.0;  tac.x[fi]=5.0;  tac.c[ri].y[fi]=60.0;
+  fi=3; tac.x1[fi]=6.0; tac.x2[fi]=10.0; tac.x[fi]=8.0;  tac.c[ri].y[fi]=40.0;
+  fi=4; tac.x1[fi]=10.; tac.x2[fi]=13.0; tac.x[fi]=11.5; tac.c[ri].y[fi]=50.0;
+
+
+  if(verbose>1) {printf("\n plotting test data with x\n"); fflush(stdout);}
+  strcpy(fname, "tacplothistogram1.svg"); (void)remove(fname);
+  tac.isframe=0;
+  ret=tacPlotHistogramSVG(&tac, "Main title", nan(""), nan(""), nan(""), nan(""), fname, status);
+  if(ret!=TPCERROR_OK) {tacFree(&tac); return(11);}
+  if(access(fname, 0)==-1) {tacFree(&tac); return(12);}
+
+  if(verbose>1) {printf("\n plotting test data with x1 and x2\n"); fflush(stdout);}
+  strcpy(fname, "tacplothistogram2.svg"); (void)remove(fname);
+  tac.isframe=1;
+  ret=tacPlotHistogramSVG(&tac, "Main title", nan(""), nan(""), nan(""), nan(""), fname, status);
+  if(ret!=TPCERROR_OK) {tacFree(&tac); return(21);}
+  if(access(fname, 0)==-1) {tacFree(&tac); return(22);}
+
+  for(fi=0; fi<tac.sampleNr; fi++) for(ri=0; ri<tac.tacNr; ri++) tac.c[ri].y[fi]*=-1.0;
+
+  if(verbose>1) {printf("\n plotting negative test data with x1 and x2\n"); fflush(stdout);}
+  strcpy(fname, "tacplothistogram2n.svg"); (void)remove(fname);
+  tac.isframe=1;
+  ret=tacPlotHistogramSVG(&tac, "Main title", nan(""), nan(""), nan(""), nan(""), fname, status);
+  if(ret!=TPCERROR_OK) {tacFree(&tac); return(23);}
+  if(access(fname, 0)==-1) {tacFree(&tac); return(24);}
+
+  if(verbose>1) {printf("\n plotting negative test data with x\n"); fflush(stdout);}
+  strcpy(fname, "tacplothistogram1n.svg"); (void)remove(fname);
+  tac.isframe=0;
+  ret=tacPlotHistogramSVG(&tac, "Main title", nan(""), nan(""), nan(""), nan(""), fname, status);
+  if(ret!=TPCERROR_OK) {tacFree(&tac); return(13);}
+  if(access(fname, 0)==-1) {tacFree(&tac); return(14);}
+
+  for(fi=0; fi<tac.sampleNr; fi++) for(ri=0; ri<tac.tacNr; ri++) tac.c[ri].y[fi]+=35.0;
+
+  if(verbose>1) {printf("\n plotting +/- test data with x\n"); fflush(stdout);}
+  strcpy(fname, "tacplothistogram1np.svg"); (void)remove(fname);
+  tac.isframe=0;
+  ret=tacPlotHistogramSVG(&tac, "Main title", nan(""), nan(""), nan(""), nan(""), fname, status);
+  if(ret!=TPCERROR_OK) {tacFree(&tac); return(15);}
+  if(access(fname, 0)==-1) {tacFree(&tac); return(16);}
+
+  if(verbose>1) {printf("\n plotting +/- test data with x1 and x2\n"); fflush(stdout);}
+  strcpy(fname, "tacplothistogram2np.svg"); (void)remove(fname);
+  tac.isframe=1;
+  ret=tacPlotHistogramSVG(&tac, "Main title", nan(""), nan(""), nan(""), nan(""), fname, status);
+  if(ret!=TPCERROR_OK) {tacFree(&tac); return(25);}
+  if(access(fname, 0)==-1) {tacFree(&tac); return(26);}
+
+
+  fflush(stdout); fflush(stderr);
+  tacFree(&tac);
+  statusSet(status, __func__, __FILE__, __LINE__, 0);
+  return(0);
+}
+/*****************************************************************************/
+
+/*****************************************************************************/