text:
namespace chrono {
namespace vehicle {

/// @addtogroup vehicle_wheeled_tire
/// @{

/// ANCF tire template.
/// This tire is modeled as a mesh composed of ANCF shell elements.
class CH_VEHICLE_API ChANCFTire : public ChDeformableTire {
  public:
    ChANCFTire(const std::string& name);

    virtual ~ChANCFTire() {}

    /// Get the name of the vehicle subsystem template.
    virtual std::string GetTemplateName() const override { return "ANCFTire"; }

  protected:
    /// Tire profile.
    struct Profile {
        std::vector<double> t;  ///< independent parameter
        std::vector<double> x;  ///< x coordinate (radial direction)
        std::vector<double> y;  ///< y coordinate (transversal direction)
    };

    /// Tire section.
    struct Section {
        int num_divs;                                                ///< number of section divisions
        int num_layers;                                              ///< number of layers
        std::vector<double> thickness;                               ///< layer thickness
        std::vector<double> angle;                                   ///< layer ply angle
        std::vector<std::shared_ptr<fea::ChMaterialShellANCF>> mat;  ///< layer material
    };

    /// Create the ChLoad for applying pressure to the tire.
    virtual void CreatePressureLoad() override final;

    /// Create the contact surface for the tire mesh.
    virtual void CreateContactSurface() override final;

    /// Create the tire-rim connections.
    virtual void CreateRimConnections(std::shared_ptr<ChBody> wheel) override final;

    /// Utility class to generate a tire mesh using 4-node ANCF shell elements (ChElementShellANCF_3423).
    /// Returns a vector with the nodes that must be connected to the wheel rim.
    static std::vector<std::shared_ptr<fea::ChNodeFEAbase>> CreateMeshANCF4(
        const Profile& profile,             ///< tire profile
        const Section& bead,                ///< specification of bead section
        const Section& sidewall,            ///< specification of sidewall section
        const Section& tread,               ///< specification of tread section
        int div_circumference,              ///< number of divisions along circumference
        double rim_radius,                  ///< rim radius
        double damping,                     ///< structural damping
        std::shared_ptr<fea::ChMesh> mesh,  ///< containing FEA mesh
        const ChFrameMoving<>& wheel_frame  ///< associated wheel frame
    );

    /// Utility class to generate a tire mesh using 8-node ANCF shell elements (ChElementShellANCF_3833).
    /// Returns a vector with the nodes that must be connected to the wheel rim.
    static std::vector<std::shared_ptr<fea::ChNodeFEAbase>> CreateMeshANCF8(
        const Profile& profile,             ///< tire profile
        const Section& bead,                ///< specification of bead section
        const Section& sidewall,            ///< specification of sidewall section
        const Section& tread,               ///< specification of tread section
        int div_circumference,              ///< number of divisions along circumference
        double rim_radius,                  ///< rim radius
        double damping,                     ///< structural damping
        std::shared_ptr<fea::ChMesh> mesh,  ///< containing FEA mesh
        const ChFrameMoving<>& wheel_frame  ///< associated wheel frame
    );
};

/// @} vehicle_wheeled_tire

}  // end namespace vehicle
}  // end namespace chrono

#endif


text:
//
//  ViewController.m
//  CH_ScrollMenu
//
//  Created by CH on 16/10/9.
//  Copyright © 2016年 CH. All rights reserved.
//

#import "ViewController.h"
#import "CHScrollMenuController.h"
#import "CHTableViewController.h"
#import "CHDataMarcos.h"
#import "SDCycleScrollView.h"
#import "CHTextField.h"

@interface ViewController ()<CHScrollMenuDelegate, SDCycleScrollViewDelegate>
@property (nonatomic, strong) SDCycleScrollView *circelView;
@property (nonatomic, strong) NSArray *circelArray;
@property (nonatomic, strong) CHScrollMenuController *scrollMenuC;
@property (nonatomic, strong) NSArray *menuTitleArray;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    //展示轮播图
//    [self showRepeatScrollView];

    //展示有菜单的多页面滚动试图
//    [self showScrollMenuController];

    //测试计算显示小数点后三位
//    [self test:@222.202];

    //测试UIMenuController 禁用paste方法
    [self forbidPaste];
}

- (void)forbidPaste {
    CHTextField *tf = [[CHTextField alloc] initWithFrame:CGRectMake(20, 50, 300, 50)];
    tf.backgroundColor = [UIColor blueColor];

    [self.view addSubview:tf];
}

- (void)test:(NSNumber *)number {
    NSLog(@"%@",[self getLimitStringWithString:@"123.12312"]);
}

- (NSString *)getLimitStringWithString:(NSString *)string {
    NSString *newString = [NSString stringWithFormat:@"%.3f",string.doubleValue];
    while ([[newString substringFromIndex:newString.length - 1] isEqualToString:@"0"] || [[newString substringFromIndex:newString.length - 1] isEqualToString:@"."]) {
        newString = [newString substringToIndex:newString.length - 1];
    }
    return newString;
}

- (void)showRepeatScrollView {
    //测试本地/测试网络数据
    BOOL isTestWeb = YES;

    _circelView = [SDCycleScrollView cycleScrollViewWithFrame:CGRectMake(0, 0, CH_ScreenWidth, 180) shouldInfiniteLoop:YES imageNamesGroup:nil];
    _circelView.placeholderImage = [UIImage imageNamed:@"img_nodata"];
    _circelView.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    _circelView.autoScrollTimeInterval = 2.0;
    if (isTestWeb) {
        //加载网络图片
        NSString *webImageUrl = @"http://shoujibbs.2345.cn/m/images/shouji_banner2.png";
        _circelView.delegate = self;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            _circelView.imageURLStringsGroup = @[webImageUrl,webImageUrl,webImageUrl,webImageUrl];
        });
    }
    else {
        //加载本地图片
        _circelArray = @[@"image0", @"image1", @"image2", @"image3"];

        NSMutableArray *imageArray = [NSMutableArray new];
        for (NSString *imageName in _circelArray) {
            NSString *imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:@"jpg"];
            UIImage *image = [[UIImage alloc] initWithContentsOfFile:imagePath];
            [imageArray addObject:image];
        }
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            _circelView.localizationImageNamesGroup = imageArray;
        });
    }
    [self.view addSubview:_circelView];
}

