Full Code of hudora/pyShipping for AI

master 089c502db5d7 cached
36 files
310.0 KB
108.0k tokens
257 symbols
1 requests
Download .txt
Showing preview only (324K chars total). Download the full file or copy to clipboard to get everything.
Repository: hudora/pyShipping
Branch: master
Commit: 089c502db5d7
Files: 36
Total size: 310.0 KB

Directory structure:
gitextract_q2tr1ik7/

├── .gitignore
├── CHANGES
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.rst
├── pyshipping/
│   ├── 3dbpp.c
│   ├── __init__.py
│   ├── addressvalidation.py
│   ├── binpack.py
│   ├── binpack_simple.py
│   ├── carriers/
│   │   ├── __init__.py
│   │   └── dpd/
│   │       ├── __init__.py
│   │       ├── georoute.py
│   │       ├── georoute_test.py
│   │       └── georoutetables/
│   │           ├── COUNTRY
│   │           ├── LOCATION.DE
│   │           ├── LOCATION.EN
│   │           ├── LOCATION.FR
│   │           ├── SERVICE
│   │           ├── SERVICEINFO.CS
│   │           ├── SERVICEINFO.CZ
│   │           ├── SERVICEINFO.DE
│   │           ├── SERVICEINFO.EE
│   │           └── SERVICEINFO.EN
│   ├── fortras/
│   │   ├── __init__.py
│   │   ├── bordero.py
│   │   ├── entl.py
│   │   ├── fakt.py
│   │   ├── fortras_stat.py
│   │   └── test.py
│   ├── package.py
│   └── shipment.py
├── requirements.txt
├── setup.py
└── testdata.txt

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.pyc
*.pyo
*.so
*~
build
dist
html
pyShipping.egg-info
*.bak


================================================
FILE: CHANGES
================================================
1.9:    neue Routeninfos von DPD zum 2011-05-07
1.8:    neue Routeninfos von DPD zum 2011-01-03
1.7:    neue Routeninfos von DPD zum 6.9.2010
1.6:    addressvalidation added
1.5:    Keep Package dimensions sorted, buendelung(), __add__() and hat_gleiche_seiten() 
        added binpacking (c) David Pisinger, Silvano Martello, Daniele Vigo
        and an independent pure Python implementation
        unified PackageSize and Package, made Package sortable
1.4:    Gemeinsame Basisklasse fuer Exceptions in pyshipping.carriers.dpd.georoute
1.3p3:  Added new Routing Table for DPD (Routendatenbank Januar - April 2010)
1.3p2:  Added new Routing Table for DPD
1.3p1:  Provide extra argument basedir to fortras.bordero.ship(). Bordero files will be saved in this directory.


================================================
FILE: LICENSE
================================================
Original code in this disturibution is
Copyright 2008, 2009 HUDORA GmbH. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:

   1. Redistributions of source code must retain the above copyright notice, this list of
      conditions and the following disclaimer.

   2. Redistributions in binary form must reproduce the above copyright notice, this list
      of conditions and the following disclaimer in the documentation and/or other materials
      provided with the distribution.

THIS SOFTWARE IS PROVIDED BY HUDORA GmbH ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This distribution contains software by third parties which might be licensed differently.
See the respective sourcefiles for details.

================================================
FILE: MANIFEST.in
================================================
include README.rst
include pyshipping/carriers/dpd/georoutetables/*

================================================
FILE: Makefile
================================================
# setting the PATH seems only to work in GNUmake not in BSDmake
PATH := ./testenv/bin:$(PATH)

check:
	pep8 -r --ignore=E501 pyshipping/
	sh -c 'PYTHONPATH=. pyflakes pyshipping/'
	-sh -c 'PYTHONPATH=. pylint -iy --max-line-length=110 pyshipping/' # -rn

build:
	python setup.py build

test:
	PYTHONPATH=. python pyshipping/__init__.py # find import errors
	PYTHONPATH=. python pyshipping/shipment.py
	PYTHONPATH=. python pyshipping/package.py
	PYTHONPATH=. python pyshipping/fortras/test.py
	PYTHONPATH=. python pyshipping/binpack.py
	# These tests tend to fail because of routing table updates
	PYTHONPATH=. python pyshipping/carriers/dpd/georoute_test.py

dependencies:
	virtualenv testenv
	pip -q install -E testenv -r requirements.txt

statistics:
	sloccount --wide --details pyshipping | tee sloccount.sc

upload: build doc
	python setup.py sdist upload

doc: build
	rm -Rf html
	mkdir -p html
	mkdir -p html/fortras
	mkdir -p html/carriers
	sh -c '(cd html; pydoc -w ../pyshipping/*.py)'
	sh -c '(cd html/fortras; pydoc -w ../../pyshipping/*.py)'
	sh -c '(cd html/carriers; pydoc -w ../../pyshipping/*.py)'

install: build
	sudo python setup.py install

clean:
	rm -Rf testenv build dist html test.db pyShipping.egg-info pylint.out sloccount.sc pip-log.txt
	find . -name '*.pyc' -or -name '*.pyo' -delete

.PHONY: test build clean check upload doc install


================================================
FILE: README.rst
================================================
pyShipping provides connections to interface with shipping companies and to transport shipping related information. 

 * package - shipping/cargo related calculations based on a unit of shipping (box, crate, package), includes
   a bin packing implementation in pure Python
 * sendung - defines an abstract shippment (Sendung), with packages and calculations based on that
 * addressvalidation - check if an address is valid
 * carriers.dpd - calculation of DPD/Georoutes routing data and labels. Included tables are for shippments from Wuppertal but it should work with all other german routing tables. See this Blogpost_ about updating routing information.
 * fortras - tools for reading and writing Fortras messages. Fortras is a EDI standard for logistics related information somewhat common in Germany. See Wikipedia_ for further enlightenment

.. _Wikipedia: http://de.wikipedia.org/wiki/Fortras
.. _Blogpost: https://cybernetics.hudora.biz/intern/wordpress/2010/09/dpd-routeninformationen-aktualisieren/

It also comes with the only python based `3D Bin Packing <http://www.cs.sunysb.edu/~algorith/files/bin-packing.shtml>`_ implementation I'm aware of. The Algorithm has sufficient performance to be used in everyday shipping and warehousing applications.

You can get the whole Package at http://pypi.python.org/pypi/pyShipping

This code is BSD Licensed.

.. image:: https://d2weczhvl823v0.cloudfront.net/hudora/pyshipping/trend.png
   :alt: Bitdeli badge
   :target: https://bitdeli.com/free



================================================
FILE: pyshipping/3dbpp.c
================================================

/* ======================================================================
      3D BIN PACKING, Silvano Martello, David Pisinger, Daniele Vigo 
                             1998, 2003, 2006
   ====================================================================== */

/* This code solves the three-dimensional bin-packing problem, which
 * asks for an orthogonal packing of a given set of rectangular-shaped
 * boxes into the minimum number of three-dimensional rectangular bins.
 * Each box j=1,..,n is characterized by a width w_j, height h_j, and
 * depth d_j. An unlimited number of indentical three-dimensional bins,
 * having width W, height H and depth D is available. The boxes have fixed
 * orientation, i.e., they may not be rotated.
 *
 * A description of this code is found in the following papers:
 *
 *   S. Martello, D. Pisinger, D. Vigo, E. den Boef, J. Korst (2003)
 *   "Algorithms for General and Robot-packable Variants of the 
 *    Three-Dimensional Bin Packing Problem"
 *   submitted TOMS.
 * 
 *   S.Martello, D.Pisinger, D.Vigo (2000)
 *   "The three-dimensional bin packing problem"
 *   Operations Research, 48, 256-267
 *
 * The present code is written in ANSI-C, and has been compiled with
 * the GNU-C compiler using option "-ansi -pedantic" as well as the
 * HP-UX C compiler using option "-Aa" (ansi standard).
 *
 * This file contains the callable routine binpack3d with prototype
 *
 *   void binpack3d(int n, int W, int H, int D,
 *                  int *w, int *h, int *d, 
 *                  int *x, int *y, int *z, int *bno,
 *                  int *lb, int *ub, 
 *                  int nodelimit, int iterlimit, int timelimit, 
 *                  int *nodeused, int *iterused, int *timeused);
 *
 * the meaning of the parameters is the following:
 *   n         Size of problem, i.e., number of boxes to be packed.
 *             This value must be smaller than MAXBOXES defined below.
 *   W,H,D     Width, height and depth of every bin.
 *   w,h,d     Integer arrays of length n, where w[j], h[j], d[j]
 *             are the dimensions of box j for j=0,..,n-1.
 *   x,y,z,bno Integer arrays of length n where the solution found
 *             is returned. For each box j=0,..,n-1, the bin number
 *             it is packed into is given by bno[j], and x[j], y[j], z[j] 
 *             are the coordinates of it lower-left-backward corner.
 *   lb        Lower bound on the solution value (returned by the procedure).
 *   ub        Objective value of the solution found, i.e., number of bins
 *             used to pack the n boxes. (returned by the procedure).
 *   nodelimit maximum number of decision nodes to be explored in the
 *             main branching tree. If set to zero, the algorithm will
 *             run until an optimal solution is found (or timelimit or
 *             iterlimit is reached). Measured in thousands (see IUNIT).
 *   iterlimit maximum number of iterations in the ONEBIN algorithm
 *             which packs a single bin. If set to zero, the algorithm will
 *             run until an optimal solution is found (or timelimit or
 *             nodelimit is reached). Measured in thousands (see IUNIT).
 *   timelimit Time limit for solving the problem expressed in seconds.
 *             If set to zero, the algorithm will run until an optimal
 *             solution is found; otherwise it terminates after timelimit
 *             seconds with a heuristic solution. 
 *   nodeused  returns the number of branch-and-bound nodes investigated,
 *             measured in thousands (see IUNIT).
 *   iterused  returns the number of iterations in ONEBIN algorithm,
 *             measured in thousands (see IUNIT).
 *   timeused  returns the time used in miliseconds
 *
 * (c) Copyright 1998, 2003, 2005
 *
 *   David Pisinger                        Silvano Martello, Daniele Vigo
 *   DIKU, University of Copenhagen        DEIS, University of Bologna
 *   Universitetsparken 1                  Viale Risorgimento 2
 *   Copenhagen, Denmark                   Bologna, Italy
 *
 * This code can be used free of charge for research and academic purposes 
 * only. 
 */              

#define IUNIT        1000  /* scaling factor of nodes and iterat */ 
#define MAXBOXES      101  /* max number of boxes (plus one box) */
#define MAXBPP    1000000  /* max numer of iterations in 1-dim bpp */
#define MAXITER      1000  /* max iterations in heuristic onebin_robot */
#define MAXCLOSE       16  /* max level for which try_close is applied */

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>
#include <time.h>  
#include <limits.h>


/* ======================================================================
				   macros
   ====================================================================== */

#define TRUE  1           /* logical variables */
#define FALSE 0

#define WDIM  0           /* rotations of boxes */
#define HDIM  1
#define DDIM  2

#define LEFT   0          /* relative placements */
#define RIGHT  1
#define UNDER  2
#define ABOVE  3
#define FRONT  4
#define BEHIND 5
#define UNDEF  6
#define RELMAX 8

#define STACKDEPTH (MAXBOXES*MAXBOXES*RELMAX)

#define VOL(i)            ((i)->w * (ptype) (i)->h * (i)->d)
#define MINIMUM(i,j)      ((i) < (j) ? (i) : (j))
#define MAXIMUM(i,j)      ((i) > (j) ? (i) : (j))
#define DIF(i,j)          ((int) ((j) - (i) + 1))
#define SWAPINT(a,b)      { register int t; t=*(a);*(a)=*(b);*(b)=t; }
#define SWAP(a,b)         { register box t; t=*(a);*(a)=*(b);*(b)=t; }
#define SWAPI(a,b)        { register itype t; t=(a);(a)=(b);(b)=t; }
#define SWAPP(a,b)        { register point t; t=*(a);*(a)=*(b);*(b)=t; }
#define DF(a,b)           ((r=(a).y-(b).y) != 0 ? r : (a).x-(b).x)


/* ======================================================================
				 type declarations
   ====================================================================== */

typedef short         boolean; /* logical variable      */
typedef short         ntype;   /* number of states,bins */
typedef short         itype;   /* can hold up to W,H,D  */
typedef long          stype;   /* can hold up to W*H*D  */
typedef long          ptype;   /* product multiplication */

/* box record */
typedef struct irec {
  ntype    no;           /* box number                            */
  itype    w;            /* box width  (x-size)                   */
  itype    h;            /* box height (y-size)                   */
  itype    d;            /* box depth  (z-size)                   */
  itype    x;            /* optimal x-position                    */
  itype    y;            /* optimal y-position                    */
  itype    z;            /* optimal z-position                    */
  ntype    bno;          /* bin number                            */
  boolean  k;            /* is the box chosen?                    */
  stype    vol;          /* volume of box                         */
  struct irec *ref;      /* reference to original box (if necessary) */
} box; 

/* all problem information */
typedef struct {
  itype    W;            /* x-size of bin                         */
  itype    H;            /* y-size of bin                         */
  itype    D;            /* z-size of bin                         */
  stype    BVOL;         /* volume of a bin                       */
  ntype    n;            /* number of boxes                       */
  box      *fbox;        /* first box in problem                  */
  box      *lbox;        /* last box in problem                   */
  box      *fsol;        /* first box in current solution         */
  box      *lsol;        /* last box in current solution          */
  box      *fopt;        /* first box in optimal solution         */
  box      *lopt;        /* last box in optimal solution          */
  boolean  *closed;      /* for each bin indicator whether closed */
  box      *fclosed;     /* first box in closed bins              */
  box      *lclosed;     /* last box in closed bins               */
  ntype    noc;          /* number of closed bins                 */
  itype    mindim;       /* currently smallest box length         */
  itype    maxdim;       /* currently largest box length          */
  stype    maxfill;      /* the best filling found                */
  int      mcut;         /* how many siblings at each node in b&b */

  /* different bounds */
  ntype    bound0;       /* Bound L_0 at root node                */
  ntype    bound1;       /* Bound L_1 at root node                */
  ntype    bound2;       /* Bound L_2 at root node                */
  ntype    lb;           /* best of the above                     */
  ntype    z;            /* currently best solution               */

  /* controle of 3d filler */
  int      maxiter;      /* max iterations in onebin_robot        */
  int      miss;         /* number boxes not packed in onebin_robot */

  /* debugging and controle information */
  int      nodes;        /* nodes in branch-and-bound             */
  int      iterat;       /* iterations in onebin_decision         */
  int      subnodes;     /* nodes in branch-and-bound             */
  int      subiterat;    /* iterations in onebin_decision         */
  int      exfill;       /* number of calls to onebin_decision    */
  int      iter3d;       /* iterations in onebin_robot or general */
  int      zlayer;       /* heuristic solution layer              */
  int      zmcut;        /* heuristic solution mcut               */
  double   exacttopo;    /* number of topological sorts           */
  double   exacttopn;    /* number of topological sorts           */
  int      exactcall;    /* number of calls to exact              */
  int      exactn;       /* largest problem for exact             */
  double   genertime;    /* time used in onebin_general           */
  double   robottime;    /* time used in onebin_robot             */
  double   time;         /* computing time                        */
  double   lhtime;       /* layer heuristic computing time        */
  double   mhtime;       /* mcut heuristic computing time         */
  int      didpush;      /* did the lower bound push up bound     */
  int      maxclose;     /* max number of closed bins at any time */
  int      nodelimit;    /* maximum number of nodes in main tree  */
  int      iterlimit;    /* maximum number of iterations in ONEBIN*/
  int      timelimit;    /* maximum amount of time to be used     */
} allinfo;

/* structure for greedy algorithm */
typedef struct {
  int      lno;          /* layer number                          */
  int      d;            /* depth of layer                        */
  int      bno;          /* bin no assigned to layer              */
  int      z;            /* z level of layer within bin           */
  int      b;            /* temporary bin number                  */
} heurpair;

/* structure for extreme points in a single bin */
typedef struct {
  itype    x;            /* x-coordinate                          */
  itype    y;            /* y-coordinate                          */
  itype    z;            /* z-coordinate                          */
} point;

/* structure for a domain pair in constraint programming */
typedef struct {
  int i;                 /* index of box i                        */
  int j;                 /* index of box j                        */
  int relation;          /* relation between the two boxes        */
  boolean domain;        /* domain of the two boxes               */
} domainpair;
  

/* set of domains */
typedef char domset[RELMAX];
typedef domset domline[MAXBOXES];

/* pointer to comparison function */
typedef int (*funcptr) (const void *, const void *); 


/* ======================================================================
				  global variables
   ====================================================================== */

/* boolean variable to indicate time-out situation */
boolean stopped; 

/* counter used to ensure that 1D BPP at most performs MAXBPP iterations */
int bpiterat;

/* boolean variables to indicate when 1D packing algorithm should terminate */
boolean feasible, terminate;

/* stack of domain pairs */
domainpair domstack[STACKDEPTH];
domainpair *dompos, *domend; 

/* domain of each box */
domline domain[MAXBOXES];

/* current relation between two boxes */
char relation[MAXBOXES][MAXBOXES];

/* debug variable to see level in recursive packing algorithm */
int bblevel;


/* =======================================================================
				  error
   ======================================================================= */

void error(char *str, ...)
{
  va_list args;

  va_start(args, str);
  vprintf(str, args); printf("\n");
  va_end(args);
  printf("IRREGULAR PROGRAM TERMINATION\n");
  exit(-1);
}


/* **********************************************************************
   **********************************************************************
			     Timing routines 
   **********************************************************************
   ********************************************************************** */

/* This timing routine is based on the ANSI-C procedure "clock", which
 * has a resolution of 1000000 ticks per second. This however implies
 * that we pass the limit of a long integer after only 4295 seconds.
 * The following routine attempts to correct such situations by adding
 * the constant ULONG_MAX to the counter whenever wraparound can be
 * detected. But the user is advised to use a timing routine like "times"
 * (which however is not ANSI-C standard) for measuring longer time
 * periods.
 */

void timer(double *time)
{
  static double tstart, tend, tprev;

  if (time == NULL) {
    clock(); /* one extra call to initialize clock */
    tstart = tprev = clock();
  } else {
    tend = clock(); 
    if (tend < tprev) tstart -= ULONG_MAX; /* wraparound occured */
    tprev = tend;
    *time = (tend-tstart) / CLOCKS_PER_SEC; /* convert to seconds */
  }
}

/* test for time limit */
void check_timelimit(long max)
{
  double t;
  if (max == 0) return;
  timer(&t); 
  if (t >= max) { 
    if (!stopped) printf("TIMELIMIT\n"); 
    stopped = TRUE; 
  }
}

/* test for node limit */
void check_nodelimit(long nodes, long max)
{
  if (max == 0) return;
  if (nodes >= max) { 
    if (!stopped) printf("NODELIMIT\n"); 
    stopped = TRUE; 
  }
}

/* test for iteration limit */
void check_iterlimit(long iterations, long max)
{
  if (max == 0) return;
  if (iterations >= max) { 
    if (!stopped) printf("ITERLIMIT\n"); 
    stopped = TRUE; 
  } 
}



/* **********************************************************************
   **********************************************************************
			     Small procedures
   **********************************************************************
   ********************************************************************** */

/* ======================================================================
			    simple comparisions
   ====================================================================== */

/* Comparisons used as argument to qsort. */

int dcomp(box *a, box *b) 
{ int r; r = b->d - a->d; if (r != 0) return r; else return b->no - a->no; }
int hcomp(box *a, box *b) 
{ int r; r = b->h - a->h; if (r != 0) return r; else return b->no - a->no; }
int vcomp(box *a, box *b) /* volume decr. */
{ int r; r = b->vol-a->vol; if (r != 0) return r; else return b->no - a->no; }
int xcomp(heurpair *a, heurpair *b) /* depth decr. */
{ int r; r = b->d - a->d; if (r != 0) return r; else return b->lno - a->lno; }
int lcomp(heurpair *a, heurpair *b) /* layer number decr. */
{ int r; r = a->lno-b->lno; if (r != 0) return r; else return b->d - a->d; }


/* ======================================================================
				  palloc
   ====================================================================== */

/* Memory allocation and freeing, with implicit check */

void *palloc(long sz, long no)
{
  long size;
  void *p;

  size = sz * no;
  if (size == 0) size = 1;
  p = (void *) malloc(size);
  if (p == NULL) error("no memory size %ld", size);
  return p;
}

void pfree(void *p)
{
  if (p == NULL) error("freeing null");
  free(p);
}


/* ======================================================================
			     checksol
   ====================================================================== */

/* Check correctnes of solution, i.e., no boxes overlap, no duplicated boxes.
 */

void checksol(allinfo *a, box *f, box *l)
{
  box *i, *j, *m;
  for (i = f, m = l+1; i != m; i++) { 
    if (!i->k) continue;  /* box currently not chosen */
    for (j = f; j != m; j++) {
      if (i == j) continue;
      if (i->no == j->no) error("duplicated box %d\n", i->no); 
      if (!j->k) continue;
      if (i->bno != j->bno) continue;
      if ((i->x + i->w > j->x) && (j->x + j->w > i->x) &&
	  (i->y + i->h > j->y) && (j->y + j->h > i->y) &&
	  (i->z + i->d > j->z) && (j->z + j->d > i->z)) {
	error("overlap box %d,%d: [%d,%d,%d] [%d,%d,%d]",
	      i->no, j->no, i->w, i->h, i->d, j->w, j->h, j->d);
      }
    }
  }
}

/* ======================================================================
			       savesol
   ====================================================================== */

/* save an updated solution, checking its validity */

void savesol(allinfo *a, box *f, box *l, ntype z)
{
  box *i, *k, *m;

  /* first check validity */
  if (z >= a->z) error("not improved");
  for (i = f, m = l+1; i != m; i++) {
    if ((1 <= i->bno) && (i->bno <= z)) continue; 
    error("illegal bin %d, box %d", i->bno, i->no); 
  }

  /* now do the saving */
  a->z = z;
  for (i = f, k = a->fopt, m = l+1; i != m; i++, k++) *k = *i;
  for (i = a->fclosed, m = a->lclosed+1; i != m; i++, k++) *k = *i;
  for (i = a->fopt, m = a->lopt+1; i != m; i++) i->k = TRUE;
  if (DIF(a->fopt,k-1) != a->n) error("not correct amount of boxes");
  checksol(a, a->fopt, a->lopt);
}


/* ======================================================================
			       isortincr
   ====================================================================== */

/* A specialized routine for sorting integers in increasing order. */
/* qsort could be used equally well, but this routine is faster. */

void isortincr(int *f, int *l)
{
  register int mi;
  register int *i, *j, *m;
  register int d;

  d = l - f + 1;
  if (d < 1) error("negative interval in isortincr");
  if (d == 1) return;
  m = f + d / 2; if (*f > *m) SWAPINT(f, m);
  if (d > 2) { if (*m > *l) { SWAPINT(m, l); if (*f > *m) SWAPINT(f, m); } }
  if (d <= 3) return;
  mi = *m; i = f; j = l;
  for (;;) {
    do i++; while (*i < mi);
    do j--; while (*j > mi);
    if (i > j) break; else SWAPINT(i, j);
  }
  isortincr(f, i-1); isortincr(i, l);
}


/* ======================================================================
			       psortdecr
   ====================================================================== */

/* A specialized routine for sorting extreme points according to decreasing */
/* y-coordinate (decreasing x-coordinate in case of ties) */

void psortdecr(point *f, point *l)
{
  register point mi;
  register point *i, *j, *m;
  register int d, r;

  d = l - f + 1; 
  if (d <= 1) return;
  m = f + d / 2; if (DF(*f,*m)<0) SWAPP(f,m); 
  if (d == 2) return;
  if (DF(*m,*l)<0) { SWAPP(m,l); if (DF(*f,*m)<0) SWAPP(f,m); }
  if (d <= 3) return;
  mi = *m; i = f; j = l;
  for (;;) {
    do i++; while (DF(*i,mi) > 0);
    do j--; while (DF(*j,mi) < 0);
    if (i > j) break; else SWAPP(i, j);
  }
  psortdecr(f, i-1); psortdecr(i, l);
}


/* **********************************************************************
   **********************************************************************
			      Lower Bounds
   **********************************************************************
   ********************************************************************** */

/* ======================================================================
			      bound_zero
   ====================================================================== */

/* The continuous bound L_0 */

int bound_zero(allinfo *a, box *f, box *l)
{
  box *i, *m;
  stype vsum, lb;

  vsum = 0; 
  for (i = f, m = l+1; i != m; i++) vsum += i->vol;
  lb = (stype) ceil(vsum / (double) a->BVOL);
  return lb;
}


/* ======================================================================
                               rotate_solution
   ====================================================================== */

/* rotates the solution. After 3 rotations we return to original problem */

void rotate_solution(allinfo *a, box *f, box *l)
{
  register box *i, *m;
  register itype w, x;

  for (i = f, m = l+1; i != m; i++) {
    w = i->w; i->w = i->h; i->h = i->d; i->d = w;
    x = i->x; i->x = i->y; i->y = i->z; i->z = x;
  }
}


/* ======================================================================
			       rotate_problem
   ====================================================================== */

/* rotates the dimensions by one step */

void rotate_problem(allinfo *a, box *f, box *l)
{
  register box *i, *m;
  register itype w, x;

  for (i = f, m = l+1; i != m; i++) {
    w = i->w; i->w = i->h; i->h = i->d; i->d = w;
    x = i->x; i->x = i->y; i->y = i->z; i->z = x;
  }
  w = a->W; a->W = a->H; a->H = a->D; a->D = w;
}


/* ======================================================================
			       choose_boxes
   ====================================================================== */

/* returns a set of boxes with w > W2 and d > D2. This set is used in */
/* bound_one */

void choose_boxes(allinfo *a, box *f, box *l, int W2, int D2, 
		  box *fbox , box **lbox)
{
  box *i, *k, *m;

  for (i = f, m = l+1, k = fbox; i != m; i++) {
    if ((i->w > W2) && (i->d > D2)) { *k = *i; k++; }
  }
  *lbox = k-1;
}


/* ======================================================================
			       find_plist
   ====================================================================== */

/* returns a zero-terimanted list of distinct dimensions */

void find_plist(box *fbox, box *lbox, itype M, int dim, int *pl)
{
  register box *i, *m;
  register int *k, *j, *l;

  i = fbox; m = lbox+1; k = pl;
  switch (dim) {
    case WDIM: for (; i != m; i++) {
		 if (i->w <= M) { *k = i->w; k++; } 
	       } break;
    case HDIM: for (; i != m; i++) {
		 if (i->h <= M) { *k = i->h; k++; } 
	       } break;
    case DDIM: for (; i != m; i++) {
		 if (i->d <= M) { *k = i->d; k++; } 
	       } break;
  }
  if (k == pl) { *k = 0; return; }
  isortincr(pl, k-1);  /* sort the dimensions */
  for (j = pl+1, l = pl; j != k; j++) { /* remove duplicates */
    if (*j != *l) { l++; *l = *j; } 
  }
  l++; *l = 0;
}


/* ======================================================================
			       bound_one
   ====================================================================== */

/* Derive bound L_1 for a fixed dimension */

int bound_one_x(allinfo *a, box *f, box *l)
{
  register box *i, *m;
  register itype H, H2, h;
  register int p, j1, j2, j3, j2h, j2hp, j3h;
  int *pp, lb, lb_one, alpha, beta;
  box fbox[MAXBOXES], *lbox;
  int plist[MAXBOXES];

  if (l == f-1) return 0;
  lb = 1; H = a->H; H2 = H/2;
  choose_boxes(a, f, l, a->W/2, a->D/2, fbox, &lbox);
  if (lbox == fbox-1) { /* empty */ return lb; }

  find_plist(fbox, lbox, H2, HDIM, plist);
  for (pp = plist; *pp != 0; pp++) {
    p = *pp; j1 = j2 = j3 = j2h = j2hp = j3h = 0;
    for (i = fbox, m = lbox+1; i != m; i++) {
      h = i->h; 
      if (h > H-p) j1++;
      if ((H-p >= h) && (h > H2)) { j2++; j2h += h; j2hp += (H-h)/p; }
      if ((H2 >= h) && (h >= p)) { j3++; j3h += h; }
    }
    alpha = (int) ceil((j3h - (j2 * H - j2h)) / (double) H);
    beta  = (int) ceil((j3 - j2hp) / (double) (H/p));
    if (alpha < 0) alpha = 0;
    if (beta  < 0) beta  = 0;
    lb_one = j1 + j2 + MAXIMUM(alpha, beta);
    if (lb_one > lb) lb = lb_one;
  }
  return lb;
}


/* Derive bound L_1 as the best of all L_1 bounds for three rotations */

int bound_one(allinfo *a, box *f, box *l)
{
  int i, lb, lbx;

  lb = 0;
  for (i = WDIM; i <= DDIM; i++) {
    lbx = bound_one_x(a, f, l);
    if (lbx > lb) lb = lbx; 
    rotate_problem(a, f, l);
  } 
  return lb;
}


/* ======================================================================
			       bound_two
   ====================================================================== */

/* Derive bound L_2 for a fixed dimension */

int bound_two_x(allinfo *a, box *f, box *l)
{
  register box *i, *m;
  register itype W, H, D, w, h, d, W2, D2;
  register int p, q, k1h, k23v;
  int hlb1, lb, lb1, lbx, fract;
  int plist[MAXBOXES], qlist[MAXBOXES];
  int *qq, *pp;
  double WD, BVOL;

  /* derive bound_one */
  lb = lb1 = bound_one_x(a, f, l);
  W = a->W; H = a->H; D = a->D; hlb1 = H * lb1; 
  W2 = W/2; D2 = D/2; WD = W*(double)D; BVOL = a->BVOL;

  /* run through all values of p, q */
  find_plist(f, l, W2, WDIM, plist);
  find_plist(f, l, D2, DDIM, qlist);
  for (pp = plist; *pp != 0; pp++) {
    p = *pp;
    for (qq = qlist; *qq != 0; qq++) {
      q = *qq;
      k1h = k23v = 0;
      for (i = f, m = l+1; i != m; i++) {
	w = i->w; h = i->h; d = i->d;
	if ((w > W - p) && (d > D - q)) { k1h += h; continue; }
	if ((w >= p) && (d >= q)) { k23v += i->vol; }
      }
      fract = (int) ceil((k23v - (hlb1 - k1h)*WD) / BVOL);
      if (fract < 0) fract = 0;
      lbx = lb1 + fract;
      if (lbx > lb) lb = lbx;
    }
  }
  return lb;
}


/* Derive bound L_2 as the best of all L_2 bounds for three rotations */

int bound_two(allinfo *a, box *f, box *l)
{
  int i, lb, lbx;

  lb = 0;
  for (i = WDIM; i <= DDIM; i++) {
    lbx = bound_two_x(a, f, l);
    if (lbx > lb) lb = lbx;
    rotate_problem(a, f, l);
  } 
  return lb;
}


