SUBROUTINE DTGSEN( IJOB, WANTQ, WANTZ, SELECT, N, A, LDA, B, LDB, $ ALPHAR, ALPHAI, BETA, Q, LDQ, Z, LDZ, M, PL, $ PR, DIF, WORK, LWORK, IWORK, LIWORK, INFO ) * * -- LAPACK routine (version 3.1.1) -- * Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd.. * January 2007 * * Modified to call DLACN2 in place of DLACON, 5 Feb 03, SJH. * * .. Scalar Arguments .. LOGICAL WANTQ, WANTZ INTEGER IJOB, INFO, LDA, LDB, LDQ, LDZ, LIWORK, LWORK, $ M, N DOUBLE PRECISION PL, PR * .. * .. Array Arguments .. LOGICAL SELECT( * ) INTEGER IWORK( * ) DOUBLE PRECISION A( LDA, * ), ALPHAI( * ), ALPHAR( * ), $ B( LDB, * ), BETA( * ), DIF( * ), Q( LDQ, * ), $ WORK( * ), Z( LDZ, * ) * .. * * Purpose * ======= * * DTGSEN reorders the generalized real Schur decomposition of a real * matrix pair (A, B) (in terms of an orthonormal equivalence trans- * formation Q' * (A, B) * Z), so that a selected cluster of eigenvalues * appears in the leading diagonal blocks of the upper quasi-triangular * matrix A and the upper triangular B. The leading columns of Q and * Z form orthonormal bases of the corresponding left and right eigen- * spaces (deflating subspaces). (A, B) must be in generalized real * Schur canonical form (as returned by DGGES), i.e. A is block upper * triangular with 1-by-1 and 2-by-2 diagonal blocks. B is upper * triangular. * * DTGSEN also computes the generalized eigenvalues * * w(j) = (ALPHAR(j) + i*ALPHAI(j))/BETA(j) * * of the reordered matrix pair (A, B). * * Optionally, DTGSEN computes the estimates of reciprocal condition * numbers for eigenvalues and eigenspaces. These are Difu[(A11,B11), * (A22,B22)] and Difl[(A11,B11), (A22,B22)], i.e. the separation(s) * between the matrix pairs (A11, B11) and (A22,B22) that correspond to * the selected cluster and the eigenvalues outside the cluster, resp., * and norms of "projections" onto left and right eigenspaces w.r.t. * the selected cluster in the (1,1)-block. * * Arguments * ========= * * IJOB (input) INTEGER * Specifies whether condition numbers are required for the * cluster of eigenvalues (PL and PR) or the deflating subspaces * (Difu and Difl): * =0: Only reorder w.r.t. SELECT. No extras. * =1: Reciprocal of norms of "projections" onto left and right * eigenspaces w.r.t. the selected cluster (PL and PR). * =2: Upper bounds on Difu and Difl. F-norm-based estimate * (DIF(1:2)). * =3: Estimate of Difu and Difl. 1-norm-based estimate * (DIF(1:2)). * About 5 times as expensive as IJOB = 2. * =4: Compute PL, PR and DIF (i.e. 0, 1 and 2 above): Economic * version to get it all. * =5: Compute PL, PR and DIF (i.e. 0, 1 and 3 above) * * WANTQ (input) LOGICAL * .TRUE. : update the left transformation matrix Q; * .FALSE.: do not update Q. * * WANTZ (input) LOGICAL * .TRUE. : update the right transformation matrix Z; * .FALSE.: do not update Z. * * SELECT (input) LOGICAL array, dimension (N) * SELECT specifies the eigenvalues in the selected cluster. * To select a real eigenvalue w(j), SELECT(j) must be set to * .TRUE.. To select a complex conjugate pair of eigenvalues * w(j) and w(j+1), corresponding to a 2-by-2 diagonal block, * either SELECT(j) or SELECT(j+1) or both must be set to * .TRUE.; a complex conjugate pair of eigenvalues must be * either both included in the cluster or both excluded. * * N (input) INTEGER * The order of the matrices A and B. N >= 0. * * A (input/output) DOUBLE PRECISION array, dimension(LDA,N) * On entry, the upper quasi-triangular matrix A, with (A, B) in * generalized real Schur canonical form. * On exit, A is overwritten by the reordered matrix A. * * LDA (input) INTEGER * The leading dimension of the array A. LDA >= max(1,N). * * B (input/output) DOUBLE PRECISION array, dimension(LDB,N) * On entry, the upper triangular matrix B, with (A, B) in * generalized real Schur canonical form. * On exit, B is overwritten by the reordered matrix B. * * LDB (input) INTEGER * The leading dimension of the array B. LDB >= max(1,N). * * ALPHAR (output) DOUBLE PRECISION array, dimension (N) * ALPHAI (output) DOUBLE PRECISION array, dimension (N) * BETA (output) DOUBLE PRECISION array, dimension (N) * On exit, (ALPHAR(j) + ALPHAI(j)*i)/BETA(j), j=1,...,N, will * be the generalized eigenvalues. ALPHAR(j) + ALPHAI(j)*i * and BETA(j),j=1,...,N are the diagonals of the complex Schur * form (S,T) that would result if the 2-by-2 diagonal blocks of * the real generalized Schur form of (A,B) were further reduced * to triangular form using complex unitary transformations. * If ALPHAI(j) is zero, then the j-th eigenvalue is real; if * positive, then the j-th and (j+1)-st eigenvalues are a * complex conjugate pair, with ALPHAI(j+1) negative. * * Q (input/output) DOUBLE PRECISION array, dimension (LDQ,N) * On entry, if WANTQ = .TRUE., Q is an N-by-N matrix. * On exit, Q has been postmultiplied by the left orthogonal * transformation matrix which reorder (A, B); The leading M * columns of Q form orthonormal bases for the specified pair of * left eigenspaces (deflating subspaces). * If WANTQ = .FALSE., Q is not referenced. * * LDQ (input) INTEGER * The leading dimension of the array Q. LDQ >= 1; * and if WANTQ = .TRUE., LDQ >= N. * * Z (input/output) DOUBLE PRECISION array, dimension (LDZ,N) * On entry, if WANTZ = .TRUE., Z is an N-by-N matrix. * On exit, Z has been postmultiplied by the left orthogonal * transformation matrix which reorder (A, B); The leading M * columns of Z form orthonormal bases for the specified pair of * left eigenspaces (deflating subspaces). * If WANTZ = .FALSE., Z is not referenced. * * LDZ (input) INTEGER * The leading dimension of the array Z. LDZ >= 1; * If WANTZ = .TRUE., LDZ >= N. * * M (output) INTEGER * The dimension of the specified pair of left and right eigen- * spaces (deflating subspaces). 0 <= M <= N. * * PL (output) DOUBLE PRECISION * PR (output) DOUBLE PRECISION * If IJOB = 1, 4 or 5, PL, PR are lower bounds on the * reciprocal of the norm of "projections" onto left and right * eigenspaces with respect to the selected cluster. * 0 < PL, PR <= 1. * If M = 0 or M = N, PL = PR = 1. * If IJOB = 0, 2 or 3, PL and PR are not referenced. * * DIF (output) DOUBLE PRECISION array, dimension (2). * If IJOB >= 2, DIF(1:2) store the estimates of Difu and Difl. * If IJOB = 2 or 4, DIF(1:2) are F-norm-based upper bounds on * Difu and Difl. If IJOB = 3 or 5, DIF(1:2) are 1-norm-based * estimates of Difu and Difl. * If M = 0 or N, DIF(1:2) = F-norm([A, B]). * If IJOB = 0 or 1, DIF is not referenced. * * WORK (workspace/output) DOUBLE PRECISION array, * dimension (MAX(1,LWORK)) * On exit, if INFO = 0, WORK(1) returns the optimal LWORK. * * LWORK (input) INTEGER * The dimension of the array WORK. LWORK >= 4*N+16. * If IJOB = 1, 2 or 4, LWORK >= MAX(4*N+16, 2*M*(N-M)). * If IJOB = 3 or 5, LWORK >= MAX(4*N+16, 4*M*(N-M)). * * If LWORK = -1, then a workspace query is assumed; the routine * only calculates the optimal size of the WORK array, returns * this value as the first entry of the WORK array, and no error * message related to LWORK is issued by XERBLA. * * IWORK (workspace/output) INTEGER array, dimension (MAX(1,LIWORK)) * IF IJOB = 0, IWORK is not referenced. Otherwise, * on exit, if INFO = 0, IWORK(1) returns the optimal LIWORK. * * LIWORK (input) INTEGER * The dimension of the array IWORK. LIWORK >= 1. * If IJOB = 1, 2 or 4, LIWORK >= N+6. * If IJOB = 3 or 5, LIWORK >= MAX(2*M*(N-M), N+6). * * If LIWORK = -1, then a workspace query is assumed; the * routine only calculates the optimal size of the IWORK array, * returns this value as the first entry of the IWORK array, and * no error message related to LIWORK is issued by XERBLA. * * INFO (output) INTEGER * =0: Successful exit. * <0: If INFO = -i, the i-th argument had an illegal value. * =1: Reordering of (A, B) failed because the transformed * matrix pair (A, B) would be too far from generalized * Schur form; the problem is very ill-conditioned. * (A, B) may have been partially reordered. * If requested, 0 is returned in DIF(*), PL and PR. * * Further Details * =============== * * DTGSEN first collects the selected eigenvalues by computing * orthogonal U and W that move them to the top left corner of (A, B). * In other words, the selected eigenvalues are the eigenvalues of * (A11, B11) in: * * U'*(A, B)*W = (A11 A12) (B11 B12) n1 * ( 0 A22),( 0 B22) n2 * n1 n2 n1 n2 * * where N = n1+n2 and U' means the transpose of U. The first n1 columns * of U and W span the specified pair of left and right eigenspaces * (deflating subspaces) of (A, B). * * If (A, B) has been obtained from the generalized real Schur * decomposition of a matrix pair (C, D) = Q*(A, B)*Z', then the * reordered generalized real Schur form of (C, D) is given by * * (C, D) = (Q*U)*(U'*(A, B)*W)*(Z*W)', * * and the first n1 columns of Q*U and Z*W span the corresponding * deflating subspaces of (C, D) (Q and Z store Q*U and Z*W, resp.). * * Note that if the selected eigenvalue is sufficiently ill-conditioned, * then its value may differ significantly from its value before * reordering. * * The reciprocal condition numbers of the left and right eigenspaces * spanned by the first n1 columns of U and W (or Q*U and Z*W) may * be returned in DIF(1:2), corresponding to Difu and Difl, resp. * * The Difu and Difl are defined as: * * Difu[(A11, B11), (A22, B22)] = sigma-min( Zu ) * and * Difl[(A11, B11), (A22, B22)] = Difu[(A22, B22), (A11, B11)], * * where sigma-min(Zu) is the smallest singular value of the * (2*n1*n2)-by-(2*n1*n2) matrix * * Zu = [ kron(In2, A11) -kron(A22', In1) ] * [ kron(In2, B11) -kron(B22', In1) ]. * * Here, Inx is the identity matrix of size nx and A22' is the * transpose of A22. kron(X, Y) is the Kronecker product between * the matrices X and Y. * * When DIF(2) is small, small changes in (A, B) can cause large changes * in the deflating subspace. An approximate (asymptotic) bound on the * maximum angular error in the computed deflating subspaces is * * EPS * norm((A, B)) / DIF(2), * * where EPS is the machine precision. * * The reciprocal norm of the projectors on the left and right * eigenspaces associated with (A11, B11) may be returned in PL and PR. * They are computed as follows. First we compute L and R so that * P*(A, B)*Q is block diagonal, where * * P = ( I -L ) n1 Q = ( I R ) n1 * ( 0 I ) n2 and ( 0 I ) n2 * n1 n2 n1 n2 * * and (L, R) is the solution to the generalized Sylvester equation * * A11*R - L*A22 = -A12 * B11*R - L*B22 = -B12 * * Then PL = (F-norm(L)**2+1)**(-1/2) and PR = (F-norm(R)**2+1)**(-1/2). * An approximate (asymptotic) bound on the average absolute error of * the selected eigenvalues is * * EPS * norm((A, B)) / PL. * * There are also global error bounds which valid for perturbations up * to a certain restriction: A lower bound (x) on the smallest * F-norm(E,F) for which an eigenvalue of (A11, B11) may move and * coalesce with an eigenvalue of (A22, B22) under perturbation (E,F), * (i.e. (A + E, B + F), is * * x = min(Difu,Difl)/((1/(PL*PL)+1/(PR*PR))**(1/2)+2*max(1/PL,1/PR)). * * An approximate bound on x can be computed from DIF(1:2), PL and PR. * * If y = ( F-norm(E,F) / x) <= 1, the angles between the perturbed * (L', R') and unperturbed (L, R) left and right deflating subspaces * associated with the selected cluster in the (1,1)-blocks can be * bounded as * * max-angle(L, L') <= arctan( y * PL / (1 - y * (1 - PL * PL)**(1/2)) * max-angle(R, R') <= arctan( y * PR / (1 - y * (1 - PR * PR)**(1/2)) * * See LAPACK User's Guide section 4.11 or the following references * for more information. * * Note that if the default method for computing the Frobenius-norm- * based estimate DIF is not wanted (see DLATDF), then the parameter * IDIFJB (see below) should be changed from 3 to 4 (routine DLATDF * (IJOB = 2 will be used)). See DTGSYL for more details. * * Based on contributions by * Bo Kagstrom and Peter Poromaa, Department of Computing Science, * Umea University, S-901 87 Umea, Sweden. * * References * ========== * * [1] B. Kagstrom; A Direct Method for Reordering Eigenvalues in the * Generalized Real Schur Form of a Regular Matrix Pair (A, B), in * M.S. Moonen et al (eds), Linear Algebra for Large Scale and * Real-Time Applications, Kluwer Academic Publ. 1993, pp 195-218. * * [2] B. Kagstrom and P. Poromaa; Computing Eigenspaces with Specified * Eigenvalues of a Regular Matrix Pair (A, B) and Condition * Estimation: Theory, Algorithms and Software, * Report UMINF - 94.04, Department of Computing Science, Umea * University, S-901 87 Umea, Sweden, 1994. Also as LAPACK Working * Note 87. To appear in Numerical Algorithms, 1996. * * [3] B. Kagstrom and P. Poromaa, LAPACK-Style Algorithms and Software * for Solving the Generalized Sylvester Equation and Estimating the * Separation between Regular Matrix Pairs, Report UMINF - 93.23, * Department of Computing Science, Umea University, S-901 87 Umea, * Sweden, December 1993, Revised April 1994, Also as LAPACK Working * Note 75. To appear in ACM Trans. on Math. Software, Vol 22, No 1, * 1996. * * ===================================================================== * * .. Parameters .. INTEGER IDIFJB PARAMETER ( IDIFJB = 3 ) DOUBLE PRECISION ZERO, ONE PARAMETER ( ZERO = 0.0D+0, ONE = 1.0D+0 ) * .. * .. Local Scalars .. LOGICAL LQUERY, PAIR, SWAP, WANTD, WANTD1, WANTD2, $ WANTP INTEGER I, IERR, IJB, K, KASE, KK, KS, LIWMIN, LWMIN, $ MN2, N1, N2 DOUBLE PRECISION DSCALE, DSUM, EPS, RDSCAL, SMLNUM * .. * .. Local Arrays .. INTEGER ISAVE( 3 ) * .. * .. External Subroutines .. EXTERNAL DLACN2, DLACPY, DLAG2, DLASSQ, DTGEXC, DTGSYL, $ XERBLA * .. * .. External Functions .. DOUBLE PRECISION DLAMCH EXTERNAL DLAMCH * .. * .. Intrinsic Functions .. INTRINSIC MAX, SIGN, SQRT * .. * .. Executable Statements .. * * Decode and test the input parameters * INFO = 0 LQUERY = ( LWORK.EQ.-1 .OR. LIWORK.EQ.-1 ) * IF( IJOB.LT.0 .OR. IJOB.GT.5 ) THEN INFO = -1 ELSE IF( N.LT.0 ) THEN INFO = -5 ELSE IF( LDA.LT.MAX( 1, N ) ) THEN INFO = -7 ELSE IF( LDB.LT.MAX( 1, N ) ) THEN INFO = -9 ELSE IF( LDQ.LT.1 .OR. ( WANTQ .AND. LDQ.LT.N ) ) THEN INFO = -14 ELSE IF( LDZ.LT.1 .OR. ( WANTZ .AND. LDZ.LT.N ) ) THEN INFO = -16 END IF * IF( INFO.NE.0 ) THEN CALL XERBLA( 'DTGSEN', -INFO ) RETURN END IF * * Get machine constants * EPS = DLAMCH( 'P' ) SMLNUM = DLAMCH( 'S' ) / EPS IERR = 0 * WANTP = IJOB.EQ.1 .OR. IJOB.GE.4 WANTD1 = IJOB.EQ.2 .OR. IJOB.EQ.4 WANTD2 = IJOB.EQ.3 .OR. IJOB.EQ.5 WANTD = WANTD1 .OR. WANTD2 * * Set M to the dimension of the specified pair of deflating * subspaces. * M = 0 PAIR = .FALSE. DO 10 K = 1, N IF( PAIR ) THEN PAIR = .FALSE. ELSE IF( K.LT.N ) THEN IF( A( K+1, K ).EQ.ZERO ) THEN IF( SELECT( K ) ) $ M = M + 1 ELSE PAIR = .TRUE. IF( SELECT( K ) .OR. SELECT( K+1 ) ) $ M = M + 2 END IF ELSE IF( SELECT( N ) ) $ M = M + 1 END IF END IF 10 CONTINUE * IF( IJOB.EQ.1 .OR. IJOB.EQ.2 .OR. IJOB.EQ.4 ) THEN LWMIN = MAX( 1, 4*N+16, 2*M*( N-M ) ) LIWMIN = MAX( 1, N+6 ) ELSE IF( IJOB.EQ.3 .OR. IJOB.EQ.5 ) THEN LWMIN = MAX( 1, 4*N+16, 4*M*( N-M ) ) LIWMIN = MAX( 1, 2*M*( N-M ), N+6 ) ELSE LWMIN = MAX( 1, 4*N+16 ) LIWMIN = 1 END IF * WORK( 1 ) = LWMIN IWORK( 1 ) = LIWMIN * IF( LWORK.LT.LWMIN .AND. .NOT.LQUERY ) THEN INFO = -22 ELSE IF( LIWORK.LT.LIWMIN .AND. .NOT.LQUERY ) THEN INFO = -24 END IF * IF( INFO.NE.0 ) THEN CALL XERBLA( 'DTGSEN', -INFO ) RETURN ELSE IF( LQUERY ) THEN RETURN END IF * * Quick return if possible. * IF( M.EQ.N .OR. M.EQ.0 ) THEN IF( WANTP ) THEN PL = ONE PR = ONE END IF IF( WANTD ) THEN DSCALE = ZERO DSUM = ONE DO 20 I = 1, N CALL DLASSQ( N, A( 1, I ), 1, DSCALE, DSUM ) CALL DLASSQ( N, B( 1, I ), 1, DSCALE, DSUM ) 20 CONTINUE DIF( 1 ) = DSCALE*SQRT( DSUM ) DIF( 2 ) = DIF( 1 ) END IF GO TO 60 END IF * * Collect the selected blocks at the top-left corner of (A, B). * KS = 0 PAIR = .FALSE. DO 30 K = 1, N IF( PAIR ) THEN PAIR = .FALSE. ELSE * SWAP = SELECT( K ) IF( K.LT.N ) THEN IF( A( K+1, K ).NE.ZERO ) THEN PAIR = .TRUE. SWAP = SWAP .OR. SELECT( K+1 ) END IF END IF * IF( SWAP ) THEN KS = KS + 1 * * Swap the K-th block to position KS. * Perform the reordering of diagonal blocks in (A, B) * by orthogonal transformation matrices and update * Q and Z accordingly (if requested): * KK = K IF( K.NE.KS ) $ CALL DTGEXC( WANTQ, WANTZ, N, A, LDA, B, LDB, Q, LDQ, $ Z, LDZ, KK, KS, WORK, LWORK, IERR ) * IF( IERR.GT.0 ) THEN * * Swap is rejected: exit. * INFO = 1 IF( WANTP ) THEN PL = ZERO PR = ZERO END IF IF( WANTD ) THEN DIF( 1 ) = ZERO DIF( 2 ) = ZERO END IF GO TO 60 END IF * IF( PAIR ) $ KS = KS + 1 END IF END IF 30 CONTINUE IF( WANTP ) THEN * * Solve generalized Sylvester equation for R and L * and compute PL and PR. * N1 = M N2 = N - M I = N1 + 1 IJB = 0 CALL DLACPY( 'Full', N1, N2, A( 1, I ), LDA, WORK, N1 ) CALL DLACPY( 'Full', N1, N2, B( 1, I ), LDB, WORK( N1*N2+1 ), $ N1 ) CALL DTGSYL( 'N', IJB, N1, N2, A, LDA, A( I, I ), LDA, WORK, $ N1, B, LDB, B( I, I ), LDB, WORK( N1*N2+1 ), N1, $ DSCALE, DIF( 1 ), WORK( N1*N2*2+1 ), $ LWORK-2*N1*N2, IWORK, IERR ) * * Estimate the reciprocal of norms of "projections" onto left * and right eigenspaces. * RDSCAL = ZERO DSUM = ONE CALL DLASSQ( N1*N2, WORK, 1, RDSCAL, DSUM ) PL = RDSCAL*SQRT( DSUM ) IF( PL.EQ.ZERO ) THEN PL = ONE ELSE PL = DSCALE / ( SQRT( DSCALE*DSCALE / PL+PL )*SQRT( PL ) ) END IF RDSCAL = ZERO DSUM = ONE CALL DLASSQ( N1*N2, WORK( N1*N2+1 ), 1, RDSCAL, DSUM ) PR = RDSCAL*SQRT( DSUM ) IF( PR.EQ.ZERO ) THEN PR = ONE ELSE PR = DSCALE / ( SQRT( DSCALE*DSCALE / PR+PR )*SQRT( PR ) ) END IF END IF * IF( WANTD ) THEN * * Compute estimates of Difu and Difl. * IF( WANTD1 ) THEN N1 = M N2 = N - M I = N1 + 1 IJB = IDIFJB * * Frobenius norm-based Difu-estimate. * CALL DTGSYL( 'N', IJB, N1, N2, A, LDA, A( I, I ), LDA, WORK, $ N1, B, LDB, B( I, I ), LDB, WORK( N1*N2+1 ), $ N1, DSCALE, DIF( 1 ), WORK( 2*N1*N2+1 ), $ LWORK-2*N1*N2, IWORK, IERR ) * * Frobenius norm-based Difl-estimate. * CALL DTGSYL( 'N', IJB, N2, N1, A( I, I ), LDA, A, LDA, WORK, $ N2, B( I, I ), LDB, B, LDB, WORK( N1*N2+1 ), $ N2, DSCALE, DIF( 2 ), WORK( 2*N1*N2+1 ), $ LWORK-2*N1*N2, IWORK, IERR ) ELSE * * * Compute 1-norm-based estimates of Difu and Difl using * reversed communication with DLACN2. In each step a * generalized Sylvester equation or a transposed variant * is solved. * KASE = 0 N1 = M N2 = N - M I = N1 + 1 IJB = 0 MN2 = 2*N1*N2 * * 1-norm-based estimate of Difu. * 40 CONTINUE CALL DLACN2( MN2, WORK( MN2+1 ), WORK, IWORK, DIF( 1 ), $ KASE, ISAVE ) IF( KASE.NE.0 ) THEN IF( KASE.EQ.1 ) THEN * * Solve generalized Sylvester equation. * CALL DTGSYL( 'N', IJB, N1, N2, A, LDA, A( I, I ), LDA, $ WORK, N1, B, LDB, B( I, I ), LDB, $ WORK( N1*N2+1 ), N1, DSCALE, DIF( 1 ), $ WORK( 2*N1*N2+1 ), LWORK-2*N1*N2, IWORK, $ IERR ) ELSE * * Solve the transposed variant. * CALL DTGSYL( 'T', IJB, N1, N2, A, LDA, A( I, I ), LDA, $ WORK, N1, B, LDB, B( I, I ), LDB, $ WORK( N1*N2+1 ), N1, DSCALE, DIF( 1 ), $ WORK( 2*N1*N2+1 ), LWORK-2*N1*N2, IWORK, $ IERR ) END IF GO TO 40 END IF DIF( 1 ) = DSCALE / DIF( 1 ) * * 1-norm-based estimate of Difl. * 50 CONTINUE CALL DLACN2( MN2, WORK( MN2+1 ), WORK, IWORK, DIF( 2 ), $ KASE, ISAVE ) IF( KASE.NE.0 ) THEN IF( KASE.EQ.1 ) THEN * * Solve generalized Sylvester equation. * CALL DTGSYL( 'N', IJB, N2, N1, A( I, I ), LDA, A, LDA, $ WORK, N2, B( I, I ), LDB, B, LDB, $ WORK( N1*N2+1 ), N2, DSCALE, DIF( 2 ), $ WORK( 2*N1*N2+1 ), LWORK-2*N1*N2, IWORK, $ IERR ) ELSE * * Solve the transposed variant. * CALL DTGSYL( 'T', IJB, N2, N1, A( I, I ), LDA, A, LDA, $ WORK, N2, B( I, I ), LDB, B, LDB, $ WORK( N1*N2+1 ), N2, DSCALE, DIF( 2 ), $ WORK( 2*N1*N2+1 ), LWORK-2*N1*N2, IWORK, $ IERR ) END IF GO TO 50 END IF DIF( 2 ) = DSCALE / DIF( 2 ) * END IF END IF * 60 CONTINUE * * Compute generalized eigenvalues of reordered pair (A, B) and * normalize the generalized Schur form. * PAIR = .FALSE. DO 80 K = 1, N IF( PAIR ) THEN PAIR = .FALSE. ELSE * IF( K.LT.N ) THEN IF( A( K+1, K ).NE.ZERO ) THEN PAIR = .TRUE. END IF END IF * IF( PAIR ) THEN * * Compute the eigenvalue(s) at position K. * WORK( 1 ) = A( K, K ) WORK( 2 ) = A( K+1, K ) WORK( 3 ) = A( K, K+1 ) WORK( 4 ) = A( K+1, K+1 ) WORK( 5 ) = B( K, K ) WORK( 6 ) = B( K+1, K ) WORK( 7 ) = B( K, K+1 ) WORK( 8 ) = B( K+1, K+1 ) CALL DLAG2( WORK, 2, WORK( 5 ), 2, SMLNUM*EPS, BETA( K ), $ BETA( K+1 ), ALPHAR( K ), ALPHAR( K+1 ), $ ALPHAI( K ) ) ALPHAI( K+1 ) = -ALPHAI( K ) * ELSE * IF( SIGN( ONE, B( K, K ) ).LT.ZERO ) THEN * * If B(K,K) is negative, make it positive * DO 70 I = 1, N A( K, I ) = -A( K, I ) B( K, I ) = -B( K, I ) Q( I, K ) = -Q( I, K ) 70 CONTINUE END IF * ALPHAR( K ) = A( K, K ) ALPHAI( K ) = ZERO BETA( K ) = B( K, K ) * END IF END IF 80 CONTINUE * WORK( 1 ) = LWMIN IWORK( 1 ) = LIWMIN * RETURN * * End of DTGSEN * END


|
JB.Toolkit
A collection of useful classes, extensions and tools written in C# to aid in a wide variety of development. With components for ASP Net MVC, Winform and Console applications. Integrations for Google, AD and SQL and pre-written implementations / helpers for a variety of tasks... Just Because.
|
Accesses a given Active Directory server as specified in the web.config for retrieving additional user information More...
Public Types | |
| enum | IdentityType { IdentityType.LogonIdenity, IdentityType.Environment, IdentityType.PrincipleIdentity } |
Static Public Member Functions | |
| static ADUser | GetADUser (string username, bool callingFromWithinDomain, string adServerIpAddress, string adServerHostName, string adAdminUsername, string adAdminPassword) |
| Gets Active Directory attributes of a given user specified by username More... | |
| static ADUser | GetADUser (string username, bool callingFromWithinDomain, string adServerIpAddress, string adServerHostName, string adDomainName, string adAdminUsername, string adAdminPassword) |
| Gets Active Directory attributes of a given user specified by username More... | |
| static ADUser | GetADUserManager (string username, bool callingFromWithinDomain, string adServerIpAddress, string adServerHostName, string adAdminUsername, string adAdminPassword, bool recursivelyGetHierarchyAbove=false) |
| Returns the manager ADUser object of the user More... | |
| static ADUser | GetADUserManager (string username, bool callingFromWithinDomain, string adServerIpAddress, string adServerHostName, string adDomainName, string adAdminUsername, string adAdminPassword, bool recursivelyGetHierarchyAbove=false) |
| Returns the manager ADUser object of the user More... | |
| static string | GetUsernameFromEmailAddress (string emailAddress, bool callingFromWithinDomain, string adServerIpAddress, string adServerHostName, string adAdminUsername, string adAdminPassword) |
| Returns the username from an AD search by email address More... | |
| static string | GetNameFromEmailAddress (string emailAddress, bool callingFromWithinDomain, string adServerIpAddress, string adServerHostName, string adAdminUsername, string adAdminPassword) |
| Returns the full name from an AD search by email address More... | |
| static string | GetNameFromUsername (string username, bool callingFromWithinDomain, string adServerIpAddress, string adServerHostName, string adAdminUsername, string adAdminPassword) |
| Returns the full name from an AD search by username More... | |
| static string | GetEmailFromUsername (string username, bool callingFromWithinDomain, string adServerIpAddress, string adServerHostName, string adAdminUsername, string adAdminPassword) |
| Returns the email from an AD search by username More... | |
| static string | GetLoggedInUserEmail () |
| Returns the logged in user email (don't use this if deploying to IIS without Windows authentication) More... | |
| static byte[] | GetADPhotoFromEmail (string email, bool callingFromWithinDomain, string adServerIpAddress, string adServerHostName, string adAdminUsername, string adAdminPassword) |
| Gets the photo stored in AD user More... | |
| static byte[] | GetADPhotoFromUsername (string username, bool callingFromWithinDomain, string adServerIpAddress, string adServerHostName, string adAdminUsername, string adAdminPassword) |
| Gets the photo stored in AD user More... | |
| static SearchResultCollection | GetAllUsers (bool callingFromWithinDomain, string adServerIpAddress, string adServerHostName, string adAdminUsername, string adAdminPassword) |
| Returns all users of the domain More... | |
| static SearchResult | GetADSearchResult (string searchString, string searchFilter, bool callingFromWithinDomain, string adServerIpAddress, string adServerHostName, string adAdminUsername, string adAdminPassword) |
| Returns a DirectoryServices SearchResult object More... | |
| static SearchResultCollection | GetADSearchResults (string searchString, string searchFilter, bool callingFromWithinDomain, string adServerIpAddress, string adServerHostName, string adAdminUsername, string adAdminPassword) |
| Returns a DirectoryServices SearchResult object More... | |
| static string | GetUsername (IdentityType identityType) |
| Get username based on different types of 'IdentityType' More... | |
Accesses a given Active Directory server as specified in the web.config for retrieving additional user information
|
strong |
|
static |
Gets the photo stored in AD user
| adServerIpAddress | IP Address of AD controller |
| adServerHostName | Host name of AD controller |
| adAdminUsername | A user username who's able to read the AD DB |
| adAdminPassword | A user password who's able to read the AD DB |
|
static |
Gets the photo stored in AD user
| adServerIpAddress | IP Address of AD controller |
| adServerHostName | Host name of AD controller |
| adAdminUsername | A user username who's able to read the AD DB |
| adAdminPassword | A user password who's able to read the AD DB |
|
static |
Returns a DirectoryServices SearchResult object
| adServerIpAddress | IP Address of AD controller |
| adServerHostName | Host name of AD controller |
| adAdminUsername | A user username who's able to read the AD DB |
| adAdminPassword | A user password who's able to read the AD DB |
|
static |
Returns a DirectoryServices SearchResult object
| adServerIpAddress | IP Address of AD controller |
| adServerHostName | Host name of AD controller |
| adAdminUsername | A user username who's able to read the AD DB |
| adAdminPassword | A user password who's able to read the AD DB |
|
static |
Gets Active Directory attributes of a given user specified by username
| username | The username to retrieve AD attribute for |
| callingFromWithinDomain | Are we call from within AD or from a DMZ |
| adServerIpAddress | IP Address of AD controller |
| adServerHostName | Host name of AD controller |
| adAdminUsername | A user username who's able to read the AD DB |
| adAdminPassword | A user password who's able to read the AD DB |
|
static |
Gets Active Directory attributes of a given user specified by username
| username | The username to retrieve AD attribute for |
| callingFromWithinDomain | Are we call from within AD or from a DMZ |
| adServerIpAddress | IP Address of AD controller |
| adServerHostName | Host name of AD controller |
| adDomainName | I.e. Likely be the company name |
| adAdminUsername | A user username who's able to read the AD DB |
| adAdminPassword | A user password who's able to read the AD DB |
|
static |
Returns the manager ADUser object of the user
| adServerIpAddress | IP Address of AD controller |
| adServerHostName | Host name of AD controller |
| adAdminUsername | A user username who's able to read the AD DB |
| adAdminPassword | A user password who's able to read the AD DB |
|
static |
Returns the manager ADUser object of the user
| adServerIpAddress | IP Address of AD controller |
| adServerHostName | Host name of AD controller |
| adDomainName | I.e. Likely be the company name |
| adAdminUsername | A user username who's able to read the AD DB |
| adAdminPassword | A user password who's able to read the AD DB |
|
static |
Returns all users of the domain
| adServerIpAddress | IP Address of AD controller |
| adServerHostName | Host name of AD controller |
| adAdminUsername | A user username who's able to read the AD DB |
| adAdminPassword | A user password who's able to read the AD DB |
|
static |
Returns the email from an AD search by username
| adServerIpAddress | IP Address of AD controller |
| adServerHostName | Host name of AD controller |
| adAdminUsername | A user username who's able to read the AD DB |
| adAdminPassword | A user password who's able to read the AD DB |
|
static |
Returns the logged in user email (don't use this if deploying to IIS without Windows authentication)
|
static |
Returns the full name from an AD search by email address
| adServerIpAddress | IP Address of AD controller |
| adServerHostName | Host name of AD controller |
| adAdminUsername | A user username who's able to read the AD DB |
| adAdminPassword | A user password who's able to read the AD DB |
|
static |
Returns the full name from an AD search by username
| adServerIpAddress | IP Address of AD controller |
| adServerHostName | Host name of AD controller |
| adAdminUsername | A user username who's able to read the AD DB |
| adAdminPassword | A user password who's able to read the AD DB |
|
static |
Get username based on different types of 'IdentityType'
|
static |
Returns the username from an AD search by email address
| adServerIpAddress | IP Address of AD controller |
| adServerHostName | Host name of AD controller |
| adAdminUsername | A user username who's able to read the AD DB |
| adAdminPassword | A user password who's able to read the AD DB |


Simple input field to check the
Tab
Empty list
#24
Label 1
1.0. First
1.1. Selected
1.2. Disabled
1.3. Hidden
1.4. Last
Check one more field on the same page + Check
select
inside
label
2.0. First
2.1. Selected
2.2. Disabled
2.3. Hidden
2.4. Last
Check the
style
attribute
#20
Without the style attribute
Green
Orange
Check moving by arrows
Up
and
Down
on the hidden and disabled options
#22
.
Check auto selection when no option is selected
#23
.
Hidden
Disabled
2
Hidden
4
5
Hidden
1
2
3
4
5
A value has been change by the script to
—
0
1
2
3
4
5


My portfolio
(copied from https://dannguyen.github.io/github-for-portfolios/lessons/premanufactured-github-pages/ and being edited right now to learn MD language)
My name is Manuel C.
Currently working through the FreeCodeCamp curriculum
- Bastards Book of Regular Expressions - A free ebook on how to use regular expressions for everyday informational purposes
To be edited A picture of someone playing the didgeridoo
Here's video of that performance:


--- layout: minimal title: "Suggested #100DaysDX Topics" permalink: /topics/index.html description: "100DaysDX topic suggestions" ---
Future topics planning is handled with Github issues and labels.
Did not found what you wanted? Suggest a topic for a post!
Raise an issue in Github! I will not promise to write about your suggestion, but I promise to consider it thoroughly.


Awesome Books
List
Add new
Contact
time
Awesome Library
Add a new Book
Add
Contact Information
Do you have any questions or you just want to say "Hello"?
You can reach out to us!
LinkedIn:
Daniel Maina
Elyor Doniyorov
Twitter:
Daniel Maina
Elyor Doniyorov


Youtube API Data Trend
Author: Aaron Wu (pwu8), Abhishek Dutta (adutta2), Natalie Roe (nroe), Preston Law (plaw), Wennie Zhang (yzhang46)
This Week's Work
Since our last blog post, we have had to refine our focus for this project as a group. Specifically, we wanted to focus on performing analysis to answer a specific question about our data. Since our data set is so large (we have access to data pertaining to every video on YouTube), we knew we had to scale down the data that we would be using for this assignment to some subcategory of YouTube videos. We decided that we will be analyzing the most recently uploaded videos onto YouTube since March 1 2017. From these videos, we will be extracting information from the title, description, and user-defined tags to perform our analysis.
We also shifted the focus of the analysis that we will be performing on our data. We will be using ML to predict labels for YouTube videos based on each video’s title, description, and tags defined by the user. We obtained a set of labels from the Kaggle YouTube-8M challenge and we will be assigning one or more labels to each video in our dataset. These labels include Games, Concert, Vehicle, etc. From this, then want to have our web application be able to return the top ten videos with a particular label based on a user’s query. Furthermore, a couple visualizations that we are planning to do with our data include:
- Plotting the how particular labels trend over the course of time along with the sentiments of the labels
- Showing how popular videos with a particular tag are in different areas of the world
- Comparing how our labels compare to the tags that users assign to their own videos and seeing if users are actually accurate when tagging their videos
While our previous visualizations and analysis are not exactly related to our new focus, they helped familiarize us with our data and through our exploration we gained a strong understanding for all of the information that our data provides, which ultimately led us to to path that we are currently pursuing with our project. Please see our Final Proposal for more information.
Moving Forward
- Grab the title, description, and user-inputted tags from our set of YouTube videos
- Clean the data so that we stem words, remove caps, etc.
- Fit the data with our labels which are from YouTube-8M. We are considering using SVM or a multi-label nearest neighbor algorithm: multi-label nearest neighbor algorithm
- Create visualizations from our data (we will likely be using D3)
- Create an interactive web application that will allow users to search for keywords and get back the Top 10 YouTube videos that have the keyword as a tag.
Home Page # is: here .


Inference¶
In the context of probabilistic modeling, learning is usually called inference. In the case where the model has latent random variables, this involves computing (approximate) posterior distributions. In the case of parameterized models, this usually involves some sort of optimization. Pyro supports multiple inference algorithms, with support for stochastic variational inference (SVI) being the most extensive. Look here for more inference algorithms in future versions of Pyro.
SVI¶
-
class
SVI
(model, guide, optim, loss, loss_and_grads=None, *args, **kwargs)¶ Bases:
object
Parameters: - model – the model (callable containing Pyro primitives)
- guide – the guide (callable containing Pyro primitives)
- optim (pyro.optim.PyroOptim) – a wrapper a for a PyTorch optimizer
- loss – this is either a string that specifies the loss function to be used (currently the only supported built-in loss is ‘ELBO’) or a user-provided loss function; in the case this is a built-in loss loss_and_grads will be filled in accordingly
- loss_and_grads – if specified, this user-provided callable computes gradients for use in step() and marks which parameters in the param store are to be optimized
A unified interface for stochastic variational inference in Pyro. Most users will interact with SVI with the argument loss=”ELBO”. See the tutorial SVI Part I for a discussion.
ELBO¶
-
class
ELBO
(num_particles=1, trace_graph=False, enum_discrete=False)¶ Bases:
object
Parameters: - num_particles – the number of particles (samples) used to form the ELBO estimator.
- trace_graph – boolean. whether to keep track of dependency information when running the model and guide. this information can be used to form a gradient estimator with lower variance in the case that some of the random variables are non-reparameterized. note: for a model with many random variables, keeping track of the dependency information can be expensive. see the tutorial SVI Part III for a discussion.
- enum_discrete (bool) – whether to sum over discrete latent variables, rather than sample them.
ELBO is the top-level interface for stochastic variational inference via optimization of the evidence lower bound. Most users will not interact with ELBO directly; instead they will interact with SVI. ELBO dispatches to Trace_ELBO and TraceGraph_ELBO, where the internal implementations live.
Warning
enum_discrete is a bleeding edge feature. see SS-VAE for a discussion.
References
[1] Automated Variational Inference in Probabilistic Programming David Wingate, Theo Weber
[2] Black Box Variational Inference, Rajesh Ranganath, Sean Gerrish, David M. Blei
-
loss
(model, guide, *args, **kwargs)¶ Evaluates the ELBO with an estimator that uses num_particles many samples/particles, where num_particles is specified in the constructor.
Returns: returns an estimate of the ELBO Return type: float
-
loss_and_grads
(model, guide, *args, **kwargs)¶ Computes the ELBO as well as the surrogate ELBO that is used to form the gradient estimator. Performs backward on the latter. Num_particle many samples are used to form the estimators, where num_particles is specified in the constructor.
Returns: returns an estimate of the ELBO Return type: float
Importance¶
-
class
Importance
(model, guide=None, num_samples=None)¶ Bases:
pyro.infer.abstract_infer.TracePosterior
Parameters: - model – probabilistic model defined as a function
- guide – guide used for sampling defined as a function
- num_samples – number of samples to draw from the guide (default 10)
This method performs posterior inference by importance sampling using the guide as the proposal distribution. If no guide is provided, it defaults to proposing from the model’s prior.
-
log_z
(*args, **kwargs)¶ Estimate the marginal likelihood of observations using importance weights. Takes the same inputs as self.model and self.guide and returns a 1-element tensor containing the estimate.


Twilio Speech Recognition
English
German
Swedish
Call
{{identity}} | {{sync.state}}
{{language}} | {{sync.call.to}} | {{sync.call.status}}
{{label}}


Dominic Raab: Tory Twitter name change was 'instant rebuttal to Labour nonsense' - video
by APDominic Raab has commented on the controversy after the Conservative party was accused of trying to deceive voters by changing the name of its press office Twitter channel to 'factcheckUK' during Tuesday's night's debate between Boris Johnson and Jeremy Corbyn. The foreign secretary said the name change was an 'instant rebuttal to the nonsense that is serially and systematically put out by the Labour party'


HTML stands for Hypertext Markup Language. In simplest terms, HTML is used to display the information that either you and/or a program tells it to display.
Here is an example of HTML:

Title

This is a header tag!

This is a paragraph!

Ideally you are using the TeamTreehouse course that we recommend to start your learning process. This page is meant to aid you in learning HTML and is not a stand alone resource. Our hope is to give you a quick overview to kick start the learning process for someone who knows nothing about HTML.
HTML is made up of elements. An element is an individual component usually surrounded by similar looking tags. You can determine the type of element and what it contains by looking at the opening and closing tags. A closing tag includes a "/" at the beginning of it.
Beware, not all elements require a closing tag.

This is a header tag!

This is a paragraph!

<input type="text">

There are certain elements every HTML file has to start out with. These are written inside the head element, which gives the computer information, such as: what language you are using, viewport parameters, and where to look for your CSS stylesheets.
Here is an example of what almost every HTML file will start out looking like:
<!DOCTYPE html>

<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Learn Html</title>
<link rel="stylesheet" type="text/css" href="../styles/styles.css">

It could be helpful to bookmark this page to access the sample above quickly. Copying and pasting it at the beginning of your HTML document is a quick way to get to the enjoyable part of writing in HTML. Plus it's just easier when you don't have to try and remember this while you are learning to code.
Not only can an element contain content of its own but it can also contain other elements and most commonly does. Nesting is accomplished by indenting the lines that are contained inside the element.
An element that contains another element is called a Parent and the element that inside is called a Child. Elements that are on equal levels are called Siblings.

I'm the Parent Element!!!

I'm a child!

I'm a child and siblings with the h2 tag above me!

There is one element in HTML that is not displayed in the browser. This is the comment element. Just like every other element in HTML you use an opening and closing tag to tell the browser that it's a comment and should not be displayed.

Nesting

This the paragraph on Nesting. Its long so I made it shorter.

Paragraph Parent Sample called a Child. Example Siblings.

I'm the Parent Element!!!

I'm a child!

I'm a child and siblings with the h2 tag above me!

If you make comments in your HTML as you go, then it will be infinitely easier to find it later. This is super useful as you continue to build and maintain as you build.


The sources for this package are in the main Angular2 repo. Please file issues and pull requests against that repo. This is the repository for the upcoming 2.0 version. If you're looking for the current official version of Angular you should go to angular/angular.js
This package contains different sources for different users:
/es6
are es6 compatible files that can be transpiled to
es5 using any transpiler. This contains:dev/
: a development version that includes runtime type assertionsprod/
: a production version that does not include runtime type assertions/ts
are the TypeScript source files.License: Apache MIT 2.0
Generated using TypeDoc


| Figure 1 |
Maximum monsoon season Standardized Precipitation and Evapot ... |
| Figure 2 |
Effects of average Standardized Precipitation and Evapotrans ... |
| Figure 3 |
Effects of floods during infancy on likelihood of stunting ( ... |
| Figure 4 |
Effects of floods in the 3 months prior to measurement on li ... |


Vue forms
Basic form input handling with Vue. And Bootstrap 4 styling.
Email
Password
Text
Textarea
{{textArea}}
Slider
Calculate
{{selectedOperator.title}}
Result
Radio
{{item}}
Operators
{{item.title}}
Multi select
{{item}}
Some other select
{{item}}
Toggle boolean
Toggle value
{{ item }}
https://jsfiddle.net/mani04/3uznmk72/
https://jsfiddle.net/djibe89/at2cv9q1/
https://jsfiddle.net/lthr/jze8gqb1/


Debugging mobile apps can be tricky. Especially when debugging production apps. One of our team's most powerful tools is Charles Proxy. It is used by QA, Developers and even by other non R&D teams.
Here I will demonstrate some super easy and super powerful techniques used to debug/monitor our mobile app. (By the way - most of the stuff here are true for regular desktop/mobile web apps as well)
Connect your mobile device to your Mac/PC
So - lets get started. First you will have to connect your device to your PC/Mac. This is SUPER easy when using Charles (both mobile device and computer should be on the same network):
- Check your network's local IP address. The simplest way is using Charles' help menu:
Connect your mobile device to this address:
2.1. Android (OS 4.X and above. For Android 2.X look here) Go to settings->network->wifi and long press your selected network. Then choose the following settings (the IP is the IP you located in previous section)
2.2. iOS Go to wifi settings and choose your internal network
Charles will ask for your approval (only first time you connect your device):
And... That's it. You are connected and are ready to get started!
Monitor mobile traffic
The first thing to do is monitor your mobile app's traffic. Lets choose IMDB as a "victim" app and monitor its traffic. I'll choose "record" button and open IMDB. This is what we'll see:
What we sees here is filter I created with imdbws.com as a domain. You can set focussed domains on "view->FocussedHosts". From here we can monitor all request/response data, including timing charts!
Request data
Response Data
Time Charts
In the next part of this post I will show some advanced, super powerful use cases, like setting Rewrite rules (for changing request response on the fly), Throttling (for testing different network speeds), Using VPN, routing the traffic to staging servers and more...


SoundSpace
Create Room
Join Room
Public
Private
Create Room
Back
Join Room
Back
Currently Playing
Your browser does not support this music player.
«
»
Queue
SONG
ARTIST
ALBUM
Add/Suggest Songs


Another tornado reported near Ulundi, SAWS issues more severe storm warnings
by Andy WalkerKwaZulu-Natal is fast becoming the tornado capital of South Africa, after the second tornado of the weekend was reported in Ulundi on Saturday.
Images published by Twitter user @sakhilemagalela, and retweeted by the South African Weather Service, shows the whirlwind spinning near eDlebe Village, near the former provincial capital.
The report comes just hours after SAWS issued a severe storm alert for those near Ulundi.
“Please note that this storm could lead to damaging winds and large hail. It is moving south-east. Warning has been issued,” it tweeted around 4pm SAST.
Four tornados in less than two weeks
A tornado is defined by SAWS as a “rotating column of air extending from the cloud base of a cumuliform cloud and is in contact with the ground”.
These vortices can result in extensive damages and loss of life,” SAWS warned in an earlier statement.
This is the fourth such report of a tornado in KwaZulu-Natal since the month began.
On 13 November, tornadoes near Bergville and New Hanover were reported.
Earlier on Saturday, SAWS also confirmed that another had occurred close to Utrecht near Newcastle on Friday night.
A number of weather warnings and watches have been issued by SAWS on Saturday, covering a vast swathe of KwaZulu-Natal, Mpumalanga, southern Limpopo and Gauteng.
Follow the South African Weather Service
Weather is fickle and subject to change, so we’d recommend following SAWS on its official Twitter account, especially for severe thunderstorm updates.
And be sure to bookmark its warnings portal too, where it regularly updates the advisories and more serious information on the daily.
Developing…
Feature image: screenshot, @SAWeatherServic and @sakhilemagalela via Twitter


--- layout: post tags: [Languages, Unit Testing, xUnit.net, F#] date: 2012-06-04 05:16:35 UTC title: "Bank OCR kata in F#: user stories 3 and 4" description: "A walk-through of user story 3 and 4 of the Bank OCR kata done in F#." permalink: /2012/06/04/BankOCRkatainFuserstories3and4 --- {% include JB/setup %}
In previous posts, I've walked through the first two user stories of the Bank OCR kata in F#. In this post I'll walk through my implementation of user stories 3 and 4.
The reason I'm collecting both user stories in a single blog post is that the requirements of user story 4 represent a change from user story 3, and since I didn't use a DVCS for this kata, I no longer have my implementation for user story 3.

The test is really simple: it's 85 lines of data and three lines of code which state that the result of piping the entry argument to the ParseToPrintout function should be equal to the expected argument.
The implementation may seem more daunting. At first, I'll post the function in its entirety, and afterwards I'll break it down and walk you through it.
let ParseToPrintout entry = let toPrintable opt = match opt with | Some(d) -> d.ToString() | None -> "?" let formatEntry s = s |> ParseToDigits |> Seq.map toPrintable |> String.Concat let isLegible potentialDigits = potentialDigits |> Seq.forall Option.isSome let isValidAndLegible s = let potentialDigits = s |> ParseToDigits (potentialDigits |> isLegible) && (potentialDigits |> skipNones |> IsValid) if entry |> isValidAndLegible then entry |> formatEntry else let getCandidates chars = let withAlternatives c = seq { match c with | ' ' -> yield! [ '|'; '_' ] | '|' | '_' -> yield ' ' | _ -> yield! [] } chars |> Seq.mapi (fun i _ -> chars |> ReplaceAt i withAlternatives) |> Seq.concat let validCandidates = entry.ToCharArray() |> getCandidates |> Seq.map ToString |> Seq.filter isValidAndLegible |> Seq.toList let formatAlternatives alternatives = alternatives |> Seq.map formatEntry |> Seq.map (sprintf "'%s'") |> Seq.reduce (sprintf "%s, %s") match validCandidates with | [head] -> head |> formatEntry | [] -> if entry |> ParseToDigits |> isLegible then entry |> formatEntry |> sprintf "%s ERR" else entry |> formatEntry |> sprintf "%s ILL" | _ -> validCandidates |> formatAlternatives |> sprintf "%s AMB [%s]" (formatEntry entry)
The first thing to notice is that the ParseToPrintout function is composed of quite a few helper functions. A thing that I currently don't like so much about F# is that it's necessary to define a function before it can be used, so all the helper functions must appear before the function that defines the general flow.
It's possible to move the helper functions to other modules so that they don't clutter the implementation, but most of the functions defined here seem to me to be a very specialized part of the overall implementation. In this particular code base, I've been working under the assumption that it would be best to define a function in as narrow a scope as possible, but looking at the result, I don't think that was the right decision. In the future, I think I'll move more helper functions to a separate module.
As a start, skim past the helper functions. The actual program flow of the function starts here:
if entry |> isValidAndLegible then entry |> formatEntry
That part of the if/then branch should be fairly easy to understand. If the entry is valid (according to the checksum function from user story 2) and legible, the result is formatted and returned.
The isValidAndLegible function determines whether the entry is... well... valid and legible. The first thing it does is to pipe the string into the ParseToDigits function, which, you'll recall, returns a sequence of integer options.
To figure out whether those potential digits are legible it invokes the isLegible function, which I'll return to shortly.
In order to figure out whether the potential digits are valid, it invokes the IsValid method from user story 2. However, in order to do so, it needs to convert the sequence of integer options to a sequence of integers. It does that by invoking the skipNones helper method, that I defined in a helper module:
let skipNones sequence = Seq.collect Option.toArray sequence
This function takes a sequence of options and returns a sequence of values by skipping all the Nones. It does that by turning each option into an array of 0 or 1 elements and then concatenate all these arrays together to form a new sequence.
This array is piped into the IsValid function, which will only return true if there are exactly nine digits and the checksum is OK.
The isLegible function is simpler because all it needs to do is to verify that all the options are Somes (instead of Nones).
The formatEntry function pipes the entry into the ParseToDigits function, which returns a sequence of integer options. Each option is mapped into a string, replacing each None with a "?".
Finally, the sequence of strings is concatenated into a single string by piping it to the built-in String.Concat method.
While formatting the entry is fairly easy if it's valid and legible, it suddenly becomes much harder to attempt to find other candidates if the entry seems invalid. What needs to be done in this case is to iterate through each and every character of the entry and try to replace just a single character at a time to see if it produces a better entry.
According to user story 4, a blank space could be either a vertical pipe or and underscore, while an underscore and a pipe might instead be a blank space. This relationship can be formalized by the withAlternatives function:
let withAlternatives c = seq { match c with | ' ' -> yield! [ '|'; '_' ] | '|' | '_' -> yield ' ' | _ -> yield! [] }
This function takes as input a single character and returns a sequence of characters. If the input is ' ', the output is a list containing '|' and '_'. On the other hand, if the input is either '|' or '_', the output is a sequence with the single element ' '. For all other characters (such as line breaks), the output is an empty sequence, indicating that no replacement should be attempted.
This defines the alternatives which can be fed into the ReplaceAt helper function:
let ReplaceAt index produceReplacements sequence = sequence |> Seq.nth index |> produceReplacements |> Seq.map (fun x -> sequence |> ReplaceElementAt index x)
In this case, the ReplaceAt function takes as input a sequence of characters (the entry string) and a function providing alternatives (withAlternatives) and produces a sequence of sequence of characters where only a single character has been replaced according to the alternatives.
If that sounds difficult, perhaps these unit tests explain the behavior better:
[<Fact>] let ProduceAllReplacementsAtReturnsCorrectResult1() = let actual = ReplaceAt 0 (fun c -> [ 'I'; 'i' ]) "123" actual |> Seq.map ToString |> should equalSequence [ "I23"; "i23" ] [<Fact>] let ProduceAllReplacementsAtReturnsCorrectResult2() = let actual = ReplaceAt 1 (fun c -> [ 'V' ]) "153" actual |> Seq.map ToString |> should equalSequence [ "1V3" ]
The first unit tests replaces the first (zero-indexed) character in the input string "123" with two alternatives, 'I' and 'i', producing the output sequence of "I23" and "i23".
The second test replaces the second (zero-indexed) character in the input string "153" with the single alternative 'V' to produce the output "1V3".
The ReplaceAt function does this by first using the Seq.nth function to pick the nth character out of the sequence, and then pipe that character into the function that produces the alternatives. This produces a sequence of replacement characters, e.g. '|' and '_' as explained above.
This sequence of replacement characters is then used to produce a sequence of strings where the element at the index is replaced with each of the replacement characters. In order to do that, the original input is piped into the ReplaceElementAt helper function:
let ReplaceElementAt index element sequence = let beforeIndex = sequence |> Seq.take index let atIndex = element |> Seq.singleton let afterIndex = sequence |> Seq.skip (index + 1) afterIndex |> Seq.append atIndex |> Seq.append beforeIndex
This function takes a sequence of elements (e.g. a string) and returns another sequence (e.g. another string) by replacing the element at the specified index with the supplied element.
All these helper functions can be combined to produce a sequence of candidate strings:
chars |> Seq.mapi (fun i _ -> chars |> ReplaceAt i withAlternatives) |> Seq.concat
Of these candidates, a lot will be invalid, but filtering them to find only the valid candidates is fairly easy:
let validCandidates =
entry.ToCharArray()
|> getCandidates
|> Seq.map ToString
|> Seq.filter isValidAndLegible
|> Seq.toList
This final list of candidates is a list instead of a sequence because it enables me to use list pattern matching to deal with the cases of a single, none, or multiple valid alternatives:
match validCandidates with | [head] -> head |> formatEntry | [] -> if entry |> ParseToDigits |> isLegible then entry |> formatEntry |> sprintf "%s ERR" else entry |> formatEntry |> sprintf "%s ILL" | _ -> validCandidates |> formatAlternatives |> sprintf "%s AMB [%s]" (formatEntry entry)
If there's only a single valid alternative, the [head] match is triggered and the decomposed head variable is simply piped to the formatEntry function.
If there's no valid alternative, the [] match is triggered, and the final formatting is then based on whether or not the entry is legible or not.
Finally, if there are multiple candidates, each is formatted and appended as ambiguous alternatives to the original (invalid) input.
In case you'd like to take an even closer look at the code, I've attached the entire solution as a zip file: KataBankOCR.zip (299.62 KB)
Comments
Soon, I'll write a small blog post on one way in which SOLID relates to Functional Programming.
I second 'Real World Functional Programming: With Examples in F# and C#', it's a really great book!
One very minor change to one of your helper-functions could be to use the backward pipe operator when combining the sequences in ReplaceElementAt, that way one doesn't need to have the somewhat confusing reverse append-order. Or simply use seq { } and you won't need the atIndex. See following gist for alternatives:
https://gist.github.com/3049158
Thanks for the alternatives for the ReplaceElementAt function. I was never happy with the readability of my implementation. I like the seq { } alternative best, so I've updated my own source code :)


import logging
from .configurer import Configurer
from .messenger import Messenger
logger = logging.getLogger(__name__)
[docs]class Client:
"""Client is the main class that carries out the task of talking with the
oxD server. The oxD commands are provided as class methods that are called
to send the command to the oxD server via socket.
"""
[docs] def __init__(self, config_location):
"""Constructor of class Client
Args:
config_location (string): The complete path of the location
of the config file. Sample config at
(https://github.com/GluuFederation/oxd-python/blob/master/sample.cfg)
"""
self.config = Configurer(config_location)
self.msgr = Messenger(int(self.config.get("oxd", "port")))
self.authorization_redirect_uri = self.config.get(
"client", "authorization_redirect_uri")
self.oxd_id = None
if self.config.get("oxd", "id"):
self.oxd_id = self.config.get("oxd", "id")
logger.info("Oxd ID found during initialization. Client is"
" already registered with the OpenID Provider")
logger.info("oxd id: %s", self.oxd_id)
# list of optional params that can be passed to the oxd-server
self.opt_params = ["op_host",
"post_logout_redirect_uri",
"client_name",
"client_jwks_uri",
"client_token_endpoint_auth_method",
"client_id",
"client_secret",
"application_type"]
self.opt_list_params = ["grant_types",
"acr_values",
"contacts",
"client_logout_uris",
"client_request_uris",
"client_sector_identifier_uri",
"response_types",
"scope",
"ui_locales",
"claims_locales",
]
def __clear_data(self, response):
"""A private method that verifies that the oxd response is error free
and raises a RuntimeError when it encounters an error
"""
if response.status == "error":
error = "OxD Server Error: {0}\nDescription:{1}".format(
response.data.error, response.data.error_description)
logger.error(error)
raise RuntimeError(error)
elif response.status == "ok":
return response.data
[docs] def register_site(self):
"""Function to register the site and generate a unique ID for the site
Returns:
string: The ID of the site (also called client id) if the
registration is sucessful
Raises:
RuntimeError: If the site registration fails.
"""
if self.oxd_id:
logger.info('Client is already registered. ID: %s', self.oxd_id)
return self.oxd_id
command = {"command": "register_site"}
# add required params for the command
params = {
"authorization_redirect_uri": self.authorization_redirect_uri,
}
# add other optional params if they exist in config
for param in self.opt_params:
if self.config.get("client", param):
value = self.config.get("client", param)
params[param] = value
for param in self.opt_list_params:
if self.config.get("client", param):
value = self.config.get("client", param).split(",")
params[param] = value
command["params"] = params
logger.debug("Sending command `register_site` with params %s",
params)
response = self.msgr.send(command)
logger.debug("Recieved reponse: %s", response)
self.oxd_id = self.__clear_data(response).oxd_id
self.config.set("oxd", "id", self.oxd_id)
logger.info("Site registration successful. Oxd ID: %s", self.oxd_id)
return self.oxd_id
[docs] def get_authorization_url(self, acr_values=None, prompt=None, scope=None,
hd=None):
"""Function to get the authorization url that can be opened in the
browser for the user to provide authorization and authentication
Args:
acr_values (list, optional): acr values in the order of priority
prompt (string, optional): prompt=login is required if you want to
force alter current user session (in case user is already
logged in from site1 and site2 construsts authorization
request and want to force alter current user session)
scope (list, optional): scopes required, takes the one provided
during site registrations by default
hd (string, optional): hosted domain Google OP parameter if you
are using Google as your OpenID Provider.
https://developers.google.com/identity/protocols/OpenIDConnect#hd-param
Returns:
string: The authorization url that the user must access for
authentication and authorization
Raises:
RuntimeError: If the oxD throws an error for any reason.
"""
command = {"command": "get_authorization_url"}
if not self.oxd_id:
self.register_site()
params = {"oxd_id": self.oxd_id}
if scope and isinstance(scope, list):
params["scope"] = scope
if acr_values and isinstance(acr_values, list):
params["acr_values"] = acr_values
if prompt and isinstance(prompt, str):
params["prompt"] = prompt
if hd and isinstance(hd, str):
params["hd"] = hd
command["params"] = params
logger.debug("Sending command `get_authorization_url` with params %s",
params)
response = self.msgr.send(command)
logger.debug("Recieved reponse: %s", response)
return self.__clear_data(response).authorization_url
[docs] def get_tokens_by_code(self, code, state):
"""Function to get access code for getting the user details from the
OP. It is called after the user authorizies by visiting the auth URL.
Args:
code (string): code, parse from the callback URL querystring
state (string): state value parsed from the callback URL
Returns:
NamedTuple: The tokens object with the following data structure::
{
"access_token": "<token string>",
"expires_in": 3600,
"refresh_token": "<token string>",
"id_token": "<token string>",
"id_token_claims":
{
"iss": "https://server.example.com",
"sub": "24400320",
"aud": "s6BhdRkqt3",
"nonce": "n-0S6_WzA2Mj",
"exp":<PHONE_NUMBER>,
"iat":<PHONE_NUMBER>,
"at_hash": "MTIzNDU2Nzg5MDEyMzQ1Ng"
}
}
Since this would be returned as a NamedTuple, it can be accessed
using the dot notation as :obj:`data.access_token`,
:obj:`data.refresh_token`, :obj:`data.id_token`...etc.,
Raises:
RuntimeError: If oxD server throws an error OR if the params code
and scopes are of improper datatype.
"""
command = {"command": "get_tokens_by_code"}
params = {"oxd_id": self.oxd_id}
params["code"] = code
params["state"] = state
command["params"] = params
logger.debug("Sending command `get_tokens_by_code` with params %s",
params)
response = self.msgr.send(command)
logger.debug("Recieved response: %s", response)
return self.__clear_data(response)
[docs] def get_user_info(self, access_token):
"""Function to get the information about the user using the access code
obtained from the OP
Args:
access_token (string): access token from the get_tokens_by_code
function
Returns:
NamedTuple: The user data claims that are returned by the OP.
Refer to the /.well-known/openid-configuration URL of your OP for
the complete list of the claims for different scopes.
Raises:
RuntimeError: If the param access_token is empty OR if the oxD
Server returns an error.
"""
if not access_token:
logger.error("Empty access code sent for get_user_info")
raise RuntimeError("Empty access code")
command = {"command": "get_user_info"}
params = {"oxd_id": self.oxd_id}
params["access_token"] = access_token
command["params"] = params
logger.debug("Sending command `get_user_info` with params %s",
params)
response = self.msgr.send(command)
logger.debug("Recieved reponse: %s", response)
return self.__clear_data(response).claims
[docs] def get_logout_uri(self, id_token_hint=None, post_logout_redirect_uri=None,
state=None, session_state=None):
"""Function to logout the user.
Args:
id_token_hint (string, optional): oxd server will use last used
ID Token, if not provided
post_logout_redirect_uri (string, optional): URI to redirect,
this uri would override the value given in the site-config
state (string, optional): site state
session_state (string, optional): session state
Returns:
string: The URI to which the user must be directed in order to
perform the logout
"""
command = {"command": "get_logout_uri"}
params = {"oxd_id": self.oxd_id}
if id_token_hint:
params["id_token_hint"] = id_token_hint
if post_logout_redirect_uri:
params["post_logout_redirect_uri"] = post_logout_redirect_uri
elif self.config.get("client", "logout_redirect_uri"):
params["post_logout_redirect_uri"] = self.config.get(
"client", "logout_redirect_uri"
)
if state:
params["state"] = state
if session_state:
params["session_state"] = session_state
command["params"] = params
logger.debug("Sending command `get_logout_uri` with params %s", params)
response = self.msgr.send(command)
logger.debug("Recieved response: %s", response)
return self.__clear_data(response).uri
[docs] def update_site_registration(self):
"""Fucntion to update the site's information with OpenID Provider.
This should be called after changing the values in the cfg file.
Returns:
bool: The status for update. True for success and False for failure
"""
command = {"command": "update_site_registration"}
params = {"oxd_id": self.oxd_id,
"authorization_redirect_uri": self.authorization_redirect_uri
}
for param in self.opt_params:
if self.config.get("client", param):
value = self.config.get("client", param)
params[param] = value
for param in self.opt_list_params:
if self.config.get("client", param):
value = self.config.get("client", param).split(",")
params[param] = value
command["params"] = params
logger.debug("Sending `update_site_registration` with params %s",
params)
response = self.msgr.send(command)
logger.debug("Recieved reponse: %s", response)
if response.status == "ok":
return True
else:
return False
[docs] def uma_rs_protect(self, resources):
"""Function to be used in a UMA Resource Server to protect resources.
Args:
resources (list): list of resource to protect
Returns:
bool: The status of the request.
"""
command = {"command": "uma_rs_protect"}
params = {"oxd_id": self.oxd_id,
"resources": []}
if len(resources) < 1:
return False
params["resources"] = resources
command["params"] = params
logger.debug("Sending `uma_rs_protect` with params %s", params)
response = self.msgr.send(command)
logger.debug("Recieved response: %s", response)
if response.status == "ok":
return True
else:
return False
[docs] def uma_rs_check_access(self, rpt, path, http_method):
"""Function to be used in a UMA Resource Server to check access.
Args:
rpt (string): RPT or blank value if absent (not send by RP)
path (string): Path of resource (e.g. for http://rs.com/phones,
/phones should be passed)
http_method (string) - Http method of RP request (GET, POST, PUT,
DELETE)
Returns:
NamedTuple: The access information recieved in the format below.
If the access is granted::
{ "access": "granted" }
If the access is denied with ticket response::
{
"access": "denied",
"www-authenticate_header": "UMA realm='example',
as_uri='https://as.example.com',
error='insufficient_scope',
ticket='016f84e8-f9b9-11e0-bd6f-0021cc6004de'",
"ticket": "016f84e8-f9b9-11e0-bd6f-0021cc6004de"
}
If the access is denied without ticket response::
{ "access": "denied" }
If the resource is not Protected::
{
"error": "invalid_request",
"error_description": "Resource is not protected. Please
protect your resource first with uma_rs_protect
command."
}
"""
command = {"command": "uma_rs_check_access"}
params = {"oxd_id": self.oxd_id,
"rpt": rpt,
"path": path,
"http_method": http_method}
command["params"] = params
logger.debug("Sending command `uma_rs_check_access` with params %s",
params)
response = self.msgr.send(command)
logger.debug("Recieved response: %s", response)
return self.__clear_data(response)
[docs] def uma_rp_get_rpt(self, force_new=False):
"""Function to be used by a UMA Requesting Party to get RPT token.
Args:
force_new (boolean): indicates whether return new RPT, defaults to
false, so oxd server can cache/reuse same RPT
Returns:
String: The RPT token (if recived) or None
"""
command = {"command": "uma_rp_get_rpt"}
params = {"oxd_id": self.oxd_id,
"force_new": force_new
}
command["params"] = params
logger.debug("Sending command `uma_rp_get_rpt` with params %s", params)
response = self.msgr.send(command)
logger.debug("Recieved response: %s", response)
if response.status == "ok":
return str(response.data.rpt)
else:
return None
[docs] def uma_rp_authorize_rpt(self, rpt, ticket):
"""Function to be used by UMA Requesting Party to authorize a RPT token.
Args:
rpt (string): the RPT token to be authorized
ticket (string): the ticket to authorize the token
Returns:
NamedTuple: The server response as an named tuple.
Authorized Response (Success)::
{ "status":"ok" }
Not authorized error::
{
"status": "error",
"data": {
"code": "not_authorized",
"description": "RPT is not authorized"
}
}
Invalid ticket error::
{
"status": "error",
"data": {
"code": "invalid_ticket",
"description": "Ticket is not valid (outdated or not
present on Authorization Server)."
}
}
Invalid rpt error::
{
"status": "error",
"data": {
"code": "invalid_rpt",
"description": "RPT isn't valid (outdated or not
present on Authorization Server)."
}
}
"""
command = {"command": "uma_rp_authorize_rpt"}
params = {"oxd_id": self.oxd_id,
"rpt": rpt,
"ticket": ticket
}
command["params"] = params
logger.debug("Sending command `uma_rp_authorize_rpt` with params %s",
params)
response = self.msgr.send(command)
logger.debug("Recieved response: %s", response)
return response
[docs] def uma_rp_get_gat(self, scopes):
"""Function to be used by UMA Requesting Party to get a GAT.
GAT stands for Gluu Access Token. It is invented by Gluu and is
described here:
https://ox.gluu.org/doku.php?id=uma:oauth2_access_management
Args:
scopes (list): list of strings which describe the scopes
Returns:
string: The GAT token. If error, returns None.
"""
command = {"command": "uma_rp_get_gat"}
params = {"oxd_id": self.oxd_id,
"scopes": scopes
}
command["params"] = params
logger.debug("Sending command `uma_rp_get_gat` with params %s", params)
response = self.msgr.send(command)
logger.debug("Recieved response: %s", response)
if response.status == "ok":
return str(response.data.rpt)
else:
return None


{% extends 'layout-af.html' %} {% block pageTitle %} Armed forces Healthcare {% endblock %} {% block beforeContent %} {% endblock %} {% block content %}
Find mental health information and support for serving personnel, reservists and families.
Mental health problems are common and can affect anyone, including serving personnel, reservists and their families. It is important to get help and support if you or someone you know has mental health problems.
If you need support for a specific mental health condition, you can find information through our A-Z of mental health conditions.
If you want help from a charity, you can:
As a serving person or reservist, you will have access to mental health support no matter where you are based.
There are many people you can talk to, including:
They can talk to you about your mental health and help you get further support if you feel you need it.
If you need to talk to somebody, Combat Stress have a dedicated mental health support helpline for serving personnel and their families.
This provides confidential advice and support and is free to call for serving personnel, family members and carers.
This helpline is available 24 hours a day, 365 days a year on 0800 323 4444.
Togetherall is a mental health support service that provides:
All armed forces serving personnel, reservists, veterans and their families can access these services at any time.
HeadFit for Life is a mental fitness tool designed specifically for the armed forces community.
It provides access to self-help tools that can improve your mood, drive and confidence and help to manage the stress of everyday life.
All serving personnel and mobilised reservists can get mental health treatment and care from military mental health professionals no matter where you are based.
If you need them, your DMS GP may refer you to specialist mental health services. These services are all commissioned by the Ministry of Defence (MoD).
The priority of all specialist mental health services is to return you to your regular armed forces work as quickly and safely as possible.
Departments of Community Mental Health (DCMH) offer specialist outpatient mental health care in the UK. These are military centres dedicated to treating serving personnel with health problems.
Smaller DCMH centres are also available overseas.
Talk to your medical officer if you think you would benefit from attending a DCMH.
Specialist inpatient mental health care services for serving personnel are provided by several NHS trusts across the country.
Service personnel are assessed, stabilised and treated in hospitals as close to their home or parent unit as possible.
{% endblock %}


The Lethal Pickup
What
Lee
never expected, was the fact that his
girlfriend
,
Ivey
, was a
serial murderer
.


Looking forward to helping you achieve your goals!
Originally from a small beach town along Lake Michigan, Kiley has a warm,approachable
coaching style that attracts clients of all ages and lifestyles.
In addition to teaching various types of group fitness classes, Kiley coaches clients
both virtually and in person. She is also the Fitness/Wellness Director for a luxury
healthy living community called The Villas in Manalapan, New Jersey.
Tap here to read more...


Abilities
Water-jet - Squirtle shoots a jet of water from his mouth.
Skull Punch - Squirtle pinches his head and rams the enemy.
Throw - Squirtle attacks the enemy with his whole body.
Bubble Ray - Squirtle fires a stream of bubbles at the enemy.
Retreat - Squirtle hides in a shell and strengthens the defense.
Hydraulic pump - Squirtle attacks the enemy with a powerful jet of water.
Quick spin - Squirtle hides in its shell and attacks, spinning fast.
Bubbles - Squirtle spits bubbles at the enemy.


Fundamental NGX has a few peer dependencies you'll need to add to your project.
npm install --save<EMAIL_ADDRESS>fundamental-ui
npm install --save fundamental-ngx
angular.json
file and add the following to the styles
arrays:
"./node_modules/fundamental-ui/dist/fundamental-ui.css"
Note the path may be different if your CLI configuration is not in the root of your project directory or if you have set a custom root.
Now you're ready to use Fundamental NGX!
Usage >


 Open the folder (click on it).
Step 2: Find the file called 'MainScript'. Open the file (click on it).
Step 3: Click 'Publish', then click 'Deploy'.
Step 4:
Step 5: Copy the link that is shown there. You will need it for the installer script.
(To copy on Windows, hit ctrl-A and then ctrl-C)
(To copy on Mac, hit cmd-A and then cmd-C)
Step 6: Click on the function drop down and select 'PermissionThisApp'. Then click the run icon.
Step 7: This is the step where you give SteamSpace the permissions it needs to store data in your drive.
Click 'Continue' and then on the next screen allow the script to have the access it requests.


A script to perform routine backups of critical website files and database. Highlights include:
Visit https://aklump.github.io/website_backup for full documentation.
cloudy pm-install aklump/website_backup
Create a cron job that executes bin/website_backup at the desired interval, e.g.
0 1 * * * /var/www/mywebsite.org/app/bin/website_backup bu 2>&1 | mail -s "FOO backup"<EMAIL_ADDRESS>In some cases you must override $TMPDIR
, something like this (replacing the path to the temporary directory as appropriates for your situation):
0 1 * * * export TMPDIR="/home/foo/tmp"; /var/www/mywebsite.org/app/bin/website_backup bu 2>&1 | mail -s "FOO backup"<EMAIL_ADDRESS>You must have Cloudy installed on your system to install this package.
The installation script above will generate the following structure where .
is your repository root.
.
├── bin
│ ├── website_backup -> ../opt/website_backup/website_backup.sh
│ └── config
│ ├── website_backup.yml
│ └── website_backup.local.yml
├── opt
│ ├── cloudy
│ └── aklump
│ └── website_backup
└── {public web root}
cloudy pm-update aklump/website_backup
| Filename | Description | VCS |
|---|---|---|
| website_backup.yml | Configuration shared across all server environments: prod, staging, dev | yes |
| website_backup.local.yml | Configuration overrides for a single environment; not version controlled. | no |
./bin/website_backup
If you find this project useful... please consider making a donation.


Welcome to my personal website. My name is Gavin Lim and I am a Senior Electrical Engineering student (Class of 2019) at the University of British Columbia (UBC) in Vancouver, Canada. On this site, you will find the credentials that I have accomplished from my education, internships, and personal projects over the last four years. Any overview of my skillsets are listed below:
Software
Electrical
Mechanical
Bachelor of Applied Science, Electrical Engineering, Sept 2015 - May 2019
Relevant Course Topics:
Bachelor of Science, Honours in Biotechnology, Sept 2010 - May 2015
Hardware Engineer Intern, May 2017 - Sept 2018 (1 yr, 4 months)
Technical Support Intern, May 2016 - Sept 2016 (4 months)
Electrical Lead, Jan 2016 - Present (3 yrs)
If you would like to get in touch, please send me an email and I'll get back to you as soon as I can.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Mollitia neque assumenda ipsam nihil, molestias magnam, recusandae quos quis inventore quisquam velit asperiores, vitae? Reprehenderit soluta, eos quod consequuntur itaque. Nam.
Close ProjectThe UBC Sailbot is a student-run engineering team with a purpose of designing and building unmanned and autonomous robotic sailboats. The team's latest endeavour is the Trans-Atlantic challenge in 2016, where our 5-meter long robotic sailboat, Ada, embarked on a journey across the Atlantic ocean from Newfoundland, Canada to Ireland.
Presently, the team's next challenge is to complete the 2020 Vic-Maui Yatch Race by building a new 5-meter long sailboat named Raye.
Involvement Time: 3 Years
As lead in the Electrical team, my job is to ensure the relevant electrical projects for our next boat, Raye, are progressing in a timely and efficient manner. On top of ensuring that each members are fully engaged with their projects, my duties also include liasing with internal subteams (ie: Mechanical and Software) and external groups (ie: Sponsors and Vendors). My mantle of responsibilities cover the following projects:
Prior to a managerial role, my previous engagements within the Sailbot focused on the extensive development and testing of the team's current power system, which encompasses the solar panels, battery management system, maximum power point tracking module, and Li-Ion batteries. In brief, the purpose of each components are as follows:
In the final phase, Raye will contain four solar panels and ten 18V batteries (final number subject to change depending on power needs vs weight constraint). Each battery requires its own BMS module to ensure consistent and efficient charging and discharging, while each solar PV requires its own MPPT to maximize power generated. For testing purposes, the diagram above shows how the power system will be generally implemented.
Similar to Ada, Raye's solar panels will be placed on its deck. As a result, considerations must be made in regards to the presence of the boat's mast, which in turn will cast a shadow onto the panels. Depending on the time of day, the effect of the shadow can be drastic to the point of reducing the power output by 90%. The figures above showcase testing excursions of the solar panels.
To observe a typical charging characteristic of the power system, the input battery current and battery voltages are measured and logged over time in view of the sun with partial cloudiness.
The figure above provides a reference on the impact of sky conditions on solar panel power output. The key take away from this figure is that cloud cover will cause inconsistent and unreliable power output!
During the initial charging phase, overcast and periodic cloud covers affect the power output generated by the solar panels. This is represented by sharp drops and inconsistent average current output during the first 40 mins. Between the 15 min and 19 min time mark, the amount of irradiation reaching the panels has decreased below a threshold such that zero power output is produced. This event was attributed to the temporary blockage of the sun by clouds. Beyond the 40 min point, the fair weather condition generates a steady current of 5A which led to an increase in battery voltage to 17.6V (nominal: 18V).
The charging plot of the battery system provides an outlook of the impact cloud covers can have on the boat's power system, where brief overcast can have a dramatic impact on the system's ability to generate consistent power. As a result, weather conditions must be considered in the boat's navigation trajectory as a means of ensuring consistent and reliable power generation. With the tested power configuration, the maximum output current generated was 5.3A and the charging time from 16.4V to 17.6V of nominal 18V Li-Ion batteries required approximately 1 hr 40mins.
As part of my senior capstone project in a team of four, I am building a robotic rover with localization and navigation capabilities. Onboard sensors for localization are a Phidgets 3/3/3 High Precision IMU, and a Phidgets GPS, while an Intel NUC housing an i3 processor and an arduino nano will be used for navigation.
Involvement Time: 8 months
Note: To ensure adherence to the NDA and IP agreements with FPInnovations, certain specific details will be omitted from this project portfolio.
The client of the project, FPInnovation, is a non-profit research group aimed at fostering technological innovations in the forest industry.
Within this industry, the quality of a log is proportional to its final economic value. Distribution of hardwood, softwood, and knots are some of features that affect the log's overall quality, which can be determined using CT scanning. Current logistical procedures for determining a tree's quality involves logging and transporting the log to a scanning facility.
The purpose of the rover is to provide a more economical and faster alternative in acheiving log quality measurements. By having a mobile CT scanning rover, logs will not longer need to be transported back to the facility, thereby saving time and resources.
Note: All information depicted in this portfolio were completed by me.
The functional concept of the rover is to perform CT scanning of a log by traversing alongside it in a parallel direction. While the final rendition of the rover within the scope of this capstone project will not house an x-ray producing device (for safety reasons), the positional and navigational expectations will be implemented. To achieve these expectations, the rover will house the following components for positional and navigational capabilities:
The figure below illustrates the ROS computation architecture which shows how the robot's firmware will function. For a quick refresher on ROS concepts, refer here. There are three parts of this architecture:
Focusing on the IMU's role in the Localization System, the rover's acceleration, angular velocity, and linear velocity can be obtained using the device. PhidgetSpatial Precision 3/3/3 High Precision was selected because of its readily available API and company firmware support. The IMU sensor is connected to a ROS node named "IMU_node", which advertises the relevant imu data to a topic named "/imu_data".
Because the Phidget C++ API is not implemented in ROS, a driver is required. The phidgets_imu package (phidgets_imu_node
) contains the required drivers to display the raw linear acceleration and angular velocity in the topic, /imu/data_raw
. To fuse the angular and linear accelerations into orientation information, the imu_filter_madgwick package (/imu_filter_node
) is used, which in turn publishes the fuse data on the imu/data
topic.
ROS contains a native simulation tool made for teaching ROS and ROS packages that is represented by a turtle in the x-y plane. The node, /imu_node
, is used to relay the positional information containing linear and angular velocity from imu/data
to the turtle simulation.
Demonstration of the imu's firmware architecture is presented below, where performing yaw rotations of the imu will cause the turtle in the simulation to turn accordingly:
This project is still ongoing and therefore inconclusive.
This is a relatively simple project that I undertook as a means of spending time with my girlfriend while sharing my love for electronics. What started as a bonding event ended up being an annual tradition that involves the following procedures leading up to the 25th of December:
The resulting sweaters have LEDs mounted on them that flash at a constant frequency and duration. Below is a slideshow of the three sweaters made in 2015, 2016, and 2017 respectively. The slide shows the progression and increasing "finesse" of the sweater's electronics, where in both 2015 and 2016, a single-sided prototype board was used to house the circuit. In 2017, I opted to create my own custom PCB using Altium Designer in order improve the circuit's durability, appearance, and size. For a video demonstration of blinking effects, refer to the end of this portfolio page.
Note: Unfortunately, the 2018 sweater edition was not created due to other engagements during the holidays.
The blinking effect is achieved using a LM555 timer operating in astable mode, which allows the IC to generate a square wave output. In brief, the frequency of this wave is determined by two resistors (R1, R2) and a capactor (C1). Based on LM555 datasheet, the equations below dictate the duty cycle of the squave wave:
$$t_{on} = 0.693 \cdot C_1 \cdot (R_1 + R_2)$$
$$t_{off} = 0.693 \cdot C_1 \cdot R_2$$
$$T = t_{on} + t_{off} = 0.693 \cdot (R_1 + 2R_2) \cdot C_1$$
The respective values of the passive components were chosen below based on component availability:
$$C_1 = 220uF$$
$$R_1 = 1kΩ$$
$$R_2 = 4.7kΩ$$
This results in the following characteristics of the square wave function:
$$t_{on} = 0.869ms$$
$$t_{off} = 0.713ms$$
$$D_{on} = 0.55$$
PCBs were manufactured using OSH Park.


Routegadget 2 (RG2) is a web application for drawing and comparing orienteering routes. It is based on the
original Routegadget developed by Jarkko Ryyppö.
For further details see the
User
Guide and the
RG2 project page.
The latest version of RG2 is available for download
from GitHub.
You can see details of
all RG2
installations here.
This includes an RSS feed that provides details of new RG2 events when they are added.


Unit Name
Unit Description
Position Name
Position Description
Chart Type
Employee Type
edit/delete
First Name*
Last Name*
Employee Title
Location
Email
Phone Number
Fax Number
Extra fields
Save to JSON
Image
Employee Name


WebSocket client early test
Server:
<IP_ADDRESS>:1337
www.devicecloud.co.uk:1337
not connected
Text:
Your browser does not support HTML5 Canvas
username:
Connect
up
left
down
right
status:
disconnected
ping time:
---


MediaPickerController Properties
The MediaPickerController type exposes the following members.
Properties
| Name | Description | |
|---|---|---|
| AccessibilityCustomActions | (Inherited from UIResponder.) | |
| AllowsEditing | (Inherited from UIImagePickerController.) | |
| AllowsImageEditing | (Inherited from UIImagePickerController.) | |
| AutomaticallyAdjustsScrollViewInsets | (Inherited from UIViewController.) | |
| AutomaticallyForwardAppearanceAndRotationMethodsToChildViewControllers | (Inherited from UIViewController.) | |
| BarHideOnSwipeGestureRecognizer | (Inherited from UINavigationController.) | |
| BarHideOnTapGestureRecognizer | (Inherited from UINavigationController.) | |
| BottomLayoutGuide | (Inherited from UIViewController.) | |
| CameraCaptureMode | (Inherited from UIImagePickerController.) | |
| CameraDevice | (Inherited from UIImagePickerController.) | |
| CameraFlashMode | (Inherited from UIImagePickerController.) | |
| CameraOverlayView | (Inherited from UIImagePickerController.) | |
| CameraViewTransform | (Inherited from UIImagePickerController.) | |
| CanBecomeFirstResponder | (Inherited from UIResponder.) | |
| CanResignFirstResponder | (Inherited from UIResponder.) | |
| ChildViewControllers | (Inherited from UIViewController.) | |
| ClassHandle | (Inherited from UIImagePickerController.) | |
| ContentSizeForViewInPopover | (Inherited from UIViewController.) | |
| DebugDescription | (Inherited from NSObject.) | |
| DefinesPresentationContext | (Inherited from UIViewController.) | |
| Delegate | ||
| Description | (Inherited from NSObject.) | |
| DisablesAutomaticKeyboardDismissal | (Inherited from UIViewController.) | |
| EdgesForExtendedLayout | (Inherited from UIViewController.) | |
| EditButtonItem | (Inherited from UIViewController.) | |
| Editing | (Inherited from UIViewController.) | |
| ExtendedLayoutIncludesOpaqueBars | (Inherited from UIViewController.) | |
| ExtensionContext | (Inherited from UIViewController.) | |
| GetAnimationControllerForOperation | (Inherited from UIImagePickerController.) | |
| GetInteractionControllerForAnimationController | (Inherited from UIImagePickerController.) | |
| GetPreferredInterfaceOrientation | (Inherited from UIImagePickerController.) | |
| Handle | (Inherited from NSObject.) | |
| HidesBarsOnSwipe | (Inherited from UINavigationController.) | |
| HidesBarsOnTap | (Inherited from UINavigationController.) | |
| HidesBarsWhenKeyboardAppears | (Inherited from UINavigationController.) | |
| HidesBarsWhenVerticallyCompact | (Inherited from UINavigationController.) | |
| HidesBottomBarWhenPushed | (Inherited from UIViewController.) | |
| ImagePickerControllerDelegate | (Inherited from UIImagePickerController.) | |
| InputAccessoryView | (Inherited from UIResponder.) | |
| InputAccessoryViewController | (Inherited from UIResponder.) | |
| InputView | (Inherited from UIResponder.) | |
| InputViewController | (Inherited from UIResponder.) | |
| InteractivePopGestureRecognizer | (Inherited from UINavigationController.) | |
| InterfaceOrientation | (Inherited from UIViewController.) | |
| IsBeingDismissed | (Inherited from UIViewController.) | |
| IsBeingPresented | (Inherited from UIViewController.) | |
| IsFirstResponder | (Inherited from UIResponder.) | |
| IsMovingFromParentViewController | (Inherited from UIViewController.) | |
| IsMovingToParentViewController | (Inherited from UIViewController.) | |
| IsViewLoaded | (Inherited from UIViewController.) | |
| KeyCommands | (Inherited from UIResponder.) | |
| MediaTypes | (Inherited from UIImagePickerController.) | |
| ModalInPopover | (Inherited from UIViewController.) | |
| ModalPresentationCapturesStatusBarAppearance | (Inherited from UIViewController.) | |
| ModalPresentationStyle | (Inherited from UIViewController.) | |
| ModalTransitionStyle | (Inherited from UIViewController.) | |
| ModalViewController | (Inherited from UIViewController.) | |
| NavigationBar | (Inherited from UINavigationController.) | |
| NavigationBarHidden | (Inherited from UINavigationController.) | |
| NavigationController | (Inherited from UIViewController.) | |
| NavigationControllerDelegate | (Inherited from UIImagePickerController.) | |
| NavigationItem | (Inherited from UIViewController.) | |
| NextResponder | (Inherited from UIResponder.) | |
| NibBundle | (Inherited from UIViewController.) | |
| NibName | (Inherited from UIViewController.) | |
| ParentViewController | (Inherited from UIViewController.) | |
| PopoverPresentationController | (Inherited from UIViewController.) | |
| PreferredContentSize | (Inherited from UIViewController.) | |
| PreferredStatusBarUpdateAnimation | (Inherited from UIViewController.) | |
| PresentationController | (Inherited from UIViewController.) | |
| PresentedViewController | (Inherited from UIViewController.) | |
| PresentingViewController | (Inherited from UIViewController.) | |
| ProvidesPresentationContextTransitionStyle | (Inherited from UIViewController.) | |
| RestorationClass | (Inherited from UIViewController.) | |
| RestorationIdentifier | (Inherited from UIViewController.) | |
| RetainCount | (Inherited from NSObject.) | |
| RotatingFooterView | (Inherited from UIViewController.) | |
| RotatingHeaderView | (Inherited from UIViewController.) | |
| SearchDisplayController | (Inherited from UIViewController.) | |
| ShouldAutomaticallyForwardAppearanceMethods | (Inherited from UIViewController.) | |
| ShouldAutomaticallyForwardRotationMethods | (Inherited from UIViewController.) | |
| ShowsCameraControls | (Inherited from UIImagePickerController.) | |
| SourceType | (Inherited from UIImagePickerController.) | |
| SplitViewController | (Inherited from UIViewController.) | |
| Storyboard | (Inherited from UIViewController.) | |
| SuperHandle | (Inherited from NSObject.) | |
| SupportedInterfaceOrientations | (Inherited from UIImagePickerController.) | |
| TabBarController | (Inherited from UIViewController.) | |
| TabBarItem | (Inherited from UIViewController.) | |
| TextInputContextIdentifier | (Inherited from UIResponder.) | |
| TextInputMode | (Inherited from UIResponder.) | |
| Title | (Inherited from UIViewController.) | |
| Toolbar | (Inherited from UINavigationController.) | |
| ToolbarHidden | (Inherited from UINavigationController.) | |
| ToolbarItems | (Inherited from UIViewController.) | |
| TopLayoutGuide | (Inherited from UIViewController.) | |
| TopViewController | (Inherited from UINavigationController.) | |
| TraitCollection | (Inherited from UIViewController.) | |
| TransitioningDelegate | (Inherited from UIViewController.) | |
| UndoManager | (Inherited from UIResponder.) | |
| UserActivity | (Inherited from UIResponder.) | |
| VideoMaximumDuration | (Inherited from UIImagePickerController.) | |
| VideoQuality | (Inherited from UIImagePickerController.) | |
| View | (Inherited from UIViewController.) | |
| ViewControllers | (Inherited from UINavigationController.) | |
| VisibleViewController | (Inherited from UINavigationController.) | |
| WantsFullScreenLayout | (Inherited from UIViewController.) | |
| WeakDelegate | (Inherited from UINavigationController.) | |
| WeakTransitioningDelegate | (Inherited from UIViewController.) |


content_indexer v0.2.5 ContentIndexer.Indexer
Summary
Indexer is a Genserver that holds the index state - basically a list of index structs that have the filename, tokens and weights
Each time an index struct is added to the server/index the weightings are re-calculated. Since they are stored in memory the index searching is fast
Link to this section Summary
Functions
Adds a new file_name and associated list of tokens to the index
Re calculates all the term_weights on the entire index
Retrieves a list of all the tokens in the entire index
Returns a nested list of all the individual index items containing their file_name and associated tokens with weights
Initialises the Index with an empty list
Resets the index with an empty list
Retrieves the entire index
Link to this section Functions
Adds a new file_name and associated list of tokens to the index
## Parameters
- file_name: String that represents the file that has the content to be indexed
- tokens: List of Strings that are the tokenised content
## Example
iex> ContentIndexer.Indexer.add("test_file.md", ["bread", "butter", "jam", "mustard"])
{:ok,
[%ContentIndexer.Index{file_name: "test_file.md",
term_weights: [{"bread", -0.17328679513998632},
{"butter", -0.17328679513998632}, {"jam", -0.17328679513998632},
{"mustard", -0.17328679513998632}],
tokens: ["bread", "butter", "jam", "mustard"],
uuid: "18693629-bfa9-4ffc-8fe8-ebc0c5c72c7b"}]}
Re calculates all the term_weights on the entire index
## Example
iex>ContentIndexer.Indexer.calculate()
{:ok,
[%ContentIndexer.Index{file_name: "test_file_3.md",
term_weights: [{"orange", 0.0}, {"fruit", 0.0}, {"basket", 0.0},
{"apples", 0.0}], tokens: ["orange", "fruit", "basket", "apples"],
uuid: "2c600089-b35d-4667-a146-4635bd282811"},
%ContentIndexer.Index{file_name: "test_file_2.md",
term_weights: [{"orange", 0.0}, {"fruit", 0.0}, {"basket", 0.0},
{"apples", 0.0}], tokens: ["orange", "fruit", "basket", "apples"],
uuid: "c62c65be-4ac6-46bc-9597-2d70c65fa1a0"},
%ContentIndexer.Index{file_name: "test_file.md",
term_weights: [{"bread", 0.1013662770270411}, {"butter", 0.1013662770270411},
{"jam", 0.1013662770270411}, {"mustard", 0.1013662770270411}],
tokens: ["bread", "butter", "jam", "mustard"],
uuid: "18693629-bfa9-4ffc-8fe8-ebc0c5c72c7b"}]}
Retrieves a list of all the tokens in the entire index
## Example
iex> ContentIndexer.Indexer.corpus_of_tokens
{:ok,
[["orange", "fruit", "basket", "apples"],
["bread", "butter", "jam", "mustard"]]}
Returns a nested list of all the individual index items containing their file_name and associated tokens with weights
## Example
iex> ContentIndexer.Indexer.documents()
{:ok,
[{"test_file_3.md",
[{"orange", 0.0}, {"fruit", 0.0}, {"basket", 0.0}, {"apples", 0.0}]},
{"test_file_2.md",
[{"orange", 0.0}, {"fruit", 0.0}, {"basket", 0.0}, {"apples", 0.0}]},
{"test_file.md",
[{"bread", 0.1013662770270411}, {"butter", 0.1013662770270411},
{"jam", 0.1013662770270411}, {"mustard", 0.1013662770270411}]}]}
Initialises the Index with an empty list
Resets the index with an empty list
Retrieves the entire index


The Snow Queen
by Hans Christian Andersen
The Snow Queen is a fairy tale written by Hans Christian Andersen and first published in 1845. The story centers on the struggle between good and evil as taken on by a little boy and girl, Gerda and Kay.
Source: Andersen, H. C. (1845). The Snow Queen.
- First Story: Which Treats of a Mirror and of the Splinters
- A wicked sprite's creation spreads across the land, affecting the hearts and minds all who come in contact with it.
- Second Story: A Little Boy and a Little Girl
- Years after the creation of the mirror, Kay and Gerda play in their families gardens.
- Third Story: Of the Flower-Garden at the Old Woman’s who Understood Witchcraft
- Kay's grandmother tells the children about the Snow Queen. Kay's behavior is greatly affected by pieces of the troll-mirror. Kay encounters the Snow Queen.
- Fourth Story: The Prince and Princess
- A heartbroken Gerda continues to search for her friend. Gerda visits the palace of the Prince and Princess after receiving some promising news.
- Fifth Story: The Little Robber Maiden
- Gerda's travels are once again interrupted. Gerda receives more news about the whereabouts of Kay.
- Sixth Story: The Lapland Woman and the Finland Woman
- Gerda and her companion make a couple of stops before heading to the Snow Queen's palace.
- Seventh Story: What Took Place in the Palace of the Snow Queen, and What Happened Afterward
- Gerda finally arrives at the Snow Queen's Palace where she finds Kay and his captor on the frozen lake.
- Year Published: 1845
- Language: English
- Country of Origin: Denmark
-
Readability:
- Flesch–Kincaid Level: 5.5
- Word Count: 12,590
- Genre: Fairy Tale/Folk Tale
- Keywords: 19th century literature, fairy tales


RAMADDA Workshop
Section 2: The RAMADDA User Interface
2.0
UI Overview
2.1
Creating, editing and managing content in RAMADDA
2.2
Managing Multiple Entries
2.3
Multiple Views of Content
2.4
Properties (Metadata)
2.5
Search