- (void)showScrollMenuController {
    //第一步:所有标题的数组
    _menuTitleArray = @[@"abc", @"123", @"新闻", @"网易", @"2345", @"王健林王思聪", @"1", @"哈哈哈哈哈哈哈哈哈😄", @"abc", @"123", @"新闻", @"网易", @"2345", @"王健林王思聪", @"1", @"哈哈哈哈哈哈哈哈哈😄"];
    //第二步:初始化:提供所有素材+初始化位置
    _scrollMenuC = [CHScrollMenuController new];
    _scrollMenuC.view.frame = CGRectMake(0, 100, CH_ScreenWidth, CH_ScreenHeight-100);
    _scrollMenuC.scrollMenuDelegate = self;
    //    _scrollMenuC.firstShowIndex = 2;
    _scrollMenuC.isShowMenuButton = YES;
    _scrollMenuC.showMenuButton_Image_Down = @"forum_btn_unfold";
    _scrollMenuC.showMenuButton_Image_Up = @"forum_btn_packup";
    _scrollMenuC.maskImage = @"forum_scrollmenu_mask";
    [self.view addSubview:_scrollMenuC.view];
    _scrollMenuC.menuTitleArray = _menuTitleArray;
}
//第三步:实现数据源代理
- (NSUInteger)numberOfItemsInScrollMenu {
    return _menuTitleArray.count;
}

- (UIViewController *)scrollMenu:(CHScrollMenuController *)scrollMenu VCForItemAtIndex:(NSInteger)index {
    CHTableViewController *tableVC = [CHTableViewController new];
    tableVC.cellTitle = _menuTitleArray[index];
    return tableVC;
}

#pragma mark - CircleDelegate

- (NSInteger)numberForViews {
    return _circelArray.count;
}

- (UIView *)viewAtIndex:(NSInteger)index {
    NSString *imagePath = [[NSBundle mainBundle] pathForResource:_circelArray[index] ofType:@"jpg"];
    UIImage *image = [[UIImage alloc] initWithContentsOfFile:imagePath];
    UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
    [imageView setContentMode:UIViewContentModeScaleAspectFit];
    return imageView;
}

#pragma mark - SDCycleDelegate

/** 点击图片回调 */
- (void)cycleScrollView:(SDCycleScrollView *)cycleScrollView didSelectItemAtIndex:(NSInteger)index {
    NSLog(@"didSelectItemAtIndex-%ld",index);
}