/* **********************************************************************
   **********************************************************************
			    heuristic filling
   **********************************************************************
   ********************************************************************** */

/* ======================================================================
			      onelayer
   ====================================================================== */

/* Fill a layer of depth f->d by arranging the boxes in a number of */
/* vertical shelfs. The boxes $i$ packed are assigned coordinates */
/* (i->x, i->y) and the field i->k is set to the argument d (layer no). */

void onelayer(allinfo *a, box *f, box *m, box *l, int d)
{
  int s, t;
  itype r; /* remaining width */
  itype width[MAXBOXES], height[MAXBOXES], x[MAXBOXES];
  box *i;
  
  qsort(f, DIF(f,m), sizeof(box), (funcptr) hcomp);
  r = a->W;
  x[0] = 0; width[0] = 0; height[0] = 0;
  for (s = 1, i = f; i != l+1; s++) {
    x[s] = x[s-1] + width[s-1];
    height[s] = 0;
    width[s] = i->w; if (width[s] > r) width[s] = r;
    r -= width[s];
    for ( ; i != l+1; i++) {
      for (t = s; t != 0; t--) {
	if (i->w <= width[t]) {
	  if (height[t] + i->h <= a->H) {
	    i->y = height[t]; i->x = x[t]; i->k = d;
	    height[t] += i->h; break;
	  }
	}
      }
      if ((t == 0) && (r > 0)) break; /* new strip */
    }
  }
}


/* ======================================================================
				  countarea
   ====================================================================== */

/* Select a subset of the boxes such that the selected boxes have a total */
/* area of two times the face of a bin (the parameter: barea) */

box *countarea(box *f, box *l, stype barea)
{
  box *i;
  stype area, d;

  for (area = 0, i = f; i != l+1; i++) {
    d = i->h * (ptype) i->w;
    area += d;
    if (area > 2*barea) return i-1;
  }
  return l;
}


/* ======================================================================
				  remboxes
   ====================================================================== */

/* Remove the boxes which were chosen for a layer, i.e., where i->k != 0. */
/* The depth of the layer is set equal to the deepest box chosen. */

box *remboxes(box *f, box *l, itype *depth)
{
  box *i, *j;
  itype d;

  for (i = f, j = l, d = 0; i != j+1; ) {
    if (i->k) { if (i->d > d) d = i->d; i++; } else { SWAP(i,j); j--; }
  }
  *depth = d;
  return i;
}


/* ======================================================================
				  assignboxes
   ====================================================================== */

/* Assign z-coordinates to the boxes, once they layers have been combined */
/* to individual bins by solving a 1-dimensional Bin-packing Problem. */

void assignboxes(heurpair *t, heurpair *u, ntype maxbin, box *f, box *l)
{
  box *i, *m;
  heurpair *h;
  itype b, z;

  /* derive z-coordinates for each layer */
  for (b = 1; b <= maxbin; b++) {
    z = 0;
    for (h = t; h <= u; h++) if (h->bno == b) { h->z = z; z += h->d; }
  }

  for (i = f, m = l+1; i != m; i++) {
    h = t + i->k - 1;
    i->z = h->z; i->bno = h->bno;
  }
}


/* ======================================================================
				 onedim_binpack
   ====================================================================== */

/* One-dimensional bin-packing algorithm. In each iteration, the next */
/* box is assigned to every open bin as well as to a new bin. The  */
/* algorithm terminates when MAXBPP iterations have been performed, */
/* returning the heuristic solution found. */

void onedim_binpack(heurpair *i, heurpair *f, heurpair *l, 
		    int *b, int bno, itype *z)
{
  int j, *bc;
  heurpair *k;

  bpiterat++; if (bpiterat > MAXBPP) return;
  if (bno >= *z) return; /* no hope of improvement */

  if (i > l) {
    *z = bno; 
    for (k = f; k <= l; k++) k->bno = k->b;
  } else {
    for (j = 0; j < bno; j++) {
      bc = b + j;
      if (i->d <= *bc) {
	*bc -= i->d; i->b = j+1;
	onedim_binpack(i+1, f, l, b, bno, z);
	*bc += i->d;
      }
    }
    bc = b + bno;
    *bc -= i->d; i->b = bno+1;
    onedim_binpack(i+1, f, l, b, bno+1, z);
    *bc += i->d;
  }
}


/* ======================================================================
			      dfirst_heuristic
   ====================================================================== */

/* Heuristic algorithm for the 3D BPP. A number of layers are constructed */
/* using the shelf approach to pack every layer. Then the individual layers */
/* are combined to bins by solving a 1D BPP defined in the layer depths. */

void dfirst_heuristic(allinfo *a)
{
  box *j, *f, *l, *m;
  int i, n, h, b[MAXBOXES];
  heurpair t[MAXBOXES];
  itype d, z;

  /* initialize boxes */
  for (j = a->fbox, m = a->lbox+1; j != m; j++) {
    j->bno = j->x = j->y = j->z = 0; j->k = FALSE;
  }

  /* fill layer one by one */
  for (f = a->fbox, l = a->lbox, h = 0; ; h++) {
    n = DIF(f,l); if (n == 0) break;
    qsort(f, n, sizeof(box), (funcptr) dcomp);
    m = countarea(f, l, a->W * (ptype) a->H);
    onelayer(a, f, m, l, h+1);
    f = remboxes(f, l, &d);
    t[h].d = d; t[h].bno = h+1;  /* initially put layers into separate bins */
    t[h].z = 0; t[h].lno = h+1;  /* this ensures fes. solution if terminate */
  }

  /* split into bins by solving 1-dim binpacking */
  for (i = 0; i < h; i++) b[i] = a->D;  /* all bins are empty */
  qsort(t, h, sizeof(heurpair), (funcptr) xcomp);
  z = h+1; bpiterat = 0;
  onedim_binpack(t, t, t+h-1, b, 0, &z); 
  qsort(t, h, sizeof(heurpair), (funcptr) lcomp); /* order according to lno */

  /* now assign bin number to each boxes */
  assignboxes(t, t+h-1, z, a->fbox, a->lbox);
  if (z < a->zlayer) a->zlayer = z;
  if (a->zlayer < a->z) savesol(a, a->fbox, a->lbox, a->zlayer);
}


/* ======================================================================
				dfirst3_heuristic
   ====================================================================== */

/* Call the heuristic dfirst_heuristic, for three different rotations */
/* of the problem */

void dfirst3_heuristic(allinfo *a)
{
  int i;
  double t1, t2;
 
  timer(&t1); 
  a->zlayer = a->n;  /* very bad incumbent solution */
  for (i = WDIM; i <= DDIM; i++) {
    dfirst_heuristic(a);
    rotate_solution(a, a->fopt, a->lopt);
    rotate_problem(a, a->fbox, a->lbox);
  }
  timer(&t2); 
  a->lhtime = t2 - t1;
}


/* **********************************************************************
   **********************************************************************
                    fill one 3D bin using GENERAL packing
   **********************************************************************
   ********************************************************************** */


/* ======================================================================
  			         modifyandpush
   ====================================================================== */

/* Push the relation "rel" between box i and box j to a stack. If "dom"
 * is true, the relation is removed from the domain. If "dom" is false,
 * the relation "rel" is imposed between boxes "i" and "j".
 */

void modifyandpush(int i, int j, int rel, boolean dom)
{
  dompos->i = i;
  dompos->j = j;
  dompos->domain = dom;
  if (dom) { 
    dompos->relation = rel;
    domain[i][j][rel] = FALSE; 
  } else {
    dompos->relation = relation[i][j];
    relation[i][j] = rel; 
  }
  dompos++; if (dompos == domend) error("stack filled\n");
}


/* ======================================================================
  			         popdomains
   ====================================================================== */

/* Pop all relations between boxes from the stack. The stack is emptied
 * downto the depth given by "pos".
 */

void popdomains(domainpair *pos)
{
  for (; dompos != pos; ) {
    dompos--;
    if (dompos->domain) {
      domain[dompos->i][dompos->j][dompos->relation] = TRUE;
    } else {
      relation[dompos->i][dompos->j] = dompos->relation;
    }
  }
}


/* ======================================================================
  			         findcoordinates
   ====================================================================== */

/* Find coordinates of boxes according to the currently pending relations.
 * In principle this can be done by topologically sorting the boxes according
 * to e.g. the left-right relations, and then assigning coordinates from
 * the left-most box to the right-most box.
 *   The following implementation is a simplified version, which runs through
 * all pairs of boxes, and checks whether they satisfy the relation, otherwise
 * moving one of the boxes right, up or behind, according to the given
 * relation. The process is repeated until no pairs of boxes violate a
 * relation. Computational experiments have shown that the present approach
 * for the considered instances is faster than a topological sorting followed
 * by a critical path calculation.
 *   If a box during the process gets moved outsides of the bin, then
 * the algorithm terminates with FALSE. Otherwise TRUE is returned, saying
 * that a feasible packing exists which respects the current relations.
 */

boolean findcoordinates(allinfo *a, int n, box *f)
{
  register box *g, *h;
  register int sum;
  int i, j, k, W, H, D;
  boolean changed;
  char *dom, *relij;
  domset *domij;

  /* check if feasible, i.e., at least one choice for each relation */
  W = a->W; H = a->H; D = a->D; 
  for (i = 0; i < n; i++) {
    j = i+1;
    relij = &(relation[i][j]); 
    domij = &(domain[i][j]); 
    for ( ; j < n; j++, relij++, domij++) {
      if (*relij != UNDEF) continue;
      dom = *domij; if (*dom) continue; 
      dom++;        if (*dom) continue;
      dom++;        if (*dom) continue;
      dom++;        if (*dom) continue;
      dom++;        if (*dom) continue;
      dom++;        if (*dom) continue;
      return FALSE;
    }
  }

  /* initialize coordinates */
  for (i = 0; i < n; i++) { g = f+i; g->x = g->y = g->z = 0; } 

  /* now determine the coordinates */
  a->exacttopo++;
  for (k = 0; k < n; k++) {
    a->exacttopn++;
    changed = FALSE; 
    for (i = 0; i < n; i++) {
      g = f+i; j = i+1; relij = &(relation[i][j]);
      for ( ; j < n; j++, relij++) {
        h = f+j;
        switch (*relij) {
          case UNDEF :
            /* do nothing */
            break;
          case LEFT  : 
            sum = g->x + g->w;
            if (h->x < sum) {
              h->x = sum; changed = TRUE; if (sum + h->w > W) return FALSE;
            }
            break;
          case RIGHT : 
            sum = h->x + h->w;
            if (g->x < sum) {
              g->x = sum; changed = TRUE; if (sum + g->w > W) return FALSE;
            }
            break;
          case UNDER : 
            sum = g->y + g->h;
            if (h->y < sum) {
              h->y = sum; changed = TRUE; if (sum + h->h > H) return FALSE;
            }
            break;
          case ABOVE : 
            sum = h->y + h->h;
            if (g->y < sum) {
              g->y = sum; changed = TRUE; if (sum + g->h > H) return FALSE;
            }
            break;
          case FRONT : 
            sum = g->z + g->d;
            if (h->z < sum) {
              h->z = sum; changed = TRUE; if (sum + h->d > D) return FALSE;
            }
            break;
          case BEHIND: 
            sum = h->z + h->d;
            if (g->z < sum) {
              g->z = sum; changed = TRUE; if (sum + g->d > D) return FALSE;
            }
            break;
        }
      }
    }
    if (!changed) { return TRUE; }
  }
  /* there must be a loop in the graph */
  return FALSE;
}


/* ======================================================================
  			         checkdomain
   ====================================================================== */

/* Temporarily impose the relation "value" between boxes "i" and "j",
 * and check whether a feasible assignment of coordinates exists which
 * respects all currently imposed relations. 
 *   If the relation cannot be satisfied, it is removed from the domain
 * and pushed to a stack, so that it can be restored upon backtracking.
 */

void checkdomain(allinfo *a, int i, int j, int n, box *f, int value)
{
  if (domain[i][j][value] == FALSE) return; /* not allowed in any case */
  relation[i][j] = value;
  if (findcoordinates(a, n, f) == FALSE) {
    modifyandpush(i, j, value, TRUE);
  } 
}


/* ======================================================================
  			         reducedomain
   ====================================================================== */

/* Constraint propagation algorithm. For each relation in the domain of
 * boxes "i" and "j", check if the relation has the posibility of being
 * satisfied. If some of the relations cannot be satisfied any more, they
 * are removed from the domain (and pushed to a stack, so that they can
 * be restored when the master search algorithm backtracks). If only one
 * relation remains in the domain, the relation is imposed at this node
 * and all descendant nodes.
 */

boolean reducedomain(allinfo *a, int n, box *f)
{
  register int i, j, k, l, m;

  m = 0;
  for (i = 0; i < n-1; i++) {
    for (j = i+1; j < n-1; j++) {
      if (relation[i][j] == UNDEF) {
        checkdomain(a, i, j, n, f, LEFT);
        checkdomain(a, i, j, n, f, RIGHT);
        checkdomain(a, i, j, n, f, UNDER);
        checkdomain(a, i, j, n, f, ABOVE);
        checkdomain(a, i, j, n, f, FRONT);
        checkdomain(a, i, j, n, f, BEHIND);
        relation[i][j] = UNDEF;
        for (k = LEFT, l = 0; k < UNDEF; k++) {
          if (domain[i][j][k]) { l++; m = k; }
        }
        if (l == 0) return FALSE;
        if (l == 1) { modifyandpush(i, j, m, FALSE); }
      }
    } 
  }
  return TRUE;
}


/* ======================================================================
  			         recpack
   ====================================================================== */

/* Recursive algorithm based on constraint programming used for assigning
 * relative positions to each pair of boxes. Each pair of boxes initially 
 * has an associated relation with domain LEFT, RIGHT, UNDER, ABOVE, FRONT, 
 * BEHIND. In each iteration of the algorithm a pair of boxes "i" and "j"
 * is assigned the relation "rel". Constraint propagation is then used to 
 * decrease the domains of remaining relations. 
 *   If it is not possible to assign coordinates to the boxes such that the 
 * currently imposed relations between pairs of boxes are respected, we 
 * backtrack.
 *   If each pair of boxes has been assigned a relation, and it is possible
 * to assign coordinates to the boxes such that the currently imposed 
 * relations between pairs of boxes are respected, we save the solution 
 * and return.
 *   Otherwise, constraint propagation is used to decrease the domains
 * of relations corresponding to each pairs of boxes. If a domain only
 * contains a single relation, the relation is fixed.
 *   The recursive step selects the next pair of boxes following "i" and "j"
 * and repeatedly assigns each relation from the domain to the relation 
 * variable.
 */

void recpack(allinfo *a, int i, int j, int n, box *f, int rel)
{
  int i1, j1;
  domainpair *pos;
  boolean feas;

  if (stopped) return;
  a->iter3d++;
  if ((a->iter3d == a->maxiter) && (a->maxiter != 0)) terminate = TRUE;
  a->subiterat++; 
  if (a->subiterat == IUNIT) { 
    a->subiterat = 0;
    a->iterat++; check_iterlimit(a->iterat, a->iterlimit);
    check_timelimit(a->timelimit);
  }
  if (terminate) return;

  relation[i][j] = rel;

  for (i1 = 0, j1 = 0; i1 != i && j1 != j; ) {
    i1++; if (i1 >= j1) { i1 = 0; j1++; }
    if (relation[i1][j1] == UNDEF) error("relation error %d %d\n", i1, j1);
  }  

  feas = findcoordinates(a, n, f); 
  if (!feas) return;

  if ((i == n-2) && (j == n-1)) { 
    feasible = TRUE;
    terminate = TRUE;
    memcpy(a->fsol, f, sizeof(box) * n); 
    return;
  }

  pos = dompos;
  feas = reducedomain(a, n, f);
  if (feas) {
    i++; if (i >= j) { i = 0; j++; }
    bblevel++;
    rel = relation[i][j];
    if (domain[i][j][LEFT ]) recpack(a, i, j, n, f, LEFT);
    if (domain[i][j][RIGHT]) recpack(a, i, j, n, f, RIGHT);
    if (domain[i][j][UNDER]) recpack(a, i, j, n, f, UNDER);
    if (domain[i][j][ABOVE]) recpack(a, i, j, n, f, ABOVE);
    if (domain[i][j][FRONT]) recpack(a, i, j, n, f, FRONT);
    if (domain[i][j][BEHIND])recpack(a, i, j, n, f, BEHIND);
    relation[i][j] = rel;
    bblevel--;
  }
  popdomains(pos);
}


/* ======================================================================
				 general_pack
   ====================================================================== */

/* General packing procedure, which tests whether boxes f..l can be packed
 * into a single bin. The algorithm is based on constraint programming, where
 * each pair of boxes initially has an associated relation with domain LEFT, 
 * RIGHT, UNDER, ABOVE, FRONT, BEHIND. Then the recursive algorithm "recpack" 
 * is called, which repeatedly tries to assign the relation a value, using
 * constraint propagation to decrease the domains of remaining boxes.
 */

boolean general_pack(allinfo *a, box *f, box *l)
{
  register int i, j, k, n;

  dompos = domstack;
  domend = domstack + STACKDEPTH;
  feasible = FALSE;
  terminate = FALSE;
  bblevel = 1;
  n = l-f+1;
  if (n > a->exactn) a->exactn = n;

  for (i = 0; i < n; i++) {
    for (j = 0; j < n; j++) {
      relation[i][j] = UNDEF;
      for (k = LEFT; k < UNDEF; k++) {
        domain[i][j][k] = TRUE;
      }
    }
  }
  domain[0][1][RIGHT ] = FALSE;
  domain[0][1][ABOVE ] = FALSE;
  domain[0][1][BEHIND] = FALSE;

  recpack(a, 0, 0, n, f, UNDEF); 
  return feasible;
}


/* ======================================================================
                                onebin_general
   ====================================================================== */

/* Check if boxes f..l can be packed into a single bin using general 
 * packing. If "fast" is TRUE, the problem is only solved heuristically,
 * hence if the algorithm returns FALSE we cannot rule out the posibility
 * of packing all boxes into the bin.
 */

boolean onebin_general(allinfo *a, box *f, box *l, boolean fast)
{
  boolean solution;
  double t1, t2;

  /* check time limit */
  if (stopped) return FALSE;

  a->iter3d = 0;
  a->maxiter = (fast ? MAXITER : 0); /* limited or infinitly many */
  a->exactcall++;

  /* calling general pack */
  timer(&t1);
  solution = general_pack(a, f, l);
  if (solution) checksol(a, f, l);
  timer(&t2);
  a->genertime += t2 - t1;
  return solution;
}


/* ======================================================================
				envelope
   ====================================================================== */

/* Find the two-dimensional envelope of the boxes given by extreme */
/* points f to l. */

void envelope(point *f, point *l, point *s1, point **sm,
	     itype W, itype H, itype D, itype RW, itype RH, itype cz, 
             point **ll, int *nz, stype *area)
{
  register point *i, *s, *t;
  register itype x, xx, y, z, ix, iy, iz, mz;
  register stype sum;

  /* find corner points and area */
  x = xx = z = 0; y = H; sum = 0; mz = D;
  for (i = t = f, s = s1; i != l; i++) {
    iz = i->z; if (iz <= cz) continue;
    if (iz < mz) mz = iz; /* find minimum next z coordinate */
    ix = i->x; if (ix <= x) {
      if (iz > z) { *t = *i; t++; } 
      continue;
    }
    iy = i->y;
    if ((x <= RW) && (iy <= RH)) { 
      s->x = x; s->y = iy; s->z = cz; s++; 
      sum += (x - xx) * (ptype) y; 
      y = iy; xx = x;
    }
    x = ix; z = iz; *t = *i; t++;
  }
  if (y != 0) sum += (W - xx) * (ptype) y;
  *sm = s-1;
  *area = sum;
  *nz = mz;
  *ll = t-1;
}

/* ======================================================================
			     checkdom
   ====================================================================== */

/* The 3D envelope is found by deriving a number of 2D envelopes. This */
/* may however introduce some "false" corner points, which are marked */
/* by the following algorithm. */

void checkdom(point *s1, point *sl, point *sm)
{
  register point *s, *t, *u;

  if (sl == s1-1) return;
  for (s = s1, t = sl+1, u = sm+1; t != u; t++) {
    while (s->x < t->x) { s++; if (s > sl) return; }
    if ((s->x == t->x) && (s->y == t->y)) t->z = 0;
  } 
}


/* ======================================================================
			     removedom
   ====================================================================== */

/* Remove "false" corner points marked by algorithm "checkdom". */

point *removedom(point *s1, point *sl)
{
  register point *i, *m, *k;
 
  for (i = k = s1, m = sl+1; i != m; i++) {
    if (i->z == 0) continue; 
    *k = *i; k++; 
  }
  return k-1;
}

/* ======================================================================
			     initboxes
   ====================================================================== */

/* Initialize boxes. Already placed boxes define extreme points, and thus */
/* they are placed into the list fc,..,lc. Not placed boxes are used to */
/* derive minimum dimensions. */

void initboxes(box *f, box *l, point *fc, point **lc, 
               int *minw, int *minh, int *mind)
{
  register box *j, *m;
  register point *k;
  register int minx, miny, minz;

  minx = *minw; miny = *minh; minz = *mind; 
  for (j = f, k = fc, m = l+1; j != m; j++) {
    if (j->k) { /* defines an extreme box */
      k->x = j->x+j->w; k->y = j->y+j->h; k->z = j->z+j->d; k++;
    } else { /* free box */
      if (j->w < minx) minx = j->w;
      if (j->h < miny) miny = j->h;
      if (j->d < minz) minz = j->d;
    }
  }
  *minw = minx; *minh = miny; *mind = minz; 
  *lc = k-1;
}


/* ======================================================================
			      findplaces
   ====================================================================== */

/* Find all corner points, where a new box may be placed as well as the */
/* volume of the "envelope" occupied by already placed boxes. Already */
/* placed boxes are given by f,..,l, while a list of possible placings */
/* is returned in s1,..,sm. The return value of the procedure is an upper */
/* bound on the possible filling of this bin. */

stype findplaces(allinfo *a, box *f, box *l, 
                 point *s1, point **sm, stype fill)
{
  register point *k;
  int minw, minh, mind, W, H, D, RW, RH, z, zn;
  point *sk, *lc, *sl, *st, *s0;
  stype vol, area;
  point fc[MAXBOXES+1];

  /* select boxes which are chosen, and find min dimensions of unchosen */
  minw = W = a->W; minh = H = a->H; mind = D = a->D; 
  initboxes(f, l, fc, &lc, &minw, &minh, &mind);

  /* sort the boxes according to max y (first) max x (second) */
  if (lc >= fc) psortdecr(fc, lc); /* order decreasing */

  /* for each z-coordinate find the 2D envelope */
  vol = 0; sl = s1-1; sk = s1; s0 = NULL;
  RW = W - minw; RH = H - minh; 
  lc++; k = lc; k->x = W+1; k->y = 0; k->z = a->D+1;
  for (z = 0; z != D; z = zn) {
    /* find 2D envelope for all boxes which cover *z */
    envelope(fc, lc+1, sl+1, &st, W, H, D, RW, RH, z, &lc, &zn, &area);
    if (zn + mind > D) zn = D; /* nothing fits between zn and D */
    vol += area * (ptype) (zn - z); /* update volume */
    checkdom(sk, sl, st); /* check for dominance */
    sk = sl+1; sl = st; 
    if (z == 0) s0 = sl;
  }
  *sm = removedom(s0+1, sl);
  return fill + (a->BVOL - vol); /* bound is curr filling + all free vol */
}


/* ======================================================================
				branch
   ====================================================================== */

/* Recursive algorithm for solving a knapsack filling of a single bin. */
/* In each iteration, the set of feasible positions for placing a new */
/* box is found, and an upper bound on the filling is derived. If the */
/* bound indicates that an improved solution still may be obtained, every */
/* box is tried to be placed at every feasible position, before calling */
/* the algorithm recursively. */

void branch(allinfo *a, box *f, box *l, int miss, stype fill)
{
  register box *i;
  register point *s;
  int d;
  stype bound;
  point *sl, s1[9*MAXBOXES];

  if (stopped) return;
  a->iter3d++;
  if ((a->iter3d == a->maxiter) && (a->maxiter != 0)) terminate = TRUE;
  if (a->iter3d % 1000 == 0) check_timelimit(a->timelimit);
  a->subiterat++; 
  if (a->subiterat == IUNIT) { 
    a->subiterat = 0;
    a->iterat++; check_iterlimit(a->iterat, a->iterlimit);
  }
  if (terminate) return;

  /* find min/max dimensions of remaining boxes */
  if (miss == 0) {
    /* none left -> good: save solution */
    memcpy(a->fsol, f, sizeof(box) * DIF(f,l));
    a->maxfill = a->BVOL; terminate = TRUE; a->miss = miss;
  } else {
    /* check if better filling */
    if (fill > a->maxfill) {
      memcpy(a->fsol, f, sizeof(box) * DIF(f,l)); 
      a->maxfill = fill; a->miss = miss;
    }

    /* find bound and positions to place new boxes */
    bound = findplaces(a, f, l, s1, &sl, fill);

    if (bound > a->maxfill) {
      /* for each position in S, try to place an box there */
      for (s = s1; s != sl+1; s++) {
	for (i = f, d = 0; i != l+1; i++) {
	  if (i->k) continue; /* already chosen */

	  /* see if box fits at position s */
	  if ((int) (s->x) + i->w > a->W) continue;
	  if ((int) (s->y) + i->h > a->H) continue;
	  if ((int) (s->z) + i->d > a->D) continue;

	  /* place box and call recursively */
	  i->k = TRUE; i->x = s->x; i->y = s->y; i->z = s->z;
	  branch(a, f, l, miss - 1, fill + i->vol);
	  i->k = FALSE; i->x = i->y = i->z = 0; d++;
	  if (d == a->mcut) break; /* terminate after mcut branches */
	  if (terminate) break;
	}
      }
    }
  }
}


/* ======================================================================
				mcut_heuristic
   ====================================================================== */

/* Knapsack filling of a single bin, solved heuristically. The heuristic */
/* is based on the exact algorithm for knapsack filling a single bin, where */
/* only a limited number of sub-nodes are considered at every branching node */
/* (the so-called m-cut approach) */
 
void mcut_heuristic(allinfo *a)
{
  box *f, *l, *i, *j, *m;
  int b, n;

  /* initialize boxes */
  for (i = a->fbox, m = a->lbox+1; i != m; i++) {
    i->bno = i->x = i->y = i->z = 0; i->k = FALSE;
  }

  /* fill bins one by one */
  f = a->fbox; l = a->lbox;
  for (b = 1; ; b++) {
    /* fill one bin */
    for (i = f; i <= l; i++) i->k = FALSE; /* box not chosen */
    a->iter3d = 0;
    a->maxfill = 0;
    a->miss = DIF(f,l);
    a->maxiter = 5*MAXITER;
    terminate = FALSE;
    n = DIF(f,l);
    a->mcut = 2;
    if (n < 15) a->mcut = 3;
    if (n < 10) a->mcut = 4;
    branch(a, f, l, n, 0);

    /* copy solution */
    for (i = a->fsol, j = f, m = l+1; j != m; i++, j++) *j = *i;

    /* remove chosen boxes */
    for (i = f; i <= l;) if (i->k) { i->bno = b; SWAP(i,l); l--; } else i++;

    /* check if finished */
    if (l == f-1) break;
  }
  if (b < a->zmcut) a->zmcut = b; /* save solution */
  if (a->zmcut < a->z) savesol(a, a->fbox, a->lbox, a->zmcut);
}


/* ======================================================================
				mcut3_heuristic
   ====================================================================== */

/* Knapsack filling of a single bin, solved heuristically. Three different */
/* rotations of the problem are considered, and the best found solution */
/* is selected. */

void mcut3_heuristic(allinfo *a)
{
  int i;
  double t1, t2;
 
  timer(&t1); 
  a->zmcut = a->n;  /* very bad lower bound */
  for (i = WDIM; i <= DDIM; i++) {
    mcut_heuristic(a);
    rotate_solution(a, a->fopt, a->lopt);
    rotate_problem(a, a->fbox, a->lbox);
  }
  timer(&t2);
  a->mhtime = t2 - t1;
}


/* **********************************************************************
   **********************************************************************
 	       branch-and-bound for 3D bin-packing problem
   **********************************************************************
   ********************************************************************** */


/* ======================================================================
				 fits
   ====================================================================== */

/* The routine "fitsm" checks whether a given subset of boxes fits into */
/* a single bin. To improve performance, specialized algorithms are derived */
/* for cases with two to three boxes */

boolean fits2(box *i, box *j, itype W, itype H, itype D)
{
  /* all coordinates are initialized to zero, so just adjust changes! */
  /* the 2-box solution is always guillotine cuttable */
  if (i->w + j->w <= W) { j->x = i->w; return TRUE; }
  if (i->h + j->h <= H) { j->y = i->h; return TRUE; }
  if (i->d + j->d <= D) { j->z = i->d; return TRUE; }
  return FALSE;
}


boolean fits2p(box *i, box *j, itype W, itype H, itype D)
{
  if (i->w + j->w <= W) return TRUE;
  if (i->h + j->h <= H) return TRUE;
  if (i->d + j->d <= D) return TRUE;
  return FALSE;
}


boolean fits3(box *i, box *j, box *k, itype W, itype H, itype D)
{
  box *t;
  itype w, h, d, r;

  /* all coordinates are initialized to zero, so just adjust changes! */
  /* the 3-box solution can either be cut by guillotine cuts */
  for (r = 1; r <= 3; r++) {
    /* cut (i,j) and (k) in one of three dimensions */
    w = W - k->w; h = H - k->h; d = D - k->d; 
    if ((i->w<=w) && (j->w<=w) && fits2(i,j,w,H,D)) { k->x = w; return TRUE; } 
    if ((i->h<=h) && (j->h<=h) && fits2(i,j,W,h,D)) { k->y = h; return TRUE; } 
    if ((i->d<=d) && (j->d<=d) && fits2(i,j,W,H,d)) { k->z = d; return TRUE; } 
    t = i; i = j; j = k; k = t;
  }


  /* (xi,yi,zi) = (0,0,0); (xj,yj,zj) = (wi,0,0); (xk,yk,zk) = (0,hi,dj) */
  if ((i->w+j->w <= W) && (i->h+k->h <= H) && (j->d+k->d <= D)) {
    j->x = i->w; k->y = i->h; k->z = j->d; return TRUE;
  } 
  /* (xi,yi,zi) = (0,0,0); (xj,yj,zj) = (wk,0,di); (xk,yk,zk) = (0,hi,0) */
  if ((j->w+k->w <= W) && (i->h+k->h <= H) && (i->d+j->d <= D)) {
    j->x = k->w; j->z = i->d; k->y = i->h; return TRUE;
  } 
  /* (xi,yi,zi) = (0,0,0); (xj,yj,zj) = (0,hi,dk); (xk,yk,zk) = (wi,0,0) */
  if ((i->w+k->w <= W) && (i->h+j->h <= H) && (k->d+j->d <= D)) {
    j->y = i->h; j->z = k->d; k->x = i->w; return TRUE;
  } 
  /* (xi,yi,zi) = (0,0,0); (xj,yj,zj) = (0,hi,0); (xk,yk,zk) = (wj,0,di) */
  if ((j->w+k->w <= W) && (i->h+j->h <= H) && (k->d+i->d <= D)) {
    j->y = i->h; k->x = j->w; k->z = i->d; return TRUE;
  } 
  /* (xi,yi,zi) = (0,0,0); (xj,yj,zj) = (wi,0,0); (xk,yk,zk) = (0,hj,di) */ 
  if ((i->w+j->w <= W) && (j->h+k->h <= H) && (i->d+k->d <= D)) { 
    j->x = i->w; k->y = j->h; k->z = i->d; return TRUE;
  } 
  /* (xi,yi,zi) = (0,0,0); (xj,yj,zj) = (0,0,di); (xk,yk,zk) = (wi,hj,0) */ 
  if ((i->w+k->w <= W) && (j->h+k->h <= H) && (i->d+j->d <= D)) { 
    j->z = i->d; k->x = i->w; k->y = j->h; return TRUE;
  } 
  return FALSE; 
}  