/** 图片滚动回调 */
- (void)cycleScrollView:(SDCycleScrollView *)cycleScrollView didScrollToIndex:(NSInteger)index {
    NSLog(@"didScrollToIndex-%ld",index);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

redpajama_set_name: RedPajamaGithub


text:
require File.dirname(__FILE__) + '/../test_helper'
require 'cages_controller'

# Re-raise errors caught by the controller.
class CagesController; def rescue_action(e) raise e end; end

class CagesControllerTest < Test::Unit::TestCase
  fixtures :cages

  def setup
    @controller = CagesController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
  end

  def test_index
    get :index
    assert_response :success
    assert_template 'list'
  end

  def test_list
    get :list

    assert_response :success
    assert_template 'list'

    assert_not_nil assigns(:cages)
  end

  def test_show
    get :show, :id => 1

    assert_response :success
    assert_template 'show'

    assert_not_nil assigns(:cage)
    assert assigns(:cage).valid?
  end

  def test_new
    get :new

    assert_response :success
    assert_template 'new'

    assert_not_nil assigns(:cage)
  end

  def test_create
    num_cages = Cage.count

    post :create, :cage => {}

    assert_response :redirect
    assert_redirected_to :action => 'list'

    assert_equal num_cages + 1, Cage.count
  end

  def test_edit
    get :edit, :id => 1

    assert_response :success
    assert_template 'edit'

    assert_not_nil assigns(:cage)
    assert assigns(:cage).valid?
  end

  def test_update
    post :update, :id => 1
    assert_response :redirect
    assert_redirected_to :action => 'show', :id => 1
  end

  def test_destroy
    assert_not_nil Cage.find(1)

    post :destroy, :id => 1
    assert_response :redirect
    assert_redirected_to :action => 'list'

    assert_raise(ActiveRecord::RecordNotFound) {
      Cage.find(1)
    }
  end
end


text:
import React from 'react';
import createReactClass from 'create-react-class';
import Overlay from './Overlay';

export default createReactClass({
  render: function() {
    const {
      schema,
      fieldMap,
      actionMap,
      data,
      validators,
      fields,
      actions,
      ...other
    } = this.props;

    return (
      <Overlay
        schema={schema}
        fieldMap={fieldMap}
        actionMap={actionMap}
        data={data}
        validators={validators}
        fields={fields || [
          {
            key: 'question',
            type: 'custom',
            props: {
              render: (form) => {
                return (
                  <p>
                    No fields have been provided.
                  </p>
                );
              }
            }
          }
        ]}
        actions={actions || [
          {
            type: 'raised',
            props: (form) => {
              return {
                label: 'Create',
                primary: true,
                disabled: form.hasError,
                onClick: () => {
                  form.callbacks.onSubmit(form.data)
                }
              }
            }
          }
        ]}
        {...other}
      />
    );
  }
});


text:
package org.udidi.coolweather.gson;

/**
 * Created by He.RO on 2017/3/22.
 */

public class AQI {
    public AQICity city;

    public class AQICity {
        public String aqi;
        public String pm25;
    }
}


text:
package org.apache.cxf.example.wstransferexample.server.resourcefactory;

import org.apache.cxf.ws.transfer.Representation;
import org.apache.cxf.ws.transfer.shared.faults.PutDenied;
import org.apache.cxf.ws.transfer.validationtransformation.ResourceValidator;
import org.w3c.dom.Element;

/**
 *
 * @author Erich Duda
 */
public class StudentPutResourceValidator implements ResourceValidator {

    public static final String UID_NAMESPACE = "http://university.edu/student";

    public static final String UID_NAME = "uid";

    @Override
    public boolean validate(Representation newRepresentation, Representation oldRepresentation) {
        Element newRepresentationEl = (Element) newRepresentation.getAny();
        Element oldRepresentationEl = (Element) oldRepresentation.getAny();

        String newUid = newRepresentationEl.getElementsByTagNameNS(UID_NAMESPACE, UID_NAME).item(0).getTextContent();
        String oldUid = oldRepresentationEl.getElementsByTagNameNS(UID_NAMESPACE, UID_NAME).item(0).getTextContent();

        if (!newUid.equals(oldUid)) {
            throw new PutDenied();
        }
        return true;
    }

}


text:
package com.datatorrent.lib.db;

/**
 * This is the base implementation for a pass-through output adapter of a transactional key value store.
 * This implementation provides the "exactly once" guarantee.
 * A concrete operator should be created from this skeleton implementation.
 * <p>
 * "Pass-through" means it does not wait for end window to write to the store. It will begin transaction at begin window and write to the store as the tuples
 * come and commit the transaction at end window.
 * </p>
 * @displayName Abstract Pass Through Transactionable Key Value Store Output
 * @category Outupt
 * @tags key value, transactional
 *
 * @param <T> The tuple type
 * @param <S> The store type
 * @since 0.9.3
 */
@org.apache.hadoop.classification.InterfaceStability.Evolving
public abstract class AbstractPassThruTransactionableKeyValueStoreOutputOperator<T, S extends TransactionableKeyValueStore>
    extends AbstractPassThruTransactionableStoreOutputOperator<T, S>
{
}


text:
<!DOCTYPE html>

        <meta charset="utf-8" />
        <title>Chaitanya Krishna Ande - python</title>
        <link rel="stylesheet" href="http://icymist.github.io/theme/css/main.css" />
        <link href="http://icymist.github.io/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="Chaitanya Krishna Ande Atom Feed" />

Chaitanya Krishna Ande

About

How To

Investing

Productivity

Install Anaconda for a painless Python experience

                Published: Fri 27 March 2015

        <address class="vcard author">
                By                         Chaitanya Krishna Ande
        </address>

In How To.

tags: linux python

I have recently migrated from the Enthought Python Distribution to the Anaconda Python Distribution.
Using anaconda has been a breeze compared to Enthought.
Somehow, I couldn't get around my head to understand the User and System enviroments in Enthought.
Also, I didn't quite understand how the command line management is different from the GUI.
With Anacond it has been pretty simple.
Just install, clone to get a new environment and start working.

To install a python environment for my blog using pelican, I just had to do the following.

Install anaconda and add anaconda python to your path.

chmod +x Anaconda-2.1.0-Linux-x86_64.sh
./Anaconda-2.1.0-Linux-x86_64.sh

Clone the root environment to a new environment named pelican.

conda create -n pelican --clone root

Install pelican and markdown. markdown wasn't present in the base Anaconda distribution.

pip install pelican

Change to the new pelican python environment and start working.

source activate pelican

That was it. And, to get back to the root environment:

source deactivate

Other useful conda commands.

Info about available environments.

conda info -e

Completely remove an environment.

conda remove -n <name of environment> --all

    Page 1 / 1

social

atom feed

                <address id="about" class="vcard body">
                Proudly powered by Pelican, which takes great advantage of Python.
                </address>

The theme is by Smashing Magazine, thanks!


text:
var tests = [
  {
    name: 'HTTP cache invalidates after a successful response from a POST',
    requests: [
      {
        template: "fresh"
      }, {
        request_method: "POST",
        request_body: "abc"
      }, {
        expected_type: "not_cached"
      }
    ]
  },
  {
    name: 'HTTP cache does not invalidate after a failed response from an unsafe request',
    requests: [
      {
        template: "fresh"
      }, {
        request_method: "POST",
        request_body: "abc",
        response_status: [500, "Internal Server Error"]
      }, {
        expected_type: "cached"
      }
    ]
  },
  {
    name: 'HTTP cache invalidates after a successful response from a PUT',
    requests: [
      {
        template: "fresh"
      }, {
        template: "fresh",
        request_method: "PUT",
        request_body: "abc"
      }, {
        expected_type: "not_cached"
      }
    ]
  },
  {
    name: 'HTTP cache invalidates after a successful response from a DELETE',
    requests: [
      {
        template: "fresh"
      }, {
        request_method: "DELETE",
        request_body: "abc"
      }, {
        expected_type: "not_cached"
      }
    ]
  },
  {
    name: 'HTTP cache invalidates after a successful response from an unknown method',
    requests: [
      {
        template: "fresh"
      }, {
        request_method: "FOO",
        request_body: "abc"
      }, {
        expected_type: "not_cached"
      }
    ]
  },

  {
    name: 'HTTP cache invalidates Location URL after a successful response from a POST',
    requests: [
      {
        template: "location"
      }, {
        request_method: "POST",
        request_body: "abc",
        template: "lcl_response"
      }, {
        template: "location",
        expected_type: "not_cached"
      }
    ]
  },
  {
    name: 'HTTP cache does not invalidate Location URL after a failed response from an unsafe request',
    requests: [
      {
        template: "location"
      }, {
        template: "lcl_response",
        request_method: "POST",
        request_body: "abc",
        response_status: [500, "Internal Server Error"]
      }, {
        template: "location",
        expected_type: "cached"
      }
    ]
  },
  {
    name: 'HTTP cache invalidates Location URL after a successful response from a PUT',
    requests: [
      {
        template: "location"
      }, {
        template: "lcl_response",
        request_method: "PUT",
        request_body: "abc"
      }, {
        template: "location",
        expected_type: "not_cached"
      }
    ]
  },
  {
    name: 'HTTP cache invalidates Location URL after a successful response from a DELETE',
    requests: [
      {
        template: "location"
      }, {
        template: "lcl_response",
        request_method: "DELETE",
        request_body: "abc"
      }, {
        template: "location",
        expected_type: "not_cached"
      }
    ]
  },
  {
    name: 'HTTP cache invalidates Location URL after a successful response from an unknown method',
    requests: [
      {
        template: "location"
      }, {
        template: "lcl_response",
        request_method: "FOO",
        request_body: "abc"
      }, {
        template: "location",
        expected_type: "not_cached"
      }
    ]
  },

  {
    name: 'HTTP cache invalidates Content-Location URL after a successful response from a POST',
    requests: [
      {
        template: "content_location"
      }, {
        request_method: "POST",
        request_body: "abc",
        template: "lcl_response"
      }, {
        template: "content_location",
        expected_type: "not_cached"
      }
    ]
  },
  {
    name: 'HTTP cache does not invalidate Content-Location URL after a failed response from an unsafe request',
    requests: [
      {
        template: "content_location"
      }, {
        template: "lcl_response",
        request_method: "POST",
        request_body: "abc",
        response_status: [500, "Internal Server Error"]
      }, {
        template: "content_location",
        expected_type: "cached"
      }
    ]
  },
  {
    name: 'HTTP cache invalidates Content-Location URL after a successful response from a PUT',
    requests: [
      {
        template: "content_location"
      }, {
        template: "lcl_response",
        request_method: "PUT",
        request_body: "abc"
      }, {
        template: "content_location",
        expected_type: "not_cached"
      }
    ]
  },
  {
    name: 'HTTP cache invalidates Content-Location URL after a successful response from a DELETE',
    requests: [
      {
        template: "content_location"
      }, {
        template: "lcl_response",
        request_method: "DELETE",
        request_body: "abc"
      }, {
        template: "content_location",
        expected_type: "not_cached"
      }
    ]
  },
  {
    name: 'HTTP cache invalidates Content-Location URL after a successful response from an unknown method',
    requests: [
      {
        template: "content_location"
      }, {
        template: "lcl_response",
        request_method: "FOO",
        request_body: "abc"
      }, {
        template: "content_location",
        expected_type: "not_cached"
      }
    ]
  }

];
run_tests(tests);


text:
var chai   = require("chai");
var expect = chai.expect;
var gretro = require("../");

describe("Canvas#getWidth", function() {

  it("should return canvas width", function() {
    var canvas = new gretro.Canvas(100, 1);

    expect(canvas.getWidth()).to.equal(100);
  });

});


text:
package org.arquillian.smart.testing.surefire.provider;

/*
    This code copies from Apache commons-lang,
    under the Apache License 2.0 (see: https://git.io/vN2bs)
*/

public class StringUtils {

    public static boolean isNumeric(String str) {
        if (str == null) {
            return false;
        } else {
            int sz = str.length();

            for(int i = 0; i < sz; ++i) {
                if (!Character.isDigit(str.charAt(i))) {
                    return false;
                }
            }

            return true;
        }
    }

}


text:
from datetime import datetime
import json
from urllib import urlencode
from dimagi.utils.couch.resource_conflict import retry_resource

from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
from django.http import Http404, HttpResponseRedirect, HttpResponse
from django.template.loader import render_to_string
from django.shortcuts import render
from django.contrib import messages
from django.utils.translation import ugettext as _, ugettext_lazy
from corehq.apps.app_manager.views import _clear_app_cache

from corehq.apps.appstore.forms import AddReviewForm
from corehq.apps.appstore.models import Review
from corehq.apps.domain.decorators import require_superuser
from corehq.elastic import es_query, parse_args_for_es, fill_mapping_with_facets
from corehq.apps.domain.models import Domain
from dimagi.utils.couch.database import apply_update

SNAPSHOT_FACETS = ['project_type', 'license', 'author.exact']
DEPLOYMENT_FACETS = ['deployment.region']
SNAPSHOT_MAPPING = [
    ("", True, [
        {"facet": "project_type", "name": ugettext_lazy("Category"), "expanded": True },
        {
            "facet": "license",
            "name": ugettext_lazy("License"),
            "expanded": True,
            "mapping": {
                'cc': 'CC BY',
                'cc-sa': 'CC BY-SA',
                'cc-nd': 'CC BY-ND',
                'cc-nc': 'CC BY-NC',
                'cc-nc-sa': 'CC BY-NC-SA',
                'cc-nc-nd': 'CC BY-NC-ND',
            }
        },
        {"facet": "author.exact", "name": ugettext_lazy("Author"), "expanded": True },
    ]),
]
DEPLOYMENT_MAPPING = [
    ("", True, [
        {"facet": "deployment.region", "name": "Region", "expanded": True },
    ]),
]

def rewrite_url(request, path):
    return HttpResponseRedirect('/exchange%s?%s' % (path, request.META['QUERY_STRING']))

def inverse_dict(d):
    return dict([(v, k) for k, v in d.iteritems()])

def can_view_app(req, dom):
    if not dom or not dom.is_snapshot:
        return False
    if not dom.is_approved and (not getattr(req, "couch_user", "") or not req.couch_user.is_domain_admin(dom.copied_from.name)):
        return False
    return True

def project_info(request, domain, template="appstore/project_info.html"):
    dom = Domain.get(domain)
    if not can_view_app(request, dom):
        raise Http404()

    if request.method == "POST" and dom.copied_from.name not in request.couch_user.get_domains():
        form = AddReviewForm(request.POST)
        if form.is_valid():
            title = form.cleaned_data['review_title']
            rating = int(request.POST.get('rating'))
            if rating < 1:
                rating = 1
            if rating > 5:
                rating = 5
            info = form.cleaned_data['review_info']
            date_published = datetime.now()
            user = request.user.username

            old_review = Review.get_by_version_and_user(domain, user)

            if len(old_review) > 0: # replace old review
                review = old_review[0]
                review.title = title
                review.rating = rating
                review.info = info
                review.date_published = date_published
            else:
                review = Review(title=title, rating=rating, user=user, info=info, date_published = date_published, domain=domain, project_id=dom.copied_from._id)
            review.save()
        else:
            form = AddReviewForm()
    else:
        form = AddReviewForm()

    copies = dom.copies_of_parent()

    reviews = Review.get_by_app(dom.copied_from._id)
    average_rating = Review.get_average_rating_by_app(dom.copied_from._id)
    num_ratings = Review.get_num_ratings_by_app(dom.copied_from._id)

    if average_rating:
        average_rating = round(average_rating, 1)

    images = set()
    audio = set()

    return render(request, template, {
        "project": dom,
        "applications": dom.full_applications(include_builds=False),
        "form": form,
        "copies": copies,
        "reviews": reviews,
        "average_rating": average_rating,
        "num_ratings": num_ratings,
        "images": images,
        "audio": audio,
        "url_base": reverse('appstore'),
        'display_import': True if getattr(request, "couch_user", "") and request.couch_user.get_domains() else False
    })

def deduplicate(hits):
    unique_names = set()
    unique_hits = []
    for hit in hits:
        if not hit['_source']['name'] in unique_names:
            unique_hits.append(hit)
            unique_names.add(hit['_source']['name'])
    return unique_hits

def appstore(request, template="appstore/appstore_base.html"):
    page_length = 10
    include_unapproved = True if request.GET.get('is_approved', "") == "false" else False
    if include_unapproved and not request.user.is_superuser:
        raise Http404()
    params, _ = parse_args_for_es(request)
    page = params.pop('page', 1)
    page = int(page[0] if isinstance(page, list) else page)
    results = es_snapshot_query(params, SNAPSHOT_FACETS)
    hits = results.get('hits', {}).get('hits', [])
    hits = deduplicate(hits)
    d_results = [Domain.wrap(res['_source']) for res in hits]

    sort_by = request.GET.get('sort_by', None)
    if sort_by == 'best':
        d_results = Domain.popular_sort(d_results)
    elif sort_by == 'newest':
        pass
    else:
        d_results = Domain.hit_sort(d_results)

    persistent_params = {}
    if sort_by:
        persistent_params["sort_by"] = sort_by
    if include_unapproved:
        persistent_params["is_approved"] = "false"
    persistent_params = urlencode(persistent_params) # json.dumps(persistent_params)

    average_ratings = list()
    for result in d_results:
        average_ratings.append([result.name, Review.get_average_rating_by_app(result.copied_from._id)])

    more_pages = False if len(d_results) <= page*page_length else True

    facet_map = fill_mapping_with_facets(SNAPSHOT_MAPPING, results, params)
    vals = dict(
        apps=d_results[(page-1)*page_length:page*page_length],
        page=page,
        prev_page=(page-1),
        next_page=(page+1),
        more_pages=more_pages,
        sort_by=sort_by,
        average_ratings=average_ratings,
        include_unapproved=include_unapproved,
        facet_map=facet_map,
        facets=results.get("facets", []),
        query_str=request.META['QUERY_STRING'],
        search_query=params.get('search', [""])[0],
        persistent_params=persistent_params,
    )
    return render(request, template, vals)

def appstore_api(request):
    params, facets = parse_args_for_es(request)
    results = es_snapshot_query(params, facets)
    return HttpResponse(json.dumps(results), mimetype="application/json")

def es_snapshot_query(params, facets=None, terms=None, sort_by="snapshot_time"):
    if terms is None:
        terms = ['is_approved', 'sort_by', 'search']
    if facets is None:
        facets = []
    q = {"sort": {sort_by: {"order" : "desc"} },
         "query":   {"bool": {"must":
                                  [{"match": {'doc_type': "Domain"}},
                                   {"term": {"published": True}},
                                   {"term": {"is_snapshot": True}}]}},
         "filter":  {"and": [{"term": {"is_approved": params.get('is_approved', None) or True}}]}}

    search_query = params.get('search', "")
    if search_query:
        q['query']['bool']['must'].append({
            "match" : {
                "_all" : {
                    "query" : search_query,
                    "operator" : "and"
                }
            }
        })

    return es_query(params, facets, terms, q)

def appstore_default(request):
    from corehq.apps.appstore.dispatcher import AppstoreDispatcher
    return HttpResponseRedirect(reverse(AppstoreDispatcher.name(), args=['advanced']))

@require_superuser
def approve_app(request, domain):
    domain = Domain.get(domain)
    if request.GET.get('approve') == 'true':
        domain.is_approved = True
        domain.save()
    elif request.GET.get('approve') == 'false':
        domain.is_approved = False
        domain.save()
    return HttpResponseRedirect(request.META.get('HTTP_REFERER') or reverse('appstore'))

@login_required
@retry_resource(3)
def import_app(request, domain):
    user = request.couch_user
    if not user.is_eula_signed():
        messages.error(request, 'You must agree to our eula to download an app')
        return project_info(request, domain)

    from_project = Domain.get(domain)

    if request.method == 'POST' and from_project.is_snapshot:
        if not from_project.published:
            messages.error(request, "This project is not published and can't be downloaded")
            return project_info(request, domain)

        to_project_name = request.POST['project']
        if not user.is_member_of(to_project_name):
            messages.error(request, _("You don't belong to that project"))
            return project_info(request, domain)

        for app in from_project.full_applications(include_builds=False):
            new_doc = from_project.copy_component(app['doc_type'], app.get_id, to_project_name, user)
        _clear_app_cache(request, to_project_name)

        from_project.downloads += 1
        from_project.save()
        messages.success(request, render_to_string("appstore/partials/view_wiki.html", {"pre": _("Application successfully imported!")}), extra_tags="html")
        return HttpResponseRedirect(reverse('view_app', args=[to_project_name, new_doc.id]))
    else:
        return HttpResponseRedirect(reverse('project_info', args=[domain]))

@login_required
def copy_snapshot(request, domain):
    user = request.couch_user
    if not user.is_eula_signed():
        messages.error(request, 'You must agree to our eula to download an app')
        return project_info(request, domain)

    dom = Domain.get(domain)
    if request.method == "POST" and dom.is_snapshot:
        from corehq.apps.registration.forms import DomainRegistrationForm
        args = {'domain_name': request.POST['new_project_name'], 'eula_confirmed': True}
        form = DomainRegistrationForm(args)

        if request.POST.get('new_project_name', ""):
            if not dom.published:
                messages.error(request, "This project is not published and can't be downloaded")
                return project_info(request, domain)

            if form.is_valid():
                new_domain = dom.save_copy(form.cleaned_data['domain_name'], user=user)
            else:
                messages.error(request, form.errors)
                return project_info(request, domain)

            if new_domain is None:
                messages.error(request, _("A project by that name already exists"))
                return project_info(request, domain)

            def inc_downloads(d):
                d.downloads += 1

            apply_update(dom, inc_downloads)
            messages.success(request, render_to_string("appstore/partials/view_wiki.html", {"pre": _("Project copied successfully!")}), extra_tags="html")
            return HttpResponseRedirect(reverse('view_app',
                args=[new_domain.name, new_domain.full_applications()[0].get_id]))
        else:
            messages.error(request, _("You must specify a name for the new project"))
            return project_info(request, domain)
    else:
        return HttpResponseRedirect(reverse('project_info', args=[domain]))

def project_image(request, domain):
    project = Domain.get(domain)
    if project.image_path:
        image = project.fetch_attachment(project.image_path)
        return HttpResponse(image, content_type=project.image_type)
    else:
        raise Http404()

@login_required
def deployment_info(request, domain, template="appstore/deployment_info.html"):
    dom = Domain.get_by_name(domain)
    if not dom or not dom.deployment.public:
        raise Http404()

    # get facets
    results = es_deployments_query({}, DEPLOYMENT_FACETS)
    facet_map = fill_mapping_with_facets(DEPLOYMENT_MAPPING, results, {})

    return render(request, template, {
        'domain': dom,
        'search_url': reverse('deployments'),
        'url_base': reverse('deployments'),
        'facet_map': facet_map,
    })

@login_required
def deployments(request, template="appstore/deployments.html"):
    params, _ = parse_args_for_es(request)
    params = dict([(DEPLOYMENT_MAPPING.get(p, p), params[p]) for p in params])
    page = int(params.pop('page', 1))
    results = es_deployments_query(params, DEPLOYMENT_FACETS)
    d_results = [Domain.wrap(res['_source']) for res in results['hits']['hits']]

    more_pages = False if len(d_results) <= page*10 else True

    facet_map = fill_mapping_with_facets(DEPLOYMENT_MAPPING, results, params)
    include_unapproved = True if request.GET.get('is_approved', "") == "false" else False
    vals = { 'deployments': d_results[(page-1)*10:page*10],
             'page': page,
             'prev_page': page-1,
             'next_page': (page+1),
             'more_pages': more_pages,
             'include_unapproved': include_unapproved,
             'facet_map': facet_map,
             'query_str': request.META['QUERY_STRING'],
             'search_url': reverse('deployments'),
             'search_query': params.get('search', [""])[0]}
    return render(request, template, vals)

def deployments_api(request):
    params, facets = parse_args_for_es(request)
    params = dict([(DEPLOYMENT_MAPPING.get(p, p), params[p]) for p in params])
    results = es_deployments_query(params, facets)
    return HttpResponse(json.dumps(results), mimetype="application/json")

def es_deployments_query(params, facets=None, terms=None, sort_by="snapshot_time"):
    if terms is None:
        terms = ['is_approved', 'sort_by', 'search']
    if facets is None:
        facets = []
    q = {"query":   {"bool": {"must":
                                  [{"match": {'doc_type': "Domain"}},
                                   {"term": {"deployment.public": True}}]}}}

    search_query = params.get('search', "")
    if search_query:
        q['query']['bool']['must'].append({
            "match" : {
                "_all" : {
                    "query" : search_query,
                    "operator" : "and"
                }
            }
        })
    return es_query(params, facets, terms, q)

def media_files(request, domain, template="appstore/media_files.html"):
    dom = Domain.get(domain)
    if not can_view_app(request, dom):
        raise Http404()

    return render(request, template, {
        "project": dom,
        "url_base": reverse('appstore')
    })


text:
package picard.sam.SamErrorMetric;

import htsjdk.samtools.metrics.MetricsFile;
import htsjdk.samtools.util.IOUtil;
import htsjdk.samtools.util.QualityUtil;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import java.io.File;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static picard.cmdline.CommandLineProgramTest.CHR_M_REFERENCE;

public class CollectSamErrorMetricsTest {
    private static final File OUTPUT_DATA_PATH = IOUtil.createTempDir("CollectSamErrorMetricsTest", null);
    private static final String TEST_DIR = "testdata/picard/sam/BamErrorMetrics";

    @BeforeClass
    public void setup() {
        ReadBaseStratification.setLongHomopolymer(6);
    }

    @DataProvider
    public Object[][] parseDirectiveData() {
        return new Object[][]{
                {"ERROR", "error_by_all"},
                {"ERROR:ALL", "error_by_all"},
                {"ERROR:GC_CONTENT", "error_by_gc"},
                {"ERROR:READ_ORDINALITY", "error_by_read_ordinality"},
                {"ERROR:READ_BASE", "error_by_read_base"},
                {"ERROR:REFERENCE_BASE", "error_by_ref_base"},
                {"ERROR:PRE_DINUC", "error_by_pre_dinuc"},
                {"ERROR:POST_DINUC", "error_by_post_dinuc"},
                {"ERROR:HOMOPOLYMER_LENGTH", "error_by_homopolymer_length"},
                {"ERROR:HOMOPOLYMER", "error_by_homopolymer_and_following_ref_base"},
                {"ERROR:BINNED_HOMOPOLYMER", "error_by_binned_length_homopolymer_and_following_ref_base"},
                {"ERROR:FLOWCELL_TILE", "error_by_tile"},
                {"ERROR:READ_DIRECTION", "error_by_read_direction"},
                {"ERROR:CYCLE", "error_by_cycle"},
                {"ERROR:BINNED_CYCLE", "error_by_binned_cycle"},
                {"ERROR:INSERT_LENGTH", "error_by_insert_length"},
                {"ERROR:BASE_QUALITY", "error_by_base_quality"},
                {"ERROR:MAPPING_QUALITY", "error_by_mapping_quality"},
                {"ERROR:READ_GROUP", "error_by_read_group"},
                {"ERROR:MISMATCHES_IN_READ", "error_by_mismatches_in_read"},
                {"ERROR:ONE_BASE_PADDED_CONTEXT", "error_by_one_base_padded_context"},
                {"ERROR:TWO_BASE_PADDED_CONTEXT", "error_by_two_base_padded_context"},
                {"ERROR:CONSENSUS", "error_by_consensus"},
                {"ERROR:NS_IN_READ", "error_by_ns_in_read"},

                {"ERROR:POST_DINUC:BASE_QUALITY", "error_by_post_dinuc_and_base_quality"},
                {"ERROR:POST_DINUC:BASE_QUALITY:GC_CONTENT", "error_by_post_dinuc_and_base_quality_and_gc"},
                {" ERROR : POST_DINUC : BASE_QUALITY : GC_CONTENT ", "error_by_post_dinuc_and_base_quality_and_gc"},

                {"OVERLAPPING_ERROR", "overlapping_error_by_all"},
                {"OVERLAPPING_ERROR:ALL", "overlapping_error_by_all"},
                {"OVERLAPPING_ERROR:GC_CONTENT", "overlapping_error_by_gc"},
                {"OVERLAPPING_ERROR:READ_ORDINALITY", "overlapping_error_by_read_ordinality"},
                {"OVERLAPPING_ERROR:READ_BASE", "overlapping_error_by_read_base"},
                {"OVERLAPPING_ERROR:REFERENCE_BASE", "overlapping_error_by_ref_base"},
                {"OVERLAPPING_ERROR:PRE_DINUC", "overlapping_error_by_pre_dinuc"},
                {"OVERLAPPING_ERROR:POST_DINUC", "overlapping_error_by_post_dinuc"},
                {"OVERLAPPING_ERROR:HOMOPOLYMER_LENGTH", "overlapping_error_by_homopolymer_length"},
                {"OVERLAPPING_ERROR:HOMOPOLYMER", "overlapping_error_by_homopolymer_and_following_ref_base"},
                {"OVERLAPPING_ERROR:FLOWCELL_TILE", "overlapping_error_by_tile"},
                {"OVERLAPPING_ERROR:READ_DIRECTION", "overlapping_error_by_read_direction"},
                {"OVERLAPPING_ERROR:CYCLE", "overlapping_error_by_cycle"},
                {"OVERLAPPING_ERROR:INSERT_LENGTH", "overlapping_error_by_insert_length"},
                {"OVERLAPPING_ERROR:BASE_QUALITY", "overlapping_error_by_base_quality"},
        };
    }

    @Test(dataProvider = "parseDirectiveData")
    public void parseDirectiveGood(final String directive, final String extension) {
        parseDirective0(directive, extension);

    }

    @Test
    void testStratifiersHaveDistinctSuffixes() {
        Set<String> suffixes = new HashSet<>();

        for (final ReadBaseStratification.Stratifier stratifier : ReadBaseStratification.Stratifier.values()) {
            Assert.assertTrue(suffixes.add(stratifier.makeStratifier().getSuffix()), "found duplicate suffix: " +
                    stratifier.makeStratifier().getSuffix() + " for: " + stratifier.makeStratifier());
        }
    }

    @Test
    void testAggregatorsHaveDistinctSuffixes() {
        Set<String> suffixes = new HashSet<>();

        for (final ErrorType error : ErrorType.values()) {
            final String suffix = error.getErrorSupplier().get().getSuffix();
            Assert.assertTrue(suffixes.add(suffix), "found duplicate suffix: " + suffix);
        }
    }

    @DataProvider
    public Object[][] parseDirectiveBadData() {
        return new Object[][]{
                {"ERROR:"},
                {"ERRORS:READ_ORDINALITY"},
                {"ERROR;REFERENCE_BASE"},
                {"ERROR:what"},
        };
    }

    @Test(dataProvider = "parseDirectiveBadData", expectedExceptions = IllegalArgumentException.class)
    public void parseDirectiveBad(final String directive) {
        parseDirective0(directive, null);
    }

    private static void parseDirective0(final String directive, final String extension) {
        final BaseErrorAggregation agg = CollectSamErrorMetrics.parseDirective(directive);
        if (extension != null) {
            Assert.assertEquals(agg.getSuffix(), extension);
        }
    }

    @DataProvider(name = "OneCovariateErrorMetricsDataProvider")
    public Object[][] oneCovariateErrorMetricsDataProvider() {
        final File simpleSamWithBaseErrors1 = new File(TEST_DIR, "simpleSamWithBaseErrors1.sam");
        final File simpleSamWithBaseErrors2 = new File(TEST_DIR, "simpleSamWithBaseErrors2.sam");
        final File simpleSingleStrandConsensusSamWithBaseErrors = new File(TEST_DIR, "simpleSingleStrandConsensusSamWithBaseErrors.sam");
        final File simpleDuplexConsensusSamWithBaseErrors = new File(TEST_DIR, "simpleDuplexConsensusSamWithBaseErrors.sam");
        final File chrMReadsWithClips = new File(TEST_DIR, "chrMReadsWithClips.sam");
        final int priorQ = 30;

        //These magic numbers come from a separate implementation of the code in R.
        return new Object[][]{

                // Note that soft clipped bases are not counted.
                {".error_by_all", chrMReadsWithClips, priorQ,
                        new BaseErrorMetric("all", 62L, 49L)},
                {".error_by_base_quality", chrMReadsWithClips, priorQ,
                        new BaseErrorMetric("32", 52L, 41L)},
                {".error_by_base_quality", chrMReadsWithClips, priorQ,
                        new BaseErrorMetric("33", 10L, 8L)},
//                 Note that the homopolymer is counted as the number of bases in the read that match each other before a new base.
//                 This catches mismatches (and matches) of the ends of homopolymers.
                {".error_by_homopolymer_and_following_ref_base", chrMReadsWithClips, priorQ,
                        new BaseErrorMetric("9,T,T", 1L, 1L)},
                {".error_by_homopolymer_and_following_ref_base", simpleSamWithBaseErrors1, priorQ,
                        new BaseErrorMetric("2,G,T", 2L, 1L)},

                {".error_by_binned_length_homopolymer_and_following_ref_base", simpleSamWithBaseErrors1, priorQ,
                        new BaseErrorMetric("SHORT_HOMOPOLYMER,G,T", 6L, 1L)},

                {".error_by_read_ordinality_and_pre_dinuc", chrMReadsWithClips, priorQ,
                        new BaseErrorMetric("FIRST,A,A", 3L, 3L)},
                // Using a sam file with a single error it is easy to validate demonstrate these tests should pass
                {".error_by_all", simpleSamWithBaseErrors1, priorQ,
                        new BaseErrorMetric("all", 72L, 1L)},
                // There are two base qualities in the bam, the error occurs in quality "32"
                {".error_by_base_quality", simpleSamWithBaseErrors1, priorQ,
                        new BaseErrorMetric("32", 51L, 1L)},
                // There are two base qualities in the bam, the error occurs in quality "32", make sure we detect no errors in quality "33"
                {".error_by_base_quality", simpleSamWithBaseErrors1, priorQ,
                        new BaseErrorMetric("33", 21L, 0L)},
                // simpleSamWithBaseErrors2 contains 2 differences from the reference
                // after 2 different homopolymers.
                {".error_by_homopolymer_and_following_ref_base", simpleSamWithBaseErrors2, priorQ,
                        new BaseErrorMetric("2,T,A", 2L, 1L)},
                // Make sure that we can correctly identify an error after a homopolymer
                {".error_by_homopolymer_and_following_ref_base", simpleSamWithBaseErrors2, priorQ,
                        new BaseErrorMetric("3,C,G", 1L, 1L)},
                {".error_by_read_ordinality_and_pre_dinuc", simpleSamWithBaseErrors1, priorQ,
                        new BaseErrorMetric("FIRST,G,T", 1L, 1L)},
                // The covariate "0.5" was chosen to avoid those that have repeating decimals and could have alternative representations as
                // a string.  GC is calculated over the entire read including clipped bases, while errors are calculated only over unclipped bases.
                {".error_by_gc", simpleSamWithBaseErrors1, priorQ,
                        new BaseErrorMetric("0.5", 36L, 1L)},
                // Make sure that we can detect errors at a particular cycle
                {".error_by_cycle", simpleSamWithBaseErrors1, priorQ,
                        new BaseErrorMetric("10", 2L, 1L)},
                // There should be one error in the read with mapping quality 60.
                {".error_by_mapping_quality", simpleSamWithBaseErrors1, priorQ,
                        new BaseErrorMetric("60", 36L, 1L)},
                // There should be no errors in the read with mapping quality 0.
                {".error_by_mapping_quality", simpleSamWithBaseErrors1, priorQ,
                        new BaseErrorMetric("50", 36L, 0L)},
                // One base has an error in the read group 62A40.2
                {".error_by_read_group", simpleSamWithBaseErrors1, priorQ,
                        new BaseErrorMetric("62A40.2", 72L, 1L)},
                // No additional mismatches are found on the read with 1 mismatch.
                {".error_by_mismatches_in_read", simpleSamWithBaseErrors1, priorQ,
                        new BaseErrorMetric("1", 35L, 0L)},
                // No additional mismatches are found on the read with 1 mismatch. (Just another way to check)
                {".error_by_mismatches_in_read", simpleSamWithBaseErrors1, priorQ,
                        new BaseErrorMetric("0", 37L, 1L)},
                // There should be no errors in the CAG context because it matches reference
                {".error_by_one_base_padded_context", simpleSamWithBaseErrors1, priorQ,
                        new BaseErrorMetric("CAG", 1L, 0L)},
                // There should be one error in the GTC context
                {".error_by_one_base_padded_context", simpleSamWithBaseErrors1, priorQ,
                        new BaseErrorMetric("GTC", 1L, 1L)},
                // There should be one error in the CTT context
                {".error_by_one_base_padded_context", simpleSamWithBaseErrors2, priorQ,
                        new BaseErrorMetric("CCT", 3L, 0L)},
                // There should be no errors in the ACGGG context
                {".error_by_two_base_padded_context", simpleSamWithBaseErrors1, priorQ,
                        new BaseErrorMetric("ACGGG", 1L, 0L)},
                // There should be one error in the GGTCT context
                {".error_by_two_base_padded_context", simpleSamWithBaseErrors1, priorQ,
                        new BaseErrorMetric("GGTCT", 1L, 1L)},
                // There should be no errors in the CTTGA context (appears in sam file as TCAaG in second read)
                {".error_by_two_base_padded_context", simpleSamWithBaseErrors2, priorQ,
                        new BaseErrorMetric("CTTGA", 1L, 0L)},
                // There should be one error in the CCGTG context
                {".error_by_two_base_padded_context", simpleSamWithBaseErrors2, priorQ,
                        new BaseErrorMetric("CCGTG", 1L, 1L)},
                // Reads that don't have consensus tags should be stratified as UNKNOWN
                {".error_by_consensus", simpleSamWithBaseErrors1, priorQ,
                        new BaseErrorMetric("UNKNOWN", 72L, 1L)},
                // There should be 2 errors in the one simplex singleton reads.
                {".error_by_consensus", simpleSingleStrandConsensusSamWithBaseErrors, priorQ,
                        new BaseErrorMetric("SIMPLEX_SINGLETON", 36L, 2L)},
                // There should be no errors in the simplex consensus read.
                {".error_by_consensus", simpleSingleStrandConsensusSamWithBaseErrors, priorQ,
                        new BaseErrorMetric("SIMPLEX_CONSENSUS", 36L, 0L)},
                // There should be one error in duplex singleton read.  Also the N in this read reduces total bases from 36 to 35.
                {".error_by_consensus", simpleDuplexConsensusSamWithBaseErrors, priorQ,
                        new BaseErrorMetric("DUPLEX_SINGLETON", 35L, 1L)},
                // There should be two errors in the duplex consensus read.
                {".error_by_consensus", simpleDuplexConsensusSamWithBaseErrors, priorQ,
                        new BaseErrorMetric("DUPLEX_CONSENSUS", 36L, 2L)},
                // There should be two errors in the read with no Ns.
                {".error_by_ns_in_read", simpleDuplexConsensusSamWithBaseErrors, priorQ,
                        new BaseErrorMetric("0", 36L, 2L)},
                // There should be one errors in the read with one N.
                {".error_by_ns_in_read", simpleDuplexConsensusSamWithBaseErrors, priorQ,
                        new BaseErrorMetric("1", 35L, 1L)},
                // There are two errors, one should show up in QUINTILE_1 and the other in QUINTILE_3
                // QUINTILE_5 has 16 total (2 more than the other bins) bases due to rounding.
                {".error_by_binned_cycle", simpleSamWithBaseErrors2, priorQ,
                        new BaseErrorMetric("QUINTILE_1", 14L, 1L)},
                {".error_by_binned_cycle", simpleSamWithBaseErrors2, priorQ,
                        new BaseErrorMetric("QUINTILE_2", 14L, 0L)},
                {".error_by_binned_cycle", simpleSamWithBaseErrors2, priorQ,
                        new BaseErrorMetric("QUINTILE_3", 14L, 1L)},
                {".error_by_binned_cycle", simpleSamWithBaseErrors2, priorQ,
                        new BaseErrorMetric("QUINTILE_4", 14L, 0L)},
                {".error_by_binned_cycle", simpleSamWithBaseErrors2, priorQ,
                        new BaseErrorMetric("QUINTILE_5", 16L, 0L)}
        };
    }

    @Test(dataProvider = "OneCovariateErrorMetricsDataProvider")
    public void testOneCovariateErrorMetrics(final String errorSubscript, final File samFile, final int priorQ, BaseErrorMetric expectedMetric) {
        final File referenceFile = CHR_M_REFERENCE;
        final File vcf = new File(TEST_DIR, "NIST.selected.vcf");

        final File outputBaseFileName = new File(OUTPUT_DATA_PATH, "test");
        final File errorByAll = new File(outputBaseFileName.getAbsolutePath() + errorSubscript);
        errorByAll.deleteOnExit();
        outputBaseFileName.deleteOnExit();

        final String[] args = {
                "INPUT=" + samFile,
                "OUTPUT=" + outputBaseFileName,
                "REFERENCE_SEQUENCE=" + referenceFile.getAbsolutePath(),
                "ERROR_METRICS=" + "ERROR:TWO_BASE_PADDED_CONTEXT", // Not all covariates are included by default, but we still want to test them.
                "ERROR_METRICS=" + "ERROR:CONSENSUS",
                "ERROR_METRICS=" + "ERROR:NS_IN_READ",
                "ERROR_METRICS=" + "ERROR:BINNED_CYCLE",
                "VCF=" + vcf.getAbsolutePath()
        };

        Assert.assertEquals(new CollectSamErrorMetrics().instanceMain(args), 0);

        ErrorMetric.setPriorError(QualityUtil.getErrorProbabilityFromPhredScore(priorQ));
        expectedMetric.calculateDerivedFields();

        // Note that soft clipped bases are not counted
        List<BaseErrorMetric> metrics = MetricsFile.readBeans(errorByAll);

        BaseErrorMetric metric = metrics
                .stream()
                .filter(m -> m.COVARIATE.equals(expectedMetric.COVARIATE))
                .findAny()
                .orElseThrow(() -> new AssertionError("didn't find metric with COVARIATE==" + expectedMetric.COVARIATE));

        Assert.assertEquals(metric, expectedMetric);
    }

    @AfterClass()
    public void cleanup() {
        IOUtil.deleteDirectoryTree(OUTPUT_DATA_PATH);
    }

    @DataProvider
    public Object[][] readCycleBinData() {
        // Test most edge cases of BaseErrorAggregation.CycleBin
        return new Object[][]{
                {0.0, ReadBaseStratification.CycleBin.QUINTILE_1},
                {0.2, ReadBaseStratification.CycleBin.QUINTILE_1},
                {0.2 + Math.ulp(0.2), ReadBaseStratification.CycleBin.QUINTILE_2},
                {0.4, ReadBaseStratification.CycleBin.QUINTILE_2},
                {0.4 + Math.ulp(0.4), ReadBaseStratification.CycleBin.QUINTILE_3},
                {0.54, ReadBaseStratification.CycleBin.QUINTILE_3},
                {0.6, ReadBaseStratification.CycleBin.QUINTILE_3},
                {0.6 + Math.ulp(0.6), ReadBaseStratification.CycleBin.QUINTILE_4},
                {0.8, ReadBaseStratification.CycleBin.QUINTILE_4},
                {0.8 + Math.ulp(0.8), ReadBaseStratification.CycleBin.QUINTILE_5},
                {1.0, ReadBaseStratification.CycleBin.QUINTILE_5},
        };
    }

    @Test(dataProvider = "readCycleBinData")
    public void testReadCycleBin(final double relativePosition, final ReadBaseStratification.CycleBin cycleBin) {
        Assert.assertEquals(ReadBaseStratification.CycleBin.valueOf(relativePosition), cycleBin);
    }

    @DataProvider
    public Object[][] readCycleBinDataError() {
        // Test cases that should throw an exception with BaseErrorAggregation.CycleBin
        return new Object[][]{
                {1.0 + Math.ulp(1.0)}, {0.0 - Math.ulp(0.0)}, {-1.0}, {-0.1}, {1.1}, {100.0}
        };
    }

    @Test(dataProvider = "readCycleBinDataError", expectedExceptions = IllegalArgumentException.class)
    public void testReadCycleBinError(final double relativePosition) {
        ReadBaseStratification.CycleBin.valueOf(relativePosition);
    }

    @Test(expectedExceptions = IllegalArgumentException.class)
    public void testTooManyDirectives() {
        final File input = new File(TEST_DIR, "simpleSamWithBaseErrors1.sam");

        final File referenceFile = CHR_M_REFERENCE;
        final File vcf = new File(TEST_DIR, "NIST.selected.vcf");

        final File outputBaseFileName = new File(OUTPUT_DATA_PATH, "test");
        outputBaseFileName.deleteOnExit();

        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("ERROR");
        for (ReadBaseStratification.Stratifier stratifier : ReadBaseStratification.Stratifier.values()) {
            stringBuilder.append(":");
            stringBuilder.append(stratifier.toString());
        }
        // one more should break it:
        stringBuilder.append(":ALL");
        final String[] args = {
                "INPUT=" + input,
                "OUTPUT=" + outputBaseFileName,
                "REFERENCE_SEQUENCE=" + referenceFile.getAbsolutePath(),
                "ERROR_METRICS=" + stringBuilder.toString(),
                "VCF=" + vcf.getAbsolutePath()
        };
        new CollectSamErrorMetrics().instanceMain(args);
    }
}