boolean fitsm(allinfo *a, box *t, box *k, boolean fast)
{
  boolean fits;
  ntype lb;

  lb = bound_two(a, t, k);
  if (lb > 1) return FALSE;
  a->exfill++; fits = FALSE;
  fits = onebin_general(a, t, k, fast); 
  return fits;
}


/* ======================================================================
				onebin_decision
   ====================================================================== */

/* The following procedure checks whether a new box "j" fits into the the */
/* bin "bno" (together with already placed boxes in the bin). If the answer */
/* is "no" then definitly it is not possible to place the box into the bin. */

boolean onebin_decision(allinfo *a, box *j, int bno)
{
  register box *i, *k, *m;
  box t[MAXBOXES];
  boolean fits;
  int size;

  for (i = a->fbox, m = j, k = t-1; i != m; i++) {
    if (i->bno == bno) { k++; *k = *i; k->x = k->y = k->z = 0; k->ref = i; }
  }
  k++; *k = *j; k->x = k->y = k->z = 0; k->ref = j; k->k = TRUE; 

  size = DIF(t,k); 
  switch (size) {
    case 0: error("no boxes in onebin_decision");
    case 1: fits = TRUE; k->x = k->y = k->z = 0; break;
    case 2: fits = fits2(t, k, a->W, a->H, a->D); break;
    case 3: fits = fits3(t, t+1, k, a->W, a->H, a->D); break;
    default: fits = fitsm(a, t, k, FALSE); break;
  }
  if (size <= 3) {
    a->subiterat++; 
    if (a->subiterat == IUNIT) { 
      a->subiterat = 0;
      a->iterat++; check_iterlimit(a->iterat, a->iterlimit);
    }
  }

  if (fits) {
    /* copy solution back */
    for (i = t, m = k+1; i != m; i++) {
      k = i->ref; k->x = i->x; k->y = i->y; k->z = i->z; k->k = TRUE;
    }
  }
  return fits;
}


/* ======================================================================
				onebin_heuristic
   ====================================================================== */

/* This is a heuristic version of "onebin_decision". If the answer is "yes" */
/* then a heuristic solution has been found where boxes f,..,l fit into a  */
/* bin. If the answer is "no" then a filling may still be possible, but it */
/* was not found by the heuristic. */ 

boolean onebin_heuristic(allinfo *a, box *f, box *l)
{
  box *i, *m;
  boolean fits;
 
  for (i = f, m = l+1; i != m; i++) { i->x = i->y = i->z = 0; }
  switch (DIF(f,l)) {
    case 0: error("no boxes in onebin_heuristic");
    case 1: fits = TRUE; break;
    case 2: fits = fits2(f, l, a->W, a->H, a->D); break;
    case 3: fits = fits3(f, f+1, l, a->W, a->H, a->D); break;
    default: fits = fitsm(a, f, l, TRUE); break;
  }
  return fits;
}


/* ======================================================================
				try_close
   ====================================================================== */

/* A bin may be closed if no more boxes fit into it. The present version */
/* uses a more advanced criterion: First, the set of boxes which fit into */
/* bin "bno" is derived. This is done, by testing whether each box (alone) */
/* fits together with the already placed boxes in the bin. Having derived */
/* the set of additional boxes that (individually) fits into the bin, it is */
/* tested whether a solution exists where all the additional boxes are */
/* placed in the bin. If this is the case, we have found a optimal placing */
/* of the additional boxes, and thus we may close the bin. */

boolean try_close(allinfo *a, box **curr, ntype bno,
                 box *oldf, box **oldl, box **oldlc, ntype *oldnoc, 
                 boolean *oldclosed, int level)
{
  register box *j, *m, *k, *r, *i, *s;
  register stype vol;
  box f[MAXBOXES];
  ntype h, n, b;
  boolean didclose, fits;

  if (level > MAXCLOSE) return FALSE;
  i = *curr; didclose = FALSE;
  for (b = 1; b <= bno; b++) {
    if (i > a->lbox) break;
    if (a->closed[b]) continue;
    for (j = a->fbox, m = i, k = f, n = 0, vol = 0; j != m; j++) {
      if (j->bno == b) { *k = *j; k->ref = j; k++; n++; vol += j->vol; }
    }
    if (n == 0) error("bin with no boxes");
    if (vol < a->BVOL/2) continue;
    for (j = i, h = 0, m = a->lbox+1; j != m; j++) {
      if ((j->no > a->n) || (j->no < 1)) error("bad no");
      fits = onebin_decision(a, j, b);
      if (fits) { *k = *j; k->ref = j; k++; h++; vol += j->vol; }
      if (vol > a->BVOL) break;
    }
    if (vol > a->BVOL) continue;
    if (onebin_heuristic(a, f, k-1)) {
      if (!didclose) { /* take backup of table when first bin closed */
        memcpy(oldclosed, a->closed, sizeof(boolean)*(bno+1));
        memcpy(oldf, a->fbox, sizeof(box)*DIF(a->fbox,a->lbox));
        *oldl = a->lbox; *oldlc = a->lclosed; *oldnoc = a->noc;
      }
      a->closed[b] = TRUE; s = a->lclosed; didclose = TRUE;
      a->noc++; if (a->noc > a->maxclose) a->maxclose = a->noc;
      for (j = f; j != k; j++) { 
        r = j->ref; r->bno = b; r->k = TRUE; 
        r->x = j->x; r->y = j->y; r->z = j->z; 
      }
      for (j = k = a->fbox, m = a->lbox+1; j != m; j++) {
        if (j->bno == b) { s++; *s = *j; } else { *k = *j; k++; } 
      }
      a->lbox = k-1; a->lclosed = s;
      i -= n; /* reposition current box */
    }
  }
  *curr = i;
  return didclose;
}


/* ======================================================================
                                free_close
   ====================================================================== */

/* Reopen a closed bin when backtracking. */

void free_close(allinfo *a, ntype bno, 
                box *oldf, box *oldl, box *oldlc, ntype oldnoc,
                boolean *oldclosed)
{
  a->lbox = oldl; a->lclosed = oldlc; a->noc = oldnoc;
  memcpy(a->fbox, oldf, sizeof(box)*DIF(a->fbox,oldl));
  memcpy(a->closed, oldclosed, sizeof(boolean)*(bno+1));
}


/* ======================================================================
				rec_binpack
   ====================================================================== */

/* Recursive algorithm for 3D Bin-packing Problem. In each iteration, the */
/* next box "i" is assigned to every open bin, as well as to a new bin. */

void rec_binpack(allinfo *a, box *i, int bno, ntype lb, int level)
{
  box of[MAXBOXES], *ol, *ox;
  boolean ocl[MAXBOXES];
  ntype b, oc;
  boolean more;

  if (bno >= a->z) return; /* used too many bins */
  if (a->z == a->lb) return; /* optimal solution found */
  a->subnodes++;
  if (a->subnodes == IUNIT) { a->subnodes = 0; a->nodes++; }
  check_nodelimit(a->nodes, a->nodelimit);
  check_iterlimit(a->iterat, a->iterlimit);
  check_timelimit(a->timelimit);
  if (stopped) return;

  if (i == a->lbox+1) {
    /* all boxes assigned, must be better solution */
    savesol(a, a->fbox, a->lbox, bno);
  } else {
    more = try_close(a, &i, bno, of, &ol, &ox, &oc, ocl, level);
    if (i == a->lbox+1) { /* all boxes went into closed bins */
      savesol(a, a->fbox, a->lbox, bno);
    } else {
      if (more) lb = a->noc + bound_two(a, a->fbox, a->lbox);
      if (lb < a->z) {
        for (b = 1; b <= bno; b++) {
          if (a->closed[b]) continue; /* cannot add to closed bin */
            if (onebin_decision(a, i, b)) {
  	    i->bno = b;
	    rec_binpack(a, i+1, bno, lb, level+1);
	    i->bno = 0;
  	  }
        }
        i->bno = bno+1; i->x = i->y = i->z = 0;
        a->closed[i->bno] = FALSE;
        rec_binpack(a, i+1, bno+1, lb, level+1);
        i->bno = 0;
      }
    }
    /* restore */
    if (more) free_close(a, bno, of, ol, ox, oc, ocl);
  }
}


/* **********************************************************************
   **********************************************************************
			     Main procedure
   **********************************************************************
   ********************************************************************** */

/* ======================================================================
				clearboxes
   ====================================================================== */

void clearboxes(allinfo *a)
{
  box *i, *m;

  for (i = a->fbox, m = a->lbox+1; i != m; i++) {
    i->x = i->y = i->z = i->bno = 0; i->k = FALSE; i->vol = VOL(i);
  }
  /* sort nonincreasing volume */
  qsort(a->fbox, (m-a->fbox), sizeof(box), (funcptr) vcomp);
}


/* ======================================================================
				copyboxes
   ====================================================================== */

void copyboxes(allinfo *a, int *w, int *h, int *d, int W, int H, int D)
{
  box *i, *m;
  int k;

  for (i = a->fbox, m = a->lbox+1, k = 0; i != m; i++, k++) {
    i->no = k+1; i->w = w[k]; i->h = h[k]; i->d = d[k];
    if ((w[k] < 1) || (w[k] > W)) error("bad w\n");
    if ((h[k] < 1) || (h[k] > H)) error("bad h\n");
    if ((d[k] < 1) || (d[k] > D)) error("bad d\n");
  }

  clearboxes(a);
}


/* ======================================================================
				returnboxes
   ====================================================================== */

void returnboxes(allinfo *a, int *x, int *y, int *z, int *bno)
{
  box *i, *m;
  int k;

  for (i = a->fopt, m = a->lopt+1; i != m; i++) {
    k = i->no-1; x[k] = i->x; y[k] = i->y; z[k] = i->z; bno[k] = i->bno;
  }
}


/* ======================================================================
				binpack3d
   ====================================================================== */

void binpack3d(int n, int W, int H, int D,
               int *w, int *h, int *d, 
               int *x, int *y, int *z, int *bno,
               int *lb, int *ub, 
               int nodelimit, int iterlimit, int timelimit, 
               int *nodeused, int *iterused, int *timeused)
{
  allinfo a;
  box t0[MAXBOXES], t1[MAXBOXES], t2[MAXBOXES], t3[MAXBOXES];
  boolean cl[MAXBOXES];
  
  /* start the timer */ 
  timer(NULL); stopped = FALSE; 
 
  /* copy info to a structure */
  if (n+1 > MAXBOXES) error("too big instance");
  a.n = n; a.W = W; a.H = H; a.D = D;
  a.fbox     = t0; 
  a.lbox     = a.fbox + a.n - 1; 
  a.fsol     = t1;
  a.lsol     = a.fsol + a.n - 1;
  a.fopt     = t2;
  a.lopt     = a.fopt + a.n - 1;
  a.fclosed  = t3;
  a.lclosed  = a.fclosed - 1;
  a.noc      = 0;
  a.closed   = cl;
  a.BVOL     = W * (ptype) H * D;
  a.maxfill  = 0;
  a.exfill   = 0;
  a.nodelimit= 0;
  a.iterlimit= 0;
  a.timelimit= 0;
  a.nodes    = 0;
  a.subnodes = 0;
  a.iterat   = 0;
  a.subiterat= 0;
  a.didpush  = 0;
  a.maxclose = 0;
  a.genertime= 0;
  a.robottime= 0;
  a.z        = a.n+1;

  /* copy boxes to internal structure */
  copyboxes(&a, w, h, d, W, H, D);

  /* find bounds */
  a.bound0 = bound_zero(&a, a.fbox, a.lbox);
  a.bound1 = bound_one(&a, a.fbox, a.lbox);
  a.bound2 = bound_two(&a, a.fbox, a.lbox);
  a.lb = a.bound2;

  /* find heuristic solution */
  dfirst3_heuristic(&a);

  /* initialize search limits for exact search */
  a.nodelimit= nodelimit;
  a.iterlimit= iterlimit;
  a.timelimit= timelimit;

  /* outer tree enummeration */
  clearboxes(&a); /* clear positions */
  rec_binpack(&a, a.fbox, 0, a.lb, 1);
  timer(&(a.time));

  /* check found solution */
  /* checksol(&a, a.fopt, a.lopt); */

  /* copy boxes back to arrays */
  returnboxes(&a, x, y, z, bno);
  *ub = a.z;
  *lb = (stopped ? a.lb : a.z);
  *nodeused = a.nodes;
  *iterused = a.iterat;
  *timeused = a.time * 1000;
}




================================================
FILE: pyshipping/__init__.py
================================================
"""pyShipping contains routines related to shipping and warehousing."""


================================================
FILE: pyshipping/addressvalidation.py
================================================
#!/usr/bin/env python
# encoding: utf-8
"""
addressvalidation.py - check the validity of addresses

Should once integrate with
http://www.deutschepost.de/dpag?tab=1&skin=hi&check=yes&lang=de_DE&xmlFile=link1015574_1021
http://www.isogmbh.de/leistungen/dataquality-management/adressvalidierung.html
or http://opengeodb.hoppe-media.com/


Created by Maximillian Dornseif on 2009-09-03.
Copyright (c) 2009, 2010 HUDORA. All rights reserved.
"""

import unittest


def validate(adr, servicelevel=1):
    """Validates an address and returns a possibly corrected address.

    'adr' should be a object conforming to the address protocol
        - see http://cybernetics.hudora.biz/projects/wiki/AddressProtocol
    'servicelevel' can be an integer with the following values:
    1 - generic validation, no money/effort should be spend on correction and suggestions
    2 - TBD.

    returns (status, message, [corrected addresses and variants])

    status can be:
    '10invalid' - address is for sure non deliverable in this form
    '20troubled' - bounced before or is unlikely to be correct - possible alternatives are returned
    '30ok' - likely to work
    '31ok' - likely to work but was corrected
    '40verified' - we are sure it works
    """

    adr['land'] = adr['land'].strip()
    adr['plz'] = adr['plz'].strip()

    if adr['land'] != 'IE' and not adr['plz']:
        return ('10invalid', 'Postleitzahl fehlt', [adr])

    if adr['land'] == 'DE' and len(adr.get('plz', '')) != 5:
        return ('10invalid', 'Postleitzahl fehlerhaft', [adr])

    return ('30ok', '', [adr])


class AddressvalidationTests(unittest.TestCase):
    """Tests for the address validation facility."""

    def setUp(self):
        """Set up test address base."""
        self.address = {'name1': 'HUDORA GmbH',
                        'name2': 'Abt. Cybernetics',
                        'strasse': 'Jägerwald 13',
                        'land': 'DE',
                        'plz': '42897',
                        'ort': 'Remscheid',
                        'tel': '+49 2191 60912 0',
                        'fax': '+49 2191 60912 50',
                        'mobil': '+49 175 00000xx',
                        'email': 'nobody@hudora.de'}

    def test_good_address(self):
        """Test if correct addresses are considered correct."""
        self.assertEqual(validate(self.address)[0], '30ok')
        self.assertEqual(validate(self.address)[1], '')

    def test_missing_zip(self):
        """Test if correct addresses are considered correct."""
        self.address['plz'] = ''
        self.assertEqual(validate(self.address)[0], '10invalid')

    def test_short_zip(self):
        """Test if correct addresses are considered correct."""
        self.address['plz'] = '123'
        self.assertEqual(validate(self.address)[0], '10invalid')

    def test_long_zip(self):
        """Test if correct addresses are considered correct."""
        self.address['plz'] = '12345 Rade'
        self.assertEqual(validate(self.address)[0], '10invalid')


if __name__ == '__main__':
    unittest.main()


================================================
FILE: pyshipping/binpack.py
================================================
#!/usr/bin/env python
# encoding: utf-8
"""
binpack.py

Created by Maximillian Dornseif on 2010-08-16.
Copyright (c) 2010 HUDORA. All rights reserved.
"""


import binpack_simple


def binpack(packages, bin=None, iterlimit=5000):
    return binpack_simple.binpack(packages, bin, iterlimit)


def test(func):
    import time
    from package import Package
    fd = open('testdata.txt')
    vorher = 0
    nachher = 0
    start = time.time()
    counter = 0
    for line in fd:
        counter += 1
        if counter > 450:
            break
        packages = [Package(pack) for pack in line.strip().split()]
        if not packages:
            continue
        bins, rest = func(packages)
        if rest:
            print "invalid data", rest, line
        else:
            vorher += len(packages)
            nachher += len(bins)
    print time.time() - start,
    print vorher, nachher, float(nachher) / vorher * 100


if __name__ == '__main__':
    print "py",
    test(binpack)


import time
from pyshipping.package import Package


================================================
FILE: pyshipping/binpack_simple.py
================================================
#!/usr/bin/env python
# encoding: utf-8
"""
binpack_simple.py

This code implemnts 3D bin packing in pure Python

Bin packing in this context is calculating the best way to store a number of differently sized boxes in a
number of fixed sized "bins". It is what usually happens in a Warehouse bevore shipping.

The Algorithm has a simple fit first approach, but can archive relative good results because it tries
different rectangular rotations of the packages. Since the Algorithm can't interate over all possible
combinations we use a heuristic approach.

For a few dozen packages it reaches adaequate runtime. Below are the results calculated about a set of
500 real world packing problems.

Binsize     Runtime                 Recuction in shipped Packages
600x400x400 31.5993559361 4970 2033 40.9054325956
600x445x400 31.5596890450 4970 1854 37.3038229376
600x500x400 29.1432909966 4970 1685 33.9034205231


On the datasets we operate on we can archive comparable preformance to academic higly optimized C code
like David Pisinger's 3bpp:

     Runtime                 Recuction in shipped Packages
py   11.3468761444 2721 1066 39.1767732451
3bpp 9.95857691765 2721 1086 39.9117971334

The Python implementation is somewhat slower but can archive slightly better packing results on our
datasets.


Created by Maximillian Dornseif on 2010-08-14.
Copyright (c) 2010 HUDORA. All rights reserved.
"""


import time
import random


def packstrip(bin, p):
    """Creates a Strip which fits into bin.

    Returns the Packages to be used in the strip, the dimensions of the strip as a 3-tuple
    and a list of "left over" packages.
    """
    # This code is somewhat optimized and somewhat unreadable
    s = []                # strip
    r = []                # rest
    ss = sw = sl = 0      # stripsize
    bs = bin.heigth       # binsize
    sapp = s.append       # speedup
    rapp = r.append       # speedup
    ppop = p.pop          # speedup
    while p and (ss <= bs):
        n = ppop(0)
        nh, nw, nl = n.size
        if ss + nh <= bs:
            ss += nh
            sapp(n)
            if nw > sw:
                sw = nw
            if nl > sl:
                sl = nl
        else:
            rapp(n)
    return s, (ss, sw, sl), r + p


def packlayer(bin, packages):
    strips = []
    layersize = 0
    layerx = 0
    layery = 0
    binsize = bin.width
    while packages:
        strip, (sizex, stripsize, sizez), rest = packstrip(bin, packages)
        if layersize + stripsize <= binsize:
            packages = rest
            if not strip:
                # we were not able to pack anything
                break
            layersize += stripsize
            layerx = max([sizex, layerx])
            layery = max([sizez, layery])
            strips.extend(strip)
        else:
            # Next Layer please
            packages = strip + rest
            break
    return strips, (layerx, layersize, layery), packages


def packbin(bin, packages):
    packages.sort()
    layers = []
    contentheigth = 0
    contentx = 0
    contenty = 0
    binsize = bin.length
    while packages:
        layer, (sizex, sizey, layersize), rest = packlayer(bin, packages)
        if contentheigth + layersize <= binsize:
            packages = rest
            if not layer:
                # we were not able to pack anything
                break
            contentheigth += layersize
            contentx = max([contentx, sizex])
            contenty = max([contenty, sizey])
            layers.extend(layer)
        else:
            # Next Bin please
            packages = layer + rest
            break
    return layers, (contentx, contenty, contentheigth), packages


def packit(bin, originalpackages):
    packedbins = []
    packages = sorted(originalpackages)
    while packages:
        packagesinbin, (binx, biny, binz), rest = packbin(bin, packages)
        if not packagesinbin:
            # we were not able to pack anything
            break
        packedbins.append(packagesinbin)
        packages = rest
    # we now have a result, try to get a better result by rotating some bins

    return packedbins, rest


# In newer Python versions these van be imported:
# from itertools import permutations
def product(*args, **kwds):
    # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
    # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
    pools = map(tuple, args) * kwds.get('repeat', 1)
    result = [[]]
    for pool in pools:
        result = [x + [y] for x in result for y in pool]
    for prod in result:
        yield tuple(prod)


def permutations(iterable, r=None):
    pool = tuple(iterable)
    n = len(pool)
    r = n if r is None else r
    for indices in product(range(n), repeat=r):
        if len(set(indices)) == r:
            yield tuple(pool[i] for i in indices)


class Timeout(Exception):
    pass


def allpermutations_helper(permuted, todo, maxcounter, callback, bin, bestpack, counter):
    if not todo:
        return counter + callback(bin, permuted, bestpack)
    else:
        others = todo[1:]
        thispackage = todo[0]
        for dimensions in set(permutations((thispackage[0], thispackage[1], thispackage[2]))):
            thispackage = Package(dimensions, nosort=True)
            if thispackage in bin:
                counter = allpermutations_helper(permuted + [thispackage], others, maxcounter, callback,
                                                 bin, bestpack, counter)
            if counter > maxcounter:
                raise Timeout('more than %d iterations tries' % counter)
        return counter


def trypack(bin, packages, bestpack):
    bins, rest = packit(bin, packages)
    if len(bins) < bestpack['bincount']:
        bestpack['bincount'] = len(bins)
        bestpack['bins'] = bins
        bestpack['rest'] = rest
    if bestpack['bincount'] < 2:
        raise Timeout('optimal solution found')
    return len(packages)


def allpermutations(todo, bin, iterlimit=5000):
    random.seed(1)
    random.shuffle(todo)
    bestpack = dict(bincount=len(todo) + 1)
    try:
        # First try unpermuted
        trypack(bin, todo, bestpack)
        # now try permutations
        allpermutations_helper([], todo, iterlimit, trypack, bin, bestpack, 0)
    except Timeout:
        pass
    return bestpack['bins'], bestpack['rest']


def binpack(packages, bin=None, iterlimit=5000):
    """Packs a list of Package() objects into a number of equal-sized bins.

    Returns a list of bins listing the packages within the bins and a list of packages which can't be
    packed because they are to big."""
    if not bin:
        bin = Package("600x400x400")
    return allpermutations(packages, bin, iterlimit)


def test():
    fd = open('testdata.txt')
    vorher = 0
    nachher = 0
    start = time.time()
    for line in fd:
        packages = [Package(pack) for pack in line.strip().split()]
        if not packages:
            continue
        bins, rest = binpack(packages)
        if rest:
            print "invalid data", rest, line
        else:
            vorher += len(packages)
            nachher += len(bins)
    print time.time() - start,
    print vorher, nachher, float(nachher) / vorher * 100


if __name__ == '__main__':
    import cProfile
    cProfile.run('test()')


from pyshipping.package import Package


================================================
FILE: pyshipping/carriers/__init__.py
================================================
"""Function for specific freight carriers."""


================================================
FILE: pyshipping/carriers/dpd/__init__.py
================================================
"""Functions specific to DPD/Geopost."""


================================================
FILE: pyshipping/carriers/dpd/georoute.py
================================================
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
georoute.py - get DPD related routng information

Originally coded by md, cleand up and extended by jmv and then again reworked by md.
Copyright 2006, 2007 HUDORA GmbH. Published under a BSD License.
"""

import os
import os.path
import gzip
import logging
import sqlite3


ROUTETABLES_BASE = os.path.join(os.path.split(os.path.abspath(__file__))[0], 'georoutetables')
ROUTES_DB_BASE = '/tmp/dpdroutes'


# Quelle: http://de.wikipedia.org/wiki/Liste_der_Kfz-Nationalitätszeichen
ISO2CAR = {
    'AT': 'A',
    'BE': 'B',
    'FR': 'F',
}


class InvalidFormatError(Exception):
    """Invalid input file format."""
    pass


class GeorouteException(Exception):
    """Base class for all routing exceptions"""
    pass


class CountryError(GeorouteException):
    """Unknown country."""
    pass


class DepotError(GeorouteException):
    """Unknown depot."""
    pass


class ServiceError(GeorouteException):
    """Unknown service."""
    pass


class TranslationError(GeorouteException):
    """Cannot translate city and country to postcode."""
    pass


class RoutingDepotError(GeorouteException):
    """Unknown routing depot."""
    pass


class NoRouteError(GeorouteException):
    """Route not found."""
    pass


class Parcel(object):
    """Parcel destination data."""

    # depreciated
    def __init__(self, depot='142', service='101', country='DE', city=None, postcode=None):
        import warnings
        warnings.warn("Parcel() is deprecated", DeprecationWarning, stacklevel=2)

        self.service = service
        self.country = country
        self.city = city
        self.postcode = postcode


class Destination(object):
    """Parcel destination data."""

    def __init__(self, country='DE', postcode=None, city=None, service='101'):
        self.service = service
        self.country = country
        self.city = city
        self.postcode = postcode


class Route:
    """Output of the routing algorithm."""

    def __init__(self, d_depot, o_sort, d_sort, grouping_priority, barcode_id,
                 iata_code, service_text, service_mark, country, serviceinfo, countrynum,
                 routingtable_version, postcode):
        self.d_depot = d_depot
        self.o_sort = o_sort
        self.d_sort = d_sort
        self.grouping_priority = grouping_priority
        self.barcode_id = barcode_id
        self.iata_code = iata_code
        self.service_text = service_text
        self.service_mark = service_mark
        self.country = country
        self.serviceinfo = serviceinfo
        self.countrynum = countrynum
        self.routingtable_version = routingtable_version
        self.postcode = postcode

    def __unicode__(self):
        output = u"""Output parameters:
Country: %s
D-Depot: %s
O-Sort: %s
D-Sort: %s
Grouping priority: %s
Barcode ID: %s
ITA Code: %s
Service Text: %s""" % (self.country, self.d_depot, self.o_sort, self.d_sort,
                       self.grouping_priority, self.barcode_id, self.iata_code,
                       self.service_text)
        if self.service_mark:
            output += "\nService Mark: %s" % self.service_mark
        if self.iata_code:
            output += "\nIATA Code: %s" % self.iata_code
        if self.serviceinfo:
            output += "\nService Info: %s" % self.serviceinfo

        return output

    def __repr__(self):
        return repr(vars(self))

    def routingdata(self):
        return {'o_sort': self.o_sort, 'd_sort': self.d_sort,
                'd_depot': self.d_depot, 'country': self.country,
                'service_text': self.service_text, 'serviceinfo': self.serviceinfo}


def _readfile(filename):
    """Read file line-by-line skipping comments."""
    if os.path.exists(filename + '.gz'):
        fhandle = gzip.GzipFile(filename + '.gz')
    else:
        fhandle = file(filename)
    for line in fhandle:
        line = line.strip().decode('latin1')
        if line.startswith('#'):
            continue
        yield line.split('|')


class RouteData(object):
    """More convenient representation of the georoute data."""

    def __init__(self, routingdepot='0142'):
        """Routingdepot the depot from where you are sending."""
        self.routingdepot = routingdepot
        self.routingdepotgroups = ''
        self.routingdepotcountry = ''

        services = os.path.join(ROUTETABLES_BASE, 'SERVICE')
        self.version = None
        for line in file(services):
            if line.startswith('#Version: '):
                self.version = line.split(':')[1].strip()
                break
        if self.version is None:
            raise InvalidFormatError("There's no version in the SERVICE file")

        self.countries = {}
        for line in _readfile(os.path.join(ROUTETABLES_BASE, 'COUNTRY')):
            isonum, isoname = line[:2]
            self.countries[isoname.upper()] = isonum

        self.depots = {}
        for line in _readfile(os.path.join(ROUTETABLES_BASE, 'DEPOTS')):
            geopostdepotnumber = line[0]
            self.depots[geopostdepotnumber] = tuple(line)

        self.services = {}
        for line in _readfile(os.path.join(ROUTETABLES_BASE, 'SERVICE')):
            servicecode = line[0]
            self.services[servicecode] = tuple(line)

        self.serviceinfo = {}
        for line in _readfile(os.path.join(ROUTETABLES_BASE, 'SERVICEINFO.DE')):
            servicecode = line[0]
            self.serviceinfo[servicecode] = line[1]

        filename = ROUTES_DB_BASE + ('-%s-%s.db' % (routingdepot, self.version))
        self.db = sqlite3.connect(filename)

        self.read_depots(ROUTETABLES_BASE)
        self.read_locations(ROUTETABLES_BASE)
        self.read_routes(ROUTETABLES_BASE)

    def read_depots(self, path):
        """Read DEPOTS file and save all the information in a
        SQLite database."""
        c = self.db.cursor()

        c.execute("""SELECT COUNT(*)
                     FROM sqlite_master
                     WHERE type = 'table' AND name = 'depots'""")
        if not c.fetchone()[0]:
            logging.info("regenerating depots table")
            c.execute("""CREATE TABLE depots
            (DepotNumber TEXT PRIMARY KEY,
             IATACode TEXT,
             GroupId TEXT,
             Name1 TEXT,
             Name2 TEXT,
             Address1 TEXT,
             Address2 TEXT,
             Postcode TEXT,
             CityName TEXT,
             Country TEXT,
             Phone TEXT,
             Fax TEXT,
             Mail TEXT,
             Web TEXT)""")

            for line in _readfile(os.path.join(path, 'DEPOTS')):
                c.execute("""INSERT INTO depots
                             VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
                          line[:14])
                if line[0] == self.routingdepot:
                    self.routingdepotgroups = line[2]
                    self.routingdepotgrouplist = line[2].split(',')
                    self.routingdepotcountry = line[9]
            c.execute('VACUUM;')

    def read_locations(self, path):
        """Read LOCATION file and save all the information in a SQLite database."""
        c = self.db.cursor()

        c.execute("""SELECT COUNT(*)
                     FROM sqlite_master
                     WHERE type='table' AND name='location'""")
        if not c.fetchone()[0]:
            logging.info("regenerating location table")
            c.execute("""CREATE TABLE location
            (Area TEXT,
             City TEXT,
             Country TEXT,
             Postcode TEXT)""")

            for line in _readfile(os.path.join(path, 'LOCATION.DE')):
                c.execute('INSERT INTO location VALUES (?,?,?,?)',
                          line[:4])
            c.execute('VACUUM;')

    def read_routes(self, path):
        """Read ROUTES file and save all the information in a SQLite database."""
        # self.db = sqlite3.connect(ROUTES_DB)
        c = self.db.cursor()

        c.execute("""SELECT COUNT(*)
                     FROM sqlite_master
                     WHERE type='table' AND name='routes'""")
        if not c.fetchone()[0]:
            logging.info("regenerating routes table")
            c.execute("""CREATE TABLE routes
            (id INTEGER PRIMARY KEY,
            DestinationCountry TEXT,
            BeginPostCode TEXT,
            EndPostCode TEXT,
            ServiceCodes TEXT,
            RoutingPlaces TEXT,
            SendingDate TEXT,
            OSort TEXT,
            DDepot TEXT,
            GroupingPriority TEXT,
            DSort TEXT,
            BarcodeID TEXT)""")

            c.execute("""SELECT COUNT(*)
                         FROM sqlite_master
                         WHERE type='table' AND name='routedepots'""")
            if not c.fetchone()[0]:
                logging.info("regenerating routedepots table")
                c.execute("""CREATE TABLE routedepots
                (route INTEGER,
                 depot TEXT)""")

            c.execute("PRAGMA synchronous=OFF;")
            c.execute("PRAGMA temp_store=MEMORY;")
            i = 1
            for line in _readfile(os.path.join(path, 'ROUTES')):
                services = self.expand_services(line[3])
                c.execute('INSERT INTO routes VALUES (?,?,?,?,?,?,?,?,?,?,?,?)',
                          [i] + line[:3] + [services] + line[4:-1])
                self.expand_depots(i, line[4], c)
                i += 1

            c.execute("CREATE INDEX routes_DestinationCountry ON routes(DestinationCountry)")
            c.execute("CREATE INDEX routes_BeginPostCode ON routes(BeginPostCode)")
            c.execute("CREATE INDEX routes_EndPostCode ON routes(EndPostCode)")
            c.execute("CREATE INDEX routedepots_route ON routedepots(route)")
            c.execute("CREATE INDEX routedepots_depot ON routedepots(depot)")
            c.execute('VACUUM;')  # also commits the database

    def expand_services(self, services):
        """Expand services list."""
        services_list = []
        for service in services.split(','):
            if len(service) > 4:
                start = int(service[1:4])
                end = int(service[4:])
                for i in range(start, end + 1):
                    services_list.append(unicode(i))
            else:
                services_list.append(service[1:])

        return ','.join(services_list)

    def expand_depots(self, route, depots, c):
        """Parse depots list and generate route->depots relationship."""
        # but only four "our" depot.
        # if you change the self.routingdepot, you have to rebuild the database
        if depots == '':
            c.execute("""INSERT INTO routedepots(route, depot) VALUES (?, ?)""", (route, depots))
            return

        for depot in depots.split(','):
            if depot.startswith('C'):
                if depot[1:] == self.routingdepotcountry:
                    c.execute("""INSERT INTO routedepots(route, depot) VALUES(?, ?)""", (route,
                                                                                         self.routingdepot))
            elif depot.startswith('D'):
                if len(depot) > 5:
                    start = int(depot[1:5])
                    end = int(depot[5:])
                    for i in range(start, end + 1):
                        if ("%04d" % i) == self.routingdepot:
                            c.execute("""INSERT INTO routedepots(route, depot) VALUES(?, ?)""",
                                      (route, self.routingdepot))
                else:
                    if depot[1:5] == self.routingdepot:
                        c.execute("""INSERT INTO routedepots(route, depot) VALUES(?, ?)""",
                                  (route, depot[1:5]))
            elif depot.startswith('G'):
                if depot[1:] in self.routingdepotgroups:
                    c.execute("INSERT INTO routedepots(route, depot) VALUES(?, ?)",
                              (route, self.routingdepot))
            else:
                raise InvalidFormatError("Unable to parse depot '%s'" % depot)

    def get_countrynum(self, isoname):
        """Return country ISO code."""
        if not isoname.upper() in self.countries:
            raise CountryError("Country %s unknown" % isoname)
        return self.countries[isoname.upper()]

    def get_depot(self, depotnumber):
        """Return depot."""
        if not depotnumber in self.depots:
            raise DepotError("Depot %s unknown" % depotnumber)
        return self.depots[depotnumber]

    def get_service(self, servicecode):
        """Return service."""
        if not servicecode in self.services:
            raise ServiceError("Service %s unknown" % servicecode)
        return self.services[servicecode]

    def get_servicetext(self, servicecode):
        """Return service info to be printed on label."""
        if not servicecode in self.serviceinfo:
            return ''
        return self.serviceinfo[servicecode]

    def translate_location(self, city, country):
        """Return postcode for given city and country."""
        cur = self.db.cursor()
        cur.execute("SELECT Postcode FROM location WHERE City=? AND Country=?", (city, country))
        rows = cur.fetchall()
        if not rows:
            raise TranslationError("Cannot find postcode for location %s, %s" % (city, country))
        return rows[0][0]


class Router(object):
    """Routes parcels."""

    def __init__(self, data):
        self.route_data = data
        self.db = self.route_data.db

    def route(self, parcel):
        """Find route."""

        self.current_subset = []
        self.conditions = ['1=1']
        self.cleanup_postcode(parcel)

        self.select_country(parcel)
        if parcel.postcode is None:
            parcel.postcode = self.route_data.translate_location(parcel.city, parcel.country)
            self.subsetstack.append(self.subset)

        self.select_postcode(parcel)
        self.select_service(parcel)
        self.select_depot(parcel)
        # Sending date is not used yet, according to documentation

        # If there are several routes, always use the first one.
        # In prior versions, an exception was raised instead.
        if len(self.current_subset) >= 1:
            rows = self.select_routes("1=1")
            (service_text, service_mark) = self.route_data.get_service(parcel.service)[1:3]
            depot = self.route_data.get_depot(rows[0][8])
            iata_code = depot[1]
            country = depot[9]
            if not country:
                country = parcel.country
            serviceinfo = self.route_data.get_servicetext(parcel.service)

            return Route(rows[0][8], rows[0][7], rows[0][10], rows[0][9], rows[0][11],
                         iata_code, service_text, service_mark, country,
                         serviceinfo, self.route_data.get_countrynum(country),
                         self.route_data.version, parcel.postcode)
        raise NoRouteError("No route found for %r|%r|%r" % \
              (parcel.country, parcel.postcode, parcel.service))

    def add_condition(self, condition):
        self.conditions.append(condition)

    def select_routes(self, condition, params=()):
        """Find routes matching condition and currently selected subset of the routes table.
        If routes are found, save their ids for narrowing future searches.
        If no routes are found, do not change current subset.
        """

        subsetcondition = ' AND '.join(self.conditions)
        cur = self.db.cursor()
        cur.execute("SELECT * FROM routes WHERE %s AND %s" % (subsetcondition, condition), params)
        rows = cur.fetchall()

        # Save matched rows if there were any results
        if rows:
            self.add_condition(condition)
        self.current_subset = [unicode(row[0]) for row in rows]
        return rows

    def select_country(self, parcel):
        """Select all routes with the given country."""
        rows = self.select_routes("DestinationCountry='%s'" % (parcel.country.upper().replace("'", ''), ))
        if not rows:
            raise CountryError("Country %s unknown" % parcel.country)

    def cleanup_postcode(self, parcel):
        """Removes spaces and country prefixes from postcodes."""

        if not parcel.postcode:
            return
        parcel.postcode = parcel.postcode.replace(' ', '').strip()
        if parcel.postcode.startswith('-'):
            parcel.postcode = parcel.postcode[1:]
            self.cleanup_postcode(parcel)
        if parcel.postcode.upper().startswith(parcel.country.upper()):
            parcel.postcode = parcel.postcode[len(parcel.country):]
            self.cleanup_postcode(parcel)
        if parcel.country.upper() in ISO2CAR:
            if parcel.postcode.upper().startswith(ISO2CAR[parcel.country.upper()]):
                parcel.postcode = parcel.postcode[len(ISO2CAR[parcel.country.upper()]):]
                self.cleanup_postcode(parcel)
        if parcel.country.upper() == 'DE' and parcel.postcode.upper().startswith('CH-'):
            parcel.country = 'CH'
            parcel.postcode = parcel.postcode[2:]
            self.cleanup_postcode(parcel)
        if parcel.country.upper() == 'DE' and parcel.postcode.upper().startswith('BE-'):
            parcel.country = 'BE'
            parcel.postcode = parcel.postcode[2:]
            self.cleanup_postcode(parcel)
        if parcel.country.upper() == 'DE' and parcel.postcode.upper().startswith('B-'):
            parcel.country = 'BE'
            parcel.postcode = parcel.postcode[1:]
            self.cleanup_postcode(parcel)
        if parcel.country.upper() == 'DE' and parcel.postcode.upper().startswith('AT-'):
            parcel.country = 'AT'
            parcel.postcode = parcel.postcode[2:]
            self.cleanup_postcode(parcel)
        if parcel.country.upper() == 'DE' and parcel.postcode.upper().startswith('A-'):
            parcel.country = 'AT'
            parcel.postcode = parcel.postcode[1:]
            self.cleanup_postcode(parcel)

    def select_postcode(self, parcel):
        """Select all routes matching the given postcode."""

        # direct match
        rows = self.select_routes("BeginPostCode='%s'" % parcel.postcode.replace("'", ''))
        if not rows:
            # range
            rows = self.select_routes("BeginPostCode<='%s' AND EndPostCode>='%s'"
                                       % (parcel.postcode.replace("'", ''), parcel.postcode.replace("'", '')))
        if not rows:
            # catch all
            rows = self.select_routes("BeginPostCode=''")
        if not rows:
            raise NoRouteError("Postcode %r|%r unknown" % (parcel.country, parcel.postcode))

    def select_service(self, parcel):
        """Select all routes with the given service code."""

        # we have to redo postcode query as a backoff strategy
        self.conditions.pop()
        postcodequeries = ["BeginPostCode='%s'" % parcel.postcode.replace("'", ''),
            "BeginPostCode<='%s' AND EndPostCode>='%s'" % (parcel.postcode.replace("'", ''),
                                                           parcel.postcode.replace("'", '')),
            "BeginPostCode=''"]
        for postcodequery in postcodequeries:
            rows = self.select_routes("%s AND ServiceCodes LIKE '%%%s%%'" % (postcodequery, parcel.service))
            if not rows:
                # catch all
                rows = self.select_routes("%s AND ServiceCodes = ''" % (postcodequery))
            if rows:
                break
        if not rows:
            raise ServiceError("No route for service found %r|%r|%r unknown" % \
                (parcel.country, parcel.postcode, parcel.service))

    def select_depot(self, parcel):
        """Select all routes with the given depot."""
        subset = "route IN (%s)" % ','.join([unicode(route) for route in self.current_subset])
        cur = self.db.cursor()
        cur.execute("SELECT route FROM routedepots WHERE depot=%s AND %s" % (self.route_data.routingdepot,
                                                                             subset))
        rows = cur.fetchall()
        if not rows:
            cur.execute("SELECT route FROM routedepots WHERE %s" % (subset))
            rows = cur.fetchall()
        if not rows:
            raise RoutingDepotError("No route found for %r|%r|%r|%r|%r" % \
                  (parcel.country, parcel.postcode, parcel.service, self.route_data.routingdepot, subset))
        self.current_subset = [unicode(row[0]) for row in rows]


def get_route_without_cache(country=None, postcode=None, city=None, servicecode='101'):
    router = Router(RouteData())
    return router.route(Destination(country, postcode, city))


def get_route(country=None, postcode=None, city=None, servicecode='101'):
    # this includes somewhat overly complex caching
    filename = ROUTES_DB_BASE + ('_cache.db')
    cache_db = sqlite3.connect(filename, isolation_level=None)
    cur = cache_db.cursor()
    # ensure table exists
    cur.execute("""SELECT COUNT(*)
                 FROM sqlite_master
                 WHERE type = 'table' AND name = 'routing_cache'""")
    if not cur.fetchone()[0]:
        logging.info("regenerating cache table")
        cur.execute("""CREATE TABLE routing_cache
        (country_postcode_servicecode TEXT PRIMARY KEY,
        d_depot TEXT,
        o_sort TEXT,
        d_sort TEXT,
        grouping_priority TEXT,
        barcode_id TEXT,
        iata_code TEXT,
        service_text TEXT,
        service_mark TEXT,
        country TEXT,
        serviceinfo TEXT,
        countrynum TEXT,
        routingtable_version TEXT,
        postcode TEXT
        )""")

    # check if entry is cached
    cur.execute("SELECT * FROM routing_cache WHERE country_postcode_servicecode='%s'"
                 % ("%s_%s_%s" % (country, postcode, servicecode)))
    rows = cur.fetchall()
    if not rows:
        # nothing found
        route = get_route_without_cache(country, postcode, city, servicecode)
        cur.execute("""INSERT INTO routing_cache
                             VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
                             ("%s_%s_%s" % (country, postcode, servicecode),
                             route.d_depot,
                             route.o_sort,
                             route.d_sort,
                             route.grouping_priority,
                             route.barcode_id,
                             route.iata_code,
                             route.service_text,
                             route.service_mark,
                             route.country,
                             route.serviceinfo,
                             route.countrynum,
                             route.routingtable_version,
                             route.postcode))
        # For some reason closing the cache generated occasionally runtime errors.
        # cache_db.close()
    else:
        if len(rows) > 1:
            raise RuntimeError("to many cache hits!")
        route = Route(*rows[0][1:])
    return route


# compability layer to old georoute code prior to huLOG revision 1710

def find_route(depot, servicecode, land, plz):
    """Legacy method - to be removed."""

    import warnings
    warnings.warn("georoute.find_route() is deprecated use get_route() instead",
                  DeprecationWarning, stacklevel=2)

    if unicode(depot) != '0142':
        raise RuntimeError("wrong depot")
    return get_route(unicode(land), unicode(plz), servicecode=unicode(servicecode))


================================================
FILE: pyshipping/carriers/dpd/georoute_test.py
================================================
#!/usr/bin/env python
# -*- encoding: utf-8 -*-

"""Test routing resolver for DPD. Coded by jmv, extended by md"""

import time
import unittest
from pyshipping.carriers.dpd.georoute import get_route, get_route_without_cache
from pyshipping.carriers.dpd.georoute import RouteData, Router, Destination
from pyshipping.carriers.dpd.georoute import ServiceError, CountryError, TranslationError


class TestCase(unittest.TestCase):
    """Provide sophisticated dictionary comparision."""

    def assertDicEq(self, dict1, dict2):
        """Asserts if two dicts are unequal.

        Raise an Exception which mentions the different entries of those dicts.
        """
        if dict1 != dict2:
            difference = set()
            for key, value in dict1.items():
                if dict2.get(key) != value:
                    difference.add((key, value, dict2.get(key)))
            for key, value in dict2.items():
                if dict1.get(key) != value:
                    difference.add((key, dict1.get(key), value))
            raise self.failureException, \
                ('%r != %r: %s' % (dict1, dict2, list(difference)))


class RouteDataTest(TestCase):

    def setUp(self):
        self.data = RouteData()
        self.db = self.data.db

    def test_version(self):
        self.assertEqual(self.data.version, '20110905')

    def test_get_country(self):
        self.assertRaises(CountryError, self.data.get_countrynum, 'URW')
        self.assertEqual(self.data.get_countrynum('JP'), '392')
        self.assertEqual(self.data.get_countrynum('DE'), '276')
        self.assertEqual(self.data.get_countrynum('de'), '276')

    def test_read_depots(self):
        c = self.db.cursor()
        c.execute("""SELECT * FROM depots WHERE DepotNumber=?""", ('0015', ))
        rows = c.fetchall()
        self.assertEqual(1, len(rows))
        self.assertEqual((u'0015', u'', u'', u'Betriebsgesellschaft DPD GmbH',
                          u'', u'Otto-Hahn-Strasse 5', u'', u'59423', u'Unna', u'DE',
                          u'+49-(0) 23 03-8 88-0', u'+49-(0) 23 03-8 88-31', u'', u''),
                         rows[0])

    def test_expand_depots(self):
        c = self.db.cursor()
        c.execute("""SELECT id
                     FROM routes
                     WHERE DestinationCountry='DE' AND BeginPostCode='42477'""")
        rows = c.fetchall()
        # Interestingly we sometimes get two routes
        # self.assertEqual(1, len(rows))
        route = rows[0][0]
        c.execute("SELECT depot FROM routedepots WHERE route=?", (route, ))
        rows = c.fetchall()
        self.assertEqual(1, len(rows))

    def test_get_service(self):
        self.assertEqual(self.data.get_service('180'), ('180', 'AM1-NO', '', '022,160', ''))
        self.assertRaises(ServiceError, self.data.get_service, '100000')

    def test_get_servicetext(self):
        text = self.data.get_servicetext('185')
        self.assertEqual('DPD 10:00 Unfrei / ex works', text)

    def test_translate_location(self):
        self.assertEqual('1', self.data.translate_location('Dublin', 'IE'))
        self.assertRaises(TranslationError, self.data.translate_location, 'Cahir', 'IE')


class RouterTest(TestCase):

    def setUp(self):
        self.data = RouteData()
        self.router = Router(self.data)

    def test_known_routes_de(self):
        route = self.router.route(Destination(postcode='42477'))
        self.assertDicEq(route.routingdata(), {'d_depot': u'0142', 'serviceinfo': '', 'country': u'DE',
                                               'd_sort': u'65', 'o_sort': u'42', 'service_text': u'D'})
        route = self.router.route(Destination(postcode='42897'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0142', 'serviceinfo': '', 'country': 'DE',
                                               'd_sort': '15', 'o_sort': '42', 'service_text': 'D'})
        route = self.router.route(Destination(postcode='53111'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0150', 'serviceinfo': '', 'country': 'DE',
                                               'd_sort': u'205', 'o_sort': '50', 'service_text': 'D'})
        route = self.router.route(Destination(postcode='53111', country='DE'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0150', 'serviceinfo': '', 'country': 'DE',
                                               'd_sort': u'205', 'o_sort': '50', 'service_text': 'D'})
        route = self.router.route(Destination('DE', '53111'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0150', 'serviceinfo': '', 'country': 'DE',
                                               'd_sort': u'205', 'o_sort': '50', 'service_text': 'D'})
        route = self.router.route(Destination('DE', '53111', city='Bonn'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0150', 'serviceinfo': '', 'country': 'DE',
                                               'd_sort': u'205', 'o_sort': '50', 'service_text': 'D'})
        route = self.router.route(Destination('DE', '53111', 'Bonn'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0150', 'serviceinfo': '', 'country': 'DE',
                                               'd_sort': u'205', 'o_sort': '50', 'service_text': 'D'})

    def test_known_routes_world(self):
        route = self.router.route(Destination(postcode='66400', country='FR'))
        self.assertDicEq(route.routingdata(), {'d_depot': u'0470', 'serviceinfo': '', 'country': u'FR',
                                               'd_sort': u'U50', 'o_sort': u'16', 'service_text': u'D'})
        route = self.router.route(Destination('FR', '66400', 'Ceret'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0470', 'serviceinfo': '', 'country': 'FR',
                                               'd_sort': 'U50', 'o_sort': '16', 'service_text': 'D'})
        route = self.router.route(Destination('BE', '3960', 'Bree/Belgien'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0532', 'serviceinfo': '', 'country': 'BE',
                                               'd_sort': u'A353', 'o_sort': '52', 'service_text': 'D'})
        route = self.router.route(Destination('CH', '6005', 'Luzern'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0616', 'serviceinfo': '', 'country': 'CH',
                                               'd_sort': '40', 'o_sort': '78', 'service_text': 'D'})
        route = self.router.route(Destination('AT', '1210', 'Wien'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0622', 'serviceinfo': '', 'country': 'AT',
                                               'd_sort': '10', 'o_sort': '62', 'service_text': 'D'})
        route = self.router.route(Destination('AT', '4820', 'Bad Ischl'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0624', 'serviceinfo': '', 'country': 'AT',
                                               'd_sort': '63', 'o_sort': '62', 'service_text': 'D'})
        route = self.router.route(Destination('AT', '7400', 'Oberwart'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0628', 'serviceinfo': '', 'country': 'AT',
                                               'd_sort': u'3270', 'o_sort': '62', 'service_text': 'D'})
        route = self.router.route(Destination('AT', '4400', 'Steyr'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0624', 'serviceinfo': '', 'country': 'AT',
                                               'd_sort': '70', 'o_sort': '62', 'service_text': 'D'})
        route = self.router.route(Destination('AT', '1220', 'Wien'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0622', 'serviceinfo': '', 'country': 'AT',
                                               'd_sort': '30', 'o_sort': '62', 'service_text': 'D'})
        route = self.router.route(Destination('AT', '6890', 'Lustenau'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0627', 'serviceinfo': '', 'country': 'AT',
                                               'd_sort': '01', 'o_sort': '62', 'service_text': 'D'})
        route = self.router.route(Destination('BE', '3520', 'ZONHOVEN'))
        self.assertDicEq(route.routingdata(), {'d_depot': u'0532', 'serviceinfo': '', 'country': u'BE',
                                               'd_sort': u'A369', 'o_sort': u'52', 'service_text': u'D'})
        route = self.router.route(Destination('BE', '4890', 'Thimister'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0532', 'serviceinfo': '', 'country': 'BE',
                                               'd_sort': u'B326', 'o_sort': '52', 'service_text': 'D'})
        route = self.router.route(Destination('CH', '8305', 'Dietlikon'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0615', 'serviceinfo': '', 'country': 'CH',
                                               'd_sort': '77', 'o_sort': '78', 'service_text': 'D'})
        route = self.router.route(Destination('CH', '4051', 'Basel'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0610', 'serviceinfo': '', 'country': 'CH',
                                               'd_sort': u'16', 'o_sort': '78', 'service_text': 'D'})
        route = self.router.route(Destination('CH', '8808', 'Pf<C3><A4>ffikon'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0615', 'serviceinfo': '', 'country': 'CH',
                                               'd_sort': '71', 'o_sort': '78', 'service_text': 'D'})
        route = self.router.route(Destination('DK', '9500', 'Hobro'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0504', 'serviceinfo': '', 'country': 'DK',
                                               'd_sort': u'405', 'o_sort': '20', 'service_text': 'D'})
        # Lichtenstein is routed via CH
        route = self.router.route(Destination('LI', '8399', 'Windhof / Luxembourg'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0617', 'serviceinfo': '', 'country': 'CH',
                                               'd_sort': '', 'o_sort': '78', 'service_text': 'D'})
        route = self.router.route(Destination('LI', '9495', 'Triesen'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0617', 'serviceinfo': '', 'country': 'CH',
                                               'd_sort': '', 'o_sort': '78', 'service_text': 'D'})
        route = self.router.route(Destination('LI', '8440', 'Steinfort'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0617', 'serviceinfo': '', 'country': 'CH',
                                               'd_sort': '', 'o_sort': '78', 'service_text': 'D'})
        route = self.router.route(Destination('CZ', '41742', 'Krupka 1'))
        self.assertDicEq(route.routingdata(), {'d_depot': '1380', 'serviceinfo': '', 'country': 'CZ',
                                               'd_sort': '21', 'o_sort': '10', 'service_text': 'D'})
        route = self.router.route(Destination('ES', '28802', 'Alcala de Henares (Madrid)'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0728', 'serviceinfo': '', 'country': 'ES',
                                               'd_sort': '01', 'o_sort': '16', 'service_text': 'D'})
        route = self.router.route(Destination('ES', '28010', 'Madrid'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0728', 'serviceinfo': '', 'country': 'ES',
                                               'd_sort': '01', 'o_sort': '16', 'service_text': 'D'})
        route = self.router.route(Destination('FR', '84170', 'MONTEUX'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0447', 'serviceinfo': '', 'country': 'FR',
                                               'd_sort': u'S65', 'o_sort': '16', 'service_text': 'D'})
        route = self.router.route(Destination('FR', '91044', 'Evry Cedex'))
        self.assertDicEq(route.routingdata(), {'d_depot': u'0408', 'serviceinfo': '', 'country': u'FR',
                                               'd_sort': u'S61', 'o_sort': u'50', 'service_text': u'D'})

    def test_difficult_routingdepots(self):
        route = self.router.route(Destination('AT', '3626', 'H<C3><BC>nibach'))
        self.assertDicEq(route.routingdata(), {'d_depot': u'0623', 'serviceinfo': u'', 'country': 'AT',
                                               'd_sort': u'01', 'o_sort': u'62', 'service_text': u'D'})
        route = self.router.route(Destination('AT', '8225', 'P<C3><B6>llau'))
        self.assertDicEq(route.routingdata(), {'d_depot': u'0628', 'serviceinfo': u'', 'country': 'AT',
                                               'd_sort': u'1290', 'o_sort': u'62', 'service_text': u'D'})
        route = self.router.route(Destination('AT', '5020', 'Salzburg'))
        self.assertDicEq(route.routingdata(), {'d_depot': u'0625', 'serviceinfo': u'', 'country': 'AT',
                                               'd_sort': u'1000', 'o_sort': u'62', 'service_text': u'D'})
        route = self.router.route(Destination('SE', '65224', 'Karlstad'))
        self.assertDicEq(route.routingdata(), {'d_depot': u'0307', 'serviceinfo': u'', 'country': 'SE',
                                               'd_sort': u'01', 'o_sort': u'20', 'service_text': u'D'})
        route = self.router.route(Destination('AT', '2734', 'Buchberg/Schneeberg'))
        self.assertDicEq(route.routingdata(), {'d_depot': u'0621', 'serviceinfo': u'', 'country': 'AT',
                                               'd_sort': u'64', 'o_sort': u'62', 'service_text': u'D'})

    def test_difficult_service(self):
        route = self.router.route(Destination('AT', '4240', 'Freistadt Österreich'))
        self.assertDicEq(route.routingdata(), {'d_depot': u'0634', 'serviceinfo': '', 'country': u'AT',
                                               'd_sort': u'22', 'o_sort': u'62', 'service_text': u'D'})
        route = self.router.route(Destination('AT', '5101', 'Bergheim bei Salzburg'))
        self.assertDicEq(route.routingdata(), {'d_depot': u'0625', 'serviceinfo': u'', 'country': 'AT',
                                               'd_sort': u'2509', 'o_sort': u'62', 'service_text': u'D'})
        route = self.router.route(Destination('AT', '8230', 'Hartberg'))
        self.assertDicEq(route.routingdata(), {'d_depot': u'0628', 'serviceinfo': u'', 'country': 'AT',
                                               'd_sort': u'1290', 'o_sort': u'62', 'service_text': u'D'})
        route = self.router.route(Destination('AT', '8045', 'Graz/<C3><96>sterreich'))
        self.assertDicEq(route.routingdata(), {'d_depot': u'0628', 'serviceinfo': u'', 'country': 'AT',
                                               'd_sort': u'2840', 'o_sort': u'62', 'service_text': u'D'})

    def test_postcode_with_country(self):
        route = self.router.route(Destination(postcode='FR-66400', country='FR'))
        self.assertDicEq(route.routingdata(), {'d_depot': u'0470', 'serviceinfo': '', 'country': u'FR',
                                               'd_sort': u'U50', 'o_sort': u'16', 'service_text': u'D'})
        route = self.router.route(Destination(postcode='FR 66400', country='FR'))
        self.assertDicEq(route.routingdata(), {'d_depot': u'0470', 'serviceinfo': '', 'country': u'FR',
                                               'd_sort': u'U50', 'o_sort': u'16', 'service_text': u'D'})
        route = self.router.route(Destination(postcode='FR66400', country='FR'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0470', 'serviceinfo': '', 'country': 'FR',
                                               'd_sort': 'U50', 'o_sort': '16', 'service_text': 'D'})
        route = self.router.route(Destination(postcode='F-66400', country='FR'))
        self.assertDicEq(route.routingdata(), {'d_depot': '0470', 'serviceinfo': '', 'country': 'FR',
                                               'd_sort': 'U50', 'o_sort': '16', 'service_text': 'D'})

    def test_postcode_spaces(self):
        route = self.router.route(Destination(postcode='42 477'))
        self.assertDicEq(route.routingdata(), {'o_sort': '42', 'serviceinfo': '', 'country': 'DE',
                                               'd_sort': '65', 'd_depot': '0142', 'service_text': 'D'})
        route = self.router.route(Destination(postcode=' 42477'))
        self.assertDicEq(route.routingdata(), {'d_depot': u'0142', 'serviceinfo': '', 'country': u'DE',
                                               'd_sort': u'65', 'o_sort': u'42', 'service_text': u'D'})
        route = self.router.route(Destination(postcode=' 42477 '))
        self.assertDicEq(route.routingdata(), {'d_depot': '0142', 'serviceinfo': '', 'country': 'DE',
                                               'd_sort': '65', 'o_sort': '42', 'service_text': 'D'})
        # real live sample
        route = self.router.route(Destination('GB', 'GU148HN', 'Hampshire'))
        self.assertDicEq(route.routingdata(), {'d_depot': '1550', 'serviceinfo': '', 'country': 'GB',
                                               'd_sort': '', 'o_sort': '52', 'service_text': 'D'})
        route = self.router.route(Destination('GB', 'GU 14 8HN', 'Hampshire'))
        self.assertDicEq(route.routingdata(), {'d_depot': '1550', 'serviceinfo': '', 'country': 'GB',
                                               'd_sort': '', 'o_sort': '52', 'service_text': 'D'})

    def test_problematic_routes(self):
        # Lichtenstein is problematic because usually it is routed trough Swizerland.
        route = self.router.route(Destination('LI', '8440'))
        self.assertDicEq(route.routingdata(), {'d_depot': u'0617', 'serviceinfo': '', 'country': u'CH',
                                               'd_sort': u'', 'o_sort': u'78', 'service_text': u'D'})
        route = self.router.route(Destination('LI', '8440', 'Steinfort'))
        self.assertDicEq(route.routingdata(), {'d_depot': u'0617', 'serviceinfo': '', 'country': u'CH',
                                               'd_sort': u'', 'o_sort': u'78', 'service_text': u'D'})

    def test_international(self):
        # AR   | 1426  | Buenos Aire
        self.assertDicEq(get_route('AR', '1426').routingdata(),
            {'d_depot': u'0920', 'serviceinfo': u'', 'country': u'AR', 'd_sort': u'', 'o_sort': u'16',
             'service_text': u'D'})
        # AZ   | 1073 | Baku
        self.assertDicEq(get_route('AZ', '1073').routingdata(),
            {'d_depot': u'0918', 'serviceinfo': '', 'country': 'AZ', 'd_sort': u'CDG', 'o_sort': u'16',
             'service_text': u'D'})
        # BE   | 3960 | Bree/Belgien
        self.assertDicEq(get_route('BE', '3960').routingdata(),
            {'d_depot': u'0532', 'serviceinfo': u'', 'country': u'BE', 'd_sort': u'A353', 'o_sort': u'52',
            'service_text': u'D'})
        # BG   | 1766 | Sofia
        self.assertDicEq(get_route('BG', '1766').routingdata(),
            {'d_depot': u'1660', 'serviceinfo': '', 'country': u'BG', 'd_sort': u'', 'o_sort': u'62', 'service_text': u'D'})
        # CH   | 3601 | Thun/Schweiz
        self.assertDicEq(get_route('CH', '3601').routingdata(),
            {'d_depot': u'0612', 'serviceinfo': u'', 'country': u'CH', 'd_sort': u'', 'o_sort': u'78',
            'service_text': u'D'})
        # CZ   | 16300 | Praha 6- Repy
        self.assertDicEq(get_route('CZ', '16300').routingdata(),
            {'d_depot': u'1391', 'serviceinfo': u'', 'country': u'CZ', 'd_sort': u'B854', 'o_sort': u'10',
            'service_text': u'D'})
        # CZ   | 71000 | Ostrava
        self.assertDicEq(get_route('CZ', '71000').routingdata(),
            {'d_depot': u'1384', 'serviceinfo': u'', 'country': u'CZ', 'd_sort': u'412', 'o_sort': u'10',
            'service_text': u'D'})
        # DE   | 04316 | Leipzig
        self.assertDicEq(get_route('DE', '04316').routingdata(),
            {'d_depot': u'0104', 'serviceinfo': '', 'country': u'DE', 'd_sort': u'2CPO', 'o_sort': u'10',
             'service_text': u'D'})
        # DE   | 99974 | M<C3><BC>hlhausen / Th<C3><BC>ringen
        self.assertDicEq(get_route('DE', '99974').routingdata(),
            {'d_depot': u'0234', 'serviceinfo': u'', 'country': u'DE', 'd_sort': u'C015', 'o_sort': u'KK02',
            'service_text': u'D'})
        # DK   | 4000  | Roskilde
        self.assertDicEq(get_route('DK', '4000').routingdata(),
            {'d_depot': u'0500', 'serviceinfo': u'', 'country': u'DK', 'd_sort': u'01', 'o_sort': u'20',
            'service_text': u'D'})
        # DK   | 9500   | Hobro
        self.assertDicEq(get_route('DK', '9500').routingdata(),
            {'d_depot': u'0504', 'serviceinfo': u'', 'country': u'DK', 'd_sort': u'405', 'o_sort': u'20',
            'service_text': u'D'})
        # EE   | 10621  | Tallinn
        self.assertDicEq(get_route('EE', '10621').routingdata(),
            {'d_depot': u'0560', 'serviceinfo': u'', 'country': u'EE', 'd_sort': u'0005', 'o_sort': u'13',
            'service_text': u'D'})
        # ES   | 08227  | Terrassa
        self.assertDicEq(get_route('ES', '08227').routingdata(),
            {'d_depot': u'0708', 'serviceinfo': u'', 'country': u'ES', 'd_sort': u'01', 'o_sort': u'16',
            'service_text': u'D'})
        # FI   | 94700  | Kemi
        self.assertDicEq(get_route('FI', '94700').routingdata(),
            {'d_depot': u'1614', 'serviceinfo': u'', 'country': u'FI', 'd_sort': u'510', 'o_sort': u'15',
            'service_text': u'D'})
        # FR   | 91044  | EVRY-LISSES
        self.assertDicEq(get_route('FR', '91044').routingdata(),
            {'d_depot': u'0408', 'serviceinfo': '', 'country': u'FR', 'd_sort': u'S61', 'o_sort': u'50',
             'service_text': u'D'})
        # GB   | BT387AR              | Carrickfergus
        self.assertDicEq(get_route('GB', 'BT387AR').routingdata(),
            {'d_depot': u'1598', 'serviceinfo': u'', 'country': u'GB', 'd_sort': u'', 'o_sort': u'52',
            'service_text': u'D'})
        # GB   | CB13SW               | Cambridge
        self.assertDicEq(get_route('GB', 'CB13SW').routingdata(),
            {'d_depot': u'1550', 'serviceinfo': u'', 'country': u'GB', 'd_sort': u'', 'o_sort': u'52',
            'service_text': u'D'})
        # GB   | G43 2DX              | Glasgow
        self.assertDicEq(get_route('GB', 'G43 2DX').routingdata(),
            {'d_depot': u'1550', 'serviceinfo': u'', 'country': u'GB', 'd_sort': u'', 'o_sort': u'52',
            'service_text': u'D'})
        # GB   | G432DX               | Glasgow
        self.assertDicEq(get_route('GB', 'G432DX').routingdata(),
            {'d_depot': u'1550', 'serviceinfo': u'', 'country': u'GB', 'd_sort': u'', 'o_sort': u'52',
            'service_text': u'D'})
        # GB   | N41NR                | London
        self.assertDicEq(get_route('GB', 'N41NR').routingdata(),
            {'d_depot': u'1550', 'serviceinfo': u'', 'country': u'GB', 'd_sort': u'', 'o_sort': u'52',
            'service_text': u'D'})
        # GR   | 17341  | Athens
        self.assertDicEq(get_route('GR', '17341').routingdata(),
            {'d_depot': u'1251', 'serviceinfo': u'', 'country': u'GR', 'd_sort': u'', 'o_sort': u'62',
            'service_text': u'D'})
        # GR   | 64200  | Chrisoupoli-KAVALA
        self.assertDicEq(get_route('GR', '64200').routingdata(),
            {'d_depot': u'1251', 'serviceinfo': u'', 'country': u'GR', 'd_sort': u'', 'o_sort': u'62',
            'service_text': u'D'})
        # HR   | 10000 | Zagreb
        self.assertDicEq(get_route('HR', '10000').routingdata(),
            {'d_depot': u'1750', 'serviceinfo': u'', 'country': u'HR', 'd_sort': u'SVI', 'o_sort': u'62',
            'service_text': u'D'})
        # HR   | 44000 | Sisak
        self.assertDicEq(get_route('HR', '44000').routingdata(),
            {'d_depot': u'1750', 'serviceinfo': u'', 'country': u'HR', 'd_sort': u'020', 'o_sort': u'62',
            'service_text': u'D'})
        # HU   | 1121  | Budapest
        self.assertDicEq(get_route('HU', '1121').routingdata(),
            {'d_depot': u'1640', 'serviceinfo': u'', 'country': u'HU', 'd_sort': u'027', 'o_sort': u'62',
            'service_text': u'D'})
        # HU   | 9400  | Sopron
        self.assertDicEq(get_route('HU', '9400').routingdata(),
            {'d_depot': u'1657', 'serviceinfo': u'', 'country': u'HU', 'd_sort': u'684', 'o_sort': u'62',
            'service_text': u'D'})
        # IT   | 34100    | Trieste / Italien
        self.assertDicEq(get_route('IT', '34100').routingdata(),
            {'d_depot': u'0835', 'serviceinfo': u'', 'country': u'IT', 'd_sort': u'01', 'o_sort': u'16',
            'service_text': u'D'})
        # LI   | 09494    | Schaan
        self.assertDicEq(get_route('LI', '09494').routingdata(),
            {'d_depot': u'0617', 'serviceinfo': u'', 'country': u'CH', 'd_sort': u'', 'o_sort': u'78',
            'service_text': u'D'})
        # LI   | 9999     | Wemperhardt
        self.assertDicEq(get_route('LI', '9999').routingdata(),
            {'d_depot': u'0617', 'serviceinfo': u'', 'country': u'CH', 'd_sort': u'', 'o_sort': u'78',
            'service_text': u'D'})
        # LI   | CH-9491  | Ruggell
        self.assertDicEq(get_route('LI', 'CH-9491').routingdata(),
            {'d_depot': u'0617', 'serviceinfo': u'', 'country': u'CH', 'd_sort': u'', 'o_sort': u'78',
            'service_text': u'D'})
        # LI   | 9491  | Ruggell
        self.assertDicEq(get_route('LI', '9491').routingdata(),
            {'d_depot': u'0617', 'serviceinfo': u'', 'country': u'CH', 'd_sort': u'', 'o_sort': u'78',
            'service_text': u'D'})
        # LT   | 3031     | Kaunas
        self.assertDicEq(get_route('LT', '3031').routingdata(),
            {'d_depot': u'0594', 'serviceinfo': '', 'country': u'LT', 'd_sort': u'732', 'o_sort': u'13',
             'service_text': u'D'})
        # LV   | 1039     | Riga
        self.assertDicEq(get_route('LV', '1039').routingdata(),
            {'d_depot': u'0575', 'serviceinfo': u'', 'country': u'LV', 'd_sort': u'R39', 'o_sort': u'13',
            'service_text': u'D'})
        # NL   | 7443TC   | Nijverdal
        self.assertDicEq(get_route('NL', '7443TC').routingdata(),
            {'d_depot': u'0512', 'serviceinfo': '', 'country': u'NL', 'd_sort': u'B535', 'o_sort': u'52',
             'service_text': u'D'})
        # NL   | 9405 JB  | Assen
        self.assertDicEq(get_route('NL', '9405 JB').routingdata(),
            {'d_depot': u'0513', 'serviceinfo': u'', 'country': u'NL', 'd_sort': u'B241', 'o_sort': u'52',
            'service_text': u'D'})
        # NO   | 6800     | Förde
        self.assertDicEq(get_route('NO', '6800').routingdata(),
            {'d_depot': u'0360', 'serviceinfo': u'', 'country': u'NO', 'd_sort': u'01', 'o_sort': u'15',
            'service_text': u'D'})
        # PL   | 80516    | Gdansk
        self.assertDicEq(get_route('PL', '80516').routingdata(),
            {'d_depot': u'1306', 'serviceinfo': u'', 'country': u'PL', 'd_sort': u'', 'o_sort': u'13',
            'service_text': u'D'})
        # PL   | 22300    | Krasnystaw
        self.assertDicEq(get_route('PL', '22300').routingdata(),
            {'d_depot': u'1300', 'serviceinfo': u'', 'country': u'PL', 'd_sort': u'', 'o_sort': u'13',
            'service_text': u'D'})
        # SE   | 11358    | Stockholm
        self.assertDicEq(get_route('SE', '11358').routingdata(),
            {'d_depot': u'0312', 'serviceinfo': u'', 'country': u'SE', 'd_sort': u'01', 'o_sort': u'20',
            'service_text': u'D'})
        # SE   | 75752    | Uppsala
        self.assertDicEq(get_route('SE', '75752').routingdata(),
            {'d_depot': u'0312', 'serviceinfo': u'', 'country': u'SE', 'd_sort': u'01', 'o_sort': u'20',
            'service_text': u'D'})
        # SI   | 1225     | Lukovica
        self.assertDicEq(get_route('SI', '1225').routingdata(),
            {'d_depot': u'1696', 'serviceinfo': '', 'country': u'SI', 'd_sort': u'14', 'o_sort': u'62',
             'service_text': u'D'})
        # SK   | 82105    | Bratislava
        self.assertDicEq(get_route('SK', '82105').routingdata(),
            {'d_depot': u'0660', 'serviceinfo': '', 'country': u'SK', 'd_sort': u'10', 'o_sort': u'10',
             'service_text': u'D'})

    def test_incorrectCountry(self):
        self.assertRaises(CountryError, get_route, 'URG', '42477')

    def test_incorrectLocation(self):
        self.assertRaises(TranslationError, get_route, 'DE', None)

    def test_incorrectService(self):
        self.assertRaises(ServiceError, get_route, 'DE', '0001')

    def test_select_routes(self):
        self.router.conditions = ['1=1']
        rows = self.router.select_routes('DestinationCountry=?', ('UZ', ))
        self.assert_(len(rows) > 0)

    def test_cache(self):
        self.assertDicEq(vars(get_route('LI', '8440')), vars(get_route_without_cache('LI', '8440')))


class HighLevelTest(TestCase):

    def test_get_route(self):
        self.assertDicEq(vars(get_route('DE', '42897')),
            {'service_mark': u'', 'o_sort': u'42', 'serviceinfo': u'', 'barcode_id': u'37',
             'grouping_priority': u'', 'country': u'DE', 'countrynum': u'276',
             'routingtable_version': u'20110905', 'iata_code': u'', 'd_sort': u'15',
             'postcode': u'42897', 'd_depot': u'0142', 'service_text': u'D'})
        self.assertDicEq(vars(get_route('DE', '42897', 'Remscheid')),
            {'service_mark': u'', 'o_sort': u'42', 'serviceinfo': u'', 'barcode_id': u'37',
             'grouping_priority': u'', 'country': u'DE', 'countrynum': u'276',
             'routingtable_version': u'20110905', 'iata_code': u'', 'd_sort': u'15',
             'postcode': u'42897', 'd_depot': u'0142', 'service_text': u'D'})
        self.assertDicEq(vars(get_route('DE', '42897', 'Remscheid', '101')),
            {'service_mark': u'', 'o_sort': u'42', 'serviceinfo': u'', 'barcode_id': u'37',
             'grouping_priority': u'', 'country': u'DE', 'countrynum': u'276',
             'routingtable_version': u'20110905', 'iata_code': u'', 'd_sort': u'15',
             'postcode': u'42897', 'd_depot': u'0142', 'service_text': u'D'})
        self.assertDicEq(vars(get_route('LI', '8440')),
            {'service_mark': u'', 'o_sort': u'78', 'serviceinfo': u'', 'barcode_id': u'37',
             'grouping_priority': u'', 'country': u'CH', 'countrynum': u'756',
             'routingtable_version': u'20110905', 'iata_code': u'', 'd_sort': u'',
             'postcode': u'8440', 'd_depot': u'0617', 'service_text': u'D'})
        self.assertDicEq(vars(get_route(u'LI', '8440')),
            {'service_mark': u'', 'o_sort': u'78', 'serviceinfo': u'', 'barcode_id': u'37',
             'grouping_priority': u'', 'country': u'CH', 'countrynum': u'756',
             'routingtable_version': u'20110905', 'iata_code': u'', 'd_sort': u'',
             'postcode': u'8440', 'd_depot': u'0617', 'service_text': u'D'})
        self.assertDicEq(vars(get_route(u'LI', u'8440')),
            {'service_mark': u'', 'o_sort': u'78', 'serviceinfo': u'', 'barcode_id': u'37',
             'grouping_priority': u'', 'country': u'CH', 'countrynum': u'756',
             'routingtable_version': u'20110905', 'iata_code': u'', 'd_sort': u'',
             'postcode': u'8440', 'd_depot': u'0617', 'service_text': u'D'})

    def test_cache(self):
        self.assertEqual(vars(get_route('LI', '8440')), vars(get_route_without_cache('LI', '8440')))

if __name__ == '__main__':
    start = time.time()
    router = Router(RouteData())
    stamp = time.time()
    router.route(Destination('AT', '4240', 'Freistadt Österreich')).routingdata()
    end = time.time()
    # print ("took %.3fs to find a single route (including %.3fs initialisation overhead)"
    #        % (end-start, stamp-start))

    unittest.main()


================================================
FILE: pyshipping/carriers/dpd/georoutetables/COUNTRY
================================================
#Filename: COUNTRY
#Version: 20140505
#Expiration: 20140831
#Hash: 01f9fd1927280385e2afd8287ca19c8528cb4ad2
#Reference: http://extranet.dpd.de/georoute/references_dpd_20140505.txt
#Fields: ISO-NumCountryCode|ISO-Alpha2CountryCode|ISO-Alpha3CountryCode|DestinationLanguages|FlagPostCodeNo|
#Key: ISO-NumCountryCode|
004|AF|AFG|EN|0|
008|AL|ALB|SQ|0|
010|AQ|ATA|EN|1|
012|DZ|DZA|FR|0|
016|AS|ASM|EN|0|
020|AD|AND|FR,ES,CA|0|
024|AO|AGO|PT|1|
028|AG|ATG|EN|1|
031|AZ|AZE|AZ|0|
032|AR|ARG|ES|0|
036|AU|AUS|EN|0|
040|AT|AUT|DE|0|
044|BS|BHS|EN|1|
048|BH|BHR|AR|0|
050|BD|BGD|BN,EN|0|
051|AM|ARM|HY|0|
052|BB|BRB|EN|1|
056|BE|BEL|FR,NL,DE|0|
060|BM|BMU|EN|0|
064|BT|BTN|DZ|0|
068|BO|BOL|ES,AY,QU|1|
070|BA|BIH|BS,HR,SR|0|
072|BW|BWA|EN|0|
074|BV|BVT|NO,EN|1|
076|BR|BRA|PT|0|
084|BZ|BLZ|EN|0|
086|IO|IOT|EN|0|
090|SB|SLB|EN|1|
092|VG|VGB|EN|0|
096|BN|BRN|MS,EN|0|
100|BG|BGR|BG|0|
104|MM|MMR|MY|0|
108|BI|BDI|FR,RN|1|
112|BY|BLR|BE,RU|0|
116|KH|KHM|KM|0|
120|CM|CMR|FR,EN|0|
124|CA|CAN|EN,FR|0|
132|CV|CPV|PT|0|
136|KY|CYM|EN|0|
140|CF|CAF|FR,SG|0|
144|LK|LKA|SI,TA,EN|0|
148|TD|TCD|FR,AR|1|
152|CL|CHL|ES|0|
156|CN|CHN|ZH|0|
158|TW|TWN|EN|0|
162|CX|CXR|EN|1|
166|CC|CCK|EN|0|
170|CO|COL|ES|1|
174|KM|COM|FR,AR|0|
175|YT|MYT|FR,MG,SW|0|
178|CG|COG|FR,LN|0|
180|CD|COD|FR|0|
184|CK|COK|EN|0|
188|CR|CRI|ES|0|
191|HR|HRV|HR|0|
192|CU|CUB|ES|0|
196|CY|CYP|EL,TR|0|
203|CZ|CZE|CS|0|
204|BJ|BEN|FR|0|
208|DK|DNK|DA|0|
212|DM|DMA|EN|0|
214|DO|DOM|ES|0|
218|EC|ECU|ES,QU|0|
222|SV|SLV|ES|1|
226|GQ|GNQ|ES,FR|1|
231|ET|ETH|AM|0|
232|ER|ERI|AR,TI,EN|0|
233|EE|EST|ET|0|
234|FO|FRO|FO|0|
238|FK|FLK|EN|0|
239|GS|SGS|EN|0|
242|FJ|FJI|EN|0|
246|FI|FIN|FI,SV|0|
248|AX|ALA|SV|0|
250|FR|FRA|FR|0|
254|GF|GUF|FR,EN|0|
258|PF|PYF|FR,TY|0|
260|TF|ATF|FR,EN|1|
262|DJ|DJI|FR,AR,AA|1|
266|GA|GAB|FR|0|
268|GE|GEO|KA|0|
270|GM|GMB|EN|1|
275|PS|PSE|AR,EN|0|
276|DE|DEU|DE|0|
288|GH|GHA|EN|1|
292|GI|GIB|ES,EN|1|
296|KI|KIR|EN|1|
300|GR|GRC|EL,EN|0|
304|GL|GRL|DA|0|
308|GD|GRD|EN|1|
312|GP|GLP|FR|0|
316|GU|GUM|EN|0|
320|GT|GTM|ES|0|
324|GN|GIN|FR|0|
328|GY|GUY|EN|0|
332|HT|HTI|FR,HT|0|
334|HM|HMD|EN|1|
336|VA|VAT|IT|0|
340|HN|HND|ES|0|
344|HK|HKG|EN,ZH|1|
348|HU|HUN|HU,EN|0|
352|IS|ISL|IS,EN|0|
356|IN|IND|EN,HI|0|
360|ID|IDN|ID|0|
364|IR|IRN|FA|0|
368|IQ|IRQ|AR,KU|0|
372|IE|IRL|EN,GA|0|
376|IL|ISR|AR,HE|0|
380|IT|ITA|IT|0|
384|CI|CIV|FR|0|
388|JM|JAM|EN|1|
392|JP|JPN|JA,EN|0|
398|KZ|KAZ|KK,RU|0|
400|JO|JOR|AR|0|
404|KE|KEN|EN,SW|0|
408|KP|PRK|KO|1|
410|KR|KOR|KO|0|
414|KW|KWT|AR|0|
417|KG|KGZ|UZ,KY,RU|0|
418|LA|LAO|LO|0|
422|LB|LBN|AR,EN,FR|1|
426|LS|LSO|EN|0|
428|LV|LVA|LV|0|
430|LR|LBR|EN|0|
434|LY|LBY|AR|0|
438|LI|LIE|DE|0|
440|LT|LTU|LT|0|
442|LU|LUX|LB,FR,DE|0|
446|MO|MAC|PT,ZH|1|
450|MG|MDG|MG,FR,EN|0|
454|MW|MWI|EN|1|
458|MY|MYS|MS|0|
462|MV|MDV|DV|0|
466|ML|MLI|FR|1|
470|MT|MLT|MT,EN|0|
474|MQ|MTQ|FR|0|
478|MR|MRT|AR|1|
480|MU|MUS|EN,FR|0|
484|MX|MEX|ES|0|
492|MC|MCO|FR|0|
496|MN|MNG|MN|0|
498|MD|MDA|MO|0|
499|ME|MNE|SR|0|
500|MS|MSR|EN|1|
504|MA|MAR|AR|0|
508|MZ|MOZ|PT|0|
512|OM|OMN|AR|0|
516|NA|NAM|AF,EN|1|
520|NR|NRU|NA|1|
524|NP|NPL|NE|0|
528|NL|NLD|NL|0|
530|AN|ANT|NL|1|
531|CW|CUW|EN,NL|1|
533|AW|ABW|EN|1|
534|SX|SXM|EN,NL|1|
535|BQ|BES|NL|1|
540|NC|NCL|FR|0|
548|VU|VUT|FR,EN|0|
554|NZ|NZL|EN|0|
558|NI|NIC|ES|0|
562|NE|NER|FR|0|
566|NG|NGA|EN|0|
570|NU|NIU|EN|1|
574|NF|NFK|EN|1|
578|NO|NOR|NO,NB,NN|0|
580|MP|MNP|EN|1|
581|UM|UMI|EN|1|
583|FM|FSM|EN|0|
584|MH|MHL|EN|1|
585|PW|PLW|EN|0|
586|PK|PAK|EN|0|
591|PA|PAN|ES|1|
598|PG|PNG|EN|0|
600|PY|PRY|ES,GN|0|
604|PE|PER|ES,QU,AY|0|
608|PH|PHL|EN|0|
612|PN|PCN|EN|0|
616|PL|POL|PL|0|
620|PT|PRT|PT|0|
624|GW|GNB|PT|0|
626|TL|TLS|PT,ID|1|
630|PR|PRI|ES,EN|0|
634|QA|QAT|AR|1|
638|RE|REU|FR|0|
642|RO|ROU|RO|0|
643|RU|RUS|RU|0|
646|RW|RWA|FR,EN,RW|1|
654|SH|SHN|EN|0|
659|KN|KNA|EN|1|
660|AI|AIA|EN|1|
662|LC|LCA|EN|1|
663|MF|MAF|FR|0|
666|PM|SPM|FR|0|
670|VC|VCT|EN|1|
674|SM|SMR|IT|0|
678|ST|STP|PT|1|
682|SA|SAU|AR|0|
686|SN|SEN|FR|0|
688|RS|SRB|SR|0|
690|SC|SYC|FR,EN|1|
694|SL|SLE|EN|1|
702|SG|SGP|EN,MS,TA|0|
703|SK|SVK|SK|0|
704|VN|VNM|VI|0|
705|SI|SVN|SL|0|
706|SO|SOM|SO,EN|0|
710|ZA|ZAF|AF,EN|0|
716|ZW|ZWE|EN|1|
724|ES|ESP|ES|0|
728|SS|SSD|EN|0|
729|SD|SDN|AR,EN|0|
732|EH|ESH|AR,ES,FR|0|
740|SR|SUR|EN,NL|1|
744|SJ|SJM|NO|0|
748|SZ|SWZ|EN,SS|0|
752|SE|SWE|SV|0|
756|CH|CHE|DE,FR,IT|0|
760|SY|SYR|AR|0|
762|TJ|TJK|TG|0|
764|TH|THA|TH|0|
768|TG|TGO|FR|1|
772|TK|TKL|EN|0|
776|TO|TON|EN,TO|1|
780|TT|TTO|EN|0|
784|AE|ARE|AR|1|
788|TN|TUN|AR|0|
792|TR|TUR|TR|0|
795|TM|TKM|TK|0|
796|TC|TCA|EN|0|
798|TV|TUV|EN|1|
800|UG|UGA|EN,SW|1|
804|UA|UKR|UK,RU|0|
807|MK|MKD|MK|0|
818|EG|EGY|AR|0|
826|GB|GBR|EN|0|
831|GG|GGY|EN|0|
832|JE|JEY|EN|0|
833|IM|IMN|EN|0|
834|TZ|TZA|EN,SW|1|
840|US|USA|EN|0|
850|VI|VIR|EN|0|
854|BF|BFA|FR|0|
858|UY|URY|ES|0|
860|UZ|UZB|UZ|0|
862|VE|VEN|ES|0|
876|WF|WLF|FR|0|
882|WS|WSM|SM|1|
887|YE|YEM|AR|0|
894|ZM|ZMB|EN|0|
991|IC|ISC|ES|0|
999|ZZ|ZZZ|EN|0|


================================================
FILE: pyshipping/carriers/dpd/georoutetables/LOCATION.DE
================================================
#Filename: LOCATION.DE
#Version: 20140505
#Expiration: 20140831
#Hash: 6eedc807e450c4e8a76ee039ff22226d38b95f30
#Reference: http://extranet.dpd.de/georoute/references_dpd_20140505.txt
#Fields: AreaName|CityName|ISO-Alpha2CountryCode|PostCode|
#Key: AreaName|CityName|
|Dublin|IE|1|
|Irland ohne Dublin|IE|2|


================================================
FILE: pyshipping/carriers/dpd/georoutetables/LOCATION.EN
================================================
#Filename: LOCATION.EN
#Version: 20140505
#Expiration: 20140831
#Hash: 6f7eff135d73b9bf60bc6d9c983313d8811fbcb2
#Reference: http://extranet.dpd.de/georoute/references_dpd_20140505.txt
#Fields: AreaName|CityName|ISO-Alpha2CountryCode|PostCode|
#Key: AreaName|CityName|
|Dublin|IE|1|
|Ireland excluding Dublin|IE|2|


================================================
FILE: pyshipping/carriers/dpd/georoutetables/LOCATION.FR
================================================
#Filename: LOCATION.FR
#Version: 20140505
#Expiration: 20140831
#Hash: 71ba3db7983f8bcfa2a1b1cd327572ffcafaa331
#Reference: http://extranet.dpd.de/georoute/references_dpd_20140505.txt
#Fields: AreaName|CityName|ISO-Alpha2CountryCode|PostCode|
#Key: AreaName|CityName|
|Dublin|IE|1|
|Reste de l'Irlande (sauf Dublin)|IE|2|


================================================
FILE: pyshipping/carriers/dpd/georoutetables/SERVICE
================================================
#Filename: SERVICE
#Version: 20140505
#Expiration: 20140831
#Hash: c8aa7333314e4057f7cf43d42ec66e9f89588345
#Reference: http://extranet.dpd.de/georoute/references_dpd_20140505.txt
#Fields: ServiceCode|ServiceText|ServiceMark|ServiceElements|
#Key: ServiceCode|
101|D||001|
102|D-HAZ||001,150|
103|D-6||001,130|
104|D-6-HAZ||001,130,150|
105|D-EXW||001,110|
106|D-EXW-HAZ||001,110,150|
107|D-6-EXW||001,110,130|
108|D-6-EXW-HAZ||001,110,130,150|
109|D-COD||001,100|
110|D-COD-HAZ||001,100,150|
111|D-6-COD||001,100,130|
112|D-6-COD-HAZ||001,100,130,150|
113|D-SWAP||001,120|
114|D-SWAP-HAZ||001,120,150|
115|D-6-SWAP||001,120,130|
116|D-6-SWAP-HAZ||001,120,130,150|
117|D||001,170|
118|D-BACK||001,121|
119|D-BACK-HAZ||001,121,150|
120|D+||001,180|
121|D-HAZ+||001,150,180|
122|D-6+||001,130,180|
123|D-6-HAZ+||001,130,150,180|
124|D-EXW+||001,110,180|
125|D-EXW-HAZ+||001,110,150,180|
126|D-6-EXW+||001,110,130,180|
127|D-6-EXW-HAZ+||001,110,130,150,180|
128|D-COD+||001,100,180|
129|D-COD-HAZ+||001,100,150,180|
130|D-6-COD+||001,100,130,180|
131|D-6-COD-HAZ+||001,100,130,150,180|
132|D-SWAP+||001,120,180|
133|D-SWAP-HAZ+||001,120,150,180|
134|D-6-SWAP+||001,120,130,180|
135|D-6-SWAP-HAZ+||001,120,130,150,180|
136|D|X|002|
137|D-6|X|002,130|
138|D-EXW|X|002,110|
139|D-6-EXW|X|002,110,130|
140|D-COD|X|002,100|
141|D-6-COD|X|002,100,130|
142|D-SWAP|X|002,120|
143|D-6-SWAP|X|002,120,130|
144|D|X|002,170|
145|D-BACK|X|002,121|
146|D+|X|002,180|
147|D-6+|X|002,130,180|
148|D-EXW+|X|002,110,180|
149|D-6-EXW+|X|002,110,130,180|
150|D-COD+|X|002,100,180|
151|D-6-COD+|X|002,100,130,180|
152|D-SWAP+|X|002,120,180|
153|D-6-SWAP+|X|002,120,130,180|
154|PARCELLetter|X|005|
155|PM2||010|
156|PM2-NO||010,160|
157|PM2-HAZ||010,150|
158|PM2-EXW||010,110|
159|PM2-EXW-NO||010,110,160|
160|PM2-EXW-HAZ||010,110,150|
161|PM2-COD||010,100|
162|PM2-COD-NO||010,100,160|
163|PM2-COD-HAZ||010,100,150|
164|PM2-SWAP||010,120|
165|PM2-SWAP-HAZ||010,120,150|
166|PM2-BACK||010,121|
167|PM2-BACK-HAZ||010,121,150|
168|PM2+||010,180|
169|PM2-NO+||010,160,180|
170|PM2-HAZ+||010,150,180|
171|PM2-EXW+||010,110,180|
172|PM2-EXW-NO+||010,110,160,180|
173|PM2-EXW-HAZ+||010,110,150,180|
174|PM2-COD+||010,100,180|
175|PM2-COD-NO+||010,100,160,180|
176|PM2-COD-HAZ+||010,100,150,180|
177|PM2-SWAP+||010,120,180|
178|PM2-SWAP-HAZ+||010,120,150,180|
179|AM1||022|
180|AM1-NO||022,160|
181|AM1-HAZ||022,150|
182|AM1-6||022,130|
183|AM1-6-NO||022,130,160|
184|AM1-6-HAZ||022,130,150|
185|AM1-EXW||022,110|
186|AM1-EXW-NO||022,110,160|
187|AM1-EXW-HAZ||022,110,150|
188|AM1-6-EXW||022,110,130|
189|AM1-6-EXW-NO||022,110,130,160|
190|AM1-6-EXW-HAZ||022,110,130,150|
191|AM1-COD||022,100|
192|AM1-COD-NO||022,100,160|
193|AM1-COD-HAZ||022,100,150|
194|AM1-6-COD||022,100,130|
195|AM1-6-COD-NO||022,100,130,160|
196|AM1-6-COD-HAZ||022,100,130,150|
197|AM1-SWAP||022,120|
198|AM1-SWAP-HAZ||022,120,150|
199|AM1-6-SWAP||022,120,130|
200|AM1-6-SWAP-HAZ||022,120,130,150|
201|AM1-BACK||022,121|
202|AM1-BACK-HAZ||022,121,150|
203|AM1+||022,180|
204|AM1-NO+||022,160,180|
205|AM1-HAZ+||022,150,180|
206|AM1-6+||022,130,180|
207|AM1-6-NO+||022,130,160,180|
208|AM1-6-HAZ+||022,130,150,180|
209|AM1-EXW+||022,110,180|
210|AM1-EXW-NO+||022,110,160,180|
211|AM1-EXW-HAZ+||022,110,150,180|
212|AM1-6-EXW+||022,110,130,180|
213|AM1-6-EXW-NO+||022,110,130,160,180|
214|AM1-6-EXW-HAZ+||022,110,130,150,180|
215|AM1-COD+||022,100,180|
216|AM1-COD-NO+||022,100,160,180|
217|AM1-COD-HAZ+||022,100,150,180|
218|AM1-6-COD+||022,100,130,180|
219|AM1-6-COD-NO+||022,100,130,160,180|
220|AM1-6-COD-HAZ+||022,100,130,150,180|
221|AM1-SWAP+||022,120,180|
222|AM1-SWAP-HAZ+||022,120,150,180|
223|AM1-6-SWAP+||022,120,130,180|
224|AM1-6-SWAP-HAZ+||022,120,130,150,180|
225|AM2||023|
226|AM2-NO||023,160|
227|AM2-HAZ||023,150|
228|AM2-6||023,130|
229|AM2-6-NO||023,130,160|
230|AM2-6-HAZ||023,130,150|
231|AM2-EXW||023,110|
232|AM2-EXW-NO||023,110,160|
233|AM2-EXW-HAZ||023,110,150|
234|AM2-6-EXW||023,110,130|
235|AM2-6-EXW-NO||023,110,130,160|
236|AM2-6-EXW-HAZ||023,110,130,150|
237|AM2-COD||023,100|
238|AM2-COD-NO||023,100,160|
239|AM2-COD-HAZ||023,100,150|
240|AM2-6-COD||023,100,130|
241|AM2-6-COD-NO||023,100,130,160|
242|AM2-6-COD-HAZ||023,100,130,150|
243|AM2-SWAP||023,120|
244|AM2-SWAP-HAZ||023,120,150|
245|AM2-6-SWAP||023,120,130|
246|AM2-6-SWAP-HAZ||023,120,130,150|
247|AM2-BACK||023,121|
248|AM2-BACK-HAZ||023,121,150|
249|AM2+||023,180|
250|AM2-NO+||023,160,180|
251|AM2-HAZ+||023,150,180|
252|AM2-6+||023,130,180|
253|AM2-6-NO+||023,130,160,180|
254|AM2-6-HAZ+||023,130,150,180|
255|AM2-EXW+||023,110,180|
256|AM2-EXW-NO+||023,110,160,180|
257|AM2-EXW-HAZ+||023,110,150,180|
258|AM2-6-EXW+||023,110,130,180|
259|AM2-6-EXW-NO+||023,110,130,160,180|
260|AM2-6-EXW-6-HAZ+||023,110,130,150,180|
261|AM2-COD+||023,100,180|
262|AM2-COD-NO+||023,100,160,180|
263|AM2-COD-HAZ+||023,100,150,180|
264|AM2-6-COD+||023,100,130,180|
265|AM2-6-COD-NO+||023,100,130,160,180|
266|AM2-6-COD-HAZ+||023,100,130,150,180|
267|AM2-SWAP+||023,120,180|
268|AM2-SWAP-HAZ+||023,120,150,180|
269|AM2-6-SWAP+||023,120,130,180|
270|AM2-6-SWAP-HAZ+||023,120,130,150,180|
271|SD||040|
272|SD-HAZ||040,150|
273|SD-EXW||040,110|
274|SD-EXW-HAZ||040,110,150|
275|SD-COD||040,100|
276|SD-COD-HAZ||040,100,150|
277|SD-SWAP||040,120|
278|SD-SWAP-HAZ||040,120,150|
279|SD-BACK||040,121|
280|SD-BACK-HAZ||040,121,150|
281|SD+||040,180|
282|SD-HAZ+||040,150,180|
283|SD-EXW+||040,110,180|
284|SD-EXW-HAZ+||040,110,150,180|
285|SD-COD+||040,100,180|
286|SD-COD-HAZ+||040,100,150,180|
287|SD-SWAP+||040,120,180|
288|SD-SWAP-HAZ+||040,120,150,180|
289|STANDARD BAG||060|
290|MAIL BAG||005,060|
291|IP||080|
292|POD BOX||081|
293|EXPRESS BAG||020,060|
294|MAIL||006,060|
298|RETURN SENDER||070,071|
299|RETURN|E|020,070|
300|RETURN||070|
301|RETURN-HAZ||070,150|
302|IE2|E|030|
303|IE2-MPS|E|030,140|
304|IE2-6|E|030,130|
305|IE2-6-MPS|E|030,130,140|
306|IE2-SWAP|E|030,120|
307|IE2-SWAP-MPS|E|030,120,140|
308|IE2-6-SWAP|E|030,120,130|
309|IE2-6-SWAP-MPS|E|030,120,130,140|
310|IE2-COD|E|030,100|
311|IE2-COD-MPS|E|030,100,140|
312|IE2-6-COD|E|030,100,130|
313|IE2-6-COD-MPS|E|030,100,130,140|
314|IE1|E|031|
325|D||001,012|
326|D|X|002,012|
327|D-B2C||001,013|
328|D-B2C|X|002,013|
329|D-COD-B2C||001,013,100|
330|D-COD-B2C|X|002,013,100|
331|D-TYRE UNP||001,014|
332|RETURN||072|
333|D-B2C+||001,013,180|
334|D-B2C+|X|002,013,180|
335|D-COD-B2C+||001,013,100,180|
336|D-COD-B2C+|X|002,013,100,180|
337|D-B2C-PSD||001,013,200|
338|D-B2C-PSD|X|002,013,200|
340|DPD MAX||090|
341|D-B2C-COD-PSD||001,013,100,200|
342|D-B2C-COD-PSD|X|002,013,100,200|
350|AM0||021|
351|AM0-EXW||021,110|
352|AM0-COD||021,100|
353|AM0-SWAP||021,120|
354|AM0-BACK||021,121|
355|AM0+||021,180|
356|AM0-EXW+||021,110,180|
357|AM0-COD+||021,100,180|
358|AM0-SWAP+||021,120,180|
359|D||001,190|
360|D|X|002,190|
361|D||001,110,190|
362|D|X|002,110,190|
363|D||001,210|
364|D|X|002,210|
365|D-TYRE||001,240|
366|D-TYRE-B2C||001,240,013|
367|D-TYRE-COD||001,240,100|
800|EXP||020|
801|EXP-COD||020,100|
802|EXP-EXW||020,110|
803|PS||610|
804|PS-COD||610,100|
805|PS-EXP||610,020|
806|D-SWAP-B2C+||001,013,120,180|
807|D-6-SWAP-B2C+||001,013,120,130,180|
808|D-SWAP-COD-B2C+||001,013,100,120,180|
809|D6-SWAP-COD-B2C+||001,013,100,120,130,180|
810|AM1||022|
811|AM2||023|
812|PM2||010|
813|TFR||600|
814|AM1-6||022,130|
815|AM2-6||023,130|
816|FIX||601|
817|PRIVAT||602|
818|D-SWAP-COD+||001,100,120,180|
819|D-6-SWAP-COD+||001,100,120,130,180|
820|D-6-B2C+||001,013,130,180|
821|D-6-B2C+|X|002,013,130,180|
822|D-6-COD-B2C+||001,013,100,130,180|
823|D-6-COD-B2C+|X|002,013,100,130,180|
824|D-ECO||001,250|
825|DPD MAX||090|
826|DPD MAX - COD||090,100|
827|D-SWAP-B2C||001,013,120|
828|D-SWAP-B2C|X|002,013,120|
829|D-6-B2C||001,013,130|
830|D-6-B2C|X|002,013,130|
831|D-6-COD-B2C||001,013,100,130|
832|D-6-COD-B2C|X|002,013,100,130|
833|D-6-SWAP-B2C||001,013,120,130|
834|D-6-SWAP-B2C|X|002,013,120,130|
835|D-SWAP-COD-B2C||001,013,100,120|
836|D-SWAP-COD-B2C|X|002,013,100,120|
837|D-6-SWAP-COD-B2C||001,013,100,120,130|
838|D-6-SWAP-COD-B2C|X|002,013,100,120,130|
839|D-EVE||001,220|
840|D-EVE|X|002,220|
841|D-COD-EVE||001,100,220|
842|D-COD-EVE|X|002,100,220|
843|SPAL||230|
844|D-B2C-PRIV||001,013,620|
845|D-B2C-PRIV|X|002,013,620|
846|D-B2C-HOME||001,013,621|
847|D-B2C-HOME|X|002,013,621|
848|D-SWAP-COD||001,100,120|
849|D-ECO-COD||001,250,100|
850|AM2-ECH-SHOP-NO||023,529,511,160|


================================================
FILE: pyshipping/carriers/dpd/georoutetables/SERVICEINFO.CS
================================================
#Filename: SERVICEINFO.CS
#Version: 20140505
#Expiration: 20140831
#Hash: 755fd81761d0474d68f00ff70ce4516fa8fef173
#Reference: http://extranet.dpd.de/georoute/references_dpd_20140505.txt
#Fields: ServiceCode|ServiceFieldInfo|
#Key: ServiceCode|
102|nebezpecne zbozi / hazardous goods|
103|sobota /saturday|
106|ex works nebezpecne zbozi / ex works hazardous goods|
109|dobirka / C.O.D.|
110|dobirka nebezpecne zbozi / C.O.D. hazardous goods|
113|vymena / exchange|
117|Collection-upon-Delivery|
121|nebezpecne zbozi / hazardous goods|
124|ex works|
125|ex works nebezpecne zbozi / ex works hazardous goods|
128|dobirka / C.O.D.|
129|dobirka  nebezpecne zbozi / C.O.D. hazardous goods|
132|vymena / exchange|
137|sobota / saturday|
138|ex works|
140|dobirka / C.O.D.|
142|vymena / exchange|
144|Collection-upon-Delivery|
148|ex works|
150|dobirka / C.O.D.|
152|vymena / exchange|
154|DPD PARCELLetter|
155|DPD 18:00 / DPD GUARANTEE|
158|DPD 18:00 ex works / DPD GUARANTEE ex works|
161|DPD 18:00 dobirka / DPD GUARANTEE C.O.D.|
164|DPD 18:00 vymena / DPD GUARANTEE exchange|
168|DPD 18:00 / DPD GUARANTEE|
171|DPD 18:00 ex works / DPD GUARANTEE ex works|
174|DPD 18:00 dobirka / DPD GUARANTEE C.O.D.|
179|DPD 10:00|
185|DPD 10:00 ex works|
191|DPD 10:00 dobirka / C.O.D.|
197|DPD 10:00 vymena / exchange|
203|DPD 10:00|
209|DPD 10:00 ex works|
215|DPD 10:00 dobirka / C.O.D.|
225|DPD 12:00|
228|DPD 12:00 sobota / Saturday|
231|DPD 12:00 ex works|
234|DPD 12:00 sobota ex works / Saturday ex works|
237|DPD 12:00 dobirka / C.O.D.|
240|DPD 12:00 sobota dobirka / Saturday C.O.D.|
243|DPD 12:00 vymena  / exchange|
249|DPD 12:00|
252|DPD 12:00 sobota / Saturday|
255|DPD 12:00 ex works|
258|DPD 12:00 sobota ex works / Saturday ex works|
261|DPD 12:00 dobirka / C.O.D.|
271|stejny den / same day|
327|PRIVATE ADDRESS / B2C|
328|PRIVATE ADDRESS / B2C|
329|PRIVATE ADDRESS / B2C - C.O.D.|
330|PRIVATE ADDRESS / B2C - C.O.D.|
333|SOUKROMA ADRESA / KONTAKTOVAT PRIJEMCE|
334|SOUKROMA ADRESA / KONTAKTOVAT PRIJEMCE|
335|SOUKROMA ADRESA DOBIRKA / KONTAKTOVAT PRIJEMCE|
336|SOUKROMA ADRESA DOBIRKA / KONTAKTOVAT PRIJEMCE|
352|DPD 8:30 dobirka / C.O.D.|
353|DPD 8:30 vymena / exchange|
357|DPD 8:30 dobirka / C.O.D.|
358|DPD 8:30 vymena / exchange|
839|VECERNI DORUCENI 17:00-20:00|
840|VECERNI DORUCENI 17:00-20:00|
841|VECERNI DORUCENI 17:00-20:00 DOBIRKA|
842|VECERNI DORUCENI 17:00-20:00 DOBIRKA|


================================================
FILE: pyshipping/carriers/dpd/georoutetables/SERVICEINFO.CZ
================================================
#Filename: SERVICEINFO.CZ
#Version: 20110905
#Expiration: 20120101
#Hash: de42bef2636b38cd7f371000f71df64f66cc0eda
#Reference: http://extranet.dpd.de/georoute/references_dpd_20110905.txt
#Fields: ServiceCode|ServiceFieldInfo|
#Key: ServiceCode|
102|nebezpecne zbozi / hazardous goods|
103|sobota /saturday|
106|ex works nebezpecne zbozi / ex works hazardous goods|
109|dobirka / C.O.D.|
110|dobirka nebezpecne zbozi / C.O.D. hazardous goods|
113|vymena / exchange|
117|Collection-upon-Delivery|
121|nebezpecne zbozi / hazardous goods|
124|ex works|
125|ex works nebezpecne zbozi / ex works hazardous goods|
128|dobirka / C.O.D.|
129|dobirka  nebezpecne zbozi / C.O.D. hazardous goods|
132|vymena / exchange|
137|sobota / saturday|
138|ex works|
140|dobirka / C.O.D.|
142|vymena / exchange|
144|Collection-upon-Delivery|
148|ex works|
150|dobirka / C.O.D.|
152|vymena / exchange|
154|DPD PARCELLetter|
155|DPD 18:00 / DPD GUARANTEE|
158|DPD 18:00 ex works / DPD GUARANTEE ex works|
161|DPD 18:00 dobirka / DPD GUARANTEE C.O.D.|
164|DPD 18:00 vymena / DPD GUARANTEE exchange|
168|DPD 18:00 / DPD GUARANTEE|
171|DPD 18:00 ex works / DPD GUARANTEE ex works|
174|DPD 18:00 dobirka / DPD GUARANTEE C.O.D.|
179|DPD 10:00|
185|DPD 10:00 ex works|
191|DPD 10:00 dobirka / C.O.D.|
197|DPD 10:00 vymena / exchange|
203|DPD 10:00|
209|DPD 10:00 ex works|
215|DPD 10:00 dobirka / C.O.D.|
225|DPD 12:00|
228|DPD 12:00 sobota / Saturday|
231|DPD 12:00 ex works|
234|DPD 12:00 sobota ex works / Saturday ex works|
237|DPD 12:00 dobirka / C.O.D.|
240|DPD 12:00 sobota dobirka / Saturday C.O.D.|
243|DPD 12:00 vymena  / exchange|
249|DPD 12:00|
252|DPD 12:00 sobota / Saturday|
255|DPD 12:00 ex works|
258|DPD 12:00 sobota ex works / Saturday ex works|
261|DPD 12:00 dobirka / C.O.D.|
271|stejny den / same day|
327|SOUKROMA ADRESA / KONTAKTOVAT PRIJEMCE|
328|SOUKROMA ADRESA / KONTAKTOVAT PRIJEMCE|
329|SOUKROMA ADRESA DOBIRKA / KONTAKTOVAT PRIJEMCE|
330|SOUKROMA ADRESA DOBIRKA / KONTAKTOVAT PRIJEMCE|
333|SOUKROMA ADRESA / KONTAKTOVAT PRIJEMCE|
334|SOUKROMA ADRESA / KONTAKTOVAT PRIJEMCE|
335|SOUKROMA ADRESA DOBIRKA / KONTAKTOVAT PRIJEMCE|
336|SOUKROMA ADRESA DOBIRKA / KONTAKTOVAT PRIJEMCE|
352|DPD 8:30 dobirka / C.O.D.|
353|DPD 8:30 vymena / exchange|
357|DPD 8:30 dobirka / C.O.D.|
358|DPD 8:30 vymena / exchange|


================================================
FILE: pyshipping/carriers/dpd/georoutetables/SERVICEINFO.DE
================================================
#Filename: SERVICEINFO.DE
#Version: 20140505
#Expiration: 20140831
#Hash: 8124d0f60be0fe5c28a7f0af019c975fc2bd3542
#Reference: http://extranet.dpd.de/georoute/references_dpd_20140505.txt
#Fields: ServiceCode|ServiceFieldInfo|
#Key: ServiceCode|
102|Gefahrgut / hazardous goods|
103|Samstag / saturday|
105|Unfrei / ex works|
106|Unfrei / ex works Gefahrgut / hazardous goods|
109|Nachnahme / C.O.D.|
110|Nachnahme / C.O.D. Gefahrgut / hazardous goods|
113|Austausch / exchange|
117|Mitnahme/Collection-upon-Delivery|
121|Gefahrgut / hazardous goods|
124|Unfrei / ex works|
125|Unfrei / ex works Gefahrgut / hazardous goods|
128|Nachnahme / C.O.D.|
129|Nachnahme / C.O.D. Gefahrgut / hazardous goods|
132|Austausch / exchange|
137|Samstag / saturday|
138|Unfrei / ex works|
140|Nachnahme / C.O.D.|
142|Austausch / exchange|
144|Mitnahme/Collection-upon-Delivery|
148|Unfrei / ex works|
150|Nachnahme / C.O.D.|
152|Austausch / exchange|
154|DPD PARCELLetter|
155|DPD 18:00 / DPD GUARANTEE|
158|DPD 18:00 / DPD GUARANTEE Unfrei / ex works|
161|DPD 18:00 / DPD GUARANTEE Nachnahme / C.O.D.|
164|DPD 18:00 / DPD GUARANTEE Austausch / exchange|
168|DPD 18:00 / DPD GUARANTEE|
171|DPD 18:00 / DPD GUARANTEE Unfrei / ex works|
174|DPD 18:00 / DPD GUARANTEE Nachnahme / C.O.D.|
179|DPD 10:00|
185|DPD 10:00 Unfrei / ex works|
191|DPD 10:00 Nachnahme / C.O.D.|
197|DPD 10:00 Austausch / exchange|
203|DPD 10:00|
209|DPD 10:00 Unfrei / ex works|
215|DPD 10:00 Nachnahme / C.O.D.|
225|DPD 12:00|
228|DPD 12:00 Samstag / saturday|
231|DPD 12:00 Unfrei / ex works|
234|DPD 12:00 Samstag / saturday Unfrei / ex works|
237|DPD 12:00 Nachnahme / C.O.D.|
240|DPD 12:00 Samstag / saturday Nachnahme / C.O.D.|
243|DPD 12:00 Austausch / exchange|
249|DPD 12:00|
252|DPD 12:00 Samstag / saturday|
255|DPD 12:00 Unfrei / ex works|
258|DPD 12:00 Samstag / saturday Unfrei / ex works|
261|DPD 12:00 Nachnahme / C.O.D.|
264|DPD 12:00 Samstag / saturday Nachnahme / C.O.D.|
270|DPD 12:00 Samstag Austausch Gefahrgut / Saturday Swap Hazardous Goods|
271|same day|
294|DPD Mail|
302|DPD EXPRESS|
325|EXPRESS ECONOMY|
326|EXPRESS ECONOMY|
329|Nachnahme / C.O.D.|
330|Nachnahme / C.O.D.|
335|Nachnahme / C.O.D.|
336|Nachnahme / C.O.D.|
337|DPD PaketShop Zustellung / Parcel Shop Delivery|
338|DPD PaketShop Zustellung / Parcel Shop Delivery|
341|DPD PaketShop Zustellung Nachnahme / Parcel Shop Delivery C.O.D.|
342|DPD PaketShop Zustellung Nachnahme / Parcel Shop Delivery C.O.D.|
350|DPD 8:30|
351|DPD 8:30 Unfrei / ex works|
352|DPD 8:30 Nachnahme / C.O.D.|
353|DPD 8:30 Austausch / exchange|
354|DPD 8:30|
355|DPD 8:30|
356|DPD 8:30 Unfrei / ex works|
357|DPD 8:30 Nachnahme / C.O.D.|
358|DPD 8:30 Austausch / exchange|
361|Unfrei / ex works|
362|Unfrei / ex works|
365|Reifen / Tyre|
366|Reifen / Tyre B2C|
367|Reifen / Tyre Nachnahme / C.O.D.|
810|Service Werktag 09:00 Uhr|
811|Service Werktag 12:00 Uhr|
812|Service Werktag 17:00 Uhr|
813|Service Werktag Zeitfenster|
814|Service Samstag 09:00 Uhr|
815|Service Samstag 12:00 Uhr|
816|Service Fixtermin|
817|Privatpaket|
820|Samstag / saturday|
821|Samstag / saturday|
822|Samstag / saturday Nachnahme / C.O.D.|
823|Samstag / saturday Nachnahme / C.O.D.|
827|Austausch / exchange|
828|Austausch / exchange|
829|Samstag / saturday|
830|Samstag / saturday|
831|Samstag / saturday Nachnahme / C.O.D.|
832|Samstag / saturday Nachnahme / C.O.D.|
833|Samstag / saturday Austausch / exchange|
834|Samstag / saturday Austausch / exchange|
835|Austausch / exchange Nachnahme / C.O.D.|
836|Austausch / exchange Nachnahme / C.O.D.|
837|Samstag / saturday Austausch / exchange Nachnahme / C.O.D.|
838|Samstag / saturday Austausch / exchange Nachnahme / C.O.D.|
839|Abendzustellung / Evening Delivery|
840|Abendzustellung / Evening Delivery|
841|Abendzustellung Nachnahme / Evening Delivery C.O.D.|
842|Abendzustellung Nachnahme / Evening Delivery C.O.D.|
844|Consumer Home|
845|Consumer Home|
846|Consumer Home & Shop|
847|Consumer Home & Shop|


================================================
FILE: pyshipping/carriers/dpd/georoutetables/SERVICEINFO.EE
================================================
#Filename: SERVICEINFO.EE
#Version: 20110905
#Expiration: 20120101
#Hash: d8d55ca2e78b6aad2ecb2a4169dd58b108007c6e
#Reference: http://extranet.dpd.de/georoute/references_dpd_20110905.txt
#Fields: ServiceCode|ServiceFieldInfo|
#Key: ServiceCode|
102|hazardous goods|
103|saturday|
105|ex works|
106|ex works / hazardous goods|
109|Lunapakk (COD)|
110|Lunapakk (COD) / hazardous goods|
113|exchange|
117|Collection-upon-Delivery|
121|hazardous goods|
124|ex works|
125|ex works / hazardous goods|
128|Lunapakk (COD)|
129|Lunapakk (COD) / hazardous goods|
132|exchange|
137|saturday|
138|ex works|
140|Lunapakk (COD)|
142|exchange|
144|Collection-upon-Delivery|
148|ex works|
150|Lunapakk (COD)|
152|exchange|
154|DPD PARCELLetter|
155|DPD GUARANTEE|
158|DPD GUARANTEE ex works|
161|DPD GUARANTEE Lunapakk (COD)|
164|DPD GUARANTEE exchange|
168|DPD GUARANTEE|
171|DPD GUARANTEE ex works|
174|DPD GUARANTEE Lunapakk (COD)|
179|DPD 10:00|
185|DPD 10:00 ex works|
191|DPD 10:00 Lunapakk (COD)|
197|DPD 10:00 exchange|
203|DPD 10:00|
209|DPD 10:00 ex works|
215|DPD 10:00 Lunapakk (COD)|
225|DPD 12:00|
228|DPD 12:00 saturday|
231|DPD 12:00 ex works|
234|DPD 12:00 saturday / ex works|
237|DPD 12:00 Lunapakk (COD)|
240|DPD 12:00 saturday / Lunapakk (COD)|
243|DPD 12:00 exchange|
249|DPD 12:00|
252|DPD 12:00 saturday|
255|DPD 12:00 ex works|
258|DPD 12:00 saturday / ex works|
261|DPD 12:00 Lunapakk (COD)|
264|DPD 12:00 saturday / Lunapakk (COD)|
271|same day|
294|DPD Mail|
302|DPD EXPRESS|
325|EXPRESS ECONOMY|
326|EXPRESS ECONOMY|
329|Lunapakk (COD)|
330|Lunapakk (COD)|
335|Lunapakk (COD)|
336|Lunapakk (COD)|
350|DPD 8:30|
351|DPD 8:30 ex works|
352|DPD 8:30 Lunapakk (COD)|
353|DPD 8:30 exchange|
354|DPD 8:30|
355|DPD 8:30|
356|DPD 8:30 ex works|
357|DPD 8:30 Lunapakk (COD)|
358|DPD 8:30 exchange|
820|saturday|
821|saturday|
822|saturday / Lunapakk (COD)|
823|saturday / Lunapakk (COD)|
827|exchange|
828|exchange|
829|saturday|
830|saturday|
831|saturday / Lunapakk (COD)|
832|saturday / Lunapakk (COD)|
833|saturday / exchange|
834|saturday / exchange|
835|exchange / Lunapakk (COD)|
836|exchange / Lunapakk (COD)|
837|saturday / exchange / Lunapakk (COD)|
838|saturday / exchange / Lunapakk (COD)|
840|1 day transit time|
841|2 days transit time|
842|3 days transit time|
843|4 days transit time|
844|5 days transit time|
845|1 day transit time / Lunapakk (COD)|
846|2 days transit time / Lunapakk (COD)|
847|3 days transit time / Lunapakk (COD)|
848|4 days transit time / Lunapakk (COD)|
849|5 days transit time / Lunapakk (COD)|


================================================
FILE: pyshipping/carriers/dpd/georoutetables/SERVICEINFO.EN
================================================
#Filename: SERVICEINFO.EN
#Version: 20140505
#Expiration: 20140831
#Hash: 85037002df19b84563bcfdbc1153cd85ff629e9f
#Reference: http://extranet.dpd.de/georoute/references_dpd_20140505.txt
#Fields: ServiceCode|ServiceFieldInfo|
#Key: ServiceCode|
102|hazardous goods|
103|saturday|
105|ex works|
106|ex works / hazardous goods|
109|C.O.D.|
110|C.O.D. / hazardous goods|
113|exchange|
117|Collection-upon-Delivery|
121|hazardous goods|
124|ex works|
125|ex works / hazardous goods|
128|C.O.D.|
129|C.O.D. / hazardous goods|
132|exchange|
137|saturday|
138|ex works|
140|C.O.D.|
142|exchange|
144|Collection-upon-Delivery|
148|ex works|
150|C.O.D.|
152|exchange|
154|DPD PARCELLetter|
155|DPD GUARANTEE|
158|DPD GUARANTEE ex works|
161|DPD GUARANTEE C.O.D.|
164|DPD GUARANTEE exchange|
168|DPD GUARANTEE|
171|DPD GUARANTEE ex works|
174|DPD GUARANTEE C.O.D.|
179|DPD 10:00|
185|DPD 10:00 ex works|
191|DPD 10:00 C.O.D.|
197|DPD 10:00 exchange|
203|DPD 10:00|
209|DPD 10:00 ex works|
215|DPD 10:00 C.O.D.|
225|DPD 12:00|
228|DPD 12:00 saturday|
231|DPD 12:00 ex works|
234|DPD 12:00 saturday / ex works|
237|DPD 12:00 C.O.D.|
240|DPD 12:00 saturday / C.O.D.|
243|DPD 12:00 exchange|
249|DPD 12:00|
252|DPD 12:00 saturday|
255|DPD 12:00 ex works|
258|DPD 12:00 saturday / ex works|
261|DPD 12:00 C.O.D.|
264|DPD 12:00 saturday / C.O.D.|
270|DPD 12:00 Saturday Swap Hazardous Goods|
271|same day|
294|DPD Mail|
302|DPD EXPRESS|
325|EXPRESS ECONOMY|
326|EXPRESS ECONOMY|
329|C.O.D.|
330|C.O.D.|
335|C.O.D.|
336|C.O.D.|
337|Parcel Shop Delivery|
338|Parcel Shop Delivery|
341|Parcel Shop Delivery C.O.D.|
342|Parcel Shop Delivery C.O.D.|
350|DPD 8:30|
351|DPD 8:30 ex works|
352|DPD 8:30 C.O.D.|
353|DPD 8:30 exchange|
354|DPD 8:30|
355|DPD 8:30|
356|DPD 8:30 ex works|
357|DPD 8:30 C.O.D.|
358|DPD 8:30 exchange|
361|ex works|
362|ex works|
365|Tyre|
366|Tyre B2C|
367|Tyre C.O.D.|
820|saturday|
821|saturday|
822|saturday / C.O.D.|
823|saturday / C.O.D.|
827|exchange|
828|exchange|
829|saturday|
830|saturday|
831|saturday / C.O.D.|
832|saturday / C.O.D.|
833|saturday / exchange|
834|saturday / exchange|
835|exchange / C.O.D.|
836|exchange / C.O.D.|
837|saturday / exchange / C.O.D.|
838|saturday / exchange / C.O.D.|
839|Evening Delivery|
840|Evening Delivery|
841|Evening Delivery COD|
842|Evening Delivery COD|
843|Special Pallet|
844|Consumer Home|
845|Consumer Home|
846|Consumer Home & Shop|
847|Consumer Home & Shop|
848|4 days transit time / C.O.D.|
849|5 days transit time / C.O.D.|


================================================
FILE: pyshipping/fortras/__init__.py
================================================
"""This module contains tools for reading and writing Fortras messages. Fortras is a EDI standard for
logistics related information somewhat common in Germany. See http://de.wikipedia.org/wiki/Fortras for further
enlightenment."""

# You may consider this BSD licensed.

from pyshipping.fortras.bordero import ship
__all__ = [ship]


================================================
FILE: pyshipping/fortras/bordero.py
================================================
#!/usr/bin/env python
# encoding: utf-8
"""
bordero.py - implements BORD messages. BORD is similar to IFTMIN in EDIFACT. Think of it as an work order
to a freight forwarder.

This implementation is specific for usage between HUDORA GmbH and Mäuler but could be used as the basis for a
more generic implementation.

Created by Lars Ronge and Maximillian Dornseif on 2006-11-06.
You may consider this BSD licensed.
"""

import os
import time


#def _transcode(data):
#    """Decode utf-8 to latin1 (which is used by fortras)."""
#    return data.decode('utf-8', 'replace').encode('latin-1', 'replace')


def _clip(length, data):
    """Clip a string to a maximum length."""
    # I wonder if this can't be done with clever formatstring usage.
    if not isinstance(data, str):
        data = data.decode('latin-1')
    if len(data) > length:
        return data[:length]
    return data

# zwei führende Nullen weg !!!
# übertragung: "übergepackt"
# übertragung: Palettenformat 1.5, 1, 0.5
# überticragung: lieferfenster
# DFü: Teil- Und Komplettladungen - verkehrsartschluessel : statt L (LKW) ein X (komplettladung)
# Mäuler Telefonummer
# Liste Auslandssendungen

#doctext = """Bordero-Kopf-Satz"""
#Afelder = [
#    dict(length=18, startpos=0,   endpos=19, name='borderonr',       fieldclass=IntegerFieldZeropadded),
#    dict(length=8,  startpos=19,  endpos=27, name='datum',           fieldclass=DateFieldReverse,
#         default=datetime.date.today()),
#    dict(length=1,  startpos=27,  endpos=28,  name='versandweg',     fieldclass=RightAdjustedField,
#         default='L', choices=('L', 'X')),
#    dict(length=2,  startpos=28,  endpos=30,  name='fahrzeugnummer', fieldclass=IntegerFieldZeropadded),
#    dict(length=10, startpos=34,  endpos=44,  name='versenderid',    fieldclass=RightAdjustedField,
#          default='L515'),
#    dict(length=13, startpos=44,  endpos=57,  name='frachtfuehrer',  fieldclass=RightAdjustedField,
#          default='Maeuler'),
#    dict(length=9,  startpos=53,  endpos=63,  name='plz',            fieldclass=RightAdjustedField,
#          default='42897'),
#    dict(length=12, startpos=63,  endpos=76,  name='ort',            fieldclass=RightAdjustedField,
#          default='Remscheid'),
#    dict(length=15, startpos=76,  endpos=91,  name='ladeeinheit1',   fieldclass=RightAdjustedField),
#    dict(length=15, startpos=91,  endpos=106, name='ladeeinheit1',   fieldclass=RightAdjustedField),
#    dict(length=10, startpos=106, endpos=117, name='plombe1',        fieldclass=RightAdjustedField),
#    dict(length=10, startpos=117, endpos=127, name='plombe1',        fieldclass=RightAdjustedField),
#    dict(length=10, startpos=117, endpos=127, name='release',        fieldclass=FixedField, default='6'),
#]
# Verkehrsartschlüssel (Satzart ‘A’, Position 031)
# B = Bahn
# E = Bordero für den eigenen Nahverkehr
# H = VP an ZKP
# K = Kombi
# L = LKW
# P = Packmittel-Clearing
# W = Weiterleitungsbordero
# X = Teilladungs-/Ladungspartie
# Y = Bordero ZKP an EB ohne Weiterverladung
# Z = ZKP an EP
# C = Codis
# N = Night Star Express
# S = SystemPlus international
# T = Teppichkurier


#doctext="""Versender-Adress-Satz - Teil 1"""
#Bfelder = [
#    dict(length=35, startpos= 0 , endpos= 35, name='versender_name1', default='HUDORA GmbH'),
#    dict(length=35, startpos= 35, endpos= 70, name='versender_name2'),
#    dict(length=35, startpos= 70, endpos=105, name='versender_strasse', default='Jägerwald 13'),
#    dict(length= 3, startpos=105, endpos=107, name='versender_land', default='DE'),
#    dict(length= 9, startpos=107, endpos=116, name='versender_plz', default='42897'),
#    dict(length= 3, startpos=116, endpos=119, name='frei', fieldclass=FixedField, default='   '),
#    dict(length= 3, startpos=119, endpos=122, name='codis_abholstelle', fieldclass=FixedField, default='   '),
#    dict(length= 1, startpos=122, endpos=123, name='codis_laufkennzeichen',
#         fieldclass=FixedField, default=' '),
#]
#
#doctext="""Versender-Adress-Satz - Teil 2"""
#Cfelder = [
#    dict(length=35, startpos=  0, endpos= 35, name='versenderort'),
#    dict(length= 3, startpos= 35, endpos= 38, name='versender_berechnungsland', fieldclass=FixedField,
#         default='   '),
#    dict(length= 9, startpos= 38, endpos= 48, name='versender_berechnugnsplz', fieldclass=FixedField,
#         default='         '),
#    dict(length=35, startpos= 48, endpos= 83, name='versender_berechnungsort', fieldclass=FixedField,
#         default='                                   '),
#    dict(length=17, startpos= 83, endpos=100, name='versender_kundennummer', fieldclass=RightAdjustedField,
#         default='11515'),
#    dict(length= 9, startpos=100, endpos=109, name='warenwert', fieldclass=DecimalFieldWithoutDot, precision=2),
#    dict(length= 3, startpos=109, endpos=112, name='waehrung', default='EUR'),
#    dict(length=13, startpos=112, endpos=123, name='frei', fieldclass=FixedField, default='             '),
#]
#
#doctext = """Empfänger-Adress-Satz  - Teil 1"""
#Dfelder = [
#    dict(length=35, startpos=  0, endpos= 35, name='name1'),
#    dict(length=35, startpos= 35, endpos= 70, name='name2'),
#    dict(length=35, startpos= 70, endpos=105, name='stadtteil'),
#    dict(length=19, startpos=105, endpos=123, name='frei'),
#]
#
#doctext = """Empfänger-Adress-Satz - Teil 2"""
#Efelder = [
#    dict(length=25, startpos=  0, endpos= 35, name='strasse'),
#    dict(length= 3, startpos= 35, endpos= 38, name='land'),
#    dict(length= 9, startpos= 38, endpos= 47, name='plz'),
#    dict(length=35, startpos= 47, endpos= 81, name='Empfängerort muss'),
#    dict(length= 3, startpos= 81, endpos= 84, name='Zustellbezirk Empfänger'),
#    dict(length=10, startpos= 84, endpos= 94, name='Matchcode Empfänger- Nachname'),
#    dict(length=17, startpos= 94, endpos=111, name='Kunden-Nr. Empfänger'),
#    dict(length=10, startpos=111, endpos=121, name='Original-ID des VP beim  Empfangspartner*'),
#    dict(length= 2, startpos=123, endpos=123, name='Frei'),
#]
#
#dictext="""Sendungs-Positions-Satz (je Sendungsteil; mehrfach möglich)."""
#Ffelder = [
#    dict(kength= 4, startpos=  0, endpos=  4, name='packstueck_anzahl'),
#    dict(kength= 2, startpos=  4, endpos=  6, name='verpackungsart'), # ???
#    dict(kength= 4, startpos=  6, endpos= 10, name='packstueck_anzahl_auf_paletten', fieldclass=FixedField,
#         default='    '),
#    dict(kength= 2, startpos= 10, endpos= 12, name='verpackungsart_auf_paletten', fieldclass=FixedField,
#         default='  '),
#    dict(kength=20, startpos= 10, endpos= 30, name='wareninhalt'),
#    dict(kength=20, startpos= 30, endpos= 50, name='zeichen',
#         doc="Zeichen der Sendung beim Versender"),
#    dict(kength= 5, startpos= 50, endpos= 55, name='gewicht', fieldclass=IntegerField,
#         doc="Bruttogewicht in KG."),
#    dict(kength= 5, startpos= 55, endpos= 60, name='gewicht_frachtpflichtig', fieldclass=IntegerField,
#         doc="In der Regel max(gewicht, N), wobei N=150 oder 200"),
#    # Die Satzart ‘F’ darf nicht mit einem 2. Sendungsteil versehen werden,
#    # wenn die Satzarten ‘G’ bzw. ‚Y‘ und ‚Z‘ (Gefahrgut) oder Satzart ‘H’ (Packstück-Nr.) folgen!
#    dict(kength=68, startpos=  60, endpos=123, name='frei'),
#]
# Verpackungsartschlüssel (Satzart ‘F’, Positionen 009/010, 015/016, 071/072 und 077/078)
# Schlüssel Art
# AB Auf Bohlen
# AD AD-Bahnbehälter
# BD BD-Bahnbehälter
# BE Beutel
# BL Ballen
# BU Bund
# CC Collico
# CO SystemPlus Collo
# CD CD-Bahnbehälter
# CP Chep-Palette
# DO Dose
# DR Drum
# EI Eimer
# EB Einweg-Behälter
# EP Einweg-Palette
# FA Fass
# FK Faltkiste
# FL Flasche
# FP DB Euro-Flachpalette
# GE Gebinde
# GP Gitterboxpalette
# GS Gestell
# HC Haus-Haus-Corlette
# HO Hobbock
# HP Halbpalette
# KA Kanne
# KB Kundeneigener Sonderbehälter
# KF Korbflasche
# KI Kiste
# KN Kanister
# KO Korb
# KP Kundeneigene Sonderpalette
# KS Kasten
# KT Karton
# PA Paket
# PK Pack
# RC Rollcontainer
# RG Ring
# RO Rolle
# SA Sack
# SB Spediteureigener Behälter
# ST Stück
# TC Tankcontainer
# TR Trommel
# UV Unverpackt
# VG Verschlag

# 'H':  '002%(barcode)-35s%(foo)35s%(foo)35s%(foo)16s',
# doctext = """Packstück-Nummern-Satz (je Packstück-Nr./Gruppe; mehrfach möglich)"""
#
# BARCODETYP_CHOICES = { 1: 'Freie, unformatierte Markierung',
#                        2: 'Nummer der Versandeinheit (EAN 128)',
#                        3: 'Nummer der Versandeinheit (EAN 128) FORTRAS',
#                        4: 'Paket-Nummer DPD (2/5 Interleaved)',
#                        5: 'Router Label-Nummer DPD (2/5 Interleaved)',
#                        6: 'Packstück-Nummer SystemPlus (2/5 Interleaved)',
#                        7: 'Router Label-Nummer SystemPlus (2/5 Interleaved)',
#                        8: 'IDS-Barcode (2/5 Interleaved)',
#                        9: 'IDS-Barcode (39)',
#                       10: 'IDS-Barcode (128)',
#                       11: 'Nummer der Versandeinheit (Philips)',
#                       12: 'Wechselbehälter-Barcode (2/5 Interleaved)',
#                       13: 'DPD Container-Nummer (2/5 Interleaved)',
#                       }
# Hfelder = [
#     dict(length= 3, startpos=  0, endpos=  3, name='barcodeart', fieldclass=IntegerFieldZerotagged,
#          choices=BARCODETYP_CHOICES),
#     dict(length=35, startpos=  3, endpos= 38, name='barcode1'),
#     dict(length=35, startpos= 38, endpos= 73, name='barcode2'),
#     dict(length=35, startpos= 73, endpos=108, name='barcode3'),
#     dict(length=16, startpos=108, endpos=123, name='frei'),
# ]
#
# # 'I': ('%(sendungsnummer)-16s%(sendungskilo)05d0000000000%(ladedm)03d    %(frankatur)-2s%(frankatur)-2s  %(foo)30s  %(foo)30s%(foo)16s  '),
# doctext = """Sendungs-Gewichte (je Sendung 1x)"""
#
# # Frankaturschlüssel des Spediteur-Übergabescheins (Satzart ‘I’, Positionen 043/044)
# # kann Bordero-Frankatur wie folgt ergeben:
# FRANKATUR_CHOICES = { 0: 'ohne Berechnung',
#                       4: 'Frei Empfangsspediteur',
#                       2: 'frei Haus',
#                       5: 'frei Haus verzollt',
#                       6: 'frei Haus unverzollt ',
#                       7: 'unfrei',
#                       9: 'Nachlieferung',
#                       }
#
# # Hinweistextschlüssel (Satzart ‘I’, Positionen 047/048 und 079/080 sowie Satzart ‚T‘
# # Positionen 002/003, 034/035 und 066/067)
# # * Exakte Definition der Formate bei Schlüsseln mit Zusatztext:
# #    1) Datum (TTMMJJJJ); Feldgröße: 8 Stellen
# #    2) Uhrzeit (SSMM); Feldgröße: 4 Stellen
# #    Die Positionen 049 - 056 enthalten das Datum (TTMMJJJJ), Position
# #    057 ist leer, die Positionen 058 - 051 enthalten die Uhrzeit (SSMM).
# HINWEISTEXTTYP_CHOICES = { 1: 'Sendung bitte avisieren unter Tel.-Nr.',
#                            2: 'Recall-Service - Nach Zustellung Rückruf unter Tel.-Nr.',
#                            3: 'Achtung Zollgut, Anlage Zollversandschein',
#                           14: 'Termindienst! Auslieferung spätestens am (nicht Eingangstag)',
#                           15: 'Fixtermin! Nicht früher oder später - am',
#                           16: 'Termingut! Unbedingt zustellen in KW:',
#                           17: 'SystemPlus 10-Uhr-Service - Zustellung bis 10.00 Uhr',
#                           18: 'SystemPlus 12-Uhr-Service - Zustellung bis 12.00 Uhr',
#                           19: 'SystemPlus Next-Day-Service (24 Std.-Service)',
#                           20: 'SystemPlus international Express Service',
#                           21: 'Sendung bitte nur liegend transportieren',
#                           22: 'Thermogut - vorgegebenen Temperaturbereich beachten',
#                           23: 'Empfänger kann Regalservice verlangen',
#                           25: 'Samstag-Service via Night Star Express',
#                           50: 'Warenwertnachnahme - Quick-Nachnahme',
#                           51: 'Zustellung unbedingt mit Hebebühnen-LKW',
#                           52: 'Sendung vor Zustellung telefonisch avisieren',
#                           53: 'Achtung Warenwertnachnahme - nur gegen bar ausliefern',
#                           58: 'Warenwertnachnahme - Verrechnungsscheck',
#                           60: 'Sendung ohne Entladung direkt ausliefern',
#                           61: 'Empfindliche Ware - vorsichtig behandeln',
#                           70: 'Lieferscheinnummer',
#                           71: 'Kundenauftragsnummer',
#                           72: 'Abholauftragsreferenz',
#                           73: 'Freie weitere Kundenreferenz',
#                           74: 'Retourenreferenz',
#                           }
#
#
# # Hinweistextschlüssel der Auftragsart (Satzart ‘I’, Position 127)
# # 'I': ('%(sendungsnummer)-16s%(sendungskilo)05d0000000000%(ladedm)03d    %(frankatur)-2s%(frankatur)-2s  %(foo)30s  %(foo)30s%(foo)16s  '),
# Ifelder = [
#     dict(lentgth=16, startpos=  0, endpos= 16, name='Sendungs-Nr. Versandpartner****** muss'),
#     dict(lentgth= 5, startpos= 16, endpos= 21, name='gewicht', fieldclass=IntegerField,
#          doc="Sendungsgewicht in KG"),
#     dict(lentgth= 5, startpos= 21, endpos= 26, name='gewicht_frachtpflichtig', fieldclass=IntegerField,
#          doc="Frachtpflichtiges Sendungsgewicht in KG"),
#     dict(lentgth= 5, startpos= 26, endpos= 31, name='kubikdezimeter', fieldclass=IntegerField),
#     dict(lentgth= 3, startpos=  0, endpos=123, name='lademeter', fieldclass=DecimalFieldWithoutDot,
#          precision=1),
#     dict(lentgth= 2, startpos=  0, endpos=123, name='zusaetzliche_ladehilfsmittel', fieldclass=IntegerField),
#     dict(lentgth= 2, startpos=  0, endpos=123, name='verpackungsart_zusaetzliche_ladehilfsmittel'), # 2 041 - 042
#     dict(lentgth= 2, startpos=  0, endpos=123, name='frankatur_fpediteur-Über- gabeschein* muss', choices=FRANKATUR_CHOICES), # 2 043 - 044
#     dict(lentgth= 2, startpos=  0, endpos=123, name='Frankatur Bordero* muss', choices=FRANKATUR_CHOICES), # 2 045 - 046
#     dict(lentgth= 2, startpos=  0, endpos=123, name='Hinweistextschlüssel 1** kann'), # 2 047 - 048
#     dict(lentgth=30, startpos=  0, endpos=123, name='Hinweiszusatztext 1 kann'), # 30 049 - 078
#     dict(lentgth= 2, startpos=  0, endpos=123, name='Hinweistextschlüssel 2** kann'), # 2 079 - 080
#     dict(lentgth=30, startpos=  0, endpos=123, name='Hinweiszusatztext 2 kann'), # 30 081 - 110
#     dict(lentgth=16, startpos=  0, endpos=123, name='Sendungs-Nr.  Empfangspartner*** kann'),  # 16 111 - 126
#     dict(lentgth= 1, startpos=121, endpos=122, name='auftragsart**** kann'), # 1 127 - 127
#     dict(lentgth= 1, startpos=122, endpos=123, name='lieferscheindaten_folgen', fieldclass=FixedField,
#          default=' '),
# ]
#** siehe beigefügter Hinweistextschlüssel
#*** bei Nachlieferungen (Original-Sendungs-Nr. des EP) bzw. bei Ersterfassung von über-
#zähligen Sendungen (vorläufige Sendungs-Nr. des EP)
#**** siehe beigefügter Schlüsseltabelle für Auftragsarten


# 'L': ('%(sendungen)05d%(packstuecke)05d%(bruttogewicht)05d'
#       + '%(kostensteuerplichtig)09d%(kostensteuerfrei)09d000000000%(kostenzoll)09d'
#       + '%(eust)09d000%(gitterboxen)03d%(europaletten)03d000000000000'
#       + '%(sonstigeladehilfsmittel)03d000N%(foo)36s'),
#                                                (Muss/Kann)              davon dezimal                      Position
# Bordero-Summen-Satz       muss
# (je Bordero 1x)
# Satzart ‘L’ muss 1 001 - 001
# Konstante ‘999’ muss 3/0 002 - 004
# Gesamt-Sendungs-Anzahl muss 5/0 005 - 009
# Gesamt-Packstück-Anzahl muss 5/0 010 - 014
# Tatsächliches Bruttoge-
# wicht gesamt in kg muss 5/0 015 - 019
# Gesamt-Empf.-Kosten
# steuerpflichtig muss 9/2 020 - 028
# Gesamt-Empf.-Kosten
# steuerfrei muss 9/2 029 - 037
# Gesamt-Versendernach-
# nahme muss 9/2 038 - 046
# Gesamt-Zoll muss 9/2 047 - 055
# Gesamt-EUSt muss 9/2 056 - 064
# Anzahl SB = spediteur-
# eigene Behälter muss 3/0 065 - 067
# Anzahl GP = Gitterbox-
# Paletten** muss 3/0 068 - 070
# Anzahl FP = Euro-Flach-
# Paletten** muss 3/0 071 - 073
# Anzahl CC = Collico muss 3/0 074 - 076
# Anzahl AD = Bahnbehälter muss 3/0 077 - 079
# Anzahl BD = Bahnbehälter muss 3/0 080 - 082
# Anzahl CD = Bahnbehälter muss 3/0 083 - 085
# Anzahl FP = zusätzliche
# Ladehilfsmittel ** muss 3/0 086 - 088
# Anzahl GP = zusätzliche
# Ladehilfsmittel** muss 3/0 089 - 091
# Clearing-Kennzeichen (J/N)*muss 1 092 - 092
# Frei  36 093 - 128
# Die Summenfelder für Frachtbeträge, Kosten und Nachnahmen innerhalb dieser Satzart sind
# währungsneutral, d.h. es ist lediglich eine Summierung der Werte aus der Satzart K vorzunehmen.
# Eine Währungsumrechnung darf nicht erfolgen.
# * J  = Kosten sind für das Clearing zu berücksichtigen (wird nicht verwendet)
# N = Kosten sind nicht für das Clearing zu berücksichtigen (wird nicht verwendet)
#
#
# 'T': ('%(textschluessel1)02s%(hinweistext1)-30s'
#       + '%(textschluessel2)02s%(hinweistext2)-30s'
#       + '%(textschluessel3)02s%(hinweistext3)-30s%(foo)28s'),
# Textschlüssel-Satz*                        kann
# (je Sendung maximal 3x)
# Satzart ‘T’ muss 1 001 - 001
# Laufende Bordero-Position muss 3/0 002 - 004
# Hinweistextschlüssel 1** kann 2 005 - 006
# Hinweiszusatztext 1 kann 30 007 - 036
# Hinweistextschlüssel 2** kann 2 037 - 038
# Hinweiszusatztext 2 kann 30 039 - 068
# Hinweistextschlüssel 3** kann 2 069 - 070
# Hinweiszusatztext 3 kann 30 071 - 100
# Frei
#
# 'J':  '%(zusatztext1)-62s%(zusatztext2)-62s',
# Satzart ‘J’ muss 1 001 - 001
# Laufende Bordero-Position muss 3/0 002 - 004
# Zusatztext 1 muss 62 005 - 066
# Zusatztext 2 kann 62 067 - 128

class Bordero(object):
    """Kapselt die Daten für ein Gefäß (LKW) - Verwendung ähnlich IFTSTAR."""

    def __init__(self, empfangspartner='11515'):
        super(Bordero, self).__init__()
        self.filename = ""
        self.empfangspartner = empfangspartner
        self.lieferungen = []
        self.satznummer = 0
        self.borderonr = None
        self.verladung = None
        self.generated_output = ''
        # definitions of records
        self.satzarten = {'A': (u'%(borderonr)018d%(datum)-8s%(versandweg)s  %(empfangspartner)-10s %(frachtfuehrer)'
                                + '-13s%(plz)-9s%(ort)-12s%(foo)-49s6'),
                          'B': u'%(name1)-35s%(name2)-35s%(strasse)-35s%(lkz)-3s%(plz)-9s       ',
                          'C': u'%(ort)-35s%(foo)-3s%(foo)9s%(foo)35s%(kdnnr)17s%(wert)09fEUR%(foo)-13s',
                          'D': u'%(name1)-35s%(name2)-35s%(foo)-35s%(foo)-19s',
                          'E': (u'%(strasse)-35s%(lkz)-3s%(plz)-9s%(ort)-35s%(foo)3s%(matchcode)-10s'
                                + '%(kdnnr)17s%(foo)10s%(foo)2s'),
                          'F': (u'%(anzahlpackstuecke)04d%(verpackungsart)2s0000  %(wareninhalt)-20s'
                                + '%(zeichennr)-20s%(sendungskilo)05d00000%(foo)-62s'),
                          'H': '002%(barcode)-35s%(foo)35s%(foo)35s%(foo)16s',
                          'I': (u'%(sendungsnummer)-16s%(sendungskilo)05d0000000000%(ladedm)03d    '
                                + '%(frankatur)-2s%(frankatur)-2s  %(foo)30s  %(foo)30s%(foo)16s  '),
                          'L': (u'%(sendungen)05d%(packstuecke)05d%(bruttogewicht)05d'
                                + '%(kostensteuerplichtig)09d%(kostensteuerfrei)09d000000000%(kostenzoll)09d'
                                + '%(eust)09d000%(gitterboxen)03d%(europaletten)03d000000000000'
                                + '%(sonstigeladehilfsmittel)03d000N%(foo)36s'),
                          'T': (u'%(textschluessel1)02s%(hinweistext1)-30s'
                                + '%(textschluessel2)02s%(hinweistext2)-30s'
                                + '%(textschluessel3)02s%(hinweistext3)-30s%(foo)28s'),
                          'J': u'%(zusatztext1)-62s%(zusatztext2)-62s',
                          }

    def add_lieferung(self, lieferung):
        """Adds a lieferung to our Bordero."""
        if self.generated_output:
            raise RuntimeError('tried to  add data to an already exported Bordero.')
        self.lieferungen.append(lieferung)

    def generate_satz(self, satzart, data):
        """Helper function to generate output for a Record."""
        for key in data.keys():
            if isinstance(data[key], str):
                data[key] = unicode(data[key], 'ascii', errors='ignore')
        ret = ((u'%s%03d' % (satzart, self.satznummer)) +
               (self.satzarten[satzart] % data))
        if len(ret) != 128:
            raise RuntimeError('bordero Satz %r kaputt! (len=%r) %r %r' % (satzart, len(ret), ret, data))
        return ret

    def generate_kopfsatz_a(self, verladung=None):
        """Generates bodero record A - header."""
        # Bordro nummer - Fortlaufend?
        # Verkehrsart - immer LKW?
        # IDNr des Verrsandpartyners beim Empfangspartern? L515
        # Frachtführername - Mäuler?
        # Ladeeinheitnr - 1 & 2? frei
        # Plombennummer ?        frei
        if not self.borderonr:
            raise RuntimeError('No bordero nr set.')
        data = {'empfangspartner': self.empfangspartner, 'frachtfuehrer': 'Maeuler', 'plz': '42897',
                'ort': 'Remscheid', 'versandweg': 'L', 'foo': '',
                'datum': time.strftime('%d%m%Y'),
                'borderonr': self.borderonr}
        if verladung:
            if verladung.spedition == 'Direktfahrt':
                data['versandweg'] = 'X'
        return self.generate_satz('A', data)

    def generate_versendersatz_b(self, lieferung):
        """Generates bodero record B - first half of sender."""
        data = {'name1': 'HUDORA GmbH', 'name2': '', 'strasse': u'Jägerwald 13', 'lkz': 'DE',
                'plz': '42897'}
        return self.generate_satz('B', data)

    def generate_versendersatz_c(self, lieferung):
        """Generates bodero record C - second half of sender."""
        data = {'ort': 'Remscheid', 'foo': ' ', 'kdnnr': self.empfangspartner, 'wert': 0}
        return self.generate_satz('C', data)

    def generate_empfaengersatz_d(self, lieferung):
        """Generates bodero record D - first half of recipient."""
        data = {'name1': _clip(35, lieferung.name1), 'name2': _clip(35, lieferung.name2), 'foo': ' '}
        return self.generate_satz('D', data)

    def generate_empfaengersatz_e(self, lieferung):
        """Generates bodero record E - second half of recipient."""
        data = {'strasse': _clip(35, lieferung.adresse), 'lkz': _clip(3, lieferung.land),
        'plz': _clip(9, lieferung.plz), 'ort': _clip(35, lieferung.ort), 'kdnnr': lieferung.kundennummer,
        'matchcode': _clip(10, (lieferung.name1 + lieferung.ort).replace(' ', '')), 'foo': ' '}
        return self.generate_satz('E', data)

    def generate_sendungspossatz_f(self, lieferung):
        """Generates bodero record F."""
        # Packstück Anzahl? Pakete? Paletten? NVEs!
        # Fuer weitere Paletten typen neuen Satz
        # Tatsächliches Gewicht einpflegen
        data = {'anzahlpackstuecke': len(lieferung.packstuecke),
        'verpackungsart': _clip(2, 'FP'),
        'sendungskilo': int(lieferung.gewicht / 1000),
        'wareninhalt': _clip(20, 'HUDORA Sportartikel'),
        'zeichennr': _clip(20, '%s/%s' % (lieferung.lieferscheinnummer, lieferung.id)),
        'foo': ''}
        return self.generate_satz('F', data)

    def generate_packstuecksatz(self, lieferung, packstueck):
        """Generates bodero record H."""
        barcode = packstueck.nve
        if not barcode.startswith('00'):
            barcode = '00' + barcode
        data = {'barcode': barcode,
        'foo': ' '}
        return self.generate_satz('H', data)

    def generate_sendungsinfosatz_i(self, lieferung):
        """Generates bodero record I."""
        # Sendungsnummer - eindeutig!
        data = {'sendungsnummer': _clip(16, "%016s" % lieferung.id),
        'ladedm': 0,
        'frankatur': '02',  # frei Haus
        'foo': ' '}
        data['sendungskilo'] = int(lieferung.gewicht / 1000)
        return self.generate_satz('I', data)

    def generate_textsatz_t(self, lieferung, schluesselliste):
        """Generates bodero record(s) T - text info."""
        satz = []
        ret = []
        for schluessel, text in schluesselliste:
            satz.append((schluessel, text))
            if len(satz) == 3:
                data = {'textschluessel1': '%02d' % int(satz[0][0]), 'hinweistext1': _clip(30, satz[0][1]),
                        'textschluessel2': '%02d' % int(satz[1][0]), 'hinweistext2': _clip(30, satz[1][1]),
                        'textschluessel3': '%02d' % int(satz[2][0]), 'hinweistext3': _clip(30, satz[2][1]),
                        'foo': ' '}
                satz = []
                ret.append(self.generate_satz('T', data))
        if len(satz) > 0:
            data = {'textschluessel1': '%02s' % int(satz[0][0]), 'hinweistext1': _clip(30, satz[0][1]),
                    'textschluessel2': '  ', 'hinweistext2': '',
                    'textschluessel3': '  ', 'hinweistext3': '',
                    'foo': ' '}
            if len(satz) > 1:
                data['textschluessel2'] = '%02d' % int(satz[1][0])
                data['hinweistext2'] = _clip(30, satz[1][1])
            ret.append(self.generate_satz('T', data))
        return '\n'.join(ret)

    def generate_textsaetze(self, lieferung):
        """Generate as many T records as needed."""
        schluesselliste = []
        if lieferung.avisieren_unter:
            # 52 = Sendung vor Zustellung telefonisch avisieren
            # 01 = Sendung bitte avisieren unter Tel.-Nr.: ...(Zusatztext)
            schluesselliste.append(('01', _clip(30, lieferung.avisieren_unter)))
        if lieferung.fixtermin:
            # 15 = Fixtermin! Nicht früher oder später - am: ... (Zusatztext)*
            datum = lieferung.fixtermin.strftime('%d%m%Y')
            zeit = lieferung.fixtermin.strftime('%H:%M')
            if zeit == '00:00':
                zeit = '     '
            timestamp = '%8s %5s' % (datum, zeit)
            schluesselliste.append(('15', _clip(30, timestamp)))
        if lieferung.hebebuehne:
            # 51 = Zustellung unbedingt mit Hebebühnen-LKW
            schluesselliste.append(('51', _clip(30, 'Zustellung mit Hebebühne')))
        if lieferung.auftragsnummer_kunde.strip():
            # 71 = Kundenauftragsnummer (Nummer im Zusatztext)
            schluesselliste.append(('71', _clip(30, lieferung.auftragsnummer_kunde)))
        # 70 = Lieferscheinnummer (Nummer im Zusatztext)
        schluesselliste.append(('70', _clip(30, lieferung.lieferscheinnummer)))
        # 73 = Freie weitere Kundenreferenz (Referenz im Zusatztext)
        # schluesselliste.append(('73', _clip(30, 'http://hudora.de/track/XXX')))

        # 14 = Termindienst! Auslieferung spätestens am (nicht Eingangstag): (Zusatztext)*
        # 16 = Termingut! Unbedingt zustellen in KW: ... (Zusatztext)
        # 61 = Empfindliche Ware - vorsichtig behandeln
        return self.generate_textsatz_t(lieferung, schluesselliste)

    def generate_zusatztextsatz_j(self, lieferung):
        """Generates bodero record J - additional text info."""
        data = {'zusatztext1': _clip(62, u'AuftragsNr: %s / KundenNr: %s' %
                                         (lieferung.auftragsnummer, lieferung.kundennummer)),
                'zusatztext2': _clip(62, u'huLOG Code: %s' % lieferung.code),
                'foo': ' '}
        return self.generate_satz('J', data)

    def generate_summensatz_l(self):
        """Generates bodero record L."""
    
Download .txt
gitextract_q2tr1ik7/

├── .gitignore
├── CHANGES
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.rst
├── pyshipping/
│   ├── 3dbpp.c
│   ├── __init__.py
│   ├── addressvalidation.py
│   ├── binpack.py
│   ├── binpack_simple.py
│   ├── carriers/
│   │   ├── __init__.py
│   │   └── dpd/
│   │       ├── __init__.py
│   │       ├── georoute.py
│   │       ├── georoute_test.py
│   │       └── georoutetables/
│   │           ├── COUNTRY
│   │           ├── LOCATION.DE
│   │           ├── LOCATION.EN
│   │           ├── LOCATION.FR
│   │           ├── SERVICE
│   │           ├── SERVICEINFO.CS
│   │           ├── SERVICEINFO.CZ
│   │           ├── SERVICEINFO.DE
│   │           ├── SERVICEINFO.EE
│   │           └── SERVICEINFO.EN
│   ├── fortras/
│   │   ├── __init__.py
│   │   ├── bordero.py
│   │   ├── entl.py
│   │   ├── fakt.py
│   │   ├── fortras_stat.py
│   │   └── test.py
│   ├── package.py
│   └── shipment.py
├── requirements.txt
├── setup.py
└── testdata.txt
Download .txt
SYMBOL INDEX (257 symbols across 13 files)

FILE: pyshipping/3dbpp.c
  type boolean (line 133) | typedef short         boolean;
  type ntype (line 134) | typedef short         ntype;
  type itype (line 135) | typedef short         itype;
  type stype (line 136) | typedef long          stype;
  type ptype (line 137) | typedef long          ptype;
  type box (line 140) | typedef struct irec {
  type allinfo (line 155) | typedef struct {
  type heurpair (line 213) | typedef struct {
  type point (line 222) | typedef struct {
  type domainpair (line 229) | typedef struct {
  type domset (line 239) | typedef domset domline[MAXBOXES];
  function error (line 276) | void error(char *str, ...)
  function timer (line 304) | void timer(double *time)
  function check_timelimit (line 320) | void check_timelimit(long max)
  function check_nodelimit (line 332) | void check_nodelimit(long nodes, long max)
  function check_iterlimit (line 342) | void check_iterlimit(long iterations, long max)
  function dcomp (line 365) | int dcomp(box *a, box *b)
  function hcomp (line 367) | int hcomp(box *a, box *b)
  function vcomp (line 369) | int vcomp(box *a, box *b) /* volume decr. */
  function xcomp (line 371) | int xcomp(heurpair *a, heurpair *b) /* depth decr. */
  function lcomp (line 373) | int lcomp(heurpair *a, heurpair *b) /* layer number decr. */
  function pfree (line 395) | void pfree(void *p)
  function checksol (line 409) | void checksol(allinfo *a, box *f, box *l)
  function savesol (line 435) | void savesol(allinfo *a, box *f, box *l, ntype z)
  function isortincr (line 463) | void isortincr(int *f, int *l)
  function psortdecr (line 492) | void psortdecr(point *f, point *l)
  function bound_zero (line 526) | int bound_zero(allinfo *a, box *f, box *l)
  function rotate_solution (line 544) | void rotate_solution(allinfo *a, box *f, box *l)
  function rotate_problem (line 562) | void rotate_problem(allinfo *a, box *f, box *l)
  function choose_boxes (line 582) | void choose_boxes(allinfo *a, box *f, box *l, int W2, int D2,
  function find_plist (line 600) | void find_plist(box *fbox, box *lbox, itype M, int dim, int *pl)
  function bound_one_x (line 632) | int bound_one_x(allinfo *a, box *f, box *l)
  function bound_one (line 668) | int bound_one(allinfo *a, box *f, box *l)
  function bound_two_x (line 688) | int bound_two_x(allinfo *a, box *f, box *l)
  function bound_two (line 728) | int bound_two(allinfo *a, box *f, box *l)
  function onelayer (line 756) | void onelayer(allinfo *a, box *f, box *m, box *l, int d)
  function box (line 793) | box *countarea(box *f, box *l, stype barea)
  function box (line 814) | box *remboxes(box *f, box *l, itype *depth)
  function assignboxes (line 834) | void assignboxes(heurpair *t, heurpair *u, ntype maxbin, box *f, box *l)
  function onedim_binpack (line 862) | void onedim_binpack(heurpair *i, heurpair *f, heurpair *l,
  function dfirst_heuristic (line 899) | void dfirst_heuristic(allinfo *a)
  function dfirst3_heuristic (line 943) | void dfirst3_heuristic(allinfo *a)
  function modifyandpush (line 976) | void modifyandpush(int i, int j, int rel, boolean dom)
  function popdomains (line 1000) | void popdomains(domainpair *pos)
  function boolean (line 1033) | boolean findcoordinates(allinfo *a, int n, box *f)
  function checkdomain (line 1133) | void checkdomain(allinfo *a, int i, int j, int n, box *f, int value)
  function boolean (line 1156) | boolean reducedomain(allinfo *a, int n, box *f)
  function recpack (line 1208) | void recpack(allinfo *a, int i, int j, int n, box *f, int rel)
  function boolean (line 1273) | boolean general_pack(allinfo *a, box *f, box *l)
  function boolean (line 1312) | boolean onebin_general(allinfo *a, box *f, box *l, boolean fast)
  function envelope (line 1341) | void envelope(point *f, point *l, point *s1, point **sm,
  function checkdom (line 1381) | void checkdom(point *s1, point *sl, point *sm)
  function point (line 1399) | point *removedom(point *s1, point *sl)
  function initboxes (line 1418) | void initboxes(box *f, box *l, point *fc, point **lc,
  function stype (line 1450) | stype findplaces(allinfo *a, box *f, box *l,
  function branch (line 1495) | void branch(allinfo *a, box *f, box *l, int miss, stype fill)
  function mcut_heuristic (line 1562) | void mcut_heuristic(allinfo *a)
  function mcut3_heuristic (line 1610) | void mcut3_heuristic(allinfo *a)
  function boolean (line 1642) | boolean fits2(box *i, box *j, itype W, itype H, itype D)
  function boolean (line 1653) | boolean fits2p(box *i, box *j, itype W, itype H, itype D)
  function boolean (line 1662) | boolean fits3(box *i, box *j, box *k, itype W, itype H, itype D)
  function boolean (line 1707) | boolean fitsm(allinfo *a, box *t, box *k, boolean fast)
  function boolean (line 1728) | boolean onebin_decision(allinfo *a, box *j, int bno)
  function boolean (line 1775) | boolean onebin_heuristic(allinfo *a, box *f, box *l)
  function boolean (line 1805) | boolean try_close(allinfo *a, box **curr, ntype bno,
  function free_close (line 1862) | void free_close(allinfo *a, ntype bno,
  function rec_binpack (line 1879) | void rec_binpack(allinfo *a, box *i, int bno, ntype lb, int level)
  function clearboxes (line 1935) | void clearboxes(allinfo *a)
  function copyboxes (line 1951) | void copyboxes(allinfo *a, int *w, int *h, int *d, int W, int H, int D)
  function returnboxes (line 1971) | void returnboxes(allinfo *a, int *x, int *y, int *z, int *bno)
  function binpack3d (line 1986) | void binpack3d(int n, int W, int H, int D,

FILE: pyshipping/addressvalidation.py
  function validate (line 19) | def validate(adr, servicelevel=1):
  class AddressvalidationTests (line 50) | class AddressvalidationTests(unittest.TestCase):
    method setUp (line 53) | def setUp(self):
    method test_good_address (line 66) | def test_good_address(self):
    method test_missing_zip (line 71) | def test_missing_zip(self):
    method test_short_zip (line 76) | def test_short_zip(self):
    method test_long_zip (line 81) | def test_long_zip(self):

FILE: pyshipping/binpack.py
  function binpack (line 14) | def binpack(packages, bin=None, iterlimit=5000):
  function test (line 18) | def test(func):

FILE: pyshipping/binpack_simple.py
  function packstrip (line 44) | def packstrip(bin, p):
  function packlayer (line 73) | def packlayer(bin, packages):
  function packbin (line 97) | def packbin(bin, packages):
  function packit (line 122) | def packit(bin, originalpackages):
  function product (line 139) | def product(*args, **kwds):
  function permutations (line 150) | def permutations(iterable, r=None):
  class Timeout (line 159) | class Timeout(Exception):
  function allpermutations_helper (line 163) | def allpermutations_helper(permuted, todo, maxcounter, callback, bin, be...
  function trypack (line 179) | def trypack(bin, packages, bestpack):
  function allpermutations (line 190) | def allpermutations(todo, bin, iterlimit=5000):
  function binpack (line 204) | def binpack(packages, bin=None, iterlimit=5000):
  function test (line 214) | def test():

FILE: pyshipping/carriers/dpd/georoute.py
  class InvalidFormatError (line 29) | class InvalidFormatError(Exception):
  class GeorouteException (line 34) | class GeorouteException(Exception):
  class CountryError (line 39) | class CountryError(GeorouteException):
  class DepotError (line 44) | class DepotError(GeorouteException):
  class ServiceError (line 49) | class ServiceError(GeorouteException):
  class TranslationError (line 54) | class TranslationError(GeorouteException):
  class RoutingDepotError (line 59) | class RoutingDepotError(GeorouteException):
  class NoRouteError (line 64) | class NoRouteError(GeorouteException):
  class Parcel (line 69) | class Parcel(object):
    method __init__ (line 73) | def __init__(self, depot='142', service='101', country='DE', city=None...
  class Destination (line 83) | class Destination(object):
    method __init__ (line 86) | def __init__(self, country='DE', postcode=None, city=None, service='10...
  class Route (line 93) | class Route:
    method __init__ (line 96) | def __init__(self, d_depot, o_sort, d_sort, grouping_priority, barcode...
    method __unicode__ (line 113) | def __unicode__(self):
    method __repr__ (line 134) | def __repr__(self):
    method routingdata (line 137) | def routingdata(self):
  function _readfile (line 143) | def _readfile(filename):
  class RouteData (line 156) | class RouteData(object):
    method __init__ (line 159) | def __init__(self, routingdepot='0142'):
    method read_depots (line 201) | def read_depots(self, path):
    method read_locations (line 237) | def read_locations(self, path):
    method read_routes (line 257) | def read_routes(self, path):
    method expand_services (line 307) | def expand_services(self, services):
    method expand_depots (line 321) | def expand_depots(self, route, depots, c):
    method get_countrynum (line 353) | def get_countrynum(self, isoname):
    method get_depot (line 359) | def get_depot(self, depotnumber):
    method get_service (line 365) | def get_service(self, servicecode):
    method get_servicetext (line 371) | def get_servicetext(self, servicecode):
    method translate_location (line 377) | def translate_location(self, city, country):
  class Router (line 387) | class Router(object):
    method __init__ (line 390) | def __init__(self, data):
    method route (line 394) | def route(self, parcel):
    method add_condition (line 430) | def add_condition(self, condition):
    method select_routes (line 433) | def select_routes(self, condition, params=()):
    method select_country (line 450) | def select_country(self, parcel):
    method cleanup_postcode (line 456) | def cleanup_postcode(self, parcel):
    method select_postcode (line 493) | def select_postcode(self, parcel):
    method select_service (line 508) | def select_service(self, parcel):
    method select_depot (line 528) | def select_depot(self, parcel):
  function get_route_without_cache (line 544) | def get_route_without_cache(country=None, postcode=None, city=None, serv...
  function get_route (line 549) | def get_route(country=None, postcode=None, city=None, servicecode='101'):
  function find_route (line 611) | def find_route(depot, servicecode, land, plz):

FILE: pyshipping/carriers/dpd/georoute_test.py
  class TestCase (line 13) | class TestCase(unittest.TestCase):
    method assertDicEq (line 16) | def assertDicEq(self, dict1, dict2):
  class RouteDataTest (line 33) | class RouteDataTest(TestCase):
    method setUp (line 35) | def setUp(self):
    method test_version (line 39) | def test_version(self):
    method test_get_country (line 42) | def test_get_country(self):
    method test_read_depots (line 48) | def test_read_depots(self):
    method test_expand_depots (line 58) | def test_expand_depots(self):
    method test_get_service (line 71) | def test_get_service(self):
    method test_get_servicetext (line 75) | def test_get_servicetext(self):
    method test_translate_location (line 79) | def test_translate_location(self):
  class RouterTest (line 84) | class RouterTest(TestCase):
    method setUp (line 86) | def setUp(self):
    method test_known_routes_de (line 90) | def test_known_routes_de(self):
    method test_known_routes_world (line 113) | def test_known_routes_world(self):
    method test_difficult_routingdepots (line 188) | def test_difficult_routingdepots(self):
    method test_difficult_service (line 205) | def test_difficult_service(self):
    method test_postcode_with_country (line 219) | def test_postcode_with_country(self):
    method test_postcode_spaces (line 233) | def test_postcode_spaces(self):
    method test_problematic_routes (line 251) | def test_problematic_routes(self):
    method test_international (line 260) | def test_international(self):
    method test_incorrectCountry (line 429) | def test_incorrectCountry(self):
    method test_incorrectLocation (line 432) | def test_incorrectLocation(self):
    method test_incorrectService (line 435) | def test_incorrectService(self):
    method test_select_routes (line 438) | def test_select_routes(self):
    method test_cache (line 443) | def test_cache(self):
  class HighLevelTest (line 447) | class HighLevelTest(TestCase):
    method test_get_route (line 449) | def test_get_route(self):
    method test_cache (line 481) | def test_cache(self):

FILE: pyshipping/fortras/bordero.py
  function _clip (line 23) | def _clip(length, data):
  class Bordero (line 368) | class Bordero(object):
    method __init__ (line 371) | def __init__(self, empfangspartner='11515'):
    method add_lieferung (line 403) | def add_lieferung(self, lieferung):
    method generate_satz (line 409) | def generate_satz(self, satzart, data):
    method generate_kopfsatz_a (line 420) | def generate_kopfsatz_a(self, verladung=None):
    method generate_versendersatz_b (line 439) | def generate_versendersatz_b(self, lieferung):
    method generate_versendersatz_c (line 445) | def generate_versendersatz_c(self, lieferung):
    method generate_empfaengersatz_d (line 450) | def generate_empfaengersatz_d(self, lieferung):
    method generate_empfaengersatz_e (line 455) | def generate_empfaengersatz_e(self, lieferung):
    method generate_sendungspossatz_f (line 462) | def generate_sendungspossatz_f(self, lieferung):
    method generate_packstuecksatz (line 475) | def generate_packstuecksatz(self, lieferung, packstueck):
    method generate_sendungsinfosatz_i (line 484) | def generate_sendungsinfosatz_i(self, lieferung):
    method generate_textsatz_t (line 494) | def generate_textsatz_t(self, lieferung, schluesselliste):
    method generate_textsaetze (line 518) | def generate_textsaetze(self, lieferung):
    method generate_zusatztextsatz_j (line 549) | def generate_zusatztextsatz_j(self, lieferung):
    method generate_summensatz_l (line 557) | def generate_summensatz_l(self):
    method generate_lieferungssaetze (line 579) | def generate_lieferungssaetze(self, lieferung):
    method generate_dataexport (line 595) | def generate_dataexport(self):
  function ship (line 608) | def ship(verladung, empfangspartner='11515', basedir='/usr/local/maeuler...
  function ship_lieferungen (line 637) | def ship_lieferungen(lieferungen, empfangspartner='11515'):

FILE: pyshipping/fortras/entl.py
  class Entladebericht (line 15) | class Entladebericht(object):
    method update_packstueck (line 138) | def update_packstueck(self, nve, datadict):
    method parse (line 179) | def parse(self, data):

FILE: pyshipping/fortras/fakt.py
  function convert_to_decimal (line 44) | def convert_to_decimal(value, factor=None):
  function convert_record (line 53) | def convert_record(record):
  function parse_fakt (line 76) | def parse_fakt(data):

FILE: pyshipping/fortras/fortras_stat.py
  class Statusmeldung (line 15) | class Statusmeldung(object):
    method update_sendung (line 109) | def update_sendung(self, sendung_id, datadict):
    method parse (line 178) | def parse(self, data):

FILE: pyshipping/fortras/test.py
  class TestPackstueck (line 16) | class TestPackstueck:
    method __init__ (line 18) | def __init__(self):
  class TestLieferung (line 26) | class TestLieferung:
    method __init__ (line 28) | def __init__(self):
    method _get_gewicht (line 55) | def _get_gewicht(self):
  class BorderoTests (line 60) | class BorderoTests(unittest.TestCase):
    method test_clip (line 62) | def test_clip(self):
    method test_bordero (line 68) | def test_bordero(self):

FILE: pyshipping/package.py
  class Package (line 15) | class Package(object):
    method __init__ (line 18) | def __init__(self, size, weight=0, nosort=False):
    method _get_gurtmass (line 39) | def _get_gurtmass(self):
    method hat_gleiche_seiten (line 54) | def hat_gleiche_seiten(self, other):
    method __getitem__ (line 62) | def __getitem__(self, key):
    method __contains__ (line 80) | def __contains__(self, other):
    method __hash__ (line 90) | def __hash__(self):
    method __eq__ (line 93) | def __eq__(self, other):
    method __cmp__ (line 105) | def __cmp__(self, other):
    method __mul__ (line 109) | def __mul__(self, multiplicand):
    method __add__ (line 122) | def __add__(self, other):
    method __str__ (line 153) | def __str__(self):
    method __repr__ (line 159) | def __repr__(self):
  function buendelung (line 166) | def buendelung(kartons, maxweight=31000, maxgurtmass=3000):
  function pack_in_bins (line 233) | def pack_in_bins(kartons, versandkarton):
  class PackageTests (line 258) | class PackageTests(unittest.TestCase):
    method test_init (line 261) | def test_init(self):
    method test_eq (line 266) | def test_eq(self):
    method test_volume (line 271) | def test_volume(self):
    method test_str (line 276) | def test_str(self):
    method test_repr (line 281) | def test_repr(self):
    method test_gurtmass (line 285) | def test_gurtmass(self):
    method test_mul (line 292) | def test_mul(self):
    method test_sort (line 296) | def test_sort(self):

FILE: pyshipping/shipment.py
  class AbstractPackstueck (line 15) | class AbstractPackstueck(object):
  class AbstractItem (line 20) | class AbstractItem(object):
    method __init__ (line 23) | def __init__(self):
    method __unicode__ (line 33) | def __unicode__(self):
    method anbruch (line 41) | def anbruch(self):
    method volumen (line 46) | def volumen(self):
    method gewicht (line 52) | def gewicht(self):
    method max_packstueck_gewicht (line 58) | def max_packstueck_gewicht(self):
    method paletten (line 65) | def paletten(self):
    method picks (line 70) | def picks(self):
    method export_kartons (line 79) | def export_kartons(self):
    method export_karton_gewichte (line 84) | def export_karton_gewichte(self):
    method packstuecke (line 98) | def packstuecke(self):
  class AbstractLieferung (line 111) | class AbstractLieferung(object):
    method __init__ (line 114) | def __init__(self):
    method transportweg (line 122) | def transportweg(self):
    method transportzeit (line 127) | def transportzeit(self):
    method versandtermin (line 132) | def versandtermin(self):
    method anbruch (line 140) | def anbruch(self):
    method volumen (line 148) | def volumen(self):
    method gewicht (line 153) | def gewicht(self):
    method max_packstueck_gewicht (line 158) | def max_packstueck_gewicht(self):
    method paletten (line 165) | def paletten(self):
    method versandpaletten (line 170) | def versandpaletten(self):
    method picks (line 175) | def picks(self):
    method packstuecke (line 182) | def packstuecke(self):
    method export_kartons (line 188) | def export_kartons(self):
    method export_karton_gewichte (line 193) | def export_karton_gewichte(self):
    method kep (line 201) | def kep(self):
  class simpleTests (line 210) | class simpleTests(unittest.TestCase):
    method test_stupid (line 213) | def test_stupid(self):
Condensed preview — 36 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (329K chars).
[
  {
    "path": ".gitignore",
    "chars": 62,
    "preview": "*.pyc\n*.pyo\n*.so\n*~\nbuild\ndist\nhtml\npyShipping.egg-info\n*.bak\n"
  },
  {
    "path": "CHANGES",
    "chars": 771,
    "preview": "1.9:    neue Routeninfos von DPD zum 2011-05-07\n1.8:    neue Routeninfos von DPD zum 2011-01-03\n1.7:    neue Routeninfos"
  },
  {
    "path": "LICENSE",
    "chars": 1462,
    "preview": "Original code in this disturibution is\nCopyright 2008, 2009 HUDORA GmbH. All rights reserved.\n\nRedistribution and use in"
  },
  {
    "path": "MANIFEST.in",
    "chars": 67,
    "preview": "include README.rst\ninclude pyshipping/carriers/dpd/georoutetables/*"
  },
  {
    "path": "Makefile",
    "chars": 1363,
    "preview": "# setting the PATH seems only to work in GNUmake not in BSDmake\nPATH := ./testenv/bin:$(PATH)\n\ncheck:\n\tpep8 -r --ignore="
  },
  {
    "path": "README.rst",
    "chars": 1504,
    "preview": "pyShipping provides connections to interface with shipping companies and to transport shipping related information. \n\n *"
  },
  {
    "path": "pyshipping/3dbpp.c",
    "chars": 68077,
    "preview": "\n/* ======================================================================\n      3D BIN PACKING, Silvano Martello, David"
  },
  {
    "path": "pyshipping/__init__.py",
    "chars": 72,
    "preview": "\"\"\"pyShipping contains routines related to shipping and warehousing.\"\"\"\n"
  },
  {
    "path": "pyshipping/addressvalidation.py",
    "chars": 3090,
    "preview": "#!/usr/bin/env python\n# encoding: utf-8\n\"\"\"\naddressvalidation.py - check the validity of addresses\n\nShould once integrat"
  },
  {
    "path": "pyshipping/binpack.py",
    "chars": 1041,
    "preview": "#!/usr/bin/env python\n# encoding: utf-8\n\"\"\"\nbinpack.py\n\nCreated by Maximillian Dornseif on 2010-08-16.\nCopyright (c) 201"
  },
  {
    "path": "pyshipping/binpack_simple.py",
    "chars": 7347,
    "preview": "#!/usr/bin/env python\n# encoding: utf-8\n\"\"\"\nbinpack_simple.py\n\nThis code implemnts 3D bin packing in pure Python\n\nBin pa"
  },
  {
    "path": "pyshipping/carriers/__init__.py",
    "chars": 46,
    "preview": "\"\"\"Function for specific freight carriers.\"\"\"\n"
  },
  {
    "path": "pyshipping/carriers/dpd/__init__.py",
    "chars": 41,
    "preview": "\"\"\"Functions specific to DPD/Geopost.\"\"\"\n"
  },
  {
    "path": "pyshipping/carriers/dpd/georoute.py",
    "chars": 23646,
    "preview": "#!/usr/bin/env python\n# -*- encoding: utf-8 -*-\n\"\"\"\ngeoroute.py - get DPD related routng information\n\nOriginally coded b"
  },
  {
    "path": "pyshipping/carriers/dpd/georoute_test.py",
    "chars": 31782,
    "preview": "#!/usr/bin/env python\n# -*- encoding: utf-8 -*-\n\n\"\"\"Test routing resolver for DPD. Coded by jmv, extended by md\"\"\"\n\nimpo"
  },
  {
    "path": "pyshipping/carriers/dpd/georoutetables/COUNTRY",
    "chars": 4846,
    "preview": "#Filename: COUNTRY\n#Version: 20140505\n#Expiration: 20140831\n#Hash: 01f9fd1927280385e2afd8287ca19c8528cb4ad2\n#Reference: "
  },
  {
    "path": "pyshipping/carriers/dpd/georoutetables/LOCATION.DE",
    "chars": 308,
    "preview": "#Filename: LOCATION.DE\n#Version: 20140505\n#Expiration: 20140831\n#Hash: 6eedc807e450c4e8a76ee039ff22226d38b95f30\n#Referen"
  },
  {
    "path": "pyshipping/carriers/dpd/georoutetables/LOCATION.EN",
    "chars": 314,
    "preview": "#Filename: LOCATION.EN\n#Version: 20140505\n#Expiration: 20140831\n#Hash: 6f7eff135d73b9bf60bc6d9c983313d8811fbcb2\n#Referen"
  },
  {
    "path": "pyshipping/carriers/dpd/georoutetables/LOCATION.FR",
    "chars": 322,
    "preview": "#Filename: LOCATION.FR\n#Version: 20140505\n#Expiration: 20140831\n#Hash: 71ba3db7983f8bcfa2a1b1cd327572ffcafaa331\n#Referen"
  },
  {
    "path": "pyshipping/carriers/dpd/georoutetables/SERVICE",
    "chars": 8387,
    "preview": "#Filename: SERVICE\n#Version: 20140505\n#Expiration: 20140831\n#Hash: c8aa7333314e4057f7cf43d42ec66e9f89588345\n#Reference: "
  },
  {
    "path": "pyshipping/carriers/dpd/georoutetables/SERVICEINFO.CS",
    "chars": 2390,
    "preview": "#Filename: SERVICEINFO.CS\n#Version: 20140505\n#Expiration: 20140831\n#Hash: 755fd81761d0474d68f00ff70ce4516fa8fef173\n#Refe"
  },
  {
    "path": "pyshipping/carriers/dpd/georoutetables/SERVICEINFO.CZ",
    "chars": 2304,
    "preview": "#Filename: SERVICEINFO.CZ\n#Version: 20110905\n#Expiration: 20120101\n#Hash: de42bef2636b38cd7f371000f71df64f66cc0eda\n#Refe"
  },
  {
    "path": "pyshipping/carriers/dpd/georoutetables/SERVICEINFO.DE",
    "chars": 3959,
    "preview": "#Filename: SERVICEINFO.DE\n#Version: 20140505\n#Expiration: 20140831\n#Hash: 8124d0f60be0fe5c28a7f0af019c975fc2bd3542\n#Refe"
  },
  {
    "path": "pyshipping/carriers/dpd/georoutetables/SERVICEINFO.EE",
    "chars": 2539,
    "preview": "#Filename: SERVICEINFO.EE\n#Version: 20110905\n#Expiration: 20120101\n#Hash: d8d55ca2e78b6aad2ecb2a4169dd58b108007c6e\n#Refe"
  },
  {
    "path": "pyshipping/carriers/dpd/georoutetables/SERVICEINFO.EN",
    "chars": 2488,
    "preview": "#Filename: SERVICEINFO.EN\n#Version: 20140505\n#Expiration: 20140831\n#Hash: 85037002df19b84563bcfdbc1153cd85ff629e9f\n#Refe"
  },
  {
    "path": "pyshipping/fortras/__init__.py",
    "chars": 332,
    "preview": "\"\"\"This module contains tools for reading and writing Fortras messages. Fortras is a EDI standard for\nlogistics related "
  },
  {
    "path": "pyshipping/fortras/bordero.py",
    "chars": 32177,
    "preview": "#!/usr/bin/env python\n# encoding: utf-8\n\"\"\"\nbordero.py - implements BORD messages. BORD is similar to IFTMIN in EDIFACT."
  },
  {
    "path": "pyshipping/fortras/entl.py",
    "chars": 10322,
    "preview": "#!/usr/bin/env python\n# encoding: utf-8\n\"\"\"\nentl.py - parse Fortras ENTL messages.\n\nCreated by Maximillian Dornseif on 2"
  },
  {
    "path": "pyshipping/fortras/fakt.py",
    "chars": 2724,
    "preview": "#!/usr/bin/env python\n# encoding: utf-8\n\"\"\"\nfakt.py\n\nCreated by Christian Klein on 2011-03-31.\nCopyright (c) 2011 HUDORA"
  },
  {
    "path": "pyshipping/fortras/fortras_stat.py",
    "chars": 11827,
    "preview": "#!/usr/bin/env python\n# encoding: utf-8\n\"\"\"\nstat.py - parse Fortras STAT messages.\n\nCreated by Maximillian Dornseif on 2"
  },
  {
    "path": "pyshipping/fortras/test.py",
    "chars": 11769,
    "preview": "#!/usr/bin/env python\n# encoding: utf-8\n\"\"\"\ntest.py - tests for fortras related functionality\n\nCreated by Maximillian Do"
  },
  {
    "path": "pyshipping/package.py",
    "chars": 12143,
    "preview": "#!/usr/bin/env python\n# encoding: utf-8\n\"\"\"\npackage.py - shipping/cargo related calculations based on a unit of shipping"
  },
  {
    "path": "pyshipping/shipment.py",
    "chars": 8841,
    "preview": "#!/usr/bin/env python\n# encoding: utf-8\n\"\"\"\nsendung.py - Sendungsaufteilung - dies definiert eine abstrakte Sendung, mit"
  },
  {
    "path": "requirements.txt",
    "chars": 10,
    "preview": "# nothing\n"
  },
  {
    "path": "setup.py",
    "chars": 966,
    "preview": "long_description = \"\"\"\npyShipping provides connections to interface with shipping companies and to transport shipping re"
  },
  {
    "path": "testdata.txt",
    "chars": 58131,
    "preview": "153x64x64 153x64x64\n580x140x60 580x140x60 \n580x190x60 580x190x60 \n580x210x60 580x210x60 \n240x62x43 240x62x43\n220x200x80 "
  }
]

About this extraction

This page contains the full source code of the hudora/pyShipping GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 36 files (310.0 KB), approximately 108.0k tokens, and a symbol index with 257 